늘림목까지 완성성

This commit is contained in:
2026-02-22 21:42:41 +09:00
parent 6a9fef7be8
commit 168e7d016e
10 changed files with 2120 additions and 266 deletions

263
auto_ai_reporter.py Executable file
View File

@@ -0,0 +1,263 @@
#!/usr/bin/env python3
"""
자동 AI 분석 보고서
- 매일 13:00에 최근 10건 거래 분석
- AI가 문제점 진단 및 .env 수정 권장
"""
import os
import sys
import time
import json
import sqlite3
import datetime
import warnings
import requests
from dotenv import load_dotenv
# .env 로드
current_dir = os.path.dirname(os.path.abspath(__file__))
env_file = os.path.join(current_dir, '.env')
load_dotenv(env_file)
# Gemini deprecated 경고 억제
warnings.filterwarnings("ignore", message=".*google.generativeai.*")
# Gemini API
try:
import google.generativeai as genai
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
if GEMINI_API_KEY:
genai.configure(api_key=GEMINI_API_KEY)
model = genai.GenerativeModel('gemini-2.5-flash')
else:
model = None
except Exception as e:
print(f"❌ Gemini 초기화 실패: {e}")
model = None
# Mattermost (Ver2와 동일: mm_config.json + MM_BOT_TOKEN_)
MM_SERVER_URL = os.getenv("MM_SERVER_URL", "https://mattermost.hoonfam.org")
MM_TOKEN = os.environ.get("MM_BOT_TOKEN_", os.getenv("MATTERMOST_TOKEN", "")).strip()
MM_CONFIG_FILE = os.path.join(current_dir, "mm_config.json")
MM_CHANNEL = os.getenv("MATTERMOST_CHANNEL", "stock")
def _load_mm_channels():
try:
if os.path.exists(MM_CONFIG_FILE):
with open(MM_CONFIG_FILE, 'r', encoding='utf-8') as f:
return json.load(f).get("channels", {})
except Exception:
pass
return {}
def send_mm(message):
"""Mattermost 전송"""
channels = _load_mm_channels()
channel_id = channels.get(MM_CHANNEL)
if not channel_id or not MM_TOKEN:
print("❌ MM 설정 없음 (mm_config.json 또는 MM_BOT_TOKEN_)")
return False
api_url = f"{MM_SERVER_URL.rstrip('/')}/api/v4/posts"
headers = {"Authorization": f"Bearer {MM_TOKEN}", "Content-Type": "application/json"}
payload = {"channel_id": channel_id, "message": message}
try:
r = requests.post(api_url, headers=headers, json=payload, timeout=5)
r.raise_for_status()
return True
except Exception as e:
print(f"❌ MM 전송 에러: {e}")
return False
def get_recent_trades(limit=10):
"""최근 거래 N건 조회"""
db_path = os.path.join(os.path.dirname(__file__), 'quant_bot.db')
conn = sqlite3.connect(db_path)
cursor = conn.execute(f"""
SELECT
code, name,
buy_price, sell_price, qty,
profit_rate, realized_pnl,
strategy, sell_reason,
buy_date, sell_date, hold_minutes
FROM trade_history
ORDER BY id DESC
LIMIT {limit}
""")
trades = []
for row in cursor.fetchall():
trades.append({
'code': row[0],
'name': row[1],
'buy_price': row[2],
'sell_price': row[3],
'qty': row[4],
'profit_rate': row[5],
'realized_pnl': row[6],
'strategy': row[7],
'sell_reason': row[8],
'buy_date': row[9],
'sell_date': row[10],
'hold_minutes': row[11]
})
conn.close()
return trades
def get_trade_summary(trades):
"""거래 통계 요약"""
if not trades:
return "거래 없음"
total = len(trades)
wins = sum(1 for t in trades if t['profit_rate'] > 0)
losses = total - wins
win_rate = wins / total * 100 if total > 0 else 0
avg_profit = sum(t['profit_rate'] for t in trades) / total
total_pnl = sum(t['realized_pnl'] for t in trades)
avg_hold = sum(t['hold_minutes'] for t in trades) / total
return f"""
📊 최근 {total}건 거래 통계
- 승률: {win_rate:.1f}% ({wins}{losses}패)
- 평균 수익률: {avg_profit:.2f}%
- 총 손익: {total_pnl:,.0f}
- 평균 보유: {avg_hold:.0f}
"""
def analyze_with_ai(trades):
"""AI로 거래 분석 및 권장사항"""
if not model:
return "❌ AI 분석 불가 (Gemini API 키 없음)"
trades_text = ""
for i, t in enumerate(trades, 1):
trades_text += f"""
[거래 {i}] {t['name']} ({t['strategy']})
- 매수: {t['buy_price']:,.0f}× {t['qty']}
- 매도: {t['sell_price']:,.0f}
- 손익: {t['profit_rate']:+.2f}% ({t['realized_pnl']:,.0f}원)
- 보유: {t['hold_minutes']}
- 사유: {t['sell_reason']}
"""
prompt = f"""당신은 퀀트 트레이딩 전문가입니다.
다음은 최근 {len(trades)}건의 거래 내역입니다:
{trades_text}
{get_trade_summary(trades)}
**당신의 임무:**
1. 문제점 3가지 진단 (구체적으로)
2. .env 수정 권장사항 (변수명=값 형식)
3. 예상 효과
**출력 형식:**
## 🔍 문제점
1. [구체적 문제 1]
2. [구체적 문제 2]
3. [구체적 문제 3]
## 💡 권장 수정사항
```
RSI_OVERHEAT_THRESHOLD=XX
HIGH_PRICE_CHASE_THRESHOLD=X.XX
STOP_LOSS_PCT=-X.XX
...
```
## 📈 예상 효과
- [효과 1]
- [효과 2]
**간결하고 명확하게 답변하세요.**
"""
try:
response = model.generate_content(prompt)
return response.text
except Exception as e:
return f"❌ AI 분석 실패: {e}"
def send_daily_report():
"""13시 정기 보고서"""
print(f"\n{'='*80}")
print(f"🤖 자동 AI 분석 시작: {datetime.datetime.now()}")
print(f"{'='*80}\n")
trades = get_recent_trades(10)
if not trades:
print("❌ 거래 내역 없음")
return
print("🧠 AI 분석 중...")
analysis = analyze_with_ai(trades)
summary = get_trade_summary(trades)
message = f"""🤖 **[13시 AI 자동 분석]**
{summary}
{analysis}
---
💬 명령어 사용법:
- `!env 보기` - 현재 설정 확인
- `!env RSI_OVERHEAT_THRESHOLD=75` - 설정 변경
- `!ai 왜 승률이 낮아?` - AI 질문
"""
if send_mm(message):
print("✅ 보고서 전송 완료")
else:
print("❌ 전송 실패")
def main():
"""메인 루프"""
print("🤖 자동 AI 분석 보고서 시작")
print(f"- 보고 시간: 매일 13:00")
print(f"- 분석 대상: 최근 10건 거래")
print(f"- AI 모델: Gemini 2.5 Flash")
print()
if not model:
print("❌ Gemini API 키가 없습니다.")
return
reported_today = False
main.last_date = datetime.date.today()
while True:
now = datetime.datetime.now()
current_date = now.date()
if main.last_date != current_date:
reported_today = False
print(f"\n📅 날짜 변경: {current_date}")
main.last_date = current_date
if now.hour == 13 and now.minute == 0 and not reported_today:
send_daily_report()
reported_today = True
time.sleep(60)
time.sleep(60)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n👋 종료")
except Exception as e:
print(f"\n\n❌ 에러: {e}")
import traceback
traceback.print_exc()