GitHub で見る

Macd パターン トレーダー トリガー戦略

概要

Macd Pattern Trader Trigger Strategy は、MetaTrader 4 エキスパート アドバイザー MacdPatternTraderv05cb を StockSharp の高レベル戦略 API に移植します。このシステムは純粋な MACD ヒストグラム パターンを取引し、ゼロ ラインの下のダブルトップ構造を探してショートをオープンし、ゼロ ラインより上の鏡像ダブルボトム構造を探してロングをオープンします。取引管理はオリジナルの EA を反映しています。すべてのエントリーは、構成可能な固定ストップロスと商品ポイントで測定されるテイクプロフィットを使用して市場に提出されます。

戦略ロジック

インジケーターストリーム

  • 単一のローソク足サブスクリプションがロジックを駆動します (デフォルト: 15 分のローソク足)。完成した各ローソクは、ソース EA で使用される珍しい MT4 パラメータ (fast = 13, slow = 5, signal = 1) で構成された MovingAverageConvergenceDivergence インジケーターをフィードします。
  • MACD 幹線のみが使用されます。この戦略は、MetaTrader から iMACD(..., MODE_MAIN, shift=1..3) をエミュレートするために、最後の 3 つの完了した値をバッファリングします。

強気のセットアップ (ロングエントリー)

  1. アーミング条件 – MACD ラインは Bullish Trigger (デフォルトは 0.0015) より上にある必要があります。これにより、引き戻しシーケンスを探す戦略が準備されます。ゼロ以下に低下すると、その状態は直ちにクリアされます。
  2. プルバック ウィンドウ – 一度武装すると、MACD は Bullish Reset (デフォルトは 0.0005) 未満にフォールバックする必要があります。これは潜在的なプルバック領域をマークします。このウィンドウは、有効なパターンが確認されるか、MACD が否定的になるまでアクティブのままです。
  3. パターンの確認 – ウィンドウがアクティブである間、バッファされた最後の 3 つの MACD 読み取り値は次の条件を満たす必要があります。
    • macd_curr > macd_last (勢いが戻ります)、
    • macd_last < macd_last3 (前のバーはスイングを低く設定しました)、
    • macd_curr > Bullish Resetmacd_last < Bullish Reset (価格は浅いプルバックゾーンから反発)。
  4. 実行 – 確認されると、戦略は市場で購入されます。既存のショートポジションがある場合、注文サイズには、ロングエクスポージャーを確立する前にフラット化するために必要なボリュームが自動的に含まれます。

弱気セットアップ(ショートエントリー)

  1. アーミング条件 – MACD ラインは -Bearish Trigger (デフォルトは -0.0015) を下回る必要があります。ゼロを上回る動きはすべての弱気状態を解消します。
  2. プルバック ウィンドウ – 準備完了後、MACD は -Bearish Reset (デフォルトは -0.0005) を超えてリバウンドする必要があります。
  3. パターンの確認 – ウィンドウが開いている間、バッファされた値は次の条件を満たす必要があります。
    • macd_curr < macd_last
    • macd_last > macd_last3
    • macd_curr < -Bearish Resetmacd_last > -Bearish Reset
  4. 約定 – 成行売り注文が送信されます。ロングポジションが存在する場合、そのボリュームは注文に含まれるため、アカウントは設定された取引サイズでネットショートになります。

リスク管理

  • 固定ストップロス/テイクプロフィット – 距離はポイント(価格ステップ)で指定されます。このストラテジは、これらに楽器の PriceStep を乗算し、StartProtection を呼び出して元の SL/TP の動作を再現します。距離を 0 に設定すると、それぞれのレベルが無効になります。
  • ウィンドウごとに 1 つのシグナル – 注文後、同じ MACD パターンからの繰り返しのエントリを避けるために、アーミング フラグとウィンドウ フラグがクリアされます。

パラメーター

  • 取引量 – 成行注文量。反対のポジションは、新しい取引を開始する前に自動的にクローズされます。
  • 高速 EMA / 低速 EMA / 信号 EMA – MACD の長さ。デフォルトは元のアドバイザーを複製しますが、最適化される場合があります。
  • 強気トリガー/リセット – ロングセットアップを準備し、プルバックゾーンを定義する正のMACDしきい値(インジケーター単位)。
  • 弱気トリガー/リセット – 短期セットアップの絶対的な MACD しきい値。トリガーは実行時に負号付きで適用されます。
  • ストップロス/テイクプロフィット – ポイント単位の距離(価格ステップ)。値 0 は、対応する保護を無効にします。
  • ローソク足タイプ – MACD の計算と取引の決定に使用される時間枠。

実装メモ

  • StockSharp の高レベルの API は全体で使用されます。SubscribeCandles はインジケーターをフィードし、StartProtection は MT4 取引管理をミラーリングします。
  • MACD 履歴バッファにより、決定ロジックが前の 3 つの終了バーに対して動作し、MetaTrader の shift=1..3 呼び出しと一致することが保証されます。
  • API パッケージにはこの戦略の Python バージョンはなく、C# 実装のみがあります。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the MetaTrader advisor MacdPatternTrader.
/// Implements the MACD histogram double-top/double-bottom pattern with adaptive arming logic.
/// </summary>
public class MacdPatternTraderTriggerStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _bullishTrigger;
	private readonly StrategyParam<decimal> _bullishReset;
	private readonly StrategyParam<decimal> _bearishTrigger;
	private readonly StrategyParam<decimal> _bearishReset;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergenceSignal _macd = null!;

	private decimal? _macdPrev1;
	private decimal? _macdPrev2;
	private decimal? _macdPrev3;

	private bool _bullishArmed;
	private bool _bullishWindow;
	private bool _bullishReady;

	private bool _bearishArmed;
	private bool _bearishWindow;
	private bool _bearishReady;

	private decimal _priceStep = 1m;

	/// <summary>
	/// Trade volume used for market orders.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Fast EMA length of the MACD indicator.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA length of the MACD indicator.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Signal smoothing period of the MACD indicator.
	/// </summary>
	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	/// <summary>
	/// Positive MACD value that arms the bullish pattern.
	/// </summary>
	public decimal BullishTrigger
	{
		get => _bullishTrigger.Value;
		set => _bullishTrigger.Value = value;
	}

	/// <summary>
	/// Threshold that marks the bullish pullback zone.
	/// </summary>
	public decimal BullishReset
	{
		get => _bullishReset.Value;
		set => _bullishReset.Value = value;
	}

	/// <summary>
	/// Negative MACD value that arms the bearish pattern.
	/// </summary>
	public decimal BearishTrigger
	{
		get => _bearishTrigger.Value;
		set => _bearishTrigger.Value = value;
	}

	/// <summary>
	/// Threshold that marks the bearish pullback zone.
	/// </summary>
	public decimal BearishReset
	{
		get => _bearishReset.Value;
		set => _bearishReset.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in instrument points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in instrument points.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Candle type that drives the MACD calculation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public MacdPatternTraderTriggerStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order volume for entries", "Trading")
			
			.SetOptimize(0.05m, 0.3m, 0.05m);

		_fastPeriod = Param(nameof(FastPeriod), 13)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA length for MACD", "Indicators")
			
			.SetOptimize(8, 18, 1);

		_slowPeriod = Param(nameof(SlowPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA length for MACD", "Indicators")
			
			.SetOptimize(4, 15, 1);

		_signalPeriod = Param(nameof(SignalPeriod), 1)
			.SetGreaterThanZero()
			.SetDisplay("Signal EMA", "Signal EMA length for MACD", "Indicators")
			
			.SetOptimize(1, 5, 1);

		_bullishTrigger = Param(nameof(BullishTrigger), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Bullish Trigger", "MACD level that arms the bullish pattern", "Logic");

		_bullishReset = Param(nameof(BullishReset), 20m)
			.SetGreaterThanZero()
			.SetDisplay("Bullish Reset", "MACD pullback threshold for bullish setup", "Logic");

		_bearishTrigger = Param(nameof(BearishTrigger), 50m)
			.SetGreaterThanZero()
			.SetDisplay("Bearish Trigger", "Absolute MACD level that arms the bearish pattern", "Logic");

		_bearishReset = Param(nameof(BearishReset), 20m)
			.SetGreaterThanZero()
			.SetDisplay("Bearish Reset", "MACD pullback threshold for bearish setup", "Logic");

		_stopLossPoints = Param(nameof(StopLossPoints), 100m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in points", "Risk")
			
			.SetOptimize(50m, 200m, 25m);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 300m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in points", "Risk")
			
			.SetOptimize(100m, 400m, 50m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for indicator calculations", "Data");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_macdPrev1 = null;
		_macdPrev2 = null;
		_macdPrev3 = null;
		_bullishArmed = false;
		_bullishWindow = false;
		_bullishReady = false;
		_bearishArmed = false;
		_bearishWindow = false;
		_bearishReady = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_priceStep = Security?.PriceStep ?? 1m;
		if (_priceStep <= 0m)
			_priceStep = 1m;

		var takeProfit = TakeProfitPoints > 0m ? new Unit(TakeProfitPoints * _priceStep, UnitTypes.Absolute) : null;
		var stopLoss = StopLossPoints > 0m ? new Unit(StopLossPoints * _priceStep, UnitTypes.Absolute) : null;

		StartProtection(takeProfit, stopLoss);

		_macd = new MovingAverageConvergenceDivergenceSignal();
		_macd.Macd.ShortMa.Length = FastPeriod;
		_macd.Macd.LongMa.Length = SlowPeriod;
		_macd.SignalMa.Length = SignalPeriod;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_macd, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _macd);
			DrawOwnTrades(area);
		}
	}

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

		if (!macdValue.IsFinal || macdValue is not MovingAverageConvergenceDivergenceSignalValue macdLineValue)
			return;

		if (macdLineValue.Macd is not decimal currentMacd)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			ShiftHistory(currentMacd);
			return;
		}

		if (_macdPrev1 is null || _macdPrev2 is null || _macdPrev3 is null)
		{
			ShiftHistory(currentMacd);
			return;
		}

		var macdCurr = _macdPrev1.Value;
		var macdLast = _macdPrev2.Value;
		var macdLast3 = _macdPrev3.Value;

		EvaluateBearishPattern(macdCurr, macdLast, macdLast3);
		EvaluateBullishPattern(macdCurr, macdLast, macdLast3);

		ShiftHistory(currentMacd);
	}

	private void EvaluateBullishPattern(decimal macdCurr, decimal macdLast, decimal macdLast3)
	{
		if (macdCurr < 0m)
		{
			_bullishArmed = false;
			_bullishWindow = false;
			_bullishReady = false;
		}
		else
		{
			if (!_bullishArmed && macdCurr > BullishTrigger)
				_bullishArmed = true;

			if (_bullishArmed && macdCurr < BullishReset)
			{
				_bullishArmed = false;
				_bullishWindow = true;
			}
		}

		if (_bullishWindow && macdCurr > macdLast && macdLast < macdLast3 && macdCurr > BullishReset && macdLast < BullishReset)
		{
			_bullishReady = true;
			_bullishWindow = false;
		}

		if (!_bullishReady)
			return;

		var volumeToBuy = TradeVolume + Math.Max(0m, -Position);
		if (volumeToBuy > 0m)
			BuyMarket(volumeToBuy);

		_bullishReady = false;
		_bullishArmed = false;
		_bullishWindow = false;
	}

	private void EvaluateBearishPattern(decimal macdCurr, decimal macdLast, decimal macdLast3)
	{
		if (macdCurr > 0m)
		{
			_bearishArmed = false;
			_bearishWindow = false;
			_bearishReady = false;
		}
		else
		{
			if (!_bearishArmed && macdCurr < -BearishTrigger)
				_bearishArmed = true;

			if (_bearishArmed && macdCurr > -BearishReset)
			{
				_bearishArmed = false;
				_bearishWindow = true;
			}
		}

		if (_bearishWindow && macdCurr < macdLast && macdLast > macdLast3 && macdCurr < -BearishReset && macdLast > -BearishReset)
		{
			_bearishReady = true;
			_bearishWindow = false;
		}

		if (!_bearishReady)
			return;

		var volumeToSell = TradeVolume + Math.Max(0m, Position);
		if (volumeToSell > 0m)
			SellMarket(volumeToSell);

		_bearishReady = false;
		_bearishArmed = false;
		_bearishWindow = false;
	}

	private void ShiftHistory(decimal current)
	{
		_macdPrev3 = _macdPrev2;
		_macdPrev2 = _macdPrev1;
		_macdPrev1 = current;
	}
}