Открыть на GitHub

Стратегия TP SL Trailing

Обзор

Стратегия является прямой конверсией советника MetaTrader 5 «TP SL Trailing». Она не формирует точки входа самостоятельно, а управляет уже открытыми позициями: выставляет защитные стоп-лоссы и тейк-профиты по заданным расстояниям в пунктах и переносит стоп-лосс по мере роста прибыли. Все параметры выражены в пунктах, поэтому стратегию можно подключить к любому инструменту StockSharp.

Логика работы

  • При появлении новой позиции стратегия может сразу поставить стартовые стоп-лосс и тейк-профит согласно параметрам. Это поведение регулируется флагом Only Zero Values, полностью повторяя оригинальный советник.
  • Для длинных позиций стоп переносится вверх, когда плавающая прибыль превышает сумму «Trailing Stop + Trailing Step». Новый уровень равен текущая цена - trailing stop, что фиксирует часть прибыли.
  • Для коротких позиций применяется зеркальная логика — стоп опускается до текущая цена + trailing stop, когда выполнено условие по прибыли.
  • Если значения trailing stop и trailing step равны нулю, перенос стопа не выполняется.
  • Тейк-профит не переносится, он устанавливается только на этапе первоначальной постановки защитных ордеров, когда активирован режим Only Zero Values.

Параметры

Имя Описание
CandleType Тип и таймфрейм свечей, по которым контролируются движения цены. Чем меньше интервал, тем чаще проверяется перенос стопа.
StopLossPips Расстояние от цены входа до начального стоп-лосса в пунктах. Используется только в режиме Only Zero Values.
TakeProfitPips Расстояние до стартового тейк-профита в пунктах. Используется только в режиме Only Zero Values.
TrailingStopPips Основная дистанция трала в пунктах. Определяет, насколько далеко от текущей цены должен находиться стоп после активации.
TrailingStepPips Дополнительная дистанция, которую цена должна пройти, прежде чем стоп переместится снова. Позволяет избежать слишком частых модификаций.
OnlyZeroValues Поведение, совпадающее с оригинальным EA: защитные ордера выставляются только если у позиции отсутствуют собственные стоп и тейк.

Особенности портирования

  • Пересчёт пунктов в цену выполняется через PriceStep инструмента, что аналогично корректировке под 3/5 знаков после запятой в исходном MQL-коде.
  • При каждом переносе трала активный стоп-ордер отменяется и создаётся заново с новым уровнем. После полного закрытия позиции все защитные заявки автоматически снимаются.
  • В исходном коде оставлены комментарии только на английском языке, а данный документ подробно описывает принятые решения при конвертации.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// TP SL Trailing strategy. Uses EMA with price crossover for entries.
/// </summary>
public class TpSlTrailingStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private decimal? _prevClose;
	private decimal? _prevEma;

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

	public TpSlTrailingStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 20).SetGreaterThanZero().SetDisplay("EMA Period", "EMA lookback", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = null;
		_prevEma = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = null; _prevEma = null;
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaVal)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevClose = close; _prevEma = emaVal; return; }
		if (_prevClose == null || _prevEma == null) { _prevClose = close; _prevEma = emaVal; return; }
		if (_prevClose.Value < _prevEma.Value && close >= emaVal && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (_prevClose.Value > _prevEma.Value && close <= emaVal && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevClose = close; _prevEma = emaVal;
	}
}