ホーム
/
戦略のサンプル
GitHub で見る
ABH_BH_MFI 戦略
概要
The ABH_BH_MFI Strategy is a StockSharp high-level port of the MetaTrader expert advisor "Expert_ABH_BH_MFI". The algorithm combines bullish and bearish Harami candlestick patterns with confirmation from the Money Flow Index (MFI). Long trades are triggered when a bullish Harami forms inside a falling market while the MFI remains depressed. Short trades require a bearish Harami inside a rising market and an elevated MFI. The original MQL implementation relied on MetaTrader's signal infrastructure;この変換は決定ロジックを保持しますが、それを StockSharp のローソク足サブスクリプション、インジケーター バインディング、ポジション管理ヘルパーで表現します。
取引ロジック
1.ハラミパターン検出
The strategy stores the two most recent completed candles.
強気のハラミ には次のものが必要です。
Two candles ago was a long black (bearish) candle whose body is larger than the average body length.
The most recent candle is bullish and its open/close are engulfed by the body of the previous bearish candle.
The midpoint of the older candle lies below the simple moving average of closes, signalling a prevailing downtrend.
A bearish Harami mirrors these requirements with inverted colours and the midpoint above the moving average to confirm an uptrend.
2. マネーフローインデックスの確認
The MFI uses the configurable MfiPeriod (default 37 ) to replicate the original oscillator settings.
Long entries demand that the latest completed MFI value stays below BullishThreshold (default 40 ) to ensure capital inflow exhaustion.
Short entries require the MFI to remain above BearishThreshold (default 60 ) to show buying pressure exhaustion.
3. MFI クロスオーバーによる終了ルール
アクティブなロング ポジションは、MFI が ExitLowerLevel (デフォルト 30 ) または ExitUpperLevel (デフォルト 70 ) を上抜け、MetaTrader の条件 MFI(1) > level && MFI(2) < level に一致するとクローズされます。
アクティブなショートポジションは、MFI が買われ過ぎゾーンからクロスダウンするか売られ過ぎレベルを下回ると、元のショートエグジット条項を反映してクローズされます。
4. リスク管理
The strategy optionally applies StartProtection with stop-loss and take-profit offsets expressed in price steps.対応するパラメータをゼロに設定すると、保護距離が無効になり、MetaTrader のデフォルトが再現されます。
Position sizing uses the base Volume property;ポジションを反転すると、ソースエキスパートと同じように、フラット化して新しい方向に再開するのに十分な契約が自動的に追加されます。
パラメーター
名前
デフォルト
説明
CandleType
1時間枠
パターンとMFIについて分析された主要なローソク足シリーズ。
MfiPeriod
37
マネーフローインデックス指標を振り返ります。
BodyAveragePeriod
11
本体サイズと終値トレンドを測定する単純移動平均の長さ。
BullishThreshold
40
ロング取引を開始する前に許可される最大 MFI 値。
BearishThreshold
60
ショートトレードを開始する前に必要な最小MFI値。
ExitLowerLevel
30
ポジション出口の MFI クロスオーバー レベルを低くします。
ExitUpperLevel
70
ポジション出口の上位 MFI クロスオーバー レベル。
StopLossPoints
0
Optional stop-loss distance in price steps (0 disables).
TakeProfitPoints
0
価格ステップでのオプションの利食い距離 (0 を無効にします)。
実装メモ
ローソク足データは SubscribeCandles(CandleType) 経由で受信され、ローソク足の状態が Finished の場合にのみ処理され、MQL エキスパートのクローズドバー ロジックとの整合性が確保されます。
MFI インジケーターは .Bind(_mfi, ProcessCandle) に直接バインドされているため、ハンドラーは GetValue を呼び出さずにすぐに使用できる 10 進数値を受け取ります。
2 つの補助的な単純移動平均は、MetaTrader コードの AvgBody および CloseAvg ヘルパー関数を複製します。結果は、履歴指標のクエリを回避するためにキャッシュされます。
エグジットとエントリーの決定は、注文を送信する前に IsFormedAndOnlineAndAllowTrading() を呼び出し、StockSharp が推奨する取引安全性チェックと一貫性を保ちます。
資金管理は基本戦略ボリュームまで簡素化されています。元の「固定ロット」モジュールは、StockSharp のポジション サイジング ヘルパーに変換され、別個のクラスなしで同じ機能をカバーします。
MetaTrader トレーリング ストップ コンポーネント (TrailingNone) にはロジックがありませんでした。したがって、StockSharp バージョンでは後続のアクションが省略されますが、オプションの固定リスク ターゲットは保持されます。
デフォルトでは、ロギングは最小限です。詳細な取引診断が必要な場合は、LogInfo 呼び出しで拡張できます。
使用のヒント
戦略を開始する前に、必要なセキュリティを構成し、CandleType を割り当てます。
必要に応じて、さまざまなボラティリティ状況に合わせて MFI と出口のしきい値を調整します。
ブローカーが明示的な保護命令を要求する場合は、ゼロ以外の StopLossPoints/TakeProfitPoints を指定します。それ以外の場合は、ハードターゲットなしで取引するためにゼロのままにしておきます。
戦略によって作成されたチャート ペインを監視して、ローソク足、MFI インジケーター、および実行された取引を視覚化します。
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>
/// ABH BH MFI strategy: Harami pattern with MFI confirmation.
/// </summary>
public class AbhBhMfiStrategy : 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 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 AbhBhMfiStrategy()
{
_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), 40m)
.SetDisplay("Oversold", "MFI oversold level", "Signals");
_overbought = Param(nameof(Overbought), 60m)
.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();
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_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];
var bullishHarami = prev.OpenPrice > prev.ClosePrice
&& curr.ClosePrice > curr.OpenPrice
&& curr.OpenPrice > prev.ClosePrice
&& curr.ClosePrice < prev.OpenPrice;
var bearishHarami = prev.ClosePrice > prev.OpenPrice
&& curr.OpenPrice > curr.ClosePrice
&& curr.ClosePrice > prev.OpenPrice
&& curr.OpenPrice < prev.ClosePrice;
if (bullishHarami && mfiValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (bearishHarami && mfiValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
}
}
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 abh_bh_mfi_strategy(Strategy):
def __init__(self):
super(abh_bh_mfi_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", 40.0)
self._overbought = self.Param("Overbought", 60.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
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(abh_bh_mfi_strategy, self).OnReseted()
self._candles.clear()
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(abh_bh_mfi_strategy, self).OnStarted2(time)
self._candles.clear()
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]
bullish_harami = (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_harami = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) < float(prev.ClosePrice))
if bullish_harami 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_harami and mfi_val > self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return abh_bh_mfi_strategy()