## 배경 "전략이 혼란스럽고 현재와 맞지 않는다"를 조사한 결과, 전략 설계가 아니라 전략을 검증했다는 도구와 파라미터 소스가 어긋나 있었다. ## 파라미터 소스 단일화 - 우선순위가 adaptive > strategy_cfg(DB) > config.py 인데 문서는 맨 마지막인 config.py 를 기술하고 있었다. 11곳이 불일치(BULL RSI 46 vs DB 30, SL -7% vs -6%). - DEFAULT_PARAMS 를 config.py 에 정합 + alembic 0012 로 기존 DB 행 갱신 (운영자가 UI 로 바꾼 값은 보존, 옛 기본값과 같은 행만 갱신, 테이블 가드·멱등) ## 백테스트를 실전 로직에 정합 - calculate_adx_simple 이 정확히 period 배(14.0×) 발산 → Aggressive 게이트 `ADX>25` 가 전 레짐 100% 통과. 전환 빈도 5.6%→2.0%(-65%), 성과 상방 편향 - 백테스트 RSI 는 SMA, 실전은 Wilder → 진입 판정 10~11% 불일치 - us_backtest_engine RSI 1봉 지연 제거 (룩어헤드 없음을 절단 테스트로 증명) - backend/backtest_results/ 25건은 폐기 대상 (§8.4) ## 리스크 가드 복원 - 4% 절대 상한이 try 블록 안에 있어 예외 시 적응형 6~7% 가 통과 → try 밖으로 - 청산을 매수보다 먼저 평가 (보유 종목이 추천에 들면 손절 대신 물타기가 나갔다) - 리스크 가드 4개를 매수 전용화 → MDD -10%·보유한도 도달 시에도 손절 작동 - MDD 갱신 5회 연속 실패 시 신규 진입 차단 fail-safe (청산은 계속 허용) - 스윙 청산이 DT 보유분까지 매도하던 문제, sell_pct 기준 혼재 정정 ## 죽은 코드 제거 (2,695줄) - short_term_strategy, kosdaq_scanner, app/strategy/ 패키지 - app/trading/ + korea_investment_api_adapter (real_market_api 정본은 korea_investment_api.py:1482, adapter 는 import 0건) ## 지표·사이징 단일화 (수치 무변경 증명) - indicators_core.py: 3,274건 비교 최대 오차 0.000e+00 - sizing_core.py: 42,105건 비교 불일치 0 - 미국 전략 상수 13개 config 편입 (hex 까지 동일, 3,168 케이스 전수 비교) ## 테스트 안전 (실 KIS 호출 차단) - 유닛 테스트가 실제 KIS 에 매도 주문을 2회 전송하고 있었다. 리팩터링이 close_stale_positions 의 대상을 _execute_exit → _execute_exit_stale 로 바꿨는데 목이 전자에 걸려 빗나갔다. test_api_endpoints 도 캡처된 싱글톤 참조 누수로 실제 잔고 조회를 타 141초를 소비했다. - backend/tests/conftest.py 신설: 주문·토큰·시세·잔고를 호출 즉시 예외로 차단. 예외는 BaseException 파생(프로덕션 except Exception 폴백에 삼켜지지 않게), 클래스 메서드에 적용(캡처된 참조까지 덮음), DATABASE_URL `_test` 가드 강제 - 전체 유닛 스위트: 365초/6 failed → 11.8초/0 failed (소켓 차단 하 559 passed) ## 신규 (둘 다 플래그 OFF, 백테스트 미검증) - range_trading_strategy.py — SIDEWAYS 담당 전략이 없던 빈 칸. 미배선 - check_bear_buy_signal_v2 — BEAR 4중 AND(발화율 2.3%) → 점수제(12.4%) ## 문서 - CLAUDE.md 를 실제 코드 기준으로 재작성. §0 파라미터 우선순위, §4.1 테스트 규약, §6.4 문서화되지 않았던 진입 차단 게이트 9종, §8.4 백테스트 폐기 근거, §8.5 미해결 19항목 - 루트 md 6개 → 2개(docs/ 이동), 중복 배치 문서 병합, 스테일 worktree 제거 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
283 lines
7.1 KiB
Markdown
283 lines
7.1 KiB
Markdown
# Realtime Module
|
|
|
|
WebSocket 기반 실시간 시세 수신 및 이벤트 처리 모듈
|
|
|
|
> ⚠️ **아래 "4. 실시간 전략 실행" 예제는 더 이상 동작하지 않습니다 (2026-09-27)**
|
|
> 예제가 쓰는 `app.strategy` 패키지(`strategy_manager`, `DayTradingStrategy`)는 죽은 코드로
|
|
> 확인되어 삭제되었습니다. 현재 전략 실행 경로는 `services/swing/scheduler.py` 와
|
|
> `services/daytrading/scheduler.py` 이며, 전략 로직은 `services/mean_reversion_strategy.py`,
|
|
> `services/day_trading_strategy.py` 에 있습니다. `CLAUDE.md` §2 참고.
|
|
|
|
|
|
## 모듈 구조
|
|
|
|
```
|
|
realtime/
|
|
├── __init__.py # 모듈 내보내기
|
|
├── websocket_client.py # KIS WebSocket 클라이언트
|
|
├── event_handler.py # 이벤트 핸들러 관리자
|
|
├── market_scheduler.py # 장 스케줄 관리
|
|
├── examples.py # 사용 예시
|
|
└── README.md
|
|
```
|
|
|
|
## 사용 방법
|
|
|
|
### 1. 기본 WebSocket 연결
|
|
|
|
```python
|
|
from app.realtime import KISWebSocketClient, event_handler, EventType
|
|
from app.realtime.websocket_client import create_websocket_client
|
|
|
|
# 핸들러 등록
|
|
async def on_price_update(event_data):
|
|
print(f"{event_data['stock_code']}: {event_data['current_price']}원")
|
|
|
|
event_handler.register(EventType.PRICE_UPDATE, on_price_update)
|
|
|
|
# WebSocket 연결
|
|
ws_client = create_websocket_client()
|
|
await ws_client.connect()
|
|
|
|
# 종목 구독
|
|
await ws_client.subscribe_price("005930") # 삼성전자
|
|
|
|
# 메시지 수신 시작
|
|
await ws_client.listen()
|
|
```
|
|
|
|
### 2. 이벤트 핸들러
|
|
|
|
```python
|
|
from app.realtime import event_handler, EventType
|
|
|
|
# 특정 이벤트 핸들러
|
|
async def handle_price_update(event_data):
|
|
stock_code = event_data['stock_code']
|
|
price = event_data['current_price']
|
|
print(f"{stock_code}: {price}원")
|
|
|
|
event_handler.register(EventType.PRICE_UPDATE, handle_price_update)
|
|
|
|
# 글로벌 핸들러 (모든 이벤트 수신)
|
|
async def global_handler(event_type, event_data):
|
|
print(f"[이벤트] {event_type.value}: {event_data}")
|
|
|
|
event_handler.register_global(global_handler)
|
|
|
|
# 핸들러 해제
|
|
event_handler.unregister(EventType.PRICE_UPDATE, handle_price_update)
|
|
```
|
|
|
|
### 3. 장 스케줄러
|
|
|
|
```python
|
|
from app.realtime import market_scheduler, MarketStatus
|
|
|
|
# 현재 장 상태 확인
|
|
status = market_scheduler.get_current_status()
|
|
print(f"현재 장 상태: {status}")
|
|
|
|
# 장 개장 여부
|
|
if market_scheduler.is_market_open():
|
|
print("장 개장 중")
|
|
|
|
# 다음 장 시작까지 남은 시간
|
|
seconds = market_scheduler.get_seconds_until_market_open()
|
|
print(f"장 시작까지 {seconds}초 남음")
|
|
|
|
# 콜백 등록
|
|
async def on_market_open():
|
|
print("🔔 장 시작!")
|
|
# WebSocket 연결, 구독 등
|
|
|
|
async def on_market_close():
|
|
print("🔕 장 종료!")
|
|
# WebSocket 연결 해제
|
|
|
|
market_scheduler.register_callbacks(
|
|
on_open=on_market_open,
|
|
on_close=on_market_close
|
|
)
|
|
|
|
# 스케줄러 실행
|
|
await market_scheduler.run()
|
|
```
|
|
|
|
### 4. 실시간 전략 실행
|
|
|
|
```python
|
|
from app.realtime import event_handler, EventType
|
|
from app.strategy import strategy_manager
|
|
from app.trading import order_manager, market_data_provider
|
|
|
|
# 핸들러: 실시간 시세 → 전략 실행 → 주문
|
|
async def on_price_update(event_data):
|
|
stock_code = event_data['stock_code']
|
|
current_price = event_data['current_price']
|
|
|
|
# 시장 데이터 구성
|
|
market_data = {
|
|
"current_price": current_price,
|
|
"minute_chart": await market_data_provider.get_minute_chart(stock_code),
|
|
}
|
|
|
|
# 전략 실행
|
|
signal = await strategy_manager.run_strategy(
|
|
"DayTrading",
|
|
stock_code=stock_code,
|
|
market_data=market_data
|
|
)
|
|
|
|
# 신호 발생 시 주문
|
|
if signal:
|
|
await order_manager.place_order(
|
|
stock_code=signal.stock_code,
|
|
order_type=signal.signal_type.value,
|
|
quantity=10,
|
|
price=signal.price
|
|
)
|
|
|
|
event_handler.register(EventType.PRICE_UPDATE, on_price_update)
|
|
```
|
|
|
|
### 5. 전체 시스템 통합
|
|
|
|
```python
|
|
import asyncio
|
|
from app.realtime import event_handler, EventType, market_scheduler
|
|
from app.realtime.websocket_client import create_websocket_client
|
|
from app.strategy import strategy_manager
|
|
from app.strategy.strategies import DayTradingStrategy
|
|
from app.trading import order_manager, market_data_provider
|
|
|
|
# 1. 전략 등록
|
|
strategy = DayTradingStrategy()
|
|
strategy_manager.register_strategy(strategy)
|
|
|
|
# 2. 이벤트 핸들러
|
|
async def on_price_update(event_data):
|
|
# ... 전략 실행 로직
|
|
pass
|
|
|
|
event_handler.register(EventType.PRICE_UPDATE, on_price_update)
|
|
|
|
# 3. 장 스케줄러
|
|
ws_client = None
|
|
|
|
async def on_market_open():
|
|
global ws_client
|
|
ws_client = create_websocket_client()
|
|
await ws_client.connect()
|
|
|
|
# 관심 종목 구독
|
|
for code in ["005930", "000660"]:
|
|
await ws_client.subscribe_price(code)
|
|
|
|
asyncio.create_task(ws_client.listen())
|
|
|
|
async def on_market_close():
|
|
if ws_client:
|
|
await ws_client.disconnect()
|
|
|
|
market_scheduler.register_callbacks(
|
|
on_open=on_market_open,
|
|
on_close=on_market_close
|
|
)
|
|
|
|
# 4. 실행
|
|
await market_scheduler.run()
|
|
```
|
|
|
|
## 주요 기능
|
|
|
|
### KISWebSocketClient
|
|
- ✅ KIS API WebSocket 연결
|
|
- ✅ 실시간 현재가/호가 수신
|
|
- ✅ 체결 데이터 수신
|
|
- ✅ 종목 구독/구독해제
|
|
- ✅ 자동 재연결
|
|
- ✅ 이벤트 기반 데이터 전달
|
|
|
|
### EventHandler
|
|
- ✅ 7가지 이벤트 타입
|
|
- `PRICE_UPDATE`: 현재가 업데이트
|
|
- `ORDERBOOK_UPDATE`: 호가 업데이트
|
|
- `EXECUTION`: 체결 발생
|
|
- `ORDER_CONFIRM`: 주문 확인
|
|
- `ERROR`: 에러
|
|
- `CONNECTED`: 연결
|
|
- `DISCONNECTED`: 연결 해제
|
|
- ✅ 타입별 핸들러 등록
|
|
- ✅ 글로벌 핸들러 (모든 이벤트)
|
|
- ✅ 비동기 핸들러 실행
|
|
- ✅ 에러 핸들링
|
|
|
|
### MarketScheduler
|
|
- ✅ 4가지 장 상태
|
|
- `PRE_MARKET`: 장 전 (08:00~09:00)
|
|
- `MARKET_OPEN`: 정규장 (09:00~15:30)
|
|
- `AFTER_MARKET`: 장 후 (15:30~18:00)
|
|
- `MARKET_CLOSED`: 휴장
|
|
- ✅ 장 시작/종료 콜백
|
|
- ✅ 남은 시간 계산
|
|
- ✅ 자동 상태 감지
|
|
|
|
## 이벤트 데이터 구조
|
|
|
|
### PRICE_UPDATE
|
|
```python
|
|
{
|
|
"stock_code": "005930",
|
|
"stock_name": "삼성전자",
|
|
"current_price": 75000,
|
|
"change_rate": 1.5,
|
|
"volume": 1000000,
|
|
"timestamp": datetime(...)
|
|
}
|
|
```
|
|
|
|
### EXECUTION
|
|
```python
|
|
{
|
|
"order_id": "0000123456",
|
|
"stock_code": "005930",
|
|
"executed_price": 75000,
|
|
"executed_quantity": 10,
|
|
"timestamp": datetime(...)
|
|
}
|
|
```
|
|
|
|
## 설치 요구사항
|
|
|
|
```bash
|
|
pip install websockets
|
|
```
|
|
|
|
## 사용 예시 파일
|
|
|
|
상세한 예시는 `examples.py` 참고:
|
|
- 기본 실시간 시세 수신
|
|
- 실시간 전략 실행
|
|
- 장 스케줄러 활용
|
|
- 전체 시스템 통합
|
|
|
|
## Phase 3 완료 체크리스트
|
|
|
|
- [x] `realtime/websocket_client.py` 생성
|
|
- [x] `realtime/event_handler.py` 생성
|
|
- [x] `realtime/market_scheduler.py` 생성
|
|
- [x] `realtime/examples.py` 생성
|
|
- [x] README 문서 작성
|
|
|
|
## 다음 단계
|
|
|
|
- [ ] 기존 WebSocket 코드 마이그레이션
|
|
- [ ] API 엔드포인트 통합
|
|
- [ ] 프론트엔드 WebSocket 연결
|
|
|
|
---
|
|
|
|
**작성일**: 2026-03-06
|
|
**작성자**: 서영우
|