GitHub で見る

ZigZag EA 戦略

概要

この戦略は、オリジナルのMT5「ZigZag EA」ロジックを複製し、3つの連続したZigZagスイングポイントを待ち、前のスイング範囲の周りに2つのブレイクアウトストップ注文を配置します。変換はStockSharpの高レベルAPIを使用し、完成したローソク足で作業します。最後の2つの完成したスイングがトレードコリドーを定義し、最も最近のスイング(MQLバージョンの「room 0」)は、戦略が保留注文で武装する前にそのコリドー内に留まらなければなりません。アプローチは対称的です:buy-stopとsell-stop注文の両方を準備し、市場がブレイクアウトの方向を決定させます。

インジケーターと市場データ

  • Highest / Lowest: StockSharpはMT ZigZagインジケーターを直接公開していないため、変換は選択された深さで転がる最高値と最低値を追跡することでZigZag動作を模倣します。方向の変化は、元のEAがZigZagバッファを読み取るのと同様に、内部スイングバッファを更新します。
  • ローソク足: 戦略は設定可能なローソク足タイプ(デフォルト:1分足タイムフレーム)にサブスクライブし、バックテストとリアルトレードとの互換性を維持するために完成したローソク足のみで作業します。

トレードロジック

  1. 最後の3つのスイング値を収集します。2つの前の値がコリドー(high/low)を決定し、最後の値はブローカーのストップレベルによって定義された小さなバッファでコリドー内に留まらなければなりません。
  2. コリドーサイズ制限(MinCorridorPipsMaxCorridorPips)を適用します。ノイズを避けるために狭すぎるコリドーは無視され、巨大なストップを避けるために過度に広いコリドーはフィルタリングされます。
  3. コリドーが有効でポジションが開いていないと、対称的な保留注文を配置します:
    • Buy stophigh + EntryOffsetPips
    • Sell stoplow - EntryOffsetPips
  4. ストップとターゲットはMQL実装と全く同様にフィボナッチ比率から計算されます:FiboStopLossがコリドーの高さを乗算し、FiboTakeProfitが選択されたフィボナッチプロジェクションからコリドーを引きます。価格は拒否を避けるためにインストゥルメントのティックサイズに丸められます。
  5. 保留注文がトリガーされると、残りの保留注文はキャンセルされ、保護的なストップロスとテイクプロフィットが即座に登録されます。オプションのトレーリングロジックは、価格がトレーリング距離を超えてTrailingStepPips進んだときにストップを引き締めます。
  6. 戦略はポジションがゼロに戻ると自動的にクローズして再武装します。

リスクと注文管理

  • 保護的なストップとターゲット注文はライブのストップ/リミット注文なので、ブローカーが実行を制御し、ギャップは自然に処理されます。
  • トレーリングストップロジックはEAから引き継がれています:利益がTrailingStopPips + TrailingStepPipsを超えた後に活性化し、その後距離が少なくとも1つのトレーリングステップ増加するたびにストップを再登録します。
  • 戦略はStockSharpのStrategyクラスのベースVolumeパラメーターを使用します。MQLバージョンのマネーマネジメントブロック(固定ロット対リスクパーセンテージ)は、StockSharpではポジションサイジングが通常ブローカー固有であるため、意図的に省略されています。

セッションフィルター

  • トレードはStartHour:StartMinuteからStopHour:StopMinuteの間のみ許可されます。ストップ時刻が開始時刻より早い場合、戦略はそれを夜間セッションとして扱い、深夜を超えたトレードを許可します。
  • セッションが閉じるたびに保留注文がキャンセルされ、許可されたウィンドウ外の注文を削除したMQL動作を反映します。

パラメーター

名前 説明 デフォルト
CandleType 分析に使用するローソク足シリーズ。 1分足ローソク
ZigZagDepth スイング検出のためのローソク足数。 12
EntryOffsetPips コリドーの上下に追加されるオフセット。 5
MinCorridorPips セットアップを検証するための最小コリドー高。 20
MaxCorridorPips 許可される最大コリドー高。 100
FiboStopLoss ストップロス距離の計算に使用されるフィボナッチレベル。 61.8%
FiboTakeProfit 利益目標に使用されるフィボナッチレベル。 161.8%
StartHour / StartMinute トレードウィンドウの開始。 00:01
StopHour / StopMinute トレードウィンドウの終了。 23:59
TrailingStopPips トレーリングストップが使用する距離。 5
TrailingStepPips トレーリングストップを移動するために必要な最小改善。 5
DrawCorridorLevels 有効にすると、戦略は参照のためにチャートに垂直コリドーマーカーを描画します。 false

実装ノート

  • pip値はインストゥルメントのティックサイズから計算されます。3桁または5桁の小数を持つインストゥルメントは自動的にティックを10倍し、EAで使用される「adjusted point」ロジックを複製します。
  • コードはプロジェクトガイドラインに沿って、BuyStopSellStopSellLimitBuyLimitなどの高レベルヘルパーメソッドを使用します。
  • コメントはリポジトリ要件を満たすために英語で維持され、詳細な説明はREADMEファイルを通じて複数の言語で提供されます。
  • Pythonポートは作成されません;フォルダーはリクエスト通りC#実装のみを含みます。
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>
/// ZigZag breakout strategy using highest/lowest channels.
/// Buys on breakout above recent high, sells on breakdown below recent low.
/// </summary>
public class ZigZagEAStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _depth;

	private decimal? _prevHigh;
	private decimal? _prevLow;

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

	public int Depth
	{
		get => _depth.Value;
		set => _depth.Value = value;
	}

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

		_depth = Param(nameof(Depth), 20)
			.SetGreaterThanZero()
			.SetDisplay("Depth", "Channel lookback period", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = null;
		_prevLow = null;
	}

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

		_prevHigh = null;
		_prevLow = null;

		var highest = new Highest { Length = Depth };
		var lowest = new Lowest { Length = Depth };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, highest);
			DrawIndicator(area, lowest);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevHigh = high;
			_prevLow = low;
			return;
		}

		if (_prevHigh == null || _prevLow == null)
		{
			_prevHigh = high;
			_prevLow = low;
			return;
		}

		var close = candle.ClosePrice;

		// Breakout above channel high
		if (close > _prevHigh.Value && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Breakdown below channel low
		else if (close < _prevLow.Value && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevHigh = high;
		_prevLow = low;
	}
}