GitHub で見る

AH HM MFI 戦略

概要

AH HM MFI 戦略は、マネー フロー インデックス (MFI) によって確認されるハンマーとハンギングマンのローソク足パターンを取引します。短期的な下降トレンドに強気のハンマーが現れ、MFI が売られすぎのしきい値を下回っている場合、この戦略はロングポジションをオープンします。 MFIが買われ過ぎの閾値を上回っている間に弱気のハンギングマンが上昇トレンドを形成すると、ショートポジションがオープンします。保護的出口は、MFI が事前に定義された上限または下限の境界を超えるとトリガーされます。

中核ロジック

  1. 構成された時間枠のローソク足をサブスクライブし、2 つのインジケーターを計算します。
    • マネー フロー インデックス (設定可能な期間) (デフォルト: 47)。
    • 元の MQL 戦略からのトレンド フィルターを近似する終値の 単純移動平均 (デフォルトの長さ: 5)。
  2. ハンマー と 吊り下げられた男 パターンを検出します。
    • キャンドル本体は範囲の上部 3 分の 1 に位置します。
    • 実体に対して長い下の影。
    • 前のローソク足と比較したトレンドの方向のギャップ。
    • 前のローソク足と移動平均の中点を使用してトレンドを確認します。
  3. MFI しきい値を使用してエントリを確認します。
    • ハンマーが検出され、MFI が設定された売られ過ぎレベル (デフォルト: 40) 以下の場合は、ロングを入力します。
    • ぶら下がっている男性が検出され、MFI が設定された買われすぎレベル (デフォルト: 60) 以上である場合は、ショートを入力します。
  4. MFI 交差点を使用して出口を管理します。
    • MFI が下限または上限の出口レベル (デフォルト: 30 および 70) を超えて上向きにクロスしたときに、ショート ポジションを閉じます。
    • MFIが上方出口レベルを上方に通過するか、下方出口レベルを下方に下方に通過すると、ロングポジションを閉じます。
  5. 内蔵のリスク保護モジュールを起動して緊急停止を処理します。

パラメーター

名前 説明 デフォルト
CandleType パターン検出に使用されるローソク足のデータ型とタイムフレーム。 30分の時間枠
MfiPeriod MFI 計算のルックバック期間。 47
MaPeriod トレンド確認のために終値に適用される SMA の長さ。 5
HammerEntryThreshold ハンマー信号を入力する前に許可される最大 MFI 値。 40
HangingEntryThreshold ぶら下がりマン信号に入る前に必要な最小 MFI 値。 60
MfiUpperExitLevel MFI の上限境界。それを超えるとオープンポジションがクローズされます。 70
MfiLowerExitLevel MFI 境界の下限。それを下回るとロングポジションがクローズされ、上をクロスするとショートポジションがクローズされます。 30

注意事項

  • この戦略は、不完全な情報に基づいて行動することを避けるために、完成したキャンドルのみを評価します。
  • ハンマーと吊るされた男の検出は保守的です。長い下影とろうそくの高値付近にある本体の両方が必要です。
  • 移動平均は、元のエキスパートアドバイザーの MetaTrader 5 CloseAvg フィルターを置き換え、エントリがより広範な傾向に一致することを保証します。
namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

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

/// <summary>
/// Hammer/hanging-man lower-shadow patterns confirmed by previous SMA trend and MFI.
/// </summary>
public class AhHmMfiStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _mfiPeriod;
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<decimal> _hammerEntryThreshold;
	private readonly StrategyParam<decimal> _hangingEntryThreshold;
	private readonly StrategyParam<decimal> _mfiUpperExitLevel;
	private readonly StrategyParam<decimal> _mfiLowerExitLevel;

	private MoneyFlowIndex _mfi;
	private SimpleMovingAverage _ma;
	private ICandleMessage _previousCandle;
	private decimal? _previousMa;
	private decimal? _previousMfi;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int MfiPeriod { get => _mfiPeriod.Value; set => _mfiPeriod.Value = value; }
	public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
	public decimal HammerEntryThreshold { get => _hammerEntryThreshold.Value; set => _hammerEntryThreshold.Value = value; }
	public decimal HangingEntryThreshold { get => _hangingEntryThreshold.Value; set => _hangingEntryThreshold.Value = value; }
	public decimal MfiUpperExitLevel { get => _mfiUpperExitLevel.Value; set => _mfiUpperExitLevel.Value = value; }
	public decimal MfiLowerExitLevel { get => _mfiLowerExitLevel.Value; set => _mfiLowerExitLevel.Value = value; }

	public AhHmMfiStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame());
		_mfiPeriod = Param(nameof(MfiPeriod), 47).SetGreaterThanZero();
		_maPeriod = Param(nameof(MaPeriod), 5).SetGreaterThanZero();
		_hammerEntryThreshold = Param(nameof(HammerEntryThreshold), 40m);
		_hangingEntryThreshold = Param(nameof(HangingEntryThreshold), 60m);
		_mfiUpperExitLevel = Param(nameof(MfiUpperExitLevel), 70m);
		_mfiLowerExitLevel = Param(nameof(MfiLowerExitLevel), 30m);
	}

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

	private void ResetRuntime()
	{
		_previousCandle = null;
		_previousMa = null;
		_previousMfi = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		ResetRuntime();
		_mfi = new MoneyFlowIndex { Length = MfiPeriod };
		_ma = new SimpleMovingAverage { Length = MaPeriod };
		SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
	}

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

		var mfiValue = _mfi.Process(candle);
		var maValue = _ma.Process(candle.ClosePrice, candle.OpenTime, true);

		if (!mfiValue.IsFormed || !maValue.IsFormed)
		{
			Remember(candle, maValue.IsFormed ? maValue.ToDecimal() : null,
				mfiValue.IsFormed ? mfiValue.ToDecimal() : null);
			return;
		}

		var mfi = mfiValue.ToDecimal();
		var ma = maValue.ToDecimal();
		if (_previousMfi is decimal previousMfi)
		{
			if (Position > 0m &&
				((previousMfi <= MfiUpperExitLevel && mfi > MfiUpperExitLevel) ||
				 (previousMfi >= MfiLowerExitLevel && mfi < MfiLowerExitLevel)))
			{
				ClosePosition();
				Remember(candle, ma, mfi);
				return;
			}

			if (Position < 0m &&
				((previousMfi <= MfiLowerExitLevel && mfi > MfiLowerExitLevel) ||
				 (previousMfi <= MfiUpperExitLevel && mfi > MfiUpperExitLevel)))
			{
				ClosePosition();
				Remember(candle, ma, mfi);
				return;
			}
		}

		if (Position == 0m && _previousCandle is { } previous && _previousMa is decimal previousMa)
		{
			var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
			var range = candle.HighPrice - candle.LowPrice;
			if (body > 0m && range > 0m)
			{
				var upperShadow = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);
				var lowerShadow = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
				var bodyInUpperThird = Math.Min(candle.OpenPrice, candle.ClosePrice) >= candle.HighPrice - range / 3m;
				var lowerTailPattern = bodyInUpperThird && lowerShadow >= body * 2.5m && upperShadow <= body * 0.5m;
				var previousMidpoint = (previous.OpenPrice + previous.ClosePrice) / 2m;

				if (lowerTailPattern && candle.OpenPrice < previous.ClosePrice &&
					previousMidpoint < previousMa && mfi <= HammerEntryThreshold)
					BuyMarket();
				else if (lowerTailPattern && candle.OpenPrice > previous.ClosePrice &&
					previousMidpoint > previousMa && mfi >= HangingEntryThreshold)
					SellMarket();
			}
		}

		Remember(candle, ma, mfi);
	}

	private void Remember(ICandleMessage candle, decimal? ma, decimal? mfi)
	{
		_previousCandle = candle;
		_previousMa = ma;
		_previousMfi = mfi;
	}
}