在 GitHub 上查看

AddOn Trailing Stop

移植自 MetaTrader 专家顾问 AddOn_TrailingStop。策略本身不会开仓,只负责为当前净持仓更新跟踪止损。

工作原理

  • 订阅 Level1 数据,实时跟踪最新买价和卖价。
  • 根据品种的小数位数计算点值,使参数表现与 MetaTrader 相同(4/5 位小数 = 0.0001,2/3 位小数 = 0.01)。
  • 持有多头仓位且买价上涨 TrailingStartPips 点时,将内部止损移动到 Bid - TrailingStartPips 点。
  • 只有当新的止损价格至少比上一位置高 TrailingStepPips 点时,才会继续上移。
  • 持有空头仓位且卖价下跌 TrailingStartPips 点时,将内部止损移动到 Ask + TrailingStartPips 点。
  • 只有当新的止损价格至少比上一位置低 TrailingStepPips 点时,才会继续下移。
  • 当当前报价突破跟踪止损时,策略会以市价平掉全部仓位,并重置内部状态。

参数

  • EnableTrailing(默认 true)– 是否启用跟踪止损管理。
  • TrailingStartPips(默认 15)– 触发跟踪的盈利点数。
  • TrailingStepPips(默认 5)– 每次继续移动止损所需的额外盈利点数。
  • MagicNumber(默认 0)– 为兼容 MQL 版本保留的标识,在 StockSharp 中仅用于说明。

说明

  • 需要事先配置 SecurityPortfolio,并接收 Level1 行情。
  • 适合与负责开仓的其他策略配合使用。
  • 所有输入都通过 StrategyParam<T> 暴露,可用于优化或界面调整。
  • 当跟踪止损被触发时发送 BuyMarket/SellMarket 指令,剩余的保护性委托由 StockSharp 自动处理。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// AddOn Trailing Stop strategy: EMA crossover with trailing logic.
/// Buys when fast EMA crosses above slow EMA, sells on cross below.
/// </summary>
public class AddOnTrailingStopStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public AddOnTrailingStopStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_hasPrev)
		{
			if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
				SellMarket();
		}
		else
		{
			if (fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (fastValue < slowValue && Position >= 0)
				SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
		_hasPrev = true;
	}
}