Открыть на GitHub

Стратегия Nirvaman Imax

Обзор

Стратегия Nirvaman Imax представляет собой конвертацию эксперта MetaTrader 4 NirvamanImax.mq4 вместе с индикаторами HA, Moving Averages2 и iMAX3alert. Реализация на StockSharp сохраняет первоначальную идею: анализ Heikin-Ashi свечей, определение направления по двум фазам тренда и фильтрация через EMA-базис, дополнительно используя таймер принудительного закрытия позиций.

Индикаторы и фильтры

  • Свечи Heikin-Ashi — воссоздают индикатор HA и определяют бычьи/медвежьи тела по соотношению Heikin-открытия и закрытия.
  • Пересечение быстрых/медленных EMA — замена двух буферов индикатора iMAX3alert1. Сигнал на покупку возникает при пересечении быстрой EMA выше медленной, сигнал на продажу — при обратном пересечении.
  • EMA-фильтр тренда — эквивалент буфера Moving Averages2. Разрешает входить в длинные сделки только выше линии фильтра и в короткие — только ниже неё.
  • Временной фильтр — пропускает сделки, если час свечи попадает в окно NoTradeStartHourNoTradeEndHour. Окно поддерживает переход через полночь и сдвиг часового пояса брокера.
  • Принудительное закрытие по времени — параметр CloseAfter повторяет логику tiempoCierre и завершает сделку после превышения максимального времени удержания.
  • Стоп-лосс и тейк-профит — рассчитываются в шагах цены относительно значения PriceStep. Нулевое значение отключает соответствующий уровень.

Правила торговли

  1. Дождаться формирования Heikin-Ashi, быстрой и медленной EMA, EMA-фильтра, а также появления закрытия предыдущей свечи.
  2. Пропустить обработку, если текущий час попадает в запретный торговый интервал.
  3. Условия для длинной позиции:
    • Быстрая EMA пересекает медленную снизу вверх на текущей свече.
    • Heikin-Ashi закрывается выше открытия (бычий корпус).
    • Закрытие предыдущей свечи находится выше EMA-фильтра.
  4. Условия для короткой позиции — зеркальные.
  5. Выход из позиции осуществляется, если:
    • Диапазон свечи достигает стоп-лосса или тейк-профита.
    • Время удержания превысило CloseAfter.
    • Система защиты (вызов StartProtection()) требует закрыть позицию.

Параметры

Имя Описание Значение по умолчанию
TradeVolume Базовый объём рыночной заявки. 0.1
CandleType Таймфрейм, на котором рассчитываются индикаторы. 30 минут
FastTrendLength Длина быстрой EMA (аналог синего буфера iMAX). 10
SlowTrendLength Длина медленной EMA (аналог красного буфера iMAX). 21
FilterLength Период EMA-фильтра (аналог Moving Averages2). 13
StopLoss Дистанция стоп-лосса в шагах цены, 0 — без стопа. 50
TakeProfit Дистанция тейк-профита в шагах цены, 0 — без тейка. 100
CloseAfter Максимальное время удержания позиции. 15000 секунд
NoTradeStartHour Начало запретного торгового окна (0–23). 22
NoTradeEndHour Конец запретного окна (0–23). 2
BrokerTimeOffset Часовой сдвиг торгового сервера относительно UTC. 0

Особенности конвертации

  • Двухцветный индикатор iMAX3alert1 заменён пересечением быстрых и медленных EMA, что позволяет сохранить событийный характер сигналов.
  • Индикатор Moving Averages2 использовался в режиме EMA с периодом 13 — в стратегии применяется стандартная ExponentialMovingAverage с тем же периодом.
  • Логика сопровождения сделок соответствует исходному коду: позиция закрывается по таймауту до оценки новых сигналов, дополнительные трейлинг-стопы не добавлялись.

Рекомендации по использованию

  1. Перед запуском выберите нужный инструмент и таймфрейм (CandleType).
  2. Настройте TradeVolume, StopLoss, TakeProfit и CloseAfter под волатильность инструмента и допустимый риск.
  3. Для адаптации под другие рынки оптимизируйте периоды EMA (FastTrendLength/SlowTrendLength).
  4. При одновременном запуске нескольких стратегий используйте надстройки риск-менеджмента портфеля.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Nirvaman Imax: Dual EMA crossover with trend filter and timed exit.
/// Fast EMA crosses slow EMA for direction, filter EMA confirms trend.
/// </summary>
public class NirvamanImaxStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _filterLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public NirvamanImaxStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 21)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_filterLength = Param(nameof(FilterLength), 50)
			.SetDisplay("Filter EMA", "Trend filter EMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

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

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int FilterLength
	{
		get => _filterLength.Value;
		set => _filterLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var filter = new ExponentialMovingAverage { Length = FilterLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, filter, atr, 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, decimal filterVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		// Exit management
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (_prevFast >= _prevSlow && fastVal < slowVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (_prevFast <= _prevSlow && fastVal > slowVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry: EMA crossover confirmed by filter EMA trend
		if (Position == 0)
		{
			if (_prevFast <= _prevSlow && fastVal > slowVal && close > filterVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevFast >= _prevSlow && fastVal < slowVal && close < filterVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}