VeighNa量化社区
你的开源社区量化交易平台 | vn.py | vnpy
Member
avatar
加入于:
帖子: 18
声望: 0

`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没有调用到。
description

Member
avatar
加入于:
帖子: 5498
声望: 334

以策略收到on_trader为准,停止单被触发只是发出超价委托并不是委托成交了。
如果超价委托已成交,但是策略没有更新,可以看一下底层是否抛出报错了

Member
avatar
加入于:
帖子: 18
声望: 0

xiaohe wrote:

以策略收到on_trader为准,停止单被触发只是发出超价委托并不是委托成交了。
如果超价委托已成交,但是策略没有更新,可以看一下底层是否抛出报错了
请问底层抛出报错在哪看

Member
avatar
加入于:
帖子: 5498
声望: 334

如果用脚本启动,就看cmd是否有报错信息输出
如果用station启动,就看【交易】界面是否有报错信息输出
没有输出就说明底层没有报错。那就自己去接口收到成交推送的函数下打印排查吧

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】