在 GitHub 上查看
SpaceX 删除止损止盈按钮策略
概述
该策略复刻了 MetaTrader 面板 SpaceX_Delete_StopLoss_TakeProfit_button.mq5 中的 “DELETE SL_TP” 按钮。它作为一个实用工具,扫描投资组合中的持仓,并取消与这些持仓相关的所有止损和止盈保护订单。转换版本基于 StockSharp 的高级 API,帮助交易者在不逐一打开订单的情况下快速清除保护性挂单。
策略本身不会开仓或平仓,只会在收到指令后清理已有持仓的保护订单。因此非常适合人工管理仓位、或通过其他自动化系统建仓的交易者,在需要时一键撤除所有止损、止盈。
原始 EA 行为
MetaTrader 版本会在图表上创建一个带有 DELETE SL_TP 按钮的对话框。用户点击按钮时,EA 会遍历全部持仓并调用 PositionModify 将止损和止盈设置为零,从而移除所有保护级别,但保持持仓量不变。
核心特性:
- 不会产生新的交易。
- 处理终端中所有品种,不做过滤。
- 仅删除止损和止盈,订单备注及魔术号保持不变。
- 行为仅由图形界面的按钮触发。
StockSharp 实现方式
StockSharp 版本同样专注于移除保护订单,但通过策略参数而不是 GUI 按钮来触发。用户可以在 StockSharp Terminal/Hydra 的参数面板中切换这些参数,或在代码里直接修改。策略适用于任何能够提供止损或止盈订单信息的连接器。
实现提供两种执行方式:
- 启动时自动执行(可选):启用后策略启动时立即移除保护订单。
- 手动触发命令:布尔参数模拟原始按钮。将其设置为
true 后,定时器在下一次轮询时执行清理,并自动将参数恢复为 false。
转换逻辑会对所有识别出的保护性订单调用 CancelOrder,包括止损、止盈以及任何条件单。持仓数量不会被修改。
参数
| 名称 |
说明 |
默认值 |
Run On Start (ApplyOnStart) |
当为 true 时,策略启动后立即移除保护订单。 |
true |
All Securities (AffectAllSecurities) |
是否处理投资组合中所有品种。为 false 时只处理策略绑定的品种。 |
true |
Delete Request (DeleteRequest) |
模拟 MetaTrader 按钮的手动触发开关。设为 true 执行一次清理,完成后自动复位。 |
false |
Polling Interval (s) (PollingIntervalSeconds) |
定时器轮询手动触发参数的时间间隔(秒)。 |
1 |
工作流程
- 启动时验证轮询间隔并启动定时器。
- 如果启用了 Run On Start,立即执行一次清理。
- 每次定时器触发时检查 Delete Request。当参数为
true 时,收集所选范围内仍有持仓的品种,并取消其所有保护订单。
- 操作完成后自动将 Delete Request 复位为
false,确保每次触发只执行一次。
识别保护订单
若订单满足以下任意条件,则视为保护订单:
- 订单类型为
Stop、TakeProfit 或 Conditional。
- 订单包含止损价、止盈价或非空的订单条件。
该判断方式覆盖了大多数常见适配器。如果你的连接器使用自定义订单类型,需要相应扩展识别逻辑。
使用建议
- 将策略附加到负责持仓管理的连接器,确保相关持仓对当前投资组合可见。
- 在 Hydra 或 Terminal 的参数面板中勾选 Delete Request 来模拟按钮点击。
- 与其他策略配合使用,例如在重新设置保护订单之前先统一清空旧的止损、止盈。
- 为了获得类似按钮的即时响应,保持较小的轮询间隔(默认 1 秒)。若想减少定时器触发次数,可适当增大该值。
与原 EA 的差异
- 原版通过图表对话框即时响应,StockSharp 版本通过参数与定时器组合实现。
- StockSharp 中止损和止盈通常以独立订单存在,因此转换版本通过取消订单来实现,而不是修改持仓属性。
- 新增了作用范围参数,可仅针对绑定品种操作,这是对原 EA 的扩展。
限制
- 需要连接器能够将止损、止盈作为可取消的订单暴露出来。若经纪商只在服务器端保存保护价格而不生成订单,则无法通过该策略取消。
- 策略不提供 GUI 对话框,所有控制均通过参数完成。
- 仅负责移除保护订单,不会重新设置止损或止盈。
测试
策略不包含独立的自动化测试,因为其逻辑较为简单。建议手动测试:建立示例持仓,附加策略,并在触发后确认所有止损、止盈订单已被取消。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// SpaceX Delete SL/TP strategy: Standard Deviation breakout.
/// Buys when price breaks above upper StdDev band, sells on break below lower.
/// </summary>
public class SpaceXDeleteStopLossTakeProfitButtonStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _stdDevPeriod;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public int StdDevPeriod { get => _stdDevPeriod.Value; set => _stdDevPeriod.Value = value; }
public SpaceXDeleteStopLossTakeProfitButtonStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_smaPeriod = Param(nameof(SmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA period for baseline", "Indicators");
_stdDevPeriod = Param(nameof(StdDevPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("StdDev Period", "Standard Deviation period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var stdDev = new StandardDeviation { Length = StdDevPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, stdDev, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal stdDevValue)
{
if (candle.State != CandleStates.Finished) return;
var upper = smaValue + 2 * stdDevValue;
var lower = smaValue - 2 * stdDevValue;
if (candle.ClosePrice > upper && Position <= 0)
BuyMarket();
else if (candle.ClosePrice < lower && Position >= 0)
SellMarket();
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class space_x_delete_stop_loss_take_profit_button_strategy(Strategy):
def __init__(self):
super(space_x_delete_stop_loss_take_profit_button_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 20).SetGreaterThanZero().SetDisplay("SMA Period", "SMA period for baseline", "Indicators")
self._std_period = self.Param("StdDevPeriod", 20).SetGreaterThanZero().SetDisplay("StdDev Period", "Standard Deviation period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnStarted2(self, time):
super(space_x_delete_stop_loss_take_profit_button_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._sma_period.Value
std = StandardDeviation()
std.Length = self._std_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(sma, std, self.OnProcess).Start()
def OnProcess(self, candle, sma_val, std_val):
if candle.State != CandleStates.Finished:
return
upper = sma_val + 2 * std_val
lower = sma_val - 2 * std_val
close = float(candle.ClosePrice)
if close > upper and self.Position <= 0:
self.BuyMarket()
elif close < lower and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return space_x_delete_stop_loss_take_profit_button_strategy()