GitHub で見る
巻き込むMFI確認戦略
この戦略は、日本のローソク足の巻き込みパターンとマネー フロー インデックス (MFI) オシレーターからの確認を組み合わせることで、MetaTrader のエキスパート「Expert_ABE_BE_MFI」を再現します。マネーフローが売られ過ぎゾーンに留まっている間に、強気の巻き込みローソクが現れると、ロングポジションがオープンされます。買われ過ぎのマネーフロー条件下で弱気の巻き込みローソク足が形成されると、ショートポジションがオープンします。 MFIが動的出口閾値を超えるとポジションはクローズされ、勢いの反転を知らせます。
コアアイデア
- パターン検出 – 現在終了しているローソク足の本体は、取引方向にある前のローソク足を完全に飲み込む必要があります。
- 出来高確認 – MFI インジケーター (長さは設定可能、デフォルトは 37) は、ロングエントリーの場合は売られ過ぎレベル (40) を下回るか、ショートエントリーの場合は買われ過ぎレベル (60) を上回る必要があります。
- モメンタムエグジット – 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(...)) を利用します。
使用のヒント
MfiPeriod、OversoldLevel、および 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
class engulfing_mfi_confirmation_strategy(Strategy):
def __init__(self):
super(engulfing_mfi_confirmation_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._mfi_period = self.Param("MfiPeriod", 14)
self._oversold = self.Param("Oversold", 30.0)
self._overbought = self.Param("Overbought", 70.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
self._prev_mfi = 0.0
self._has_prev_mfi = False
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MfiPeriod(self):
return self._mfi_period.Value
@MfiPeriod.setter
def MfiPeriod(self, value):
self._mfi_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(engulfing_mfi_confirmation_strategy, self).OnReseted()
self._candles.clear()
self._prev_mfi = 0.0
self._has_prev_mfi = False
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(engulfing_mfi_confirmation_strategy, self).OnStarted2(time)
self._candles.clear()
self._has_prev_mfi = False
self._candles_since_trade = self.SignalCooldownCandles
mfi = MoneyFlowIndex()
mfi.Length = self.MfiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(mfi, self._process_candle).Start()
def _process_candle(self, candle, mfi_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
mfi_val = float(mfi_value)
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2:
curr = self._candles[-1]
prev = self._candles[-2]
if curr is None or prev is None:
return
bullish_engulfing = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) <= float(prev.ClosePrice)
and float(curr.ClosePrice) >= float(prev.OpenPrice))
bearish_engulfing = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.OpenPrice) >= float(prev.ClosePrice)
and float(curr.ClosePrice) <= float(prev.OpenPrice))
if bullish_engulfing and mfi_val < self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif bearish_engulfing and mfi_val > self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
if self._has_prev_mfi:
if self.Position > 0 and self._prev_mfi >= self.Overbought and mfi_val < self.Overbought and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
elif self.Position < 0 and self._prev_mfi <= self.Oversold and mfi_val > self.Oversold and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
self._prev_mfi = mfi_val
self._has_prev_mfi = True
def CreateClone(self):
return engulfing_mfi_confirmation_strategy()