# 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 TestStrategy(Strategy): """ 一个简单的限价买入策略: 在每根Bar线上,如果当前没有持仓,且没有待处理的买入订单,则尝试下一个限价多单。 如果在当前Bar之前有未成交的买入订单,则撤销该订单。 确保在任意时间点,最多只有一笔限价买入订单在市场中。 """ def __init__(self, context: Any, **parameters: Any): # context 类型提示可以为 BacktestContext super().__init__(context, **parameters) self.trade_volume = 1 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) # 最大持仓量 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_account_cash() # 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.get_pending_orders() # 直接访问模拟器,或者通过context提供接口 if self._last_order_id in pending_orders: success = self.cancel_order(self._last_order_id) # 这里发送的“撤单订单”会被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="BUY", volume=trade_volume, price_type="MARKET", # limit_price=limit_price, submitted_time=bar.datetime ) # 通过上下文发送订单 # trade = self.send_order(order) # if trade: # print( # f"[{bar.datetime}] 策略: 发送并立即成交限价买单 {trade.volume} 股 (open:{bar.open}, close:{bar.close}) (订单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} 已挂单)") order = self.send_order(order) if order: print(f"[{bar.datetime}]发送订单 {order.id}, direction {order.direction}") else: # print(f"[{bar.datetime}] 策略: 当前已有持仓或有未成交订单,不重复下单。") pass