实现简单单品种回测
This commit is contained in:
0
src/strategies/__init__.py
Normal file
0
src/strategies/__init__.py
Normal file
68
src/strategies/base_strategy.py
Normal file
68
src/strategies/base_strategy.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# src/strategies/base_strategy.py
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# 导入核心数据类
|
||||
from ..core_data import Bar, Order, Trade
|
||||
# 导入回测上下文 (注意相对导入路径的变化)
|
||||
from ..backtest_context import BacktestContext
|
||||
|
||||
class Strategy(ABC):
|
||||
"""
|
||||
策略抽象基类。所有具体策略都应继承此类,并实现 on_bar 方法。
|
||||
"""
|
||||
def __init__(self, context: BacktestContext, **parameters: Any):
|
||||
"""
|
||||
初始化策略。
|
||||
|
||||
Args:
|
||||
context (BacktestContext): 回测上下文对象,用于与模拟器和数据管理器交互。
|
||||
**parameters (Any): 策略所需的任何自定义参数。
|
||||
"""
|
||||
self.context = context
|
||||
self.parameters = parameters
|
||||
self.symbol = parameters.get('symbol', "DEFAULT_SYMBOL") # 策略操作的品种
|
||||
self.trade_volume = parameters.get('trade_volume', 100) # 每次下单的数量
|
||||
print(f"策略初始化: {self.__class__.__name__},参数: {self.parameters}")
|
||||
|
||||
@abstractmethod
|
||||
def on_bar(self, bar: Bar):
|
||||
"""
|
||||
每当接收到新的Bar数据时调用。
|
||||
具体策略逻辑在此方法中实现。
|
||||
|
||||
Args:
|
||||
bar (Bar): 当前的Bar数据对象。
|
||||
features (Dict[str, float]): 由数据处理模块计算并传入的特征字典。
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_init(self):
|
||||
"""
|
||||
策略初始化时调用(在回测开始前)。
|
||||
可用于设置初始状态或打印信息。
|
||||
"""
|
||||
print(f"{self.__class__.__name__} 策略初始化回调被调用。")
|
||||
|
||||
def on_trade(self, trade: Trade):
|
||||
"""
|
||||
当模拟器成功执行一笔交易时调用。
|
||||
可用于更新策略内部持仓状态或记录交易。
|
||||
|
||||
Args:
|
||||
trade (Trade): 已完成的交易记录。
|
||||
"""
|
||||
# print(f"策略接收到交易: {trade.direction} {trade.volume} {trade.symbol} @ {trade.price:.2f}")
|
||||
pass # 默认不执行任何操作,具体策略可覆盖
|
||||
|
||||
def on_order_status(self, order: Order, status: str):
|
||||
"""
|
||||
当订单状态更新时调用 (例如,未成交,已提交等)。
|
||||
在简易回测中,可能不会频繁使用。
|
||||
|
||||
Args:
|
||||
order (Order): 相关订单对象。
|
||||
status (str): 订单状态(例如 "FILLED", "PENDING", "CANCELLED")。
|
||||
"""
|
||||
pass # 默认不执行任何操作
|
||||
124
src/strategies/simple_limit_buy_strategy.py
Normal file
124
src/strategies/simple_limit_buy_strategy.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# src/strategies/simple_limit_buy_strategy.py (修改部分)
|
||||
|
||||
from typing import Dict, Any, Optional
|
||||
import pandas as pd
|
||||
|
||||
# 导入 Strategy 抽象基类
|
||||
from .base_strategy import Strategy
|
||||
# 导入核心数据类
|
||||
from ..core_data import Bar, Order, Trade
|
||||
|
||||
|
||||
class SimpleLimitBuyStrategy(Strategy):
|
||||
"""
|
||||
一个简单的限价买入策略:
|
||||
在每根Bar线上,如果当前没有持仓,且没有待处理的买入订单,则尝试下一个限价多单。
|
||||
如果在当前Bar之前有未成交的买入订单,则撤销该订单。
|
||||
确保在任意时间点,最多只有一笔限价买入订单在市场中。
|
||||
"""
|
||||
|
||||
def __init__(self, context: Any, **parameters: Any): # context 类型提示可以为 BacktestContext
|
||||
super().__init__(context, **parameters)
|
||||
self.order_id_counter = 0
|
||||
self.limit_price_factor = parameters.get('limit_price_factor', 0.999) # 限价因子
|
||||
self.max_position = parameters.get('max_position', self.trade_volume * 2) # 最大持仓量
|
||||
|
||||
self._last_order_id: Optional[str] = None # 跟踪上一根Bar发出的订单ID
|
||||
self._current_long_position: int = 0 # 策略内部维护的当前持仓
|
||||
|
||||
def on_init(self):
|
||||
super().on_init()
|
||||
# 确保初始状态正确
|
||||
self._last_order_id = None
|
||||
self._current_long_position = 0 # 或者从模拟器获取初始持仓
|
||||
|
||||
def on_trade(self, trade: Trade):
|
||||
"""
|
||||
当模拟器成功执行一笔交易时调用。
|
||||
更新策略内部持仓状态。
|
||||
"""
|
||||
super().on_trade(trade) # 调用父类方法
|
||||
# 简单起见,这里假设只交易self.symbol
|
||||
if trade.symbol == self.symbol:
|
||||
if trade.direction == "BUY":
|
||||
self._current_long_position += trade.volume
|
||||
elif trade.direction == "SELL": # 可能是平多或开空
|
||||
self._current_long_position -= trade.volume # 卖出量为正值,所以是减
|
||||
|
||||
# 如果成交的是我们之前提交的订单,清空_last_order_id
|
||||
if self._last_order_id == trade.order_id:
|
||||
self._last_order_id = None
|
||||
|
||||
# 打印当前持仓
|
||||
# print(f"[{trade.fill_time}] 策略内部持仓更新: {self.symbol} -> {self._current_long_position}")
|
||||
|
||||
def on_bar(self, bar: Bar):
|
||||
"""
|
||||
每接收到一根Bar时,执行策略逻辑。
|
||||
"""
|
||||
current_portfolio_value = self.context.get_current_portfolio_value(bar)
|
||||
print(f"[{bar.datetime}] Strategy processing Bar. Current close price: {bar.close:.2f}. Current Portfolio Value: {current_portfolio_value:.2f}")
|
||||
|
||||
# 1. 撤销上一根K线未成交的订单
|
||||
if self._last_order_id:
|
||||
# 检查这个订单是否仍然在待处理订单列表中
|
||||
pending_orders = self.context._simulator.get_pending_orders() # 直接访问模拟器,或者通过context提供接口
|
||||
if self._last_order_id in pending_orders:
|
||||
success = self.context.send_order(Order(id=self._last_order_id, symbol=self.symbol,
|
||||
direction="CANCEL", volume=0,
|
||||
price_type="CANCEL")) # 使用一个特殊Order类型表示撤单
|
||||
# 这里发送的“撤单订单”会被simulator的send_order处理,并调用simulator.cancel_order
|
||||
if success: # simulator.send_order返回Trade或None,这里我们用一个特殊处理
|
||||
# Simulator的send_order返回的是Trade,如果实现撤单,最好Simulator的cancel_order返回bool
|
||||
print(f"[{bar.datetime}] 策略: 成功撤销上一根K线未成交订单 {self._last_order_id}")
|
||||
else:
|
||||
print(f"[{bar.datetime}] 策略: 尝试撤销订单 {self._last_order_id} 失败(可能已成交或不存在)")
|
||||
|
||||
# 无论撤销成功与否,既然我们尝试了撤销,就清除记录
|
||||
self._last_order_id = None
|
||||
else:
|
||||
# 订单不在待处理列表中,说明它可能已经成交了 (在on_trade中已处理)
|
||||
# 或者在上一根K线已经被取消/过期
|
||||
self._last_order_id = None # 清理状态
|
||||
# print(f"[{bar.datetime}] 订单 {self._last_order_id} 不在待处理列表,无需撤销。")
|
||||
|
||||
# 2. 判断是否需要下单
|
||||
# 如果当前没有多头持仓,并且没有待处理的买入订单
|
||||
# (注: _last_order_id被清除后,_last_order_id为None表示当前没有待处理的我们发出的买单)
|
||||
current_positions = self.context.get_current_positions()
|
||||
self._current_long_position = current_positions.get(self.symbol, 0) # 从模拟器获取最新持仓
|
||||
|
||||
if self._current_long_position == 0 and self._last_order_id is None: # 确保只有一笔买单
|
||||
# 计算限价价格
|
||||
limit_price = bar.open * self.limit_price_factor
|
||||
trade_volume = self.trade_volume
|
||||
|
||||
# 生成唯一的订单ID
|
||||
order_id = f"{self.symbol}_BUY_{bar.datetime.strftime('%Y%m%d%H%M%S')}_{self.order_id_counter}"
|
||||
self.order_id_counter += 1
|
||||
|
||||
# 创建一个限价多单
|
||||
order = Order(
|
||||
id=order_id,
|
||||
symbol=self.symbol,
|
||||
direction="SELL",
|
||||
volume=trade_volume,
|
||||
price_type="LIMIT",
|
||||
limit_price=limit_price,
|
||||
submitted_time=bar.datetime
|
||||
)
|
||||
|
||||
# 通过上下文发送订单
|
||||
trade = self.context.send_order(order)
|
||||
if trade:
|
||||
print(
|
||||
f"[{bar.datetime}] 策略: 发送并立即成交限价买单 {trade.volume} 股 @ {trade.price:.2f} (订单ID: {order.id})")
|
||||
# 如果立即成交,_last_order_id 仍然保持 None
|
||||
else:
|
||||
# 如果未立即成交,将订单ID记录下来,以便下一根Bar撤销
|
||||
self._last_order_id = order.id
|
||||
print(
|
||||
f"[{bar.datetime}] 策略: 发送限价买单 {trade_volume} 股 @ {limit_price:.2f} (未成交,订单ID: {order.id} 已挂单)")
|
||||
else:
|
||||
# print(f"[{bar.datetime}] 策略: 当前已有持仓或有未成交订单,不重复下单。")
|
||||
pass
|
||||
Reference in New Issue
Block a user