在 GitHub 上查看

Trailing Stop Activation 策略

概述

追踪止损策略 用于管理已持仓的保护性止损位。该策略不生成入场信号,而是在持仓建立后调整止损以锁定利润。

参数

  • TrailingStop – 当价格向持仓方向移动达到该距离(价格单位)后,开始启用追踪止损。
  • StopLoss – 初始止损距离(价格单位),设置为 0 表示禁用。
  • CandleType – 用于跟踪价格的K线类型。

交易规则

  1. 当仓位建立时,如果 StopLoss 大于零,则设置初始止损。
  2. 当浮动盈利超过 TrailingStop 时,止损价格开始跟随市场移动。
  3. 当价格触及追踪止损时,仓位被平掉。
  4. 该策略同时适用于多头和空头仓位。

说明

本策略需要与提供入场信号的其他策略配合使用,专注于通过追踪止损管理退出。

using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy with EMA direction entries and trailing stop management.
/// Enters on EMA direction change, exits via trailing stop.
/// </summary>
public class TrailingStopActivationStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<decimal> _trailingStop;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevEma;
	private decimal _prevPrevEma;
	private int _count;
	private decimal _entryPrice;
	private decimal _stopPrice;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public decimal TrailingStop { get => _trailingStop.Value; set => _trailingStop.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TrailingStopActivationStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period for entries", "Indicator");

		_trailingStop = Param(nameof(TrailingStop), 500m)
			.SetGreaterThanZero()
			.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevEma = 0;
		_prevPrevEma = 0;
		_count = 0;
		_entryPrice = 0;
		_stopPrice = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };

		SubscribeCandles(CandleType)
			.Bind(ema, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;

		// Trailing stop check
		if (Position > 0)
		{
			var trail = close - TrailingStop;
			if (trail > _stopPrice)
				_stopPrice = trail;

			if (candle.LowPrice <= _stopPrice)
			{
				SellMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
		}
		else if (Position < 0)
		{
			var trail = close + TrailingStop;
			if (_stopPrice == 0 || trail < _stopPrice)
				_stopPrice = trail;

			if (candle.HighPrice >= _stopPrice)
			{
				BuyMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
		}

		_count++;

		if (_count < 3)
		{
			_prevPrevEma = _prevEma;
			_prevEma = emaValue;
			return;
		}

		// Entry on EMA direction change
		var turnUp = _prevEma < _prevPrevEma && emaValue > _prevEma;
		var turnDown = _prevEma > _prevPrevEma && emaValue < _prevEma;

		if (turnUp && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_entryPrice = close;
			_stopPrice = close - TrailingStop;
		}
		else if (turnDown && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_entryPrice = close;
			_stopPrice = close + TrailingStop;
		}

		_prevPrevEma = _prevEma;
		_prevEma = emaValue;
	}
}