在 GitHub 上查看
SampleTrailingstop MT5 策略
概述
SampleTrailingstopMt5Strategy 使用 StockSharp 高级 API 重现 MetaTrader 5 专家顾问 SampleTrailingstop-MT5.mq5。策略持续维护一对突破止损挂单,为成交后的持仓创建独立的保护单,并在出现浮动盈利时启动跟踪止损。所有距离都依据标的的最小报价变动(PriceStep)换算,完全对应原始程序以“点”为单位的设定。
交易逻辑
- 数据订阅:订阅 Level1 行情以获得最新买卖价,所有挂单和跟踪止损的更新都依赖这些数据。
- 入场挂单:
- 使用
BuyStop 在当前市场价格之上放置买入止损单,仅在上一张买入止损单完成后才会重新挂单。
- 使用
SellStop 在市场下方放置卖出止损单,与多头方向对称。
- 两个挂单采用相同的成交量、止损和止盈点距,并设置 1 天后的到期时间,与 MQL 实现保持一致。
- 仓位保护:
- 成交后跟踪净头寸和加权平均开仓价。
- 创建独立的止损(
SellStop/BuyStop)与止盈(SellLimit/BuyLimit)委托,让保护单在交易所中保持有效,即使入场挂单被取消或过期。
- 每当仓位规模或均价变化时,会同步更新保护单的价格与数量。
- 跟踪止损:
- 当浮动盈利达到预设点距时,把保护性止损移动到距离当前买价(多头)或卖价(空头)同样的距离。
- 跟踪止损不会越过开仓价,并且每次更新至少跨越一个最小报价步长。
- 仓位跟踪:每笔自己的成交都会更新净头寸数值并重新计算加权平均开仓价,从而正确处理部分成交与反向开仓。
参数
| 参数 |
说明 |
TradeVolume |
两个突破止损挂单共用的固定下单量。 |
TakeProfitPoints |
止盈距离(点)。设为 0 可关闭止盈。 |
StopLossPoints |
止损距离(点)。 |
TrailingStopPoints |
跟踪止损距离(点)。设为 0 可关闭跟踪。 |
行为说明
- 仅在上一张挂单结束后才会重新下单,与原始 EA 中
CheckPendingOrder 的逻辑一致。
- 止损、止盈以及跟踪距离均通过
Security.PriceStep 转换成价格值,适用于不同小数位的品种。
- 当仓位完全平仓后,所有保护单都会被自动撤销,同时内部均价与状态被重置。
- 策略只需要 Level1 行情,不依赖 K 线或技术指标,从而最大限度贴近原 MQL 模板。
使用步骤
- 启动前先指定要交易的品种与投资组合。
- 根据品种设置四个公开参数:成交量、止损点距、止盈点距以及跟踪止损距离。
- 运行策略后,它会自动维护突破挂单并实时管理仓位保护。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Sample Trailingstop MT5 strategy: EMA + RSI confirmation.
/// Buys when close above EMA and RSI above 50, sells when close below EMA and RSI below 50.
/// </summary>
public class SampleTrailingstopMt5Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public SampleTrailingstopMt5Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal rsiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevClose <= _prevEma && candle.ClosePrice > emaValue && rsiValue > 55 && Position <= 0)
BuyMarket();
else if (_prevClose >= _prevEma && candle.ClosePrice < emaValue && rsiValue < 45 && Position >= 0)
SellMarket();
}
_prevClose = candle.ClosePrice;
_prevEma = emaValue;
_hasPrev = true;
}
}
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 ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class sample_trailingstop_mt5_strategy(Strategy):
def __init__(self):
super(sample_trailingstop_mt5_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._ema_period = self.Param("EmaPeriod", 50)
self._rsi_period = self.Param("RsiPeriod", 21)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
def OnReseted(self):
super(sample_trailingstop_mt5_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(sample_trailingstop_mt5_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, rsi, self._process_candle).Start()
def _process_candle(self, candle, ema_value, rsi_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema_value)
rsi_val = float(rsi_value)
if self._has_prev:
if self._prev_close <= self._prev_ema and close > ema_val and rsi_val > 55 and self.Position <= 0:
self.BuyMarket()
elif self._prev_close >= self._prev_ema and close < ema_val and rsi_val < 45 and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
def CreateClone(self):
return sample_trailingstop_mt5_strategy()