GitHub で見る

巻き込むMFI確認戦略

この戦略は、日本のローソク足の巻き込みパターンとマネー フロー インデックス (MFI) オシレーターからの確認を組み合わせることで、MetaTrader のエキスパート「Expert_ABE_BE_MFI」を再現します。マネーフローが売られ過ぎゾーンに留まっている間に、強気の巻き込みローソクが現れると、ロングポジションがオープンされます。買われ過ぎのマネーフロー条件下で弱気の巻き込みローソク足が形成されると、ショートポジションがオープンします。 MFIが動的出口閾値を超えるとポジションはクローズされ、勢いの反転を知らせます。

コアアイデア

  1. パターン検出 – 現在終了しているローソク足の本体は、取引方向にある前のローソク足を完全に飲み込む必要があります。
  2. 出来高確認 – MFI インジケーター (長さは設定可能、デフォルトは 37) は、ロングエントリーの場合は売られ過ぎレベル (40) を下回るか、ショートエントリーの場合は買われ過ぎレベル (60) を上回る必要があります。
  3. モメンタムエグジット – MFI が主要な反転レベル (30 と 70) を反対方向に通過するとオープンポジションがクローズされ、MQL エキスパートの元の投票ロジックを模倣します。

インジケーター

  • マネー フロー インデックス (MFI) – 出来高調整後のモメンタムを計算します。この戦略は、踏切を検出するために最後の 2 つの MFI 読み取り値を保存します。
  • ローソク足実体分析 – 追加のインジケーターは登録されていません。飲み込み検出では、最新の 2 つの完成したキャンドルが使用されます。

取引ルール

ロングエントリー

  • 以前のローソク足は弱気で、現在のローソク足は強気です。
  • 現在のローソク体は、前の終値以下で始まり、前の始値以上で閉じます (厳密な巻き込み)。
  • 最新の MFI 値は、構成可能な OversoldLevel (デフォルトは 40) を下回っています。

ショートエントリー

  • 以前のローソク足は強気で、現在のローソク足は弱気です。
  • 現在のローソク足の本体は、前の終値以上で始まり、前の始値以下で終わります。
  • 最新の MFI 値は、構成可能な OverboughtLevel (デフォルトは 60) を超えています。

終了条件

  • **MFI が ExitLongLevel (30) または ExitShortLevel (70) を下から上抜けた場合、ショートで決済
  • MFI が上から ExitShortLevel (70) または ExitLongLevel (30) を下抜けた場合、ロングクローズ

これらの出口しきい値は、元のエキスパートの二重投票ロジックを再現し、マネー フローの長期的な動きによってポジションのタイムリーな清算が確実に引き起こされるようにします。

貿易管理

  • 成行注文 (BuyMarket / SellMarket) はエントリーとエグジットに使用されます。
  • 明示的なストップロスやテイクプロフィットは使用されません。リスク管理はMFI反転シグナルに依存します。

パラメーター

名前 説明 デフォルト 範囲・音符
CandleType 分析に使用されるローソク足の時間枠。 1分 サポートされているキャンドルのタイプ。
MfiPeriod マネーフローインデックスの長さ。 37 > 0 である必要があります。元の EA のデフォルトと一致します。
OversoldLevel 強気の巻き込み設定を裏付けるMFIレベル。 40 必要に応じて最適化を有効にします。
OverboughtLevel 弱気の巻き込みセットアップを裏付けるMFIレベル。 60 必要に応じて最適化を有効にします。
ExitLongLevel 反転を検出するための MFI 境界の下限。 30 長い終了と短い確認の両方に使用されます。
ExitShortLevel 反転を検出するための MFI の上限境界。 70 短い終了と長い確認の両方に使用されます。

変換時の注意点

  • 元の MQL の専門家は、巻き込みパターンと MFI フィルターからの「投票」を集計しました。 C# 戦略は、投票ルールを個別の開始条件と終了条件に直接変換することによって、同じ意思決定フローを再現します。
  • MQL バージョンの資金管理と後続モジュールは省略されています。 StockSharp ポジションのサイジングは戦略ボリュームによって制御されます。
  • すべてのインジケーター バインディングは、必要に応じて高レベルの API (SubscribeCandles().Bind(...)) を利用します。

使用のヒント

  • MfiPeriodOversoldLevel、および OverboughtLevel を最適化して、戦略を特定の市場に適応させます。
  • さらなる安全性が必要な場合は、ホスト アプリケーションの StartProtection を介してリスク制御 (保護停止) と組み合わせます。
  • 取引を可能にする前に、マネー フロー インデックスが完全に形成されるように、十分な履歴データを確認してください。
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Engulfing MFI Confirmation strategy: Engulfing pattern with MFI filter.
/// Bullish engulfing + oversold MFI for long, bearish engulfing + overbought MFI for short.
/// </summary>
public class EngulfingMfiConfirmationStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _mfiPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevMfi;
	private bool _hasPrevMfi;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int MfiPeriod { get => _mfiPeriod.Value; set => _mfiPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public EngulfingMfiConfirmationStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_mfiPeriod = Param(nameof(MfiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("MFI Period", "Money Flow Index period", "Indicators");
		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "MFI oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "MFI overbought level", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_prevMfi = 0m;
		_hasPrevMfi = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_hasPrevMfi = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var mfi = new MoneyFlowIndex { Length = MfiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(mfi, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		_candles.Add(candle);
		if (_candles.Count > 5)
			_candles.RemoveAt(0);

		if (_candles.Count >= 2)
		{
			var curr = _candles[^1];
			var prev = _candles[^2];

			if (curr is null || prev is null)
				return;

			var bullishEngulfing = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice <= prev.ClosePrice
				&& curr.ClosePrice >= prev.OpenPrice;

			var bearishEngulfing = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.OpenPrice >= prev.ClosePrice
				&& curr.ClosePrice <= prev.OpenPrice;

			if (bullishEngulfing && mfiValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (bearishEngulfing && mfiValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		// Exit on MFI crossing
		if (_hasPrevMfi)
		{
			if (Position > 0 && _prevMfi >= Overbought && mfiValue < Overbought && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
			else if (Position < 0 && _prevMfi <= Oversold && mfiValue > Oversold && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevMfi = mfiValue;
		_hasPrevMfi = true;
	}
}