`from vnpy_ctastrategy import (
CtaTemplate,
BarData,
TradeData,
OrderData,
StopOrder,
BarGenerator,
ArrayManager,
TickData,
)
from vnpy.trader.constant import OrderType, Offset, Direction, Interval
from vnpy_ctastrategy.base import StopOrderStatus
from datetime import time
import numpy as np
import talib
from datetime import datetime, time, timedelta
from vnpy_ctastrategy.backtesting import EngineType
class MA_TEMA_MACD_Strategy_3steps_stop_loss_20250611_RB(CtaTemplate):
"""
基于MA交叉、TEMA方向和MACD指标的组合交易策略
增强版:包含动态止损功能
"""
author = ""
# 参数
ma_short_window = 28
ma_long_window = 44
tema_window = 16
macd_short_window = 12
macd_long_window = 26
macd_signal_window = 9
pos_size = 1
# 止损参数
fixed_stop_loss = 50.0 # 固定止损金额
profit_threshold_1 = 50.0 # 第一阶段盈利阈值
profit_threshold_2 = 100.0 # 第二阶段盈利阈值
tick_size = 1 # 最小变动单位(2跳 = 2 * tick_size)
profit_pullback_ratio = 0.4 # 利润回撤比例(40%)
# 变量
ma_short_value = 0.0
ma_long_value = 0.0
tema_value = 0.0
tema_direction = "flat" # "up", "down", "flat"
macd_diff = 0.0
macd_dea = 0.0
macd_hist = 0.0
prev_ma_short_value = 0.0
prev_ma_long_value = 0.0
prev_tema_value = 0.0
prev_macd_diff = 0.0
prev_macd_dea = 0.0
prev_macd_hist = 0.0
# 止损相关变量
long_entry_price = 0.0
short_entry_price = 0.0
max_profit_long = 0.0 # 多头最大盈利
max_profit_short = 0.0 # 空头最大盈利
stop_loss_stage = 0 # 止损阶段:0=固定止损, 1=移动止损, 2=利润回撤止损
# 延迟操作标志
pending_long_entry = False
pending_short_entry = False
pending_long_exit = False
pending_short_exit = False
parameters = [
"ma_short_window",
"ma_long_window",
"tema_window",
"macd_short_window",
"macd_long_window",
"macd_signal_window",
"pos_size",
"fixed_stop_loss",
"profit_threshold_1",
"profit_threshold_2",
"tick_size",
"profit_pullback_ratio",
]
variables = [
"ma_short_value",
"ma_long_value",
"tema_value",
"tema_direction",
"macd_diff",
"macd_dea",
"macd_hist",
"pending_long_entry",
"pending_short_entry",
"pending_long_exit",
"pending_short_exit",
"long_entry_price",
"short_entry_price",
"max_profit_long",
"max_profit_short",
"stop_loss_stage",
]
"""
集合竞价时间段K线过滤配置
背景问题:
- 8:58:59开始推送tick数据(集合竞价阶段)
- 导致BarGenerator合成异常的8:00小时K线(只包含8:58-8:59两分钟数据)
- 后续还会正常合成9:00、10:00等小时K线
- 其中9:00是以9:00开始的到10:00的一小时k线数据
解决策略:
- BarGenerator正常工作,异常K线依然会生成
- 在on_1h_bar/on_15m_bar中通过bar.datetime时间判断过滤
- 异常K线不进入ArrayManager,不执行策略逻辑
- 保证策略只处理完整、有效的K线数据
会出现的异常k线的bar.datetime:
- 8:00,20:00
"""
#集合竞价前的时间排除在外,这个给小时线和15分钟更新的时候用,
#出现奇怪的k线是因为出现了8:58:59开始推送tick数据导致给了8:00合成了一个8:00开始的小时k线,后面又会合成一个9:00开始的小时k线
#实际上是不会出现这两个K线的,就算合成最早是在10:00
#这里通过判断bar.datime也就是合成出来的k线的初始时间来避免把这个k线放入到array manager(这个负责存储合成出来的k线)
#也就是奇怪的k线依旧合成,但是通过判断bar.datime来避免他们放入array manager和执行on_1hour_bar刷新的逻辑
#只能允许的时间是9:00开始到14:00, 夜盘是21:00到00:00
allowed_times_for_1hour_bar_datetime = [
(time(9, 0, 0), time(14, 59, 0)),
(time(21, 0, 0), time(22, 59, 59)),
]
exclude_tick_times = [
(time(10, 14, 50), time(10, 15, 5)), # 10:14:01 开始禁止交易,确保 10:14 能交易
(time(11, 29, 50), time(11, 30, 5)), # 11:29:01 开始禁止交易,确保 11:29 能交易
(time(14, 59, 50), time(15, 0, 5)), # 14:59:01 开始禁止交易,确保 14:59 能交易
(time(22, 59, 50), time(23, 0, 5)) # 00:59:01 开始禁止交易,确保 00:59 能交易
]
exclude_times = [
(time(10, 14, 5), time(10,15,5)), # 10:14:01 开始禁止交易,确保 10:14 能交易
(time(11, 29, 5), time(11,30,5)), # 11:29:01 开始禁止交易,确保 11:29 能交易
(time(14, 59, 5), time(15,0,5)), # 14:59:01 开始禁止交易,确保 14:59 能交易
(time(22, 59, 5), time(23,0,5)) # 00:59:01 开始禁止交易,确保 00:59 能交易
]
#推迟交易的时间段,如果这里面有信号推迟到后面一根去做
delay_times = [
(time(14, 58, 5), time(15,1,5)),
(time(22, 58, 5), time(23,1,5))
]
#集合竞价前的时间排除在外,这个只给实盘1分钟线更新的时候用
exclude_times_pre_market_auction_for_on_1min_bar = [
(time(8, 50, 0), time(9, 1, 0)),
(time(20, 50, 0), time(21, 1, 0)),
]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
"""构造函数"""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
self.am1h = ArrayManager() # 确保有足够空间计算各指标
self.bg1h = BarGenerator(self.on_bar, 1, self.on_1hour_bar, interval=Interval.HOUR)
# 订单管理列表
self.buy_vt_orderids = []
self.sell_vt_orderids = []
self.short_vt_orderids = []
self.cover_vt_orderids = []
self.tick_count = 0 # 添加tick计数器
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
self.load_bar(40) # 加载足够的历史数据用于指标计算
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
self.load_bar(40)
self.put_event()
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
self.put_event()
def on_tick(self, tick: TickData):
"""
Tick数据更新
"""
self.bg1h.update_tick(tick)
def check_dynamic_stop_loss(self, current_price):
"""
动态止损检查(在tick中调用)
"""
# 多头动态止损
if self.pos > 0 and self.long_entry_price > 0:
current_profit = current_price - self.long_entry_price
self.max_profit_long = max(self.max_profit_long, current_profit)
stop_price = self.calculate_long_stop_price(current_price)
if stop_price > 0 and current_price <= stop_price:
if not self.sell_vt_orderids:
self.sell_vt_orderids = self.sell(current_price, abs(self.pos), True)
stage_name = ["固定止损", "移动止损", "利润回撤止损"][self.stop_loss_stage]
self.write_log(f"触发多头{stage_name}:开仓价={self.long_entry_price},当前价={current_price},"
f"当前盈利={current_profit:.2f},最大盈利={self.max_profit_long:.2f},执行平仓")
# 空头动态止损
elif self.pos < 0 and self.short_entry_price > 0:
current_profit = self.short_entry_price - current_price
self.max_profit_short = max(self.max_profit_short, current_profit)
stop_price = self.calculate_short_stop_price(current_price)
if stop_price > 0 and current_price >= stop_price:
if not self.cover_vt_orderids:
self.cover_vt_orderids = self.cover(current_price, abs(self.pos), True)
stage_name = ["固定止损", "移动止损", "利润回撤止损"][self.stop_loss_stage]
self.write_log(f"触发空头{stage_name}:开仓价={self.short_entry_price},当前价={current_price},"
f"当前盈利={current_profit:.2f},最大盈利={self.max_profit_short:.2f},执行平仓")
def calculate_long_stop_price(self, current_price):
"""
计算多头止损价格
"""
if self.long_entry_price <= 0:
return 0
# 阶段1:固定止损(仅当还未进入更高阶段)
if self.stop_loss_stage == 0 and self.max_profit_long < self.profit_threshold_1:
return self.long_entry_price - self.fixed_stop_loss
# 阶段2:移动止损到开仓价+2跳
if self.profit_threshold_1 <= self.max_profit_long < self.profit_threshold_2:
if self.stop_loss_stage < 1:
self.stop_loss_stage = 1
#self.write_log(f"多头进入移动止损阶段:盈利={current_profit:.2f},止损移至开仓价+2跳")
return self.long_entry_price + (2 * self.tick_size)
# 阶段3:利润回撤40%
if self.max_profit_long >= self.profit_threshold_2:
if self.stop_loss_stage < 2:
self.stop_loss_stage = 2
#self.write_log(f"多头进入利润回撤止损阶段:盈利={current_profit:.2f},最大盈利={self.max_profit_long:.2f},保留60%利润")
pullback_stop_profit = self.max_profit_long * (1 - self.profit_pullback_ratio)
return self.long_entry_price + pullback_stop_profit
return 0 # fallback 防御性返回值
def calculate_short_stop_price(self, current_price):
"""
计算空头止损价格
"""
if self.short_entry_price <= 0:
return 0
# 阶段1:固定止损(仅当还未进入更高阶段)
if self.stop_loss_stage == 0 and self.max_profit_short < self.profit_threshold_1:
return self.short_entry_price + self.fixed_stop_loss
# 阶段2:移动止损到开仓价-2跳
if self.profit_threshold_1 <= self.max_profit_short < self.profit_threshold_2:
if self.stop_loss_stage < 1:
self.stop_loss_stage = 1
#self.write_log(f"空头进入移动止损阶段:盈利={current_profit:.2f},止损移至开仓价-2跳")
return self.short_entry_price - (2 * self.tick_size)
# 阶段3:利润回撤40%
if self.max_profit_short>= self.profit_threshold_2:
if self.stop_loss_stage < 2:
self.stop_loss_stage = 2
#self.write_log(f"空头进入利润回撤止损阶段:盈利={current_profit:.2f},最大盈利={self.max_profit_short:.2f},保留60%利润")
pullback_stop_profit = self.max_profit_short * (1 - self.profit_pullback_ratio)
return self.short_entry_price - pullback_stop_profit
return 0 # fallback 防御性返回值
def check_stop_loss(self, bar: BarData):
"""
检查是否触发止损条件(在bar中调用)
"""
self.check_dynamic_stop_loss(bar.close_price)
def on_bar(self, bar: BarData):
"""
K线数据更新(目前使用1小时K线,这里主要是给bg1h更新)
"""
self.bg1h.update_bar(bar)
# 检查并执行延迟的操作
#如果交易模式不是回测,使用实际的系统时间
if self.cta_engine.get_engine_type() == EngineType.LIVE:
# 使用系统实际时间
self.current_time = datetime.now().time()
elif self.cta_engine.get_engine_type() == EngineType.BACKTESTING:
# 回测模式下使用K线的时间
self.current_time = bar.datetime.time()
if not any(start < self.current_time < end for start, end in self.exclude_times):
self.check_stop_loss(bar)
if not any(start < self.current_time < end for start, end in self.exclude_times_pre_market_auction_for_on_1min_bar):
if self.pending_long_entry:
if self.pos == 0:
# 开多
if not self.buy_vt_orderids:
self.buy_vt_orderids = self.buy(bar.close_price, self.pos_size, True)
self.write_log(f"执行延迟的多头开仓,价格:{bar.close_price}")
elif self.pos < 0:
# 平空开多
if not self.cover_vt_orderids:
self.cover_vt_orderids = self.cover(bar.close_price, abs(self.pos), True)
self.write_log(f"执行延迟的平空,价格:{bar.close_price}")
if not self.buy_vt_orderids:
self.buy_vt_orderids = self.buy(bar.close_price, self.pos_size, True)
self.write_log(f"执行延迟的多头开仓,价格:{bar.close_price}")
self.pending_long_entry = False
elif self.pending_short_entry:
if self.pos == 0:
# 开空
if not self.short_vt_orderids:
self.short_vt_orderids = self.short(bar.close_price, self.pos_size, True)
self.write_log(f"执行延迟的空头开仓,价格:{bar.close_price}")
elif self.pos > 0:
# 平多开空
if not self.sell_vt_orderids:
self.sell_vt_orderids = self.sell(bar.close_price, abs(self.pos), True)
self.write_log(f"执行延迟的平多,价格:{bar.close_price}")
if not self.short_vt_orderids:
self.short_vt_orderids = self.short(bar.close_price, self.pos_size, True)
self.write_log(f"执行延迟的空头开仓,价格:{bar.close_price}")
self.pending_short_entry = False
elif self.pending_long_exit and self.pos > 0:
# 平多
if not self.sell_vt_orderids:
self.sell_vt_orderids = self.sell(bar.close_price, abs(self.pos), True)
self.write_log(f"执行延迟的多头平仓,价格:{bar.close_price}")
self.pending_long_exit = False
elif self.pending_short_exit and self.pos < 0:
# 平空
if not self.cover_vt_orderids:
self.cover_vt_orderids = self.cover(bar.close_price, abs(self.pos), True)
self.write_log(f"执行延迟的空头平仓,价格:{bar.close_price}")
self.pending_short_exit = False
def is_not_in_1h_filter_time(self, bar_time) -> bool:
"""检查是否在过滤时间段内"""
current_time = bar_time.time()
for start_time, end_time in self.allowed_times_for_1hour_bar_datetime:
if start_time <= current_time <= end_time:
return False
return True
# 在需要打印数据的地方添加以下代码
def print_array_last_five(self, am: ArrayManager) -> None:
"""打印 ArrayManager 中最近五个数据点"""
start_idx = max(am.size - 5, 0)
if not am.inited and am.count < 5:
start_idx = am.size - am.count
self.write_log(f"最近{min(5, am.count)}个数据点:")
for i in range(start_idx, am.size):
idx = i - am.size # 转换为负索引
self.write_log(
f"最近五个am1h数据点[{idx}]: open={am.open_array[idx]:.2f}, high={am.high_array[idx]:.2f}, "
f"low={am.low_array[idx]:.2f}, close={am.close_array[idx]:.2f}, "
f"volume={am.volume_array[idx]:.2f}, turnover={am.turnover_array[idx]:.2f}, "
f"open_interest={am.open_interest_array[idx]:.2f}"
)
def on_1hour_bar(self, bar: BarData):
"""
1小时K线数据处理
"""
# 检查是否在过滤时间段内
if self.is_not_in_1h_filter_time(bar.datetime):
#self.write_log(f"过滤小时K线: {bar.datetime} ")
return # 直接返回,不处理
# 更新K线到array manager
self.am1h.update_bar(bar)
if not self.am1h.inited:
return
# self.write_log(f"{datetime.now().time()}触发on_1hour_bar回调")
# self.write_log(f"on_1hour_bar数据({bar.datetime})")
# """打印bar完整数据信息"""
# self.write_log(f"on_1hour_bar数据: symbol={bar.symbol}, exchange={bar.exchange}, datetime={bar.datetime}, "
# f"interval={bar.interval}, volume={bar.volume}, turnover={bar.turnover}, "
# f"open_interest={bar.open_interest}, open_price={bar.open_price}, "
# f"high_price={bar.high_price}, low_price={bar.low_price}, close_price={bar.close_price}")
# self.print_array_last_five(self.am1h)
# 撤销之前的未成交订单
self.cancel_all()
# 检查是否在禁止交易时段
# 保存前一周期的指标值
self.prev_ma_short_value = self.ma_short_value
self.prev_ma_long_value = self.ma_long_value
self.prev_tema_value = self.tema_value
self.prev_macd_diff = self.macd_diff
self.prev_macd_dea = self.macd_dea
self.prev_macd_hist = self.macd_hist
# 计算指标
self.calculate_indicators()
# 判断买入信号
#如果交易模式不是回测,使用实际的系统时间
if self.cta_engine.get_engine_type() != EngineType.BACKTESTING:
# 使用系统实际时间
self.current_time = datetime.now().time()
if any(start < self.current_time < end for start, end in self.delay_times):
is_critical_time=True
else:
is_critical_time=False
#回测模式使用的时间(在on_1hour_bar中14:59:59他的bar.datetime.time()是14:59:59,0:59:59他的bar.datetime.time()是00:00:00)
# 回测模式下使用K线的时间
# 检查当前bar的时间是否为0:00或14:00
else:
is_critical_time = self.current_time == time(0, 0) or bar.datetime.time() == time(14, 0)
# 判断买入信号
if self.check_long_entry_signal():
if is_critical_time:
# 设置延迟标志,实际开仓推迟到下一个bar
self.pending_long_entry = True
self.write_log(f"在快收盘时间点({datetime.now().time()})检测到多头信号,操作推迟到下一个bar")
else:
# 正常开仓
if self.pos == 0:
# 开多
if not self.buy_vt_orderids:
self.buy_vt_orderids = self.buy(bar.close_price, self.pos_size, True)
elif self.pos < 0:
# 平空开多
if not self.cover_vt_orderids:
self.cover_vt_orderids = self.cover(bar.close_price, abs(self.pos), True)
if not self.buy_vt_orderids:
self.buy_vt_orderids = self.buy(bar.close_price, self.pos_size, True)
# 判断卖出信号
elif self.check_short_entry_signal():
if is_critical_time:
# 设置延迟标志,实际开仓推迟到下一个bar
self.pending_short_entry = True
self.write_log(f"在快收盘时间点({datetime.now().time()})检测到空头信号,操作推迟到下一个bar")
else:
# 正常开仓
if self.pos == 0:
# 开空
if not self.short_vt_orderids:
self.short_vt_orderids = self.short(bar.close_price, self.pos_size, True)
elif self.pos > 0:
# 平多开空
if not self.sell_vt_orderids:
self.sell_vt_orderids = self.sell(bar.close_price, abs(self.pos), True)
if not self.short_vt_orderids:
self.short_vt_orderids = self.short(bar.close_price, self.pos_size, True)
# 检查多头平仓信号
elif self.check_long_exit_signal() and self.pos > 0:
if is_critical_time:
# 设置延迟标志,实际平仓推迟到下一个bar
self.pending_long_exit = True
self.write_log(f"在快收盘时间点({datetime.now().time()})检测到多头平仓信号,操作推迟到下一个bar")
else:
# 正常平仓
if not self.sell_vt_orderids:
self.sell_vt_orderids = self.sell(bar.close_price, abs(self.pos), True)
# 检查空头平仓信号
elif self.check_short_exit_signal() and self.pos < 0:
if is_critical_time:
# 设置延迟标志,实际平仓推迟到下一个bar
self.pending_short_exit = True
self.write_log(f"在快收盘时间点({datetime.now().time()})检测到空头平仓信号,操作推迟到下一个bar")
else:
# 正常平仓
if not self.cover_vt_orderids:
self.cover_vt_orderids = self.cover(bar.close_price, abs(self.pos), True)
# 更新界面
self.put_event()
def calculate_indicators(self):
"""
计算相关指标
"""
# 计算移动平均线
ma_short = self.am1h.sma(self.ma_short_window, array=True)
ma_long = self.am1h.sma(self.ma_long_window, array=True)
self.ma_short_value = ma_short[-1]
self.ma_long_value = ma_long[-1]
# 计算TEMA (三重指数移动平均线)
close_array = self.am1h.close_array
self.tema_value = talib.TEMA(close_array, timeperiod=self.tema_window)[-1]
tema_array = talib.TEMA(close_array, timeperiod=self.tema_window)
# 确定TEMA方向
if len(tema_array) >= 3:
# 使用最近的3个值判断方向
if tema_array[-1] > tema_array[-2] and tema_array[-2] > tema_array[-3]:
self.tema_direction = "up"
elif tema_array[-1] < tema_array[-2] and tema_array[-2] < tema_array[-3]:
self.tema_direction = "down"
else:
self.tema_direction = "flat"
# 计算MACD
diff, dea, macd = self.am1h.macd(
self.macd_short_window,
self.macd_long_window,
self.macd_signal_window,
array=True
)
self.macd_diff = diff[-1] # MACD线
self.macd_dea = dea[-1] # 信号线
self.macd_hist = macd[-1] # 柱状图
def check_long_entry_signal(self):
"""
检查多头开仓信号
"""
# 判断5MA是否上穿20MA
ma_crossover = (self.ma_short_value > self.ma_long_value) and (self.prev_ma_short_value <= self.prev_ma_long_value)
# 判断TEMA方向是否向上
tema_up = self.tema_direction == "up"
# 判断MACD柱状图是否由负转正,或MACD线是否上穿信号线
macd_hist_turn_positive = (self.macd_hist > 0) and (self.prev_macd_hist <= 0)
macd_crossover = (self.macd_diff > self.macd_dea) and (self.prev_macd_diff <= self.prev_macd_dea)
# 满足多头开仓条件
return (ma_crossover or tema_up) and (macd_hist_turn_positive or macd_crossover)
def check_short_entry_signal(self):
"""
检查空头开仓信号
"""
# 判断5MA是否下穿20MA
ma_crossunder = (self.ma_short_value < self.ma_long_value) and (self.prev_ma_short_value >= self.prev_ma_long_value)
# 判断TEMA方向是否向下
tema_down = self.tema_direction == "down"
# 判断MACD柱状图是否由正转负,或MACD线是否下穿信号线
macd_hist_turn_negative = (self.macd_hist < 0) and (self.prev_macd_hist >= 0)
macd_crossunder = (self.macd_diff < self.macd_dea) and (self.prev_macd_diff >= self.prev_macd_dea)
# 满足空头开仓条件
return (ma_crossunder or tema_down) and (macd_hist_turn_negative or macd_crossunder)
def check_long_exit_signal(self):
"""
检查多头平仓信号
"""
# 判断5MA是否下穿20MA
ma_crossunder = (self.ma_short_value < self.ma_long_value) and (self.prev_ma_short_value >= self.prev_ma_long_value)
# 判断TEMA方向是否向下
tema_down = self.tema_direction == "down"
# 满足多头平仓条件
return ma_crossunder and tema_down
def check_short_exit_signal(self):
"""
检查空头平仓信号
"""
# 判断5MA是否上穿20MA
ma_crossover = (self.ma_short_value > self.ma_long_value) and (self.prev_ma_short_value <= self.prev_ma_long_value)
# 判断TEMA方向是否向上
tema_up = self.tema_direction == "up"
# 满足空头平仓条件
return ma_crossover and tema_up
def reset_stop_loss_variables(self, direction):
"""
重置止损相关变量
"""
if direction == "long":
self.long_entry_price = 0.0
self.max_profit_long = 0.0
elif direction == "short":
self.short_entry_price = 0.0
self.max_profit_short = 0.0
self.stop_loss_stage = 0
self.write_log(f"重置{direction}止损变量")
def on_order(self, order: OrderData):
"""
订单更新
"""
self.put_event()
def on_trade(self, trade: TradeData):
"""
成交更新
"""
# 更新开仓价格
if trade.direction == Direction.LONG and trade.offset == Offset.OPEN:
# 重置多头相关变量
self.reset_stop_loss_variables("short") # 清除空头变量
self.long_entry_price = trade.price
self.max_profit_long = 0.0
self.stop_loss_stage = 0
self.write_log(f"多头开仓成交,记录开仓价格:{self.long_entry_price}")
elif trade.direction == Direction.SHORT and trade.offset == Offset.OPEN:
# 重置空头相关变量
self.reset_stop_loss_variables("long") # 清除多头变量
self.short_entry_price = trade.price
self.max_profit_short = 0.0
self.stop_loss_stage = 0
self.write_log(f"空头开仓成交,记录开仓价格:{self.short_entry_price}")
elif trade.offset == Offset.CLOSE or trade.offset == Offset.CLOSETODAY or trade.offset == Offset.CLOSEYESTERDAY:
# 平仓时重置对应的开仓价格和相关变量
if trade.direction == Direction.SHORT: # 卖出平多
final_profit = (trade.price - self.long_entry_price) if self.long_entry_price > 0 else 0
self.write_log(f"多头平仓成交,平仓价格:{trade.price},最终盈利:{final_profit:.2f}")
self.reset_stop_loss_variables("long")
elif trade.direction == Direction.LONG: # 买入平空
final_profit = (self.short_entry_price - trade.price) if self.short_entry_price > 0 else 0
self.write_log(f"空头平仓成交,平仓价格:{trade.price},最终盈利:{final_profit:.2f}")
self.reset_stop_loss_variables("short")
self.put_event()
def on_stop_order(self, stop_order: StopOrder):
"""
停止单更新
"""
# 只处理撤销或者触发的停止单委托
if stop_order.status == StopOrderStatus.WAITING:
return
# 移除已经结束的停止单委托号
for buf_orderids in [
self.buy_vt_orderids,
self.sell_vt_orderids,
self.short_vt_orderids,
self.cover_vt_orderids
]:
if stop_order.stop_orderid in buf_orderids:
buf_orderids.remove(stop_order.stop_orderid)`
这个问题是之前这个策略好好的开平都正常,就是有时候会出现这种情况,请问各位大神有没有解决的好办法。我查看pos的时候发现pos变更的实现是通过on_trade来实现的。
这个两手的螺纹钢交易所里面是已经成交的了,停止单也说触发了。程序里面的on_trade没有调用到。