From 4697286c8e41ce4ea6d07c75af010560485d24df Mon Sep 17 00:00:00 2001 From: jongjae Date: Mon, 18 May 2026 12:57:46 +0900 Subject: [PATCH] =?UTF-8?q?[2026-05-18]=20=EC=8A=AC=EB=9E=98=EC=8B=9C=20?= =?UTF-8?q?=EC=BB=A4=EB=A7=A8=EB=93=9C=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20(git=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/commands/evening.md | 78 +++++++++++++++++++++++++++++++ .claude/commands/morning.md | 64 ++++++++++++++++++++++++++ .claude/commands/start-bot.md | 71 +++++++++++++++++++++++++++++ .claude/commands/stop-bot.md | 86 +++++++++++++++++++++++++++++++++++ 4 files changed, 299 insertions(+) create mode 100644 .claude/commands/evening.md create mode 100644 .claude/commands/morning.md create mode 100644 .claude/commands/start-bot.md create mode 100644 .claude/commands/stop-bot.md diff --git a/.claude/commands/evening.md b/.claude/commands/evening.md new file mode 100644 index 0000000..dceae57 --- /dev/null +++ b/.claude/commands/evening.md @@ -0,0 +1,78 @@ +# claude_evening — 장 후 분석 및 피드백 + +오늘 매매 결과를 분석하고 `reports/daily/날짜.md`를 생성한다. + +## 실행 순서 + +### 1. 데이터 수집 +```bash +python app/ai/evening.py --print +``` + +### 2. 분석 항목 +수집된 데이터를 바탕으로 다음을 판단한다: + +**오늘 매매 평가** +- 승률·손익 수준이 적절했는가 +- 손절이 제대로 작동했는가 +- AI 부스트 종목 성과 vs 일반 종목 비교 +- 이상 패턴 (연속 손절, 특정 시간대 집중 손실 등) + +**파라미터 조정** (문제가 명확할 때만) +- 연속 손절 3회 이상 → `SL_PCT` 축소 검토 +- 목표가 도달 후 즉시 반락 패턴 → `TP1_PCT` 상향 검토 +- 승률 < 40% 지속 → `STRATEGY_K` 조정 검토 +- 조정이 필요하면 `app/config.py`를 직접 수정 + +### 3. 실전 전환 조건 체크 +`live_ready` 섹션의 5가지 조건을 확인한다: +- 누적 운영 30거래일 이상 +- 최근 30일 승률 > 48% +- 최근 30일 MDD < -10% +- 최근 30일 샤프지수 > 1.0 +- 이번 달 L3 발동 2회 이하 + +**5가지 모두 충족 시:** +```bash +mkdir -p reports/live_ready +``` +`reports/live_ready/날짜_READY.md`를 생성하고 Discord에 🚀 알림을 보낸다. + +### 4. 일일 리포트 저장 +`reports/daily/날짜.md` 형식으로 저장한다: +```markdown +# [날짜] 일일 리포트 + +## 매매 결과 +- 총 매매: N회 / 승 N 패 N (승률 N%) +- 순손익: +N원 + +## 매매 상세 +| 종목 | 진입 | 청산 | 사유 | 손익 | +|------|------|------|------|------| + +## 분석 및 피드백 +(Claude 분석 내용) + +## 파라미터 변경 +(변경했으면 내용, 없으면 "없음") + +## 실전 전환 조건 +| 조건 | 기준 | 현재 | 통과 | +|------|------|------|------| +``` + +### 5. Discord 알림 전송 +```bash +python -c " +import asyncio, json, sys +sys.path.insert(0, '.') +from app.main import load_env; load_env() +from app.monitor.notifier import send +# 분석 결과 요약을 msg에 담아 전송 +asyncio.run(send(msg)) +" +``` + +### 6. 완료 +리포트 경로와 한 줄 요약을 출력하고 종료한다. diff --git a/.claude/commands/morning.md b/.claude/commands/morning.md new file mode 100644 index 0000000..2534f74 --- /dev/null +++ b/.claude/commands/morning.md @@ -0,0 +1,64 @@ +# claude_morning — 장 전 분석 + +오늘 날짜 기준으로 장 전 분석을 수행하고 `data/daily_context.json`을 생성한다. + +## 실행 순서 + +### 1. 데이터 수집 +```bash +python app/ai/morning.py --print +``` +위 명령을 실행해 뉴스 헤드라인과 KIS 시장 데이터를 수집한다. + +### 2. 분석 +수집된 데이터를 바탕으로 다음 항목을 판단한다: +- **시장 분위기**: 강세 / 중립 / 약세 +- **감성 점수**: 0~100 (50=중립, 70이상=강세, 30이하=약세) +- **리스크 레벨**: 낮음 / 보통 / 높음 +- **주목 섹터**: 수급·뉴스 모두 긍정적인 섹터 +- **회피 섹터**: 악재·수급 부진 섹터 +- **boosted_tickers**: 거래량 상위 + 외국인 순매수 겹치는 종목코드 +- **blacklist_tickers**: 악재(횡령·소송·거래정지 등) 종목코드 +- **position_size_multiplier**: 0.5(약세) ~ 1.0(중립) ~ 1.5(강세) +- **trade_allowed**: sentiment_score < 40이면 false + +### 3. daily_context.json 저장 +분석 결과를 `data/daily_context.json`에 저장한다. 형식: +```json +{ + "date": "YYYY-MM-DD", + "generated_at": "HH:MM:SS", + "trade_allowed": true, + "market_sentiment": "중립", + "sentiment_score": 62, + "risk_level": "보통", + "hot_sectors": ["반도체", "2차전지"], + "avoid_sectors": ["금융", "건설"], + "boosted_tickers": ["005930", "000660"], + "blacklist_tickers": [], + "position_size_multiplier": 1.0, + "reason": "50자 이내 시장 요약" +} +``` + +### 4. Discord 알림 전송 +아래 Python 코드를 실행해 분석 결과를 Discord로 전송한다: +```bash +python -c " +import asyncio, json, os, sys +sys.path.insert(0, '.') +from app.main import load_env; load_env() +from app.monitor.notifier import send +ctx = json.load(open('data/daily_context.json', encoding='utf-8')) +hot = ', '.join(ctx.get('hot_sectors', [])) or '없음' +avoid = ', '.join(ctx.get('avoid_sectors', [])) or '없음' +boosted = ', '.join(ctx.get('boosted_tickers', [])) or '없음' +flag = '✅ 거래허용' if ctx.get('trade_allowed', True) else '🚫 거래중단' +msg = f'[장전분석] {ctx[\"date\"]} {ctx.get(\"generated_at\",\"\")}\n시장: {ctx[\"market_sentiment\"]}({ctx[\"sentiment_score\"]}점) | 리스크: {ctx[\"risk_level\"]} | {flag}\n주목 섹터: {hot}\n회피 섹터: {avoid}\n관심 종목: {boosted}\n비중 배율: x{ctx.get(\"position_size_multiplier\",1.0)}\n📝 {ctx.get(\"reason\",\"\")}' +asyncio.run(send(msg)) +print('Discord 전송 완료') +" +``` + +### 5. 완료 +분석 요약을 한 줄로 출력하고 종료한다. diff --git a/.claude/commands/start-bot.md b/.claude/commands/start-bot.md new file mode 100644 index 0000000..7221f2f --- /dev/null +++ b/.claude/commands/start-bot.md @@ -0,0 +1,71 @@ +# start-bot — 매매 봇 백그라운드 시작 + +`app/main.py`를 독립 백그라운드 프로세스로 실행한다. +Claude Code가 종료된 뒤에도 봇은 계속 실행된다. + +## 실행 순서 + +### 1. 기존 봇 전부 종료 (PID 파일 + 프로세스 스캔 병행) +```python +import subprocess, os + +pid_file = r'C:\Users\whdwo\OneDrive\바탕 화면\stockbot_v3\logs\bot.pid' + +# 1-a) PID 파일로 종료 +if os.path.exists(pid_file): + try: + pid = int(open(pid_file).read().strip()) + subprocess.run(['taskkill', '/PID', str(pid), '/F'], capture_output=True) + print(f'PID 파일 종료: {pid}') + except Exception as e: + print(f'PID 파일 종료 실패: {e}') + os.remove(pid_file) + +# 1-b) Get-CimInstance로 잔존 프로세스 스캔해서 모두 종료 +r = subprocess.run( + ['powershell', '-Command', + 'Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like "*app/main.py*" -or $_.CommandLine -like "*app\\main.py*" } | Select-Object -ExpandProperty ProcessId'], + capture_output=True, text=True +) +pids = [p.strip() for p in r.stdout.strip().splitlines() if p.strip().isdigit()] +for pid in pids: + subprocess.run(['taskkill', '/PID', pid, '/F'], capture_output=True) + print(f'잔존 프로세스 종료: {pid}') + +if not pids: + print('실행 중인 봇 없음 — 새로 시작합니다') +``` + +### 2. 봇 시작 + PID 저장 +```python +import subprocess, sys, os + +os.chdir(r'C:\Users\whdwo\OneDrive\바탕 화면\stockbot_v3') +proc = subprocess.Popen( + [sys.executable, 'app/main.py'], + creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP, + stdout=open('logs/bot_stderr.log', 'a', encoding='utf-8'), + stderr=subprocess.STDOUT, + close_fds=True, +) +# PID 파일 저장 (다음 재시작 때 확실히 종료하기 위해) +with open('logs/bot.pid', 'w') as f: + f.write(str(proc.pid)) +print(f'봇 시작 완료 PID={proc.pid}') +``` + +### 3. Discord 시작 알림 +```python +import asyncio, sys, os +sys.path.insert(0, '.') +from app.main import load_env; load_env() +from app.monitor.notifier import send +mode = os.getenv('KIS_MOCK', 'true') +dry = os.getenv('DRY_RUN', 'true') +label = '[모의투자]' if mode == 'true' else '[실거래]' +asyncio.run(send(f'{label} 자동매매 봇 시작 (DRY_RUN={dry})')) +print('Discord 알림 전송 완료') +``` + +### 4. 완료 +"봇 시작 완료" 메시지를 출력하고 종료한다. diff --git a/.claude/commands/stop-bot.md b/.claude/commands/stop-bot.md new file mode 100644 index 0000000..797c28b --- /dev/null +++ b/.claude/commands/stop-bot.md @@ -0,0 +1,86 @@ +# stop-bot — 봇 정산 후 종료 + +보유 포지션 현황 확인 → 오늘 결산 Discord 전송 → 봇 프로세스 종료. +장 중 수동 종료 시 사용. 다음 `/start-bot`으로 깨끗하게 재시작 가능. + +## 실행 순서 + +### 1. 현재 수익 현황 출력 +```bash +python app/ai/status.py +``` + +### 2. 오늘 결산 Discord 전송 +```python +import asyncio, sys, os +sys.path.insert(0, '.') +from app.main import load_env; load_env() +from app.db.models import get_conn +from app.monitor.notifier import send +from datetime import datetime + +today = datetime.now().strftime("%Y-%m-%d") +now_str = datetime.now().strftime("%H:%M") + +with get_conn() as conn: + rows = conn.execute(""" + SELECT pnl FROM trades + WHERE date=? AND exit_time IS NOT NULL + """, (today,)).fetchall() + open_cnt = conn.execute("SELECT COUNT(*) FROM positions").fetchone()[0] + +pnls = [r[0] for r in rows if r[0] is not None] +total = len(pnls) +wins = sum(1 for p in pnls if p > 0) +losses = total - wins +net = sum(pnls) +sign = "+" if net >= 0 else "" + +msg = ( + f"[수동종료] {today} {now_str} 결산\n" + f"체결: {total}회 (승{wins} 패{losses}) | 실현손익: {sign}{net:,.0f}원\n" + f"미청산 포지션: {open_cnt}개 (청산 없이 종료됨)" +) +asyncio.run(send(msg)) +print('Discord 결산 전송 완료') +``` + +### 3. 봇 프로세스 종료 +```python +import subprocess, os + +pid_file = r'C:\Users\whdwo\OneDrive\바탕 화면\stockbot_v3\logs\bot.pid' + +killed = [] + +# PID 파일로 종료 +if os.path.exists(pid_file): + try: + pid = int(open(pid_file).read().strip()) + r = subprocess.run(['taskkill', '/PID', str(pid), '/F'], capture_output=True) + if r.returncode == 0: + killed.append(pid) + except Exception: + pass + os.remove(pid_file) + +# 잔존 프로세스 스캔 +r = subprocess.run( + ['powershell', '-Command', + 'Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like "*app/main.py*" -or $_.CommandLine -like "*app\\main.py*" } | Select-Object -ExpandProperty ProcessId'], + capture_output=True, text=True +) +for pid_str in r.stdout.strip().splitlines(): + if pid_str.strip().isdigit(): + pid = int(pid_str.strip()) + subprocess.run(['taskkill', '/PID', str(pid), '/F'], capture_output=True) + killed.append(pid) + +if killed: + print(f'봇 종료 완료 (PID: {", ".join(str(p) for p in set(killed))})') +else: + print('실행 중인 봇 없음') +``` + +### 4. 완료 +"봇 종료 완료. 재시작하려면 /start-bot 실행" 메시지를 출력하고 종료한다.