refactor(framework): 框架只保留抽象接口,具体实现移至strategies/shared
- FactorBase/FactorRegistry/FactorCombiner: 因子抽象接口 - SignalGenerator: 信号生成抽象接口 - RiskControl/Position/CallbackHook: 风控抽象接口 - StrategyBase: 策略抽象基类 - Executor/Portfolio: 执行器抽象接口 - ConfigLoader: 配置加载器 - 删除framework/factors/momentum.py(具体实现)
This commit is contained in:
@@ -1,55 +1,119 @@
|
||||
"""
|
||||
执行层抽象设计
|
||||
执行层抽象接口(通用)
|
||||
|
||||
核心组件:
|
||||
- Executor: 执行器抽象基类
|
||||
- BacktestExecutor: 回测执行器
|
||||
- DryRunExecutor: 模拟盘执行器
|
||||
只提供抽象基类和Portfolio数据结构,具体执行器可扩展
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any, Optional, List
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List, Optional
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
|
||||
from framework.risk import Position
|
||||
|
||||
|
||||
@dataclass
|
||||
class Portfolio:
|
||||
"""持仓组合"""
|
||||
positions: Dict[str, Any] # {code: Position}
|
||||
cash: float
|
||||
nav: float
|
||||
trades: List[Any]
|
||||
"""
|
||||
投资组合数据结构(通用)
|
||||
|
||||
def get_total_value(self) -> float:
|
||||
"""获取总价值"""
|
||||
position_value = sum(
|
||||
pos.quantity * pos.current_price
|
||||
for pos in self.positions.values()
|
||||
用于管理持仓集合
|
||||
"""
|
||||
|
||||
def __init__(self, initial_capital: float = 100000):
|
||||
"""初始化投资组合"""
|
||||
self.initial_capital = initial_capital
|
||||
self.cash = initial_capital
|
||||
self.positions: Dict[str, Position] = {}
|
||||
self.trades: List[Dict] = []
|
||||
self._net_value_history: List[float] = []
|
||||
|
||||
def add_position(self, code: str, price: float, quantity: float, time: datetime) -> None:
|
||||
"""添加持仓"""
|
||||
position = Position(
|
||||
code=code,
|
||||
entry_price=price,
|
||||
current_price=price,
|
||||
entry_time=time,
|
||||
quantity=quantity
|
||||
)
|
||||
return self.cash + position_value
|
||||
self.positions[code] = position
|
||||
self.cash -= price * quantity
|
||||
|
||||
self.trades.append({
|
||||
'action': 'BUY',
|
||||
'code': code,
|
||||
'price': price,
|
||||
'quantity': quantity,
|
||||
'time': time
|
||||
})
|
||||
|
||||
def get_position_codes(self) -> List[str]:
|
||||
"""获取持仓代码列表"""
|
||||
return list(self.positions.keys())
|
||||
def remove_position(self, code: str, price: float, time: datetime) -> float:
|
||||
"""移除持仓"""
|
||||
if code not in self.positions:
|
||||
return 0
|
||||
|
||||
position = self.positions[code]
|
||||
profit = (price - position.entry_price) * position.quantity
|
||||
self.cash += price * position.quantity
|
||||
|
||||
del self.positions[code]
|
||||
|
||||
self.trades.append({
|
||||
'action': 'SELL',
|
||||
'code': code,
|
||||
'price': price,
|
||||
'quantity': position.quantity,
|
||||
'time': time,
|
||||
'profit': profit
|
||||
})
|
||||
|
||||
return profit
|
||||
|
||||
def update_prices(self, prices: Dict[str, float]) -> None:
|
||||
"""更新持仓价格"""
|
||||
for code, price in prices.items():
|
||||
if code in self.positions:
|
||||
self.positions[code].current_price = price
|
||||
|
||||
def get_net_value(self) -> float:
|
||||
"""计算净值"""
|
||||
positions_value = sum(
|
||||
pos.current_price * pos.quantity for pos in self.positions.values()
|
||||
)
|
||||
return self.cash + positions_value
|
||||
|
||||
def record_net_value(self) -> None:
|
||||
"""记录当前净值"""
|
||||
self._net_value_history.append(self.get_net_value())
|
||||
|
||||
def get_net_value_series(self) -> pd.Series:
|
||||
"""获取净值序列"""
|
||||
return pd.Series(self._net_value_history)
|
||||
|
||||
def get_weight(self, code: str) -> float:
|
||||
"""计算持仓权重"""
|
||||
if code not in self.positions:
|
||||
return 0
|
||||
|
||||
position_value = self.positions[code].current_price * self.positions[code].quantity
|
||||
return position_value / self.get_net_value()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Portfolio(capital={self.cash:.2f}, positions={len(self.positions)})"
|
||||
|
||||
|
||||
class Executor(ABC):
|
||||
"""
|
||||
执行器抽象基类
|
||||
|
||||
支持不同执行模式:
|
||||
- backtest: 回测模式
|
||||
- dry_run: 模拟盘模式
|
||||
- live: 实盘模式(TODO)
|
||||
所有执行器必须实现execute方法
|
||||
"""
|
||||
|
||||
mode: str = "base"
|
||||
|
||||
def __init__(self, config: Optional[Dict] = None):
|
||||
self._config = config or {}
|
||||
self._portfolio = None
|
||||
def __init__(self, **params):
|
||||
"""初始化执行器参数"""
|
||||
self._params = params
|
||||
|
||||
@abstractmethod
|
||||
def execute(self, signals: pd.DataFrame, data: pd.DataFrame) -> Portfolio:
|
||||
@@ -58,121 +122,71 @@ class Executor(ABC):
|
||||
|
||||
Args:
|
||||
signals: 信号DataFrame
|
||||
data: 价格数据
|
||||
data: OHLCV数据
|
||||
|
||||
Returns:
|
||||
持仓组合
|
||||
Portfolio对象
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_mode(self) -> str:
|
||||
"""获取执行模式"""
|
||||
pass
|
||||
|
||||
@property
|
||||
def portfolio(self) -> Optional[Portfolio]:
|
||||
"""获取当前持仓"""
|
||||
return self._portfolio
|
||||
def __repr__(self) -> str:
|
||||
params_str = ', '.join([f"{k}={v}" for k, v in self._params.items()])
|
||||
return f"{self.__class__.__name__}({params_str})"
|
||||
|
||||
|
||||
class BacktestExecutor(Executor):
|
||||
"""
|
||||
回测执行器
|
||||
回测执行器(通用骨架)
|
||||
|
||||
执行回测逻辑:
|
||||
- 处理信号
|
||||
- 计算净值
|
||||
- 记录交易
|
||||
具体回测逻辑需要在strategies中定制实现
|
||||
"""
|
||||
|
||||
mode = "backtest"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
initial_capital: float = 100000.0,
|
||||
trade_cost: float = 0.001
|
||||
):
|
||||
super().__init__()
|
||||
def __init__(self, initial_capital: float = 100000, trade_cost: float = 0.001):
|
||||
super().__init__(initial_capital=initial_capital, trade_cost=trade_cost)
|
||||
self.initial_capital = initial_capital
|
||||
self.trade_cost = trade_cost
|
||||
|
||||
def execute(self, signals: pd.DataFrame, data: pd.DataFrame) -> Portfolio:
|
||||
"""执行回测"""
|
||||
# 初始化持仓
|
||||
self._portfolio = Portfolio(
|
||||
positions={},
|
||||
cash=self.initial_capital,
|
||||
nav=1.0,
|
||||
trades=[]
|
||||
)
|
||||
"""
|
||||
执行回测(简化版本)
|
||||
|
||||
# 回测逻辑(简化版)
|
||||
result = pd.DataFrame(index=signals.index)
|
||||
result['nav'] = 1.0
|
||||
result['daily_return'] = 0.0
|
||||
完整回测逻辑需要定制实现
|
||||
"""
|
||||
portfolio = Portfolio(self.initial_capital)
|
||||
|
||||
# TODO: 完整回测逻辑迁移
|
||||
# 这里只提供骨架,具体逻辑需要定制实现
|
||||
# 包括:净值计算、交易成本扣除、基准对比等
|
||||
|
||||
return self._portfolio
|
||||
|
||||
def get_mode(self) -> str:
|
||||
return "backtest"
|
||||
return portfolio
|
||||
|
||||
|
||||
class DryRunExecutor(Executor):
|
||||
"""
|
||||
模拟盘执行器
|
||||
Dry-run执行器(通用)
|
||||
|
||||
执行模拟交易:
|
||||
- 模拟下单
|
||||
- 模拟成交
|
||||
- 模拟持仓更新
|
||||
用于模拟运行,不实际执行交易
|
||||
"""
|
||||
|
||||
mode = "dry_run"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
initial_capital: float = 100000.0,
|
||||
simulated_exchange = None
|
||||
):
|
||||
super().__init__()
|
||||
self.initial_capital = initial_capital
|
||||
self.simulated_exchange = simulated_exchange
|
||||
def __init__(self, verbose: bool = True):
|
||||
super().__init__(verbose=verbose)
|
||||
self.verbose = verbose
|
||||
|
||||
def execute(self, signals: pd.DataFrame, data: pd.DataFrame) -> Portfolio:
|
||||
"""执行模拟盘"""
|
||||
# 初始化持仓
|
||||
self._portfolio = Portfolio(
|
||||
positions={},
|
||||
cash=self.initial_capital,
|
||||
nav=1.0,
|
||||
trades=[]
|
||||
)
|
||||
"""模拟执行"""
|
||||
portfolio = Portfolio(100000)
|
||||
|
||||
# 模拟执行逻辑
|
||||
# TODO: 模拟订单执行
|
||||
for date in signals.index:
|
||||
signal = signals.loc[date, 'signal']
|
||||
|
||||
if signal and self.verbose:
|
||||
print(f"[{date}] Signal: {signal}")
|
||||
|
||||
return self._portfolio
|
||||
|
||||
def get_mode(self) -> str:
|
||||
return "dry_run"
|
||||
|
||||
def simulate_order(self, code: str, direction: str, quantity: float, price: float):
|
||||
"""模拟下单"""
|
||||
# 记录模拟订单
|
||||
print(f"[DRY_RUN] {direction} {quantity} {code} @ {price}")
|
||||
|
||||
# 更新持仓
|
||||
if direction == 'BUY':
|
||||
# 模拟买入
|
||||
cost = quantity * price
|
||||
if cost <= self._portfolio.cash:
|
||||
self._portfolio.cash -= cost
|
||||
# TODO: 创建Position对象
|
||||
elif direction == 'SELL':
|
||||
# 模拟卖出
|
||||
if code in self._portfolio.positions:
|
||||
# TODO: 平仓逻辑
|
||||
pass
|
||||
return portfolio
|
||||
|
||||
|
||||
# 导出抽象接口
|
||||
__all__ = ['Portfolio', 'Executor', 'BacktestExecutor', 'DryRunExecutor']
|
||||
Reference in New Issue
Block a user