GitHub で見る

Simple MACD 戦略

Simple MACDは、MQL5アドバイザーSimple_MACD.mq5のロジックをStockSharpで再現します。戦略は完成したローソク足で計算されたMACD主線の傾きに従い、傾きが同じ方向を維持する限りポジションに追加し続けます。

概要

  • 市場: ローソク足データと継続的な取引時間を持つあらゆる銘柄。
  • コアインジケーター: 指数移動平均12/26とシグナル9を使用した移動平均収束ダイバージェンス(MACD)。
  • アプローチ: モメンタムフォロー。戦略は最近の2つの完成したMACD読み値を比較し、ラインが上昇するときはロング、ラインが下落するときはショートになります。
  • 注文タイプ: マーケット注文のみ。各シグナルは反対ポジションをクローズするために必要な量を集計し、設定された取引ボリュームを上に追加します。これはオリジナルのエキスパートアドバイザーを模倣しています。

変換上の注意

  • MQL5ボットはMACD(1)MACD(2)(前の2つの完成したバー)を比較することで、新しいバーごとに1回トリガーされました。StockSharpでは、次のバーが始まる前にローソク足が終了したときに同じ比較が実行されます。
  • MQLバージョンは明示的なポジション列挙と手動ボリューム確認に依存していました。StockSharpバージョンはBuyMarket/SellMarket呼び出しと戦略のTradeVolumeパラメーターでボリュームを自動的に集計します。
  • StockSharpは純ポジションを直接追跡するため、MQLコードのヘッジチェックは不要です。

トレードルール

エントリーとスケーリング

  1. 完成した各ローソク足でMACD主線を計算します。
  2. 最後の2つのMACD値を保存して比較します:
    • MACD(1) > MACD(2)の場合、傾きは強気です。戦略はショートをクローズし新しいロングを追加するためにTradeVolume + max(0, -ポジション)に等しいボリュームを買います。
    • MACD(1) < MACD(2)の場合、傾きは弱気です。戦略はロングをクローズし新しいショートを追加するためにTradeVolume + max(0, ポジション)を売ります。
  3. 両方の値が等しい場合、新しい注文は送信されません。

ポジション管理

  • 戦略はMACDの傾きが符号を変えない限り、現在の方向への注文を積み重ね続けます。これはすべての適格なバーで買いまたは売りを送信したオリジナルのアドバイザーと同様です。
  • 反対のシグナルは新しいポジションを構築する前に開いているすべてのエクスポージャーを解消します。
  • ストップロスまたはテイクプロフィットレベルは組み込まれていません。リスク管理は外部のマネー管理ルールまたは手動監督に依存します。

追加の保護措置

  • MACDインジケーターが完全に形成されるまでトレーディングはスキップされます。
  • 部分データでの早期アクションを防ぐために、完成したローソク足(CandleStates.Finished)のみが処理されます。
  • ログメッセージは各取引を追跡し、バックテスト分析を容易にするために意思決定に使用された2つのMACD値を表示します。

パラメーター

パラメーター デフォルト値 説明
FastPeriod 12 MACD計算の高速EMA長。
SlowPeriod 26 MACD計算の低速EMA長。
SignalPeriod 9 オリジナル設定との互換性のために保持されたシグナルEMA期間。
TradeVolume 0.1 ポジション反転を考慮する前に各シグナルで追加されるボリューム。
CandleType 1分時間軸 インジケーターに使用されるローソク足タイプ。任意の時間軸に調整可能。

すべてのパラメーターは戦略パラメーターとして公開され、意味のある場合は最適化可能とマークされています。

可視化

  • 戦略は価格ローソク足と共にチャートエリアを自動的に作成し(利用可能な場合)、MACDインジケーターの出力を重ねます。
  • 自己取引はチャートに描画され、トレンド条件下で戦略がどのくらいの頻度でポジションをスケーリングするかを示します。

推奨される使用方法

  • モメンタムが数バー持続するトレンド銘柄で適用してください。レンジバウンドな市場では頻繁な反転とホイップソー取引が発生します。
  • ベースロジックには固有のストップメカニズムがないため、ポートフォリオレベルのリスク管理と組み合わせてください。
  • ターゲット銘柄と時間軸に合わせてTradeVolumeとMACDの期間を最適化することを検討してください。

ファイル

  • CS/SimpleMacdStrategy.cs – 戦略ロジックのStockSharp実装。
  • README.mdREADME_ru.mdREADME_zh.md – 3言語の詳細なドキュメント。
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>
/// Simple MACD slope-following strategy converted from MQL5 Simple_MACD.mq5.
/// The strategy evaluates the MACD main line on completed candles and builds positions accordingly.
/// </summary>
public class SimpleMacdStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergence _macd = null!;
	private decimal? _previousMacdValue;
	private decimal? _prePreviousMacdValue;
	private int? _previousSlope;

	/// <summary>
	/// Fast EMA period used for the MACD main line.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period used for the MACD main line.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

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

	/// <summary>
	/// Trading volume applied when new orders are sent.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Candle type used to feed the MACD indicator.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initialize Simple MACD strategy with default parameters.
	/// </summary>
	public SimpleMacdStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetDisplay("MACD Fast Period", "Fast EMA length for MACD calculation", "Indicators")
			
			.SetOptimize(6, 18, 2);

		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetDisplay("MACD Slow Period", "Slow EMA length for MACD calculation", "Indicators")
			
			.SetOptimize(20, 40, 2);

		_signalPeriod = Param(nameof(SignalPeriod), 9)
			.SetDisplay("MACD Signal Period", "Signal EMA length maintained for compatibility", "Indicators")
			
			.SetOptimize(6, 18, 1);

		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
			.SetDisplay("Trade Volume", "Order volume used for each signal", "Risk")
			
			.SetOptimize(0.1m, 1m, 0.1m);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for MACD calculations", "General");
	}

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

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

		_previousMacdValue = null;
		_prePreviousMacdValue = null;
		_previousSlope = null;
	}

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

		// Configure MACD indicator to match the source MQL strategy settings.
		_macd = new MovingAverageConvergenceDivergence
		{
			ShortMa = { Length = FastPeriod },
			LongMa = { Length = SlowPeriod },
		};

		// Subscribe to candle data and bind the MACD indicator.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_macd, ProcessCandle)
			.Start();

		// Prepare visual elements when charts are available.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _macd);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
	{
		// React only to completed candles to avoid premature decisions.
		if (candle.State != CandleStates.Finished)
			return;

		// Ensure the indicator produced a valid value.
		if (!_macd.IsFormed || !macdValue.IsFinal)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var macdLine = macdValue.ToDecimal();

		// Accumulate historical MACD values for slope calculations.
		if (_previousMacdValue is null)
		{
			_previousMacdValue = macdLine;
			return;
		}

		if (_prePreviousMacdValue is null)
		{
			_prePreviousMacdValue = _previousMacdValue;
			_previousMacdValue = macdLine;
			return;
		}

		var macdPrev = _previousMacdValue.Value;
		var macdPrevPrev = _prePreviousMacdValue.Value;

		var currentSlope = macdPrev > macdPrevPrev ? 1 : macdPrev < macdPrevPrev ? -1 : 0;

		if (currentSlope == 0)
		{
			_prePreviousMacdValue = _previousMacdValue;
			_previousMacdValue = macdLine;
			return;
		}

		if (_previousSlope == currentSlope)
		{
			_prePreviousMacdValue = _previousMacdValue;
			_previousMacdValue = macdLine;
			return;
		}

		if (currentSlope > 0)
		{
			// Close shorts and open (or add to) longs when the MACD slope turns positive.
			var volumeToBuy = TradeVolume + Math.Max(0m, -Position);
			if (volumeToBuy > 0m)
			{
				BuyMarket(volumeToBuy);
				LogInfo($"Bullish slope detected. MACD(1)={macdPrev:F5}, MACD(2)={macdPrevPrev:F5}. Buying {volumeToBuy}.");
			}
		}
		else
		{
			// Close longs and open (or add to) shorts when the MACD slope turns negative.
			var volumeToSell = TradeVolume + Math.Max(0m, Position);
			if (volumeToSell > 0m)
			{
				SellMarket(volumeToSell);
				LogInfo($"Bearish slope detected. MACD(1)={macdPrev:F5}, MACD(2)={macdPrevPrev:F5}. Selling {volumeToSell}.");
			}
		}

		// Update stored values so the next candle compares the two previous MACD readings.
		_previousSlope = currentSlope;
		_prePreviousMacdValue = _previousMacdValue;
		_previousMacdValue = macdLine;
	}
}