GitHub で見る

MACD 信号クロスオーバー戦略

概要

このサンプルは、元の MetaTrader 4 エキスパート アドバイザー MACD_v1.mq4 を StockSharp の高レベル戦略に変換します。このアルゴリズムは、移動平均収束発散 (MACD) クロスオーバーを追跡し、新しいトレンドの方向にトレードします。オプションの保護エグジットは、元のアドバイザーの動作を再現します。つまり、ストップロス、遠いテイクプロフィット、および現在のポジションの半分を清算する部分的な利益目標です。

取引ロジック

  1. データ ソース – ストラテジーは、設定されたローソク足シリーズ (デフォルトでは 5 分足のローソク足) をサブスクライブし、MovingAverageConvergenceDivergenceSignal インジケーターをバインドします。
  2. エントリー条件:
    • MACD ラインが信号線の上を横切る場合は、long と入力します。ショートポジションがアクティブな場合、ロングポジションをオープンする前にクローズされます。
    • MACD ラインが信号線の下を横切る場合は short と入力します。ロングポジションが存在する場合は、まずそれがクローズされます。
  3. 終了条件:
    • 反対側の MACD クロスオーバーは現在のポジションをクローズし、反対方向に新しいポジションをオープンします。
    • StartProtection によって管理される構成可能なテイクプロフィットとストップロスは、元の TP/SL パラメーター (商品ポイントで測定) を反映します。
    • 部分的な利益目標では、価格がエントリーレベルから指定された量だけ進むと、オープンポジションの半分がクローズされます。部分イグジットはポジションごとに 1 回だけトリガーされます。

パラメーター

名前 デフォルト 説明
高速期間 23 MACD 計算の高速な EMA の長さ (MQL パラメータ a = 2300 を反映)。
停滞期 40 MACD 計算の EMA の長さが遅い (ソース内の b = 4000)。
信号周期 8 信号線の長さ (ソース内の c = 800)。
利益確定 500 保護的なテイクプロフィット注文の価格ポイントの距離。無効にするには、0 に設定します。
ストップロス 80 保護的なストップロス注文の価格ポイントの距離。無効にするには、0 に設定します。
一部利益 70 オープンポジションの半分を決済するまでの価格ポイントの距離。部分的な終了を無効にするには、0 に設定します。
キャンドルタイプ 5分の時間枠 インジケーターの計算に使用されるローソク足シリーズ。

注意事項

  • インジケーター パラメーターは、MQL スクリプトで 100 分の 1 (2300/4000/800) として表現されていたため、一般的な MACD の長さ (23/40/8) にスケールされました。
  • この戦略は、新しいポジションが蓄積されるたびに、部分出口フラグを自動的に復元します。
  • チャート描画ヘルパーは、チャート パネルが使用可能な場合、ローソク足、MACD 値、および戦略の取引を強調表示します。
  • ボリューム処理は、基本戦略 Volume プロパティに依存します。戦略を開始する前に、楽器のサイズに合わせて調整してください。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class MacdSignalCrossoverStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private bool _prevMacdAboveSignal;
	private bool _hasPrev;

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public MacdSignalCrossoverStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 23);
		_slowPeriod = Param(nameof(SlowPeriod), 40);
		_signalPeriod = Param(nameof(SignalPeriod), 8);
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame());
	}

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

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

		_prevMacdAboveSignal = false;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastPeriod },
				LongMa = { Length = SlowPeriod },
			},
			SignalMa = { Length = SignalPeriod }
		};

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

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

		if (!macdValue.IsFinal)
			return;

		var macdTyped = macdValue as MovingAverageConvergenceDivergenceSignalValue;
		if (macdTyped == null)
			return;

		var macdLine = macdTyped.Macd;
		var signalLine = macdTyped.Signal;

		if (macdLine == null || signalLine == null)
			return;

		var isMacdAboveSignal = macdLine.Value > signalLine.Value;

		if (!_hasPrev)
		{
			_hasPrev = true;
			_prevMacdAboveSignal = isMacdAboveSignal;
			return;
		}

		var crossedAbove = isMacdAboveSignal && !_prevMacdAboveSignal;
		var crossedBelow = !isMacdAboveSignal && _prevMacdAboveSignal;

		if (crossedAbove)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (crossedBelow)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevMacdAboveSignal = isMacdAboveSignal;
	}
}