GitHub で見る

Nirvaman Imax Strategy

Overview

The Nirvaman Imax strategy is a direct conversion of the MetaTrader 4 expert advisor NirvamanImax.mq4 bundled with the HA, Moving Averages2 and iMAX3alert custom indicators. The StockSharp implementation keeps the original idea of combining Heikin-Ashi candles with a two-phase trend detector and an EMA baseline filter while adopting the high-level API. The strategy works on a single instrument and timeframe and automatically closes trades after a configurable holding period.

Indicators and filters

  • Heikin-Ashi candles – reproduce the original HA indicator and classify candles as bullish or bearish by comparing the Heikin open and close values.
  • Fast/slow EMA crossover – replaces the MT4 iMAX3alert1 double-phase output. A bullish signal appears when the fast EMA crosses above the slow EMA; a bearish signal occurs on the opposite crossover.
  • EMA trend filter – mirrors the Moving Averages2 EMA buffer and acts as a baseline. Only long trades above the filter and short trades below it are allowed.
  • Time filter – skips any candle whose hour lies inside the forbidden window defined by NoTradeStartHour and NoTradeEndHour (the window supports wrap-around midnight and a broker time-zone shift).
  • Timed exit – every position is force-closed after CloseAfter elapses, reproducing the tiempoCierre logic of the MQL version.
  • Stops and targets – stop loss and take profit are applied in price steps derived from the instrument tick size. Setting either to 0 disables the corresponding protection.

Trading rules

  1. Wait until the Heikin-Ashi, fast EMA, slow EMA and filter EMA are formed and a previous candle close is available.
  2. Reject the signal if the candle time is inside the restricted trading window.
  3. Long entry:
    • Fast EMA crosses above the slow EMA on the current candle.
    • The Heikin-Ashi close is above its open (bullish body).
    • The previous candle close is above the EMA filter.
  4. Short entry:
    • Fast EMA crosses below the slow EMA on the current candle.
    • The Heikin-Ashi close is below its open (bearish body).
    • The previous candle close is below the EMA filter.
  5. Exit rules:
    • Stop loss or take profit levels are touched by the candle range.
    • The maximum position lifetime CloseAfter is exceeded.
    • Manual protection triggered via StartProtection() closes the position when the engine requests it.

Parameters

Name Description Default
TradeVolume Base market order volume. 0.1
CandleType Candle timeframe used for every indicator and signal. 30m time frame
FastTrendLength Length of the fast EMA that emulates the blue iMAX phase. 10
SlowTrendLength Length of the slow EMA that emulates the red iMAX phase. 21
FilterLength EMA period for the baseline filter (Moving Averages2 equivalent). 13
StopLoss Protective stop distance in price steps; 0 disables the stop. 50
TakeProfit Profit target distance in price steps; 0 disables the target. 100
CloseAfter Maximum holding time before the position is force-closed. 15000 s
NoTradeStartHour Hour (0–23) that marks the beginning of the no-trade window. 22
NoTradeEndHour Hour (0–23) that marks the end of the no-trade window. 2
BrokerTimeOffset Broker time zone offset (hours) applied before the time filter. 0

Conversion notes

  • The MT4 iMAX3alert1 indicator exposes two colour-coded buffers. Their crossover is translated into a fast/slow EMA crossover, which preserves the original event-driven entry logic.
  • The Moving Averages2 indicator ran in EMA mode with a default length of 13. The StockSharp version reuses a standard ExponentialMovingAverage with the same default.
  • Position life-cycle management mirrors the MQL script: the position is closed on time-out before new entries can be evaluated, and no additional trailing stop logic was added.

Usage tips

  1. Attach the strategy to a board/security and set the desired CandleType before starting it.
  2. Adjust TradeVolume, StopLoss, TakeProfit and CloseAfter to match the instrument volatility and risk tolerance.
  3. Optimise the EMA periods if you need to approximate the behaviour of the original iMAX tuning for a new market.
  4. Combine with higher level risk controls (portfolio protection, session control) when running multiple instances.
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;
	}
}