GitHub で見る

Macd パターン トレーダー ダブルトップ戦略

概要

MetaTrader 4 エキスパート アドバイザー MacdPatternTraderv04cb のポート。この戦略は、構成可能な MACD 主回線をスキャンします。 弱気のダブルトップパターンと強気のダブルボトムパターン。 MACD の間に 2 回目のスイングが最初のスイングを超えられなかった場合 プラスまたはマイナスのトリガーレベルを超えたままの場合、戦略は市場ポジションを次の方向に開きます。 予想される逆転。プロテクト注文は、元の固定の 100 ピップのストップロスと 300 ピップのテイクプロフィット距離を再現します。

取引ルール

  1. 選択したローソク足シリーズ (デフォルト: 30 分の時間枠) をサブスクライブし、次の値を使用して MACD メインラインを計算します。 構成された高速、低速、および信号期間 (デフォルト: 5、13、および 1)。
  2. 完了した最後の 3 つの MACD 値を追跡します。 MACD が TriggerLevel を上回れば、弱気のセットアップは準備完了です。 局所的な高値を形成してから下落します。セットアップは、次の MACD の高値が以前に保存された高値より低いかどうかを検証します。 MACD がまだトリガーを上回っている間は High になります。その瞬間に市場の売りが送信されます。
  3. 同じロジックをゼロ以下でミラーリングします。 MACD が -TriggerLevel を下回ったままになると、谷と次の谷が形成されます 前回よりも高い場合、この戦略は市場での買いを開始します。
  4. MACD の線が [-TriggerLevel, TriggerLevel] 内で交差するたびに、保存されている山と谷をリセットします。 範囲。これは、勢いが弱まったときにパターン検索をキャンセルする元の EA の動作と一致します。
  5. ポジションサイズは設定された TradeVolume から始まります。方向を切り替えるとき、この戦略は十分なボリュームを追加します。 新しい取引を確立する前に、反対のエクスポージャをフラット化します。
  6. 開始時に StartProtection を 1 回呼び出して、100 ピップのストップロスと 300 ピップのテイクプロフィットの両方が 再起動後もプラットフォーム。

パラメーター

名前 説明
FastPeriod MACD によって使用される高速な EMA の長さ。
SlowPeriod MACD が使用する低速の EMA の長さ。
SignalPeriod MACD の信号線の平滑化長。
TriggerLevel ダブルトップ/ダブルボトム検出を有効にするには絶対 MACD レベルが必要です。
StopLossPips ピップ単位での保護停止の距離 (デフォルトは 100)。
TakeProfitPips 利益確定の距離 (pips 単位) (デフォルトは 300)。
TradeVolume 新しいポジションの基本注文量。
CandleType インジケーターの計算に使用されるローソク足シリーズ。

注意事項

  • ストップロスとテイクプロフィットは、ピップスから商品ステップに変換されてから、 StartProtection、動作は元の MQL4 エキスパートと同一に保たれます。
  • リポジトリの要求に応じて、C# ソース コード内のすべてのインジケーターと取引コメントは英語で書かれています。 ガイドライン。
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;

/// <summary>
/// Port of the MetaTrader 4 expert advisor MacdPatternTraderv04cb.
/// Detects bearish and bullish double-top/double-bottom patterns on the MACD main line.
/// The second swing needs to be weaker than the first one before a market order is sent.
/// Fixed protective orders replicate the original 100/300 pip stop-loss and take-profit distances.
/// </summary>
public class MacdPatternTraderDoubleTopStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<decimal> _triggerLevel;
	private readonly StrategyParam<decimal> _stopLossPips;
	private readonly StrategyParam<decimal> _takeProfitPips;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private MovingAverageConvergenceDivergenceSignal _macd = null!;
	private decimal? _previousMacd;
	private decimal? _previousMacd2;
	private decimal? _firstPeak;
	private decimal? _firstTrough;
	private bool _sellPatternArmed;
	private bool _buyPatternArmed;

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public MacdPatternTraderDoubleTopStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 5)
		.SetGreaterThanZero()
		.SetDisplay("Fast EMA", "Fast moving average length used by MACD", "MACD")
		
		.SetOptimize(3, 15, 1);

		_slowPeriod = Param(nameof(SlowPeriod), 13)
		.SetGreaterThanZero()
		.SetDisplay("Slow EMA", "Slow moving average length used by MACD", "MACD")
		
		.SetOptimize(10, 30, 1);

		_signalPeriod = Param(nameof(SignalPeriod), 1)
		.SetGreaterThanZero()
		.SetDisplay("Signal EMA", "Signal smoothing period for MACD", "MACD")
		
		.SetOptimize(1, 9, 1);

		_triggerLevel = Param(nameof(TriggerLevel), 50m)
		.SetNotNegative()
		.SetDisplay("Trigger Level", "Absolute MACD level that arms the pattern logic", "MACD");

		_stopLossPips = Param(nameof(StopLossPips), 100m)
		.SetNotNegative()
		.SetDisplay("Stop-Loss (pips)", "Stop-loss distance expressed in pips", "Risk Management")
		
		.SetOptimize(50m, 200m, 10m);

		_takeProfitPips = Param(nameof(TakeProfitPips), 300m)
		.SetNotNegative()
		.SetDisplay("Take-Profit (pips)", "Take-profit distance expressed in pips", "Risk Management")
		
		.SetOptimize(150m, 500m, 10m);

		_tradeVolume = Param(nameof(TradeVolume), 0.1m)
		.SetGreaterThanZero()
		.SetDisplay("Trade Volume", "Order volume used for new entries", "Trading")
		
		.SetOptimize(0.05m, 1m, 0.05m);

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

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

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

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

/// <summary>
/// Absolute MACD level that enables pattern tracking.
/// </summary>
public decimal TriggerLevel
{
	get => _triggerLevel.Value;
	set => _triggerLevel.Value = value;
}

/// <summary>
/// Stop-loss distance expressed in pips.
/// </summary>
public decimal StopLossPips
{
	get => _stopLossPips.Value;
	set => _stopLossPips.Value = value;
}

/// <summary>
/// Take-profit distance expressed in pips.
/// </summary>
public decimal TakeProfitPips
{
	get => _takeProfitPips.Value;
	set => _takeProfitPips.Value = value;
}

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

/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
	get => _candleType.Value;
	set => _candleType.Value = value;
}

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

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

	_previousMacd = null;
	_previousMacd2 = null;
	_firstPeak = null;
	_firstTrough = null;
	_sellPatternArmed = false;
	_buyPatternArmed = false;
}

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

	Volume = TradeVolume;

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

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

var pipSize = Security?.PriceStep ?? 0.01m;
if (pipSize <= 0m) pipSize = 0.01m;

StartProtection(
	takeProfit: TakeProfitPips > 0m ? new Unit(TakeProfitPips * pipSize, UnitTypes.Absolute) : null,
	stopLoss: StopLossPips > 0m ? new Unit(StopLossPips * pipSize, UnitTypes.Absolute) : null,
	useMarketOrders: true);
}

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

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

	if (typed.Macd is not decimal macdLine)
	return;

	var previous = _previousMacd;
	var previous2 = _previousMacd2;

	if (previous.HasValue && previous2.HasValue)
	{
		ProcessSellPattern(macdLine, previous.Value, previous2.Value);
		ProcessBuyPattern(macdLine, previous.Value, previous2.Value);
	}

_previousMacd2 = previous;
_previousMacd = macdLine;
}

private void ProcessSellPattern(decimal current, decimal previous, decimal previous2)
{
	if (current > TriggerLevel && current < previous && previous > previous2)
	{
		if (!_sellPatternArmed)
		{
			_firstPeak = previous;
			_sellPatternArmed = true;
		}
	else if (_firstPeak is decimal referencePeak && previous < referencePeak)
	{
		EnterShort();
		ResetSellPattern();
	}
}
else if (current < TriggerLevel)
{
	ResetSellPattern();
}
}

private void ProcessBuyPattern(decimal current, decimal previous, decimal previous2)
{
	var negativeTrigger = -TriggerLevel;

	if (current < negativeTrigger && current > previous && previous < previous2)
	{
		if (!_buyPatternArmed)
		{
			_firstTrough = previous;
			_buyPatternArmed = true;
		}
	else if (_firstTrough is decimal referenceTrough && previous > referenceTrough)
	{
		EnterLong();
		ResetBuyPattern();
	}
}
else if (current > negativeTrigger)
{
	ResetBuyPattern();
}
}

private void EnterShort()
{
	if (!IsFormedAndOnlineAndAllowTrading())
	return;

	var volume = Volume + Math.Max(0m, Position);
	if (volume <= 0m)
	return;

	SellMarket(volume);
}

private void EnterLong()
{
	if (!IsFormedAndOnlineAndAllowTrading())
	return;

	var volume = Volume + Math.Max(0m, -Position);
	if (volume <= 0m)
	return;

	BuyMarket(volume);
}

private void ResetSellPattern()
{
	_sellPatternArmed = false;
	_firstPeak = null;
}

private void ResetBuyPattern()
{
	_buyPatternArmed = false;
	_firstTrough = null;
}

}