Открыть на GitHub

Стратегия Trend Capture Legacy

Trend Capture Legacy — перенос советника MetaTrader TrendCapture.mq4 на высокоуровневый API StockSharp. Перевод сохраняет исходную логику: направление задаёт индикатор Parabolic SAR, фильтр по слабому тренду выполняет ADX, а управление позицией включает фиксированные уровни и переход в безубыток.

Ключевые идеи

  • Обработка только закрытых свечей выбранного таймфрейма с передачей данных в Parabolic SAR (0.02/0.2) и Average Directional Index (14).
  • Вход возможен лишь при значении ADX ниже AdxThreshold, что соответствует спокойному рынку и более надёжным сигналам SAR.
  • Память о последней сделке: после прибыльного выхода стратегия повторяет то же направление, после убыточного — меняет сторону.
  • Стоп-лосс и тейк-профит задаются в пунктах от цены входа, а при прибыли в BreakEvenGuard пунктов стоп переносится в безубыток.
  • Объём рассчитывается от стоимости портфеля и параметра MaximumRisk; при отсутствии данных используется базовый объём стратегии.

Параметры

Имя Значение по умолчанию Описание
SarStep 0.02 Начальный шаг ускорения Parabolic SAR.
SarMax 0.2 Максимальный коэффициент ускорения SAR.
AdxPeriod 14 Период расчёта ADX.
AdxThreshold 20 Максимальное значение ADX, при котором допускается новый вход.
TakeProfitPoints 180 Дистанция до тейк-профита в пунктах.
StopLossPoints 50 Дистанция до стоп-лосса в пунктах.
BreakEvenGuard 5 Прибыль в пунктах, необходимая для переноса стопа в точку входа.
MaximumRisk 0.03 Доля свободной маржи, используемая для расчёта объёма сделки.
CandleType Часовые свечи Таймфрейм для расчётов и сигналов.

Управление сделками

  • Длинный вход выполняется, если цена закрытия выше SAR и ADX ниже порога; короткий — при цене ниже SAR при том же фильтре.
  • Стоп и тейк пересчитываются при каждом входе и контролируются на каждой завершённой свече.
  • Безубыток реализован переносом стопа на цену входа. Если стоп-лосс отключён (нулевая или отрицательная дистанция), защита не активируется.

Индикаторы

  • ParabolicSar — определение направления.
  • AverageDirectionalIndex — фильтр силы тренда (используется только основная линия ADX).

Примечания

  • Используется BindEx, что исключает прямой доступ к буферам индикаторов и соответствует требованиям репозитория.
  • Расчёт объёма учитывает биржевые ограничения (LotStep, MinVolume, MaxVolume).
  • История сделок для выбора направления собирается в обработчике OnNewMyTrade, поэтому частичные исполнения поддерживаются корректно.
using System;



using StockSharp.Algo.Indicators;

using StockSharp.Algo.Strategies;

using StockSharp.BusinessEntities;

using StockSharp.Messages;



namespace StockSharp.Samples.Strategies;



public class TrendCaptureLegacyStrategy : Strategy

{

	private readonly StrategyParam<int> _fastPeriod;

	private readonly StrategyParam<int> _slowPeriod;

	private readonly StrategyParam<DataType> _candleType;



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

	private int _cooldown;



	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }

	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }



	public TrendCaptureLegacyStrategy()

	{

		_fastPeriod = Param(nameof(FastPeriod), 10).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");

	}



	/// <inheritdoc />

	protected override void OnReseted()

	{

		base.OnReseted();

		_prevFast = default;

		_prevSlow = default;

		_hasPrev = default;

		_cooldown = default;

	}



	/// <inheritdoc />

	protected override void OnStarted2(DateTime time)

	{

		base.OnStarted2(time);

		_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 fast, decimal slow)

	{

		if (candle.State != CandleStates.Finished) return;

		if (!IsFormedAndOnlineAndAllowTrading()) return;

		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldown > 0)

		{

			_cooldown--;

			_prevFast = fast; _prevSlow = slow;

			return;

		}



		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)

		{

			var volume = Volume + Math.Abs(Position);

			BuyMarket(volume);

			_cooldown = 2;

		}

		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)

		{

			var volume = Volume + Math.Abs(Position);

			SellMarket(volume);

			_cooldown = 2;

		}

		_prevFast = fast; _prevSlow = slow;

	}

}