Открыть на GitHub

Стратегия AltrTrend Signal v2.2

Стратегия является портом эксперт-советника Exp_AltrTrend_Signal_v2_2 под StockSharp. Она полностью повторяет логику адаптивного канала индикатора AltrTrend Signal и исполняет сигналы с задержкой по барам, как в оригинале. Значение ADX сужает или расширяет канал, поэтому пробои возникают только при подтверждённой силе тренда.

Как работает

  1. На каждом завершённом баре заданного таймфрейма строится динамический канал. Его ширина определяется максимумом и минимумом цены внутри окна, которое растягивается или сжимается пропорционально предыдущему значению ADX (KPeriod / ADX).
  2. Внутренние границы (smin, smax) смещаются к центру на величину KPercent. Для смены тренда цена должна закрыться вне этих границ.
  3. Если тренд меняется с медвежьего на бычий и закрытие выше верхней границы, формируется сигнал покупки. Обратное условие даёт сигнал продажи. Заявки отправляются на баре, указанном параметром SignalBar, что соответствует поведению MQL5-версии.
  4. Стоп-лосс и тейк-профит переводятся из пунктов в шаги цены, чтобы защитные выходы работали так же, как в исходном роботе.

Детали

  • Вход в сделку:
    • Покупка: предыдущий тренд был нисходящим или нейтральным, цена закрылась выше внутренней верхней границы, разрешены покупки. Короткие позиции закрываются автоматически, если это разрешено.
    • Продажа: предыдущий тренд был восходящим или нейтральным, цена закрылась ниже внутренней нижней границы, разрешены продажи. Длинные позиции закрываются автоматически при разрешении.
  • Выход из сделки:
    • Обратный пробой границы при включённом разрешении на закрытие соответствующего направления.
    • Срабатывание стоп-лосса или тейк-профита в шагах цены.
  • Направление: Торговля в обе стороны с независимыми переключателями открытия и закрытия.
  • Управление риском:
    • StopLossPoints и TakeProfitPoints имитируют модуль MM оригинала, применяя фиксированные защитные расстояния после открытия рынка.
  • Настройки индикатора:
    • KPercent определяет, насколько сильно границы канала сдвигаются к центру.
    • KStop сохраняет оригинальное значение стрелки для совместимости и логирования.
    • KPeriod задаёт базовое окно до модификации ADX.
    • AdxPeriod управляет длиной индекса направленного движения, влияющего на ширину канала.
    • SignalBar задаёт задержку исполнения сигнала в количестве завершённых свечей.
  • Рекомендуемые рынки:
    • Лучше всего подходит для инструментов с ярко выраженными фазами тренда: валютные пары мажоры, золото, фьючерсы на индексы. Таймфрейм по умолчанию — H1, как в MQL5-шаблоне.

Параметры

Параметр Описание
CandleType Таймфрейм, на котором строится адаптивный канал.
KPercent Процентное сжатие внутренних границ канала.
KStop Множитель для расчёта проекции стрелки (для совместимости).
KPeriod Базовое количество свечей до поправки ADX.
AdxPeriod Период индекса направленного движения, регулирующего ширину канала.
SignalBar Количество завершённых свечей до исполнения сигнала.
AllowBuyEntries / AllowSellEntries Включение открытия позиций в каждом направлении.
AllowBuyExits / AllowSellExits Разрешение автоматического закрытия позиций по обратному сигналу.
StopLossPoints Дистанция стоп-лосса в шагах цены (0 — отключено).
TakeProfitPoints Дистанция тейк-профита в шагах цены (0 — отключено).

Порт сохраняет все переключатели и параметры управления риском исходного эксперта, что упрощает перенос результатов в StockSharp Designer, Shell или Runner.

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>
/// AltrTrend signal strategy using EMA crossover for alternating trend detection.
/// </summary>
public class AltrTrendSignalStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

	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 AltrTrendSignalStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

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

		_slowPeriod = Param(nameof(SlowPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "Indicators");
	}

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

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = null;
		_prevSlow = null;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fastVal > slowVal;

		_prevFast = fastVal;
		_prevSlow = slowVal;

		if (!prevAbove && currAbove)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (prevAbove && !currAbove)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}
	}
}