GitHub で見る

ニルヴァマン アイマックス ストラテジー

概要

Nirvaman Imax 戦略は、HA、移動平均 2、および iMAX3alert カスタム インジケーターにバンドルされている MetaTrader 4 エキスパート アドバイザー NirvamanImax.mq4 を直接変換したものです。 StockSharp の実装は、Heikin-Ashi ローソク足と 2 フェーズ トレンド検出器および EMA ベースライン フィルターを組み合わせるという元のアイデアを維持しながら、高レベルの API を採用しています。この戦略は単一の商品と時間枠で機能し、設定可能な保有期間の経過後に取引を自動的に終了します。

インジケーターとフィルター

  • Heikin-Ashi ローソク足 – 元の HA インジケーターを再現し、平均値の始値と終値を比較することでローソク足を強気または弱気として分類します。
  • 高速/低速 EMA クロスオーバー – MT4 iMAX3alert1 二相出力を置き換えます。速いEMAが遅いEMAを上抜けると強気のシグナルが現れます。弱気シグナルは反対側のクロスオーバーで発生します。
  • EMA トレンド フィルターMoving Averages2 EMA バッファをミラーリングし、ベースラインとして機能します。フィルターを超えるロング取引とフィルターを下回るショート取引のみが許可されます。
  • 時間フィルター – 時刻が NoTradeStartHourNoTradeEndHour で定義された禁止ウィンドウ内にあるローソク足をスキップします (ウィンドウは深夜のラップアラウンドとブローカーのタイムゾーン シフトをサポートしています)。
  • 時限終了CloseAfter の経過後にすべてのポジションが強制的にクローズされ、MQL バージョンの tiempoCierre ロジックが再現されます。
  • ストップとターゲット – ストップロスとテイクプロフィットは、商品のティックサイズから派生した価格ステップで適用されます。どちらかを 0 に設定すると、対応する保護が無効になります。

取引ルール

  1. Heikin-Ashi、速い EMA、遅い EMA、フィルター EMA が形成され、前のローソク足の終値が利用可能になるまで待ちます。
  2. ローソク足の時間が制限された取引ウィンドウ内にある場合は、シグナルを拒否します。
  3. 長いエントリ:
    • 現在のローソク足の速い EMA が遅い EMA を上回ります。
    • Heikin-Ashi の終値は始値を上回っています (強気の実体)。
    • 前回のローソク足の終値は EMA フィルターを上回っています。
  4. 短いエントリー:
    • 現在のローソク足の速い EMA が遅い EMA を下回ります。
    • Heikin-Ashiの終値は始値を下回っています(弱気の実体)。
    • 前回のローソク足の終値は EMA フィルターを下回っています。
  5. 終了ルール:
    • ストップロスまたはテイクプロフィットのレベルはローソク足の範囲に触れます。
    • ポジションの最大有効期間 CloseAfter を超えました。
    • StartProtection() 経由でトリガーされる手動保護は、エンジンが要求したときにポジションを決済します。

パラメーター

名前 説明 デフォルト
TradeVolume 基本市場注文量。 0.1
CandleType すべてのインジケーターとシグナルに使用されるローソク足の時間枠。 30m 時間枠
FastTrendLength 青の iMAX フェーズをエミュレートする高速 EMA の長さ。 10
SlowTrendLength 赤色の iMAX フェーズをエミュレートする低速の EMA の長さ。 21
FilterLength ベースライン フィルターの EMA 期間 (移動平均 2 と同等)。 13
StopLoss 価格ステップにおける保護停止距離。 0 は停止を無効にします。 50
TakeProfit 価格ステップでの利益目標距離。 0 はターゲットを無効にします。 100
CloseAfter ポジションが強制決済されるまでの最大保持時間。 15000 s
NoTradeStartHour 取引禁止期間の開始を示す時間 (0 ~ 23)。 22
NoTradeEndHour 取引禁止期間の終了を示す時間 (0 ~ 23)。 2
BrokerTimeOffset 時間フィルターの前に適用されるブローカーのタイム ゾーン オフセット (時間)。 0

変換メモ

  • MT4 iMAX3alert1 インジケーターは、色分けされた 2 つのバッファーを公開します。それらのクロスオーバーは高速/低速の EMA クロスオーバーに変換され、元のイベント駆動型のエントリ ロジックが保持されます。
  • 移動平均 2 インジケーターは、デフォルトの長さ 13 の EMA モードで実行されました。StockSharp バージョンは、同じデフォルトの長さの標準の ExponentialMovingAverage を再利用します。
  • ポジションのライフサイクル管理は、MQL スクリプトを反映しています。ポジションは、新しいエントリが評価される前にタイムアウト時にクローズされ、追加のトレーリング ストップ ロジックは追加されません。

使い方のヒント

  1. ストラテジーをボード/証券にアタッチし、開始する前に目的の CandleType を設定します。
  2. 金融商品のボラティリティとリスク許容度に合わせて、TradeVolumeStopLossTakeProfit、および CloseAfter を調整します。
  3. 新しい市場向けに元の iMAX チューニングの動作を近似する必要がある場合は、EMA 期間を最適化します。
  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;
	}
}