GitHub で見る

MACD Sample トレンドフィルター戦略

この戦略は、MetaTrader 5のクラシックエキスパートアドバイザー MACD Sample の直接ポートです。EMAトレンド検出器でフィルタリングされたMACDクロスオーバーを使用します。注文は標準の Volume プロパティでサイズ設定され、リスク管理はMACDヒストグラム、テイクプロフィット、トレーリングストップの設定可能なピップ閾値に基づいています。

コアロジック

  • インジケーター
    • MovingAverageConvergenceDivergenceSignal(期間 (12, 26, 9))がMACDとシグナルラインを提供します。
    • ExponentialMovingAverage(期間 26)がトレンドフィルターとして機能します。
  • エントリー条件
    • ロング:MACDがゼロ以下でシグナルラインを上抜け、MACDオープンレベル以上の大きさがあり、EMAが上昇中。
    • ショート:MACDがゼロ以上でシグナルラインを下抜け、MACDオープンレベル以上の大きさがあり、EMAが下降中。
  • エグジット条件
    • MACDがMACDクローズレベル以上の大きさでポジションに逆らって交差。
    • テイクプロフィットがエントリー価格から設定されたピップ距離に達する。
    • トレーリングストップ(利益 > トレーリング距離で起動された場合)がヒット。
  • トレーリングストップの仕組み
    • ロングポジションは、高値がエントリー価格をトレーリング距離だけ超えるとトレーリングストップが起動します。ストップは 高値 − トレーリング距離 に維持されます。
    • ショートポジションは、安値がエントリー価格をトレーリング距離だけ下回るとトレーリングストップが起動します。ストップは 安値 + トレーリング距離 に維持されます。

パラメーター

パラメーター デフォルト値 説明
FastPeriod 12 MACD内の高速EMA期間。
SlowPeriod 26 MACD内の低速EMA期間。
SignalPeriod 9 MACD内のシグナルEMA期間。
TrendPeriod 26 EMAトレンドフィルターの長さ。
MacdOpenLevelPips 3 トレードをオープンするために必要な最小MACDの大きさ(ピップ)。
MacdCloseLevelPips 2 クロスオーバーでトレードをクローズするために必要な最小MACDの大きさ(ピップ)。
TakeProfitPips 50 ピップで表されたテイクプロフィット距離。
TrailingStopPips 30 ピップで表されたトレーリングストップ距離。トレーリングを無効にするには0に設定。
CandleType 15分時間軸 計算に使用されるローソク足タイプ。

ピップ変換

オリジナルのエキスパートはMetaTraderのピップ正規化(3/5桁のシンボルに対して10を掛ける)を使用していました。変換は Security.PriceStep を検査することで同じアイデアに従います:

  • 価格ステップが3または5の小数点以下の桁数を持つ場合、ピップサイズは PriceStep * 10 です。
  • それ以外の場合、ピップサイズは PriceStep に等しくなります。
  • 価格ステップが利用できない場合、ピップベースの閾値は生の値にフォールバックします。

動作上の注意

  • 新しいシグナルが評価される前にポジションがクローズされ、MT5実装を反映します。
  • LogInfo ステートメントがエントリー、エグジット、トレーリングストップ更新を報告し、デバッグを容易にします。
  • 保護注文は自動的に配置されません;エグジットはEAのロジックを模倣するために ProcessCandle 内で管理されます。
  • Volume を使用してベーストレードサイズを定義します。反転は注文ボリュームに Math.Abs(Position) を加算することで現在のエクスポージャーを自動的にオフセットします。

MQL5バージョンとの違い

  • 処理は各ティックではなく完成したローソク足で行われます。これにより繰り返しシグナルを避けながら決定論的な動作を維持します。
  • トレーリングストップとテイクプロフィットのチェックは、オリジナルEAからのBid/Askヒットを近似するためにローソク足の高値と安値を使用します。
  • Security.PriceStep が欠落している場合、ピップパラメーターは絶対価格距離として機能し、手動で調整する必要があります。

特にティックサイズが異なる市場にポーティングする際は、取引する銘柄に合わせてピップ閾値とローソク足タイプを調整してください。

using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MACD strategy with EMA trend filter.
/// Buys when MACD histogram is positive and price is above trend EMA.
/// Sells when MACD histogram is negative and price is below trend EMA.
/// </summary>
public class MacdSampleTrendFilterStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _trendPeriod;

	private int _prevSignal;

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

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

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

	public int TrendPeriod
	{
		get => _trendPeriod.Value;
		set => _trendPeriod.Value = value;
	}

	public MacdSampleTrendFilterStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA for MACD", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA for MACD", "Indicators");

		_trendPeriod = Param(nameof(TrendPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("Trend Period", "Trend EMA period", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevSignal = 0;
	}

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

		_prevSignal = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
		var trendEma = new ExponentialMovingAverage { Length = TrendPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, trendEma, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal trend)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var close = candle.ClosePrice;
		var macdLine = fast - slow;

		// Signal: MACD positive + price above trend = bullish; MACD negative + price below trend = bearish
		var signal = 0;
		if (macdLine > 0 && close > trend)
			signal = 1;
		else if (macdLine < 0 && close < trend)
			signal = -1;

		if (signal == _prevSignal)
			return;

		var oldSignal = _prevSignal;
		_prevSignal = signal;

		if (signal == 1 && oldSignal <= 0)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		else if (signal == -1 && oldSignal >= 0)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}
	}
}