Открыть на GitHub

Стратегия Neuro Nirvaman MQ4

Общее описание

Neuro Nirvaman MQ4 — это полный перенос эксперта MetaTrader 4 NeuroNirvaman.mq4. В исходном роботе четыре потока ADX (+DI) проходят через фильтр Лагерра, два индикатора SilverTrend формируют стрелочные сигналы, а решение принимает связка из трёх перцептронов и «супервизора». Реализация на StockSharp повторяет эту структуру, работает только по завершённым свечам и допускает одновременное наличие лишь одной позиции.

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

  1. Подключение свечей — стратегия подписывается на один поток свечей (CandleType) и реагирует исключительно на состояние Finished, тем самым имитируя проверку Time[0] в MQL.
  2. Сглаживание Laguerre +DI — четыре индикатора AverageDirectionalIndex предоставляют линию +DI. Класс LaguerrePlusDiState применяет исходное значение γ = 0.764 и формирует осциллятор в диапазоне [0, 1]. Для каждого потока можно задать собственный период ADX и ширину нейтральной зоны (Laguerre*Distance).
  3. Индикатор SilverTrend — два экземпляра SilverTrendState повторяют код Sv2.mq4: вычисляют максимум и минимум за SSP свечей, сжимают канал с помощью константы Kmax = 50.6 и возвращают 1 для восходящей тенденции либо -1 для нисходящей. Параметры SilverTrend1Length и SilverTrend2Length управляют глубиной истории.
  4. Перцептроны
    • Перцептрон №1: первая Laguerre-активация и сигнал SilverTrend #1, веса X11 - 100 и X12 - 100.
    • Перцептрон №2: вторая Laguerre-активация и сигнал SilverTrend #2, веса X21 - 100 и X22 - 100.
    • Перцептрон №3: третья и четвёртая Laguerre-активации, веса X31 - 100 и X32 - 100. Каждое Laguerre-значение дискретизируется в -1, 0 или 1 в зависимости от отклонения от уровня 0.5.
  5. Supervisor (Pass) — полностью воспроизводит функцию Supervisor() из MQL:
    • Pass = 3: требуется Perceptron3 > 0. Если дополнительно Perceptron2 > 0, открывается лонг со второй парой TP/SL; иначе при Perceptron1 < 0 открывается шорт с первой парой TP/SL.
    • Pass = 2: положительное значение Perceptron2 даёт покупку (TP/SL №2), иначе выполняется продажа (TP/SL №1).
    • Pass = 1: отрицательный Perceptron1 приводит к продаже, в противном случае открывается покупка. В обоих направлениях используются параметры №1.
  6. Управление ордерами — вход осуществляется методами BuyMarket и SellMarket с объёмом TradeVolume. Цели и стопы рассчитываются как entry ± points * PriceStep. Поскольку выставляются рыночные заявки, срабатывание TP/SL моделируется проверкой экстремумов завершённой свечи, что соответствует поведению MT4 при размещении защитных ордеров на сервере брокера.

Параметры

Имя Тип Значение по умолчанию Описание
CandleType DataType 15 минут Тип свечей, используемый стратегией.
TradeVolume decimal 0.1 Объём сделки в лотах.
SilverTrend1Length int 7 Глубина первого расчёта SilverTrend (SSP).
Laguerre1Period int 14 Период ADX для первого Laguerre-потока.
Laguerre1Distance decimal 0 Ширина нейтральной зоны вокруг 0.5 (в процентах) для первого Laguerre-потока.
X11, X12 decimal 100 Весовые коэффициенты перцептрона №1 (из них вычитается 100).
TakeProfit1, StopLoss1 decimal 100 / 50 Дистанция TP/SL в пунктах для первой группы рисков и всех коротких позиций.
SilverTrend2Length int 7 Глубина второго расчёта SilverTrend.
Laguerre2Period int 14 Период ADX для второго Laguerre-потока.
Laguerre2Distance decimal 0 Ширина нейтральной зоны для второго Laguerre-потока.
X21, X22 decimal 100 Весовые коэффициенты перцептрона №2.
TakeProfit2, StopLoss2 decimal 100 / 50 Дистанция TP/SL в пунктах для второй группы рисков.
Laguerre3Period, Laguerre4Period int 14 Периоды ADX для третьего и четвёртого Laguerre-потоков.
Laguerre3Distance, Laguerre4Distance decimal 0 Ширина нейтральной зоны для третьего и четвёртого Laguerre-потоков.
X31, X32 decimal 100 Весовые коэффициенты перцептрона №3.
Pass int 3 Режим работы супервизора.

Практические советы

  • Значение веса 100 нейтрализует соответствующий вход. Для генерации сигналов необходимо сместить веса от 100.
  • SilverTrend начинает выдавать ±1 только после накопления достаточного числа свечей, поэтому на старте перцептроны могут возвращать нули — это соответствует поведению iCustom в MT4.
  • Проверка TP/SL проводится по экстремумам закрытой свечи. Резкие внутрисвечные выбросы, произошедшие между барами, могут привести к расхождениям с фактическим исполнением у брокера.
  • Стратегия никогда не удерживает более одной позиции. Новые сигналы игнорируются до полного закрытия текущей сделки.
  • Рекомендуется задавать CandleType, совпадающий с таймфреймом MT4 (например, M15 или H1), чтобы параметры индикаторов интерпретировались корректно.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// NeuroNirvaman: Perceptron-inspired strategy using RSI momentum
/// with EMA trend filter and weighted signal scoring.
/// </summary>
public class NeuroNirvamanMq4Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _atrLength;

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

	public NeuroNirvamanMq4Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_fastEmaLength = Param(nameof(FastEmaLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowEmaLength = Param(nameof(SlowEmaLength), 30)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

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

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

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int FastEmaLength
	{
		get => _fastEmaLength.Value;
		set => _fastEmaLength.Value = value;
	}

	public int SlowEmaLength
	{
		get => _slowEmaLength.Value;
		set => _slowEmaLength.Value = value;
	}

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

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

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

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

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

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var fast = new ExponentialMovingAverage { Length = FastEmaLength };
		var slow = new ExponentialMovingAverage { Length = SlowEmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

		var close = candle.ClosePrice;

		// Perceptron score: weighted RSI momentum + EMA trend alignment
		var rsiSignal = rsiVal > 50 ? 1m : -1m;
		var trendSignal = fastVal > slowVal ? 1m : -1m;
		var rsiAccel = rsiVal - _prevRsi > 0 ? 1m : -1m;
		var score = rsiSignal * 0.4m + trendSignal * 0.4m + rsiAccel * 0.2m;

		// Exit management
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m || score < -0.5m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m || score > 0.5m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry: strong perceptron score with RSI crossover confirmation
		if (Position == 0)
		{
			if (score > 0.5m && _prevRsi <= 50 && rsiVal > 50)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (score < -0.5m && _prevRsi >= 50 && rsiVal < 50)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevRsi = rsiVal;
		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}