[2026-05-18] 슬래시 커맨드 프로젝트로 이동 (git 관리)
This commit is contained in:
@@ -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. 완료
|
||||
리포트 경로와 한 줄 요약을 출력하고 종료한다.
|
||||
@@ -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. 완료
|
||||
분석 요약을 한 줄로 출력하고 종료한다.
|
||||
@@ -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. 완료
|
||||
"봇 시작 완료" 메시지를 출력하고 종료한다.
|
||||
@@ -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 실행" 메시지를 출력하고 종료한다.
|
||||
Reference in New Issue
Block a user