- Introduced a `backtest_strategy` endpoint to enable strategy backtesting with user-specified parameters. - Implemented a generic backtesting engine allowing rebalancing, equity curve tracking, and performance metric calculations. - Added `BacktestMixin` for strategies to support backtesting-related operations. - Extended BAA strategy to support backtesting with ticker data download and portfolio simulation. - Updated `urls.py` to include the new backtesting endpoint. - Enhanced logging and error handling throughout the backtesting process.
135 lines
3.8 KiB
Python
135 lines
3.8 KiB
Python
from abc import ABC, abstractmethod
|
|
from typing import Dict, Any, List
|
|
import json
|
|
|
|
import pandas as pd
|
|
|
|
|
|
class BaseQuantStrategy(ABC):
|
|
"""
|
|
퀀트 전략의 기본 클래스
|
|
모든 전략 구현체는 이 클래스를 상속받아야 합니다.
|
|
"""
|
|
|
|
@property
|
|
@abstractmethod
|
|
def name(self) -> str:
|
|
"""전략 이름"""
|
|
pass
|
|
|
|
@property
|
|
@abstractmethod
|
|
def description(self) -> str:
|
|
"""전략 설명"""
|
|
pass
|
|
|
|
@property
|
|
@abstractmethod
|
|
def version(self) -> str:
|
|
"""전략 버전"""
|
|
pass
|
|
|
|
@property
|
|
def default_parameters(self) -> Dict[str, Any]:
|
|
"""기본 파라미터"""
|
|
return {}
|
|
|
|
@abstractmethod
|
|
def execute(self, parameters: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
"""
|
|
전략을 실행합니다.
|
|
|
|
Args:
|
|
parameters: 실행 파라미터
|
|
|
|
Returns:
|
|
실행 결과
|
|
"""
|
|
pass
|
|
|
|
def validate_parameters(self, parameters: Dict[str, Any]) -> bool:
|
|
"""
|
|
파라미터 유효성 검사
|
|
|
|
Args:
|
|
parameters: 검사할 파라미터
|
|
|
|
Returns:
|
|
유효성 검사 결과
|
|
"""
|
|
return True
|
|
|
|
|
|
class BacktestMixin(ABC):
|
|
"""백테스트 가능한 전략을 위한 믹스인 클래스"""
|
|
|
|
@abstractmethod
|
|
def get_backtest_tickers(self, parameters: Dict[str, Any]) -> List[str]:
|
|
"""백테스트에 필요한 모든 티커 목록 반환"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def bulk_download_data(self, tickers: List[str], start_date, end_date) -> Dict[str, pd.Series]:
|
|
"""전체 기간 데이터 일괄 다운로드"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def calculate_portfolio_for_date(self, parameters: Dict[str, Any], as_of_date, data_cache: Dict[str, pd.Series]) -> Dict[str, Any]:
|
|
"""특정 날짜의 포트폴리오 배분 계산 (캐시 데이터 사용)
|
|
|
|
Returns:
|
|
{'mode': str, 'portfolio_weights': dict[str, float]}
|
|
"""
|
|
pass
|
|
|
|
|
|
class StrategyRegistry:
|
|
"""전략 구현체 레지스트리"""
|
|
|
|
_strategies = {}
|
|
|
|
@classmethod
|
|
def register(cls, strategy_class: BaseQuantStrategy):
|
|
"""전략 구현체를 레지스트리에 등록"""
|
|
strategy_instance = strategy_class()
|
|
key = f"{strategy_instance.name}:{strategy_instance.version}"
|
|
cls._strategies[key] = strategy_class
|
|
return strategy_class
|
|
|
|
@classmethod
|
|
def get_strategy(cls, name: str, version: str) -> BaseQuantStrategy:
|
|
"""전략 구현체를 가져옵니다"""
|
|
key = f"{name}:{version}"
|
|
strategy_class = cls._strategies.get(key)
|
|
if strategy_class:
|
|
return strategy_class()
|
|
raise ValueError(f"Strategy {key} not found in registry")
|
|
|
|
@classmethod
|
|
def list_strategies(cls) -> Dict[str, Dict[str, Any]]:
|
|
"""등록된 모든 전략 목록을 반환합니다"""
|
|
strategies = {}
|
|
for key, strategy_class in cls._strategies.items():
|
|
strategy_instance = strategy_class()
|
|
name = strategy_instance.name
|
|
if name not in strategies:
|
|
strategies[name] = {
|
|
'name': name,
|
|
'description': strategy_instance.description,
|
|
'versions': []
|
|
}
|
|
strategies[name]['versions'].append({
|
|
'version': strategy_instance.version,
|
|
'default_parameters': strategy_instance.default_parameters
|
|
})
|
|
return strategies
|
|
|
|
@classmethod
|
|
def get_strategy_key(cls, name: str, version: str) -> str:
|
|
"""전략 키를 생성합니다"""
|
|
return f"{name}:{version}"
|
|
|
|
|
|
def strategy(cls):
|
|
"""전략 구현체를 자동으로 레지스트리에 등록하는 데코레이터"""
|
|
return StrategyRegistry.register(cls) |