GitHub で見る

MACD Parabolic SARウィザード戦略

概要

この戦略は、MQL5ウィザードによって生成されたMetaTraderエキスパートアドバイザーのStockSharp変換で、MACDモメンタムとParabolic SARのトレンド方向を組み合わせています。ロジックは各インジケーターに正規化されたスコア(0..100)を割り当て、取引決定を行う前に貢献度を重み付けすることで、ウィザードのスコアリングメカニズムを再現します。

取引ロジック

  • インジケーター
    • MACD (12, 24, 9):ヒストグラムの符号が強気モメンタム(ヒストグラム > 0)または弱気モメンタム(ヒストグラム < 0)がアクティブかどうかを定義します。
    • Parabolic SAR (0.02, 0.2):SARポイント以上の終値は上昇トレンドと解釈され、SARポイント以下は下降トレンドと解釈されます。
  • スコア構築
    • MACDはロングサイドに100(強気)または0(弱気)ポイントを生成します。逆の値はショートサイドに使用されます。
    • Parabolic SARは同様に動作し、トレンドがそれぞれの方向に一致すると100ポイントを提供します。
    • 両方のスコアはユーザー定義の重み(MacdWeightSarWeight)を介して結合されます。デフォルトの重み(0.9と0.1)では、ウィザードテンプレートと同様にMACDが最終決定を支配します。
  • エントリールール
    • 強気スコアを計算:bullScore = macdBull * MacdWeight + sarBull * SarWeight
    • 弱気スコアを計算:bearScore = macdBear * MacdWeight + sarBear * SarWeight
    • bullScore >= OpenThreshold(デフォルト20)のときにロングポジションを開く(またはショートから反転)。
    • bearScore >= OpenThresholdのときにショートポジションを開く(またはロングから反転)。
  • エグジットルール
    • ロングポジションは弱気スコアが強い確認レベルCloseThreshold(デフォルト100)に達したときに閉じられます。
    • ショートポジションは強気スコアがCloseThresholdに達したときに閉じられます。
    • エグジットシグナルはエントリーシグナルの前に評価され、矛盾するトレードのクローズを優先する元のエキスパートアドバイザーの動作を模倣します。

リスク管理

  • StopLossPointsTakeProfitPointsはウィザードのポイントベースの資金管理を再現します。両方の値はインストゥルメントのPriceStepを使用して価格単位に変換され、StartProtectionに渡されます。
  • いずれかのパラメーターを0に設定して対応する保護注文を無効にします。

パラメーター

パラメーター 説明 デフォルト
MacdFastPeriod MACDの速いEMA期間。 12
MacdSlowPeriod MACDの遅いEMA期間。 24
MacdSignalPeriod MACDのシグナルSMA期間。 9
MacdWeight MACDスコアの重み(0..1)。 0.9
SarWeight Parabolic SARスコアの重み(0..1)。 0.1
OpenThreshold ポジションを開く/反転するための最小スコア。 20
CloseThreshold ポジションを終了するための最小反対スコア。 100
SarStep Parabolic SARの加速ステップ。 0.02
SarMax Parabolic SARの最大加速。 0.2
StopLossPoints 価格ポイントでのストップロス距離。 50
TakeProfitPoints 価格ポイントでのテイクプロフィット距離。 115
CandleType インジケーター計算のためのローソク足データソース。 15分足時間軸

使用上の注意

  • デフォルトパラメーターは.mq5テンプレートを反映しているため、戦略は元のウィザード生成エキスパートアドバイザーと一貫して動作します。
  • エントリーとエグジットの感度を変えるためにMacdWeightSarWeight、しきい値を調整します。例えば、OpenThresholdを増やすと新しいトレードを開く前により強い確認が必要になります。
  • 内部フィールド_lastBullScore_lastBearScoreは各バーで更新され、時間の経過とともに組み合わせスコアがどのように変化するかを監視する必要がある場合は記録または公開できます。
  • 戦略は完成したローソク足に依存するため、データフィードが選択したCandleTypeの完全なローソク足更新を提供することを確認してください。
  • 資金管理はポイントで表されます。保護注文が意図した距離に合致するように、選択したインストゥルメントが期待される価格ステップを使用していることを確認してください。
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>
/// Strategy that replicates the MetaTrader "MACD + Parabolic SAR" expert built with the MQL5 Wizard.
/// Combines the trend direction from Parabolic SAR with MACD momentum scores and uses weighted thresholds for decisions.
/// </summary>
public class MacdParabolicSarWizardStrategy : Strategy
{
	private readonly StrategyParam<int> _macdFastPeriod;
	private readonly StrategyParam<int> _macdSlowPeriod;
	private readonly StrategyParam<int> _macdSignalPeriod;
	private readonly StrategyParam<decimal> _macdWeight;
	private readonly StrategyParam<decimal> _sarWeight;
	private readonly StrategyParam<decimal> _openThreshold;
	private readonly StrategyParam<decimal> _closeThreshold;
	private readonly StrategyParam<decimal> _sarStep;
	private readonly StrategyParam<decimal> _sarMax;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergenceSignal _macd = null!;
	private ParabolicSar _parabolicSar = null!;
	private decimal _lastBullScore;
	private decimal _lastBearScore;

	/// <summary>
	/// Fast EMA period for MACD.
	/// </summary>
	public int MacdFastPeriod
	{
		get => _macdFastPeriod.Value;
		set => _macdFastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period for MACD.
	/// </summary>
	public int MacdSlowPeriod
	{
		get => _macdSlowPeriod.Value;
		set => _macdSlowPeriod.Value = value;
	}

	/// <summary>
	/// Signal line period for MACD.
	/// </summary>
	public int MacdSignalPeriod
	{
		get => _macdSignalPeriod.Value;
		set => _macdSignalPeriod.Value = value;
	}

	/// <summary>
	/// Weight of the MACD signal in the combined score (0..1).
	/// </summary>
	public decimal MacdWeight
	{
		get => _macdWeight.Value;
		set => _macdWeight.Value = value;
	}

	/// <summary>
	/// Weight of the Parabolic SAR signal in the combined score (0..1).
	/// </summary>
	public decimal SarWeight
	{
		get => _sarWeight.Value;
		set => _sarWeight.Value = value;
	}

	/// <summary>
	/// Minimum combined bullish or bearish score required to open a position.
	/// </summary>
	public decimal OpenThreshold
	{
		get => _openThreshold.Value;
		set => _openThreshold.Value = value;
	}

	/// <summary>
	/// Minimum opposite score required to exit the current position.
	/// </summary>
	public decimal CloseThreshold
	{
		get => _closeThreshold.Value;
		set => _closeThreshold.Value = value;
	}

	/// <summary>
	/// Acceleration step for Parabolic SAR.
	/// </summary>
	public decimal SarStep
	{
		get => _sarStep.Value;
		set => _sarStep.Value = value;
	}

	/// <summary>
	/// Maximum acceleration factor for Parabolic SAR.
	/// </summary>
	public decimal SarMax
	{
		get => _sarMax.Value;
		set => _sarMax.Value = value;
	}

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

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

	/// <summary>
	/// Type of candles used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initialize <see cref="MacdParabolicSarWizardStrategy"/>.
	/// </summary>
	public MacdParabolicSarWizardStrategy()
	{
		_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
		.SetDisplay("MACD Fast", "Fast EMA period for MACD", "MACD")
		;

		_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 24)
		.SetDisplay("MACD Slow", "Slow EMA period for MACD", "MACD")
		;

		_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
		.SetDisplay("MACD Signal", "Signal SMA period for MACD", "MACD")
		;

		_macdWeight = Param(nameof(MacdWeight), 0.9m)
		.SetDisplay("MACD Weight", "Relative weight of MACD in scoring", "Scoring")
		;

		_sarWeight = Param(nameof(SarWeight), 0.1m)
		.SetDisplay("SAR Weight", "Relative weight of SAR in scoring", "Scoring")
		;

		_openThreshold = Param(nameof(OpenThreshold), 90m)
		.SetDisplay("Open Threshold", "Score required to open trades", "Scoring")
		;

		_closeThreshold = Param(nameof(CloseThreshold), 90m)
		.SetDisplay("Close Threshold", "Score required to exit trades", "Scoring")
		;

		_sarStep = Param(nameof(SarStep), 0.02m)
		.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Parabolic SAR")
		;

		_sarMax = Param(nameof(SarMax), 0.2m)
		.SetDisplay("SAR Max", "Maximum acceleration for Parabolic SAR", "Parabolic SAR")
		;

		_stopLossPoints = Param(nameof(StopLossPoints), 50m)
		.SetDisplay("Stop Loss (pts)", "Stop-loss distance in points", "Risk")
		;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 115m)
		.SetDisplay("Take Profit (pts)", "Take-profit distance in points", "Risk")
		;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
		.SetDisplay("Candle Type", "Primary candle source", "General");
	}

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

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

		_lastBullScore = 0m;
		_lastBearScore = 0m;
	}

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

		// Configure MACD indicator replicating the wizard defaults.
		_macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = MacdFastPeriod },
				LongMa = { Length = MacdSlowPeriod },
			},
			SignalMa = { Length = MacdSignalPeriod }
		};

		// Configure Parabolic SAR indicator.
		_parabolicSar = new ParabolicSar
		{
			AccelerationStep = SarStep,
			AccelerationMax = SarMax,
		};

		// Subscribe to candles and bind indicators.
		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_macd, _parabolicSar, ProcessCandle)
			.Start();

		// Configure risk management using point-based distances.
		var step = Security?.PriceStep ?? 1m;
		var takeProfit = TakeProfitPoints > 0m ? new Unit(TakeProfitPoints * step, UnitTypes.Absolute) : new Unit();
		var stopLoss = StopLossPoints > 0m ? new Unit(StopLossPoints * step, UnitTypes.Absolute) : new Unit();

		StartProtection(takeProfit, stopLoss, useMarketOrders: true);

		// Prepare chart visualization if the environment supports it.
		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _parabolicSar);
			DrawIndicator(area, _macd);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue sarValue)
	{
		// Process only finished candles to avoid premature trades.
		if (candle.State != CandleStates.Finished)
			return;

		if (!macdValue.IsFinal || !sarValue.IsFinal)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
		if (macdTyped.Macd is not decimal macdLine || macdTyped.Signal is not decimal signalLine)
			return;

		var sar = sarValue.ToDecimal();

		// Translate indicator states into normalized scores (0..100).
		var macdBull = macdLine > signalLine ? 100m : 0m;
		var macdBear = macdLine < signalLine ? 100m : 0m;
		var sarBull = candle.ClosePrice > sar ? 100m : 0m;
		var sarBear = candle.ClosePrice < sar ? 100m : 0m;

		var bullScore = macdBull * MacdWeight + sarBull * SarWeight;
		var bearScore = macdBear * MacdWeight + sarBear * SarWeight;

		_lastBullScore = bullScore;
		_lastBearScore = bearScore;

		// Exit conditions take priority to mirror the wizard behaviour.
		if (Position > 0 && bearScore >= CloseThreshold)
		{
			SellMarket();
			return;
		}

		if (Position < 0 && bullScore >= CloseThreshold)
		{
			BuyMarket();
			return;
		}

		// Entry rules: open when the weighted score exceeds the open threshold.
		if (Position <= 0 && bullScore >= OpenThreshold)
		{
			BuyMarket();
			return;
		}

		if (Position >= 0 && bearScore >= OpenThreshold)
		{
			SellMarket();
		}
	}
}