在 GitHub 上查看

SampleTrailingstop MT5 策略

概述

SampleTrailingstopMt5Strategy 使用 StockSharp 高级 API 重现 MetaTrader 5 专家顾问 SampleTrailingstop-MT5.mq5。策略持续维护一对突破止损挂单,为成交后的持仓创建独立的保护单,并在出现浮动盈利时启动跟踪止损。所有距离都依据标的的最小报价变动(PriceStep)换算,完全对应原始程序以“点”为单位的设定。

交易逻辑

  1. 数据订阅:订阅 Level1 行情以获得最新买卖价,所有挂单和跟踪止损的更新都依赖这些数据。
  2. 入场挂单
    • 使用 BuyStop 在当前市场价格之上放置买入止损单,仅在上一张买入止损单完成后才会重新挂单。
    • 使用 SellStop 在市场下方放置卖出止损单,与多头方向对称。
    • 两个挂单采用相同的成交量、止损和止盈点距,并设置 1 天后的到期时间,与 MQL 实现保持一致。
  3. 仓位保护
    • 成交后跟踪净头寸和加权平均开仓价。
    • 创建独立的止损(SellStop/BuyStop)与止盈(SellLimit/BuyLimit)委托,让保护单在交易所中保持有效,即使入场挂单被取消或过期。
    • 每当仓位规模或均价变化时,会同步更新保护单的价格与数量。
  4. 跟踪止损
    • 当浮动盈利达到预设点距时,把保护性止损移动到距离当前买价(多头)或卖价(空头)同样的距离。
    • 跟踪止损不会越过开仓价,并且每次更新至少跨越一个最小报价步长。
  5. 仓位跟踪:每笔自己的成交都会更新净头寸数值并重新计算加权平均开仓价,从而正确处理部分成交与反向开仓。

参数

参数 说明
TradeVolume 两个突破止损挂单共用的固定下单量。
TakeProfitPoints 止盈距离(点)。设为 0 可关闭止盈。
StopLossPoints 止损距离(点)。
TrailingStopPoints 跟踪止损距离(点)。设为 0 可关闭跟踪。

行为说明

  • 仅在上一张挂单结束后才会重新下单,与原始 EA 中 CheckPendingOrder 的逻辑一致。
  • 止损、止盈以及跟踪距离均通过 Security.PriceStep 转换成价格值,适用于不同小数位的品种。
  • 当仓位完全平仓后,所有保护单都会被自动撤销,同时内部均价与状态被重置。
  • 策略只需要 Level1 行情,不依赖 K 线或技术指标,从而最大限度贴近原 MQL 模板。

使用步骤

  1. 启动前先指定要交易的品种与投资组合。
  2. 根据品种设置四个公开参数:成交量、止损点距、止盈点距以及跟踪止损距离。
  3. 运行策略后,它会自动维护突破挂单并实时管理仓位保护。
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;
	}
}