289 lines
7.2 KiB
Markdown
289 lines
7.2 KiB
Markdown
# Strategy Module
|
|
|
|
매매 전략을 관리하고 실행하는 핵심 모듈
|
|
|
|
## 모듈 구조
|
|
|
|
```
|
|
strategy/
|
|
├── __init__.py # 모듈 내보내기
|
|
├── base_strategy.py # BaseStrategy 추상 클래스
|
|
├── signal.py # Signal 데이터 클래스
|
|
├── strategy_manager.py # 전략 관리자 (등록/실행)
|
|
├── indicators.py # 보조지표 (RSI, BB, MACD 등)
|
|
├── scanner.py # 종목 스캔
|
|
├── strategies/ # 구체적 전략 구현
|
|
│ ├── __init__.py
|
|
│ └── day_trading.py # 데이트레이딩 전략
|
|
└── README.md
|
|
```
|
|
|
|
## 사용 방법
|
|
|
|
### 1. 전략 구현
|
|
|
|
`BaseStrategy`를 상속받아 `analyze()` 메서드 구현:
|
|
|
|
```python
|
|
from app.strategy import BaseStrategy, Signal, SignalType
|
|
from datetime import datetime
|
|
import pytz
|
|
|
|
class MyStrategy(BaseStrategy):
|
|
def __init__(self):
|
|
super().__init__(name="MyStrategy", config={"param1": 10})
|
|
|
|
async def analyze(self, stock_code, market_data, portfolio_data=None):
|
|
current_price = market_data.get("current_price")
|
|
|
|
# 매수 조건 체크
|
|
if self._should_buy(market_data):
|
|
return Signal(
|
|
stock_code=stock_code,
|
|
signal_type=SignalType.BUY,
|
|
strategy_name=self.name,
|
|
timestamp=datetime.now(pytz.timezone("Asia/Seoul")),
|
|
price=current_price,
|
|
confidence=0.8,
|
|
reason="매수 조건 충족"
|
|
)
|
|
|
|
return None
|
|
|
|
def _should_buy(self, market_data):
|
|
# 실제 로직 구현
|
|
return True
|
|
```
|
|
|
|
### 2. 전략 등록 및 실행
|
|
|
|
```python
|
|
from app.strategy import strategy_manager
|
|
from app.strategy.strategies import DayTradingStrategy
|
|
|
|
# 전략 인스턴스 생성
|
|
day_trading = DayTradingStrategy(
|
|
name="DayTrading_ORB",
|
|
config={
|
|
"max_positions": 3,
|
|
"stop_loss_pct": 0.025
|
|
}
|
|
)
|
|
|
|
# 전략 등록 (priority 낮을수록 먼저 실행)
|
|
strategy_manager.register_strategy(day_trading, priority=10)
|
|
|
|
# 단일 전략 실행
|
|
signal = await strategy_manager.run_strategy(
|
|
"DayTrading_ORB",
|
|
stock_code="005930",
|
|
market_data={
|
|
"current_price": 75000,
|
|
"minute_chart": [...],
|
|
}
|
|
)
|
|
|
|
if signal:
|
|
print(f"신호 발생: {signal}")
|
|
|
|
# 모든 전략 동시 실행
|
|
signals = await strategy_manager.run_all_strategies(
|
|
stock_code="005930",
|
|
market_data={...},
|
|
parallel=True # 병렬 실행
|
|
)
|
|
|
|
# 신호 충돌 해결
|
|
final_signal = strategy_manager.resolve_conflicts(
|
|
signals,
|
|
method="highest_confidence" # or "priority", "majority_vote"
|
|
)
|
|
```
|
|
|
|
### 3. Signal 데이터 구조
|
|
|
|
```python
|
|
from app.strategy import Signal, SignalType
|
|
from datetime import datetime
|
|
import pytz
|
|
|
|
signal = Signal(
|
|
stock_code="005930",
|
|
signal_type=SignalType.BUY,
|
|
strategy_name="MyStrategy",
|
|
timestamp=datetime.now(pytz.timezone("Asia/Seoul")),
|
|
|
|
# 주문 정보
|
|
quantity=10,
|
|
price=75000, # None = 시장가
|
|
confidence=0.85,
|
|
|
|
# 손익 관리
|
|
stop_loss_price=73000,
|
|
take_profit_price=78000,
|
|
trailing_stop_pct=0.02, # 2% 트레일링
|
|
|
|
# 메타데이터
|
|
reason="RSI 과매도 반등",
|
|
metadata={"rsi": 25, "bb_lower": 74000}
|
|
)
|
|
|
|
# JSON 변환
|
|
data = signal.to_dict()
|
|
```
|
|
|
|
### 4. 보조지표 사용
|
|
|
|
```python
|
|
from app.strategy.indicators import rsi, bollinger_bands, sma, macd, atr
|
|
|
|
# 종가 리스트
|
|
closes = [75000, 75500, 76000, 74500, 75000, ...]
|
|
|
|
# RSI
|
|
rsi_value = rsi(closes, period=14)
|
|
if rsi_value and rsi_value < 30:
|
|
print("과매도 구간")
|
|
|
|
# 볼린저 밴드
|
|
bb = bollinger_bands(closes, period=20, std_mult=2.0)
|
|
if bb:
|
|
upper, middle, lower = bb
|
|
if closes[-1] < lower:
|
|
print("하단 돌파")
|
|
|
|
# SMA
|
|
sma_20 = sma(closes, 20)
|
|
sma_60 = sma(closes, 60)
|
|
if sma_20 and sma_60 and sma_20 > sma_60:
|
|
print("골든 크로스 발생")
|
|
|
|
# ATR
|
|
highs = [76000, 76500, ...]
|
|
lows = [74500, 75000, ...]
|
|
atr_value = atr(highs, lows, closes, period=14)
|
|
```
|
|
|
|
### 5. 종목 스캔
|
|
|
|
```python
|
|
from app.strategy.scanner import Scanner, filter_price_range, filter_volume_min
|
|
|
|
# 스캀너 생성
|
|
scanner = Scanner()
|
|
|
|
# 필터 추가
|
|
scanner.add_filter(filter_price_range(10000, 100000)) # 1만~10만원
|
|
scanner.add_filter(filter_volume_min(100000)) # 최소 거래량 10만주
|
|
|
|
# 커스텀 필터
|
|
def custom_filter(data):
|
|
# 등락률 5% 이상
|
|
return data.get("change_rate", 0) >= 5.0
|
|
|
|
scanner.add_filter(custom_filter)
|
|
|
|
# 스캔 실행
|
|
kospi_codes = ["005930", "000660", "035420", ...] # KOSPI 종목 리스트
|
|
matched = await scanner.scan(kospi_codes, max_concurrent=10)
|
|
|
|
for stock in matched:
|
|
print(f"{stock['stock_name']} ({stock['stock_code']}): "
|
|
f"{stock['current_price']}원, +{stock['change_rate']}%")
|
|
```
|
|
|
|
## 주요 기능
|
|
|
|
### BaseStrategy
|
|
- ✅ 전략 추상화 (템플릿 메서드 패턴)
|
|
- ✅ `analyze()` 구현 강제
|
|
- ✅ `on_signal_executed()` 콜백
|
|
- ✅ `on_error()` 에러 핸들링
|
|
- ✅ 설정 관리 (config)
|
|
- ✅ 활성화/비활성화
|
|
|
|
### StrategyManager
|
|
- ✅ 전략 등록/삭제
|
|
- ✅ 우선순위 관리
|
|
- ✅ 단일/복수 전략 실행
|
|
- ✅ 병렬 실행 지원
|
|
- ✅ 신호 충돌 해결
|
|
- `highest_confidence`: 가장 높은 신뢰도
|
|
- `priority`: 전략 우선순위
|
|
- `majority_vote`: 다수결
|
|
- ✅ 신호 이력 관리
|
|
- ✅ 통계 정보
|
|
|
|
### Signal
|
|
- ✅ 표준화된 신호 데이터 구조
|
|
- ✅ 6가지 신호 타입 (BUY/SELL/HOLD/CLOSE/STOP_LOSS/TAKE_PROFIT)
|
|
- ✅ 손익 관리 필드
|
|
- ✅ 신뢰도 평가
|
|
- ✅ JSON 직렬화
|
|
|
|
### Indicators
|
|
- ✅ SMA (Simple Moving Average)
|
|
- ✅ EMA (Exponential Moving Average)
|
|
- ✅ RSI (Relative Strength Index)
|
|
- ✅ Bollinger Bands
|
|
- ✅ MACD
|
|
- ✅ ATR (Average True Range)
|
|
- ✅ VWAP (Volume Weighted Average Price)
|
|
|
|
### Scanner
|
|
- ✅ 조건 기반 종목 필터링
|
|
- ✅ 동시 다중 종목 스캔
|
|
- ✅ 사전 정의 필터
|
|
- 가격 범위
|
|
- 거래량 최소값
|
|
- 등락률 범위
|
|
- 시가총액 최소값
|
|
- ✅ 커스텀 필터 추가 가능
|
|
|
|
## 기존 코드 마이그레이션
|
|
|
|
기존 `services/` 폴더의 전략 파일들을 새 구조로 변환:
|
|
|
|
| 기존 파일 | 새 위치 |
|
|
|----------|----------|
|
|
| `day_trading_strategy.py` | `strategy/strategies/day_trading.py` |
|
|
| `swing_trading_strategy.py` | `strategy/strategies/swing_trading.py` |
|
|
| `momentum_strategy.py` | `strategy/strategies/momentum.py` |
|
|
|
|
변환 패턴:
|
|
```python
|
|
# 기존
|
|
def check_signal(stock_code, market_data):
|
|
if condition:
|
|
return DayTradeSignal(...)
|
|
return None
|
|
|
|
# 새 구조
|
|
class DayTradingStrategy(BaseStrategy):
|
|
async def analyze(self, stock_code, market_data, portfolio_data=None):
|
|
if condition:
|
|
return Signal(...)
|
|
return None
|
|
```
|
|
|
|
## Phase 2 완료 체크리스트
|
|
|
|
- [x] `strategy/base_strategy.py` 생성
|
|
- [x] `strategy/signal.py` 생성
|
|
- [x] `strategy/strategy_manager.py` 생성
|
|
- [x] `strategy/indicators.py` 생성
|
|
- [x] `strategy/scanner.py` 생성
|
|
- [x] `strategy/strategies/day_trading.py` 예시 생성
|
|
- [x] README 문서 작성
|
|
|
|
## 다음 단계 (Phase 3)
|
|
|
|
- [ ] WebSocket 모듈 분리 (`realtime/`)
|
|
- [ ] 실시간 시세 수신 구조 개선
|
|
- [ ] 장 스케줄 관리
|
|
|
|
---
|
|
|
|
**작성일**: 2026-03-06
|
|
**작성자**: 서영우
|