GitHub で見る

ジグアンドザグメス戦略

概要

ZigAndZagScalpelStrategy は、MetaTrader 4 "ZigAndZag" ツールキット (フォルダー 8304) の StockSharp ポートです。 オリジナルのパッケージは、カスタムインジケーターとエキスパートアドバイザーを組み合わせています。 2 つの ZigZag ウィンドウが使用されます。

  • KeelOver – 支配的なトレンドをマークするロング ルックバック スイング検出器。
  • スラローム – 実行可能なブレイクアウトを定義する短いルックバック スイング検出器。

長期ジグザグが上向きに反転すると、戦略は次のスラローム安値を探し、価格を待ちます。 そのピボットの上に構成可能な数のポイントを上昇させます。買い成行注文は、 ブレークアウト距離が満たされています。対称ルールは、KeelOver トレンドが転換したときにショートポジションをオープンします 下がると、スラロームは最高値を更新し、価格はそれを下回ります。オプションでポジションをクローズすることができます 反対のスラロームピボットが確認されるとすぐに、インジケーターのリミットアローの削除を模倣します。

この実装により、エキスパートアドバイザーによる毎日の取引リミッターが維持されます。設定可能な数値のみ 取引日ごとに許可される取引の数は、午前 0 時 (為替時間) に自動的にリセットされます。これ 元のコードから「新しい日」フラグを再現します。

仕組み

  1. CandleType によって定義されたプライマリ キャンドル ストリームを購読します。
  2. 2 つの ZigZagIndicator インスタンスをフィードします。
    • トレンド検出器の深さ = KeelOverLength
    • 深さ = SlalomLength (エントリーシグナルの場合)。
  3. 最新の KeelOver ピボットを追跡して、トレンドが上昇しているかどうかを判断します (最後のピボットは安値です)。 またはダウン(最後のピボットはハイ)。
  4. スラローム インジケーターが新しいピボットを発行したら、その方向にブレイクアウトを準備します。
  5. 加重価格 (5×Close + 2×Open + High + Low) / 9 を計算します。価格が以上に変動した場合 トレンドがサポートしている間、ピボットから離れた BreakoutDistancePoints (価格単位に変換) 動き、成行注文を実行します。
  6. グローバルトレンドが反転するか、反対のスラロームピボットが現れたら、既存のポジションを閉じます。 CloseOnOppositePivot が有効になっています。
  7. 暦日が変わるたびに、日次取引カウンターをリセットします。

パラメータ DeviationPointsBackstep は両方の ZigZag インスタンス間で共有されるため、 スイング構造は MetaTrader インジケーター バッファーと一致します。

パラメーター

名前 デフォルト 説明
CandleType 15m 両方の ZigZag ラダーを構築するために使用される主な時間枠。
KeelOverLength 55 トレンドを定義する長期的なジグザグ ルックバック (オリジナルの KeelOver)。
SlalomLength 17 エントリに使用される短期 ZigZag ルックバック (オリジナル Slalom)。
DeviationPoints 5 新しい ZigZag ピボットが確認されるまでの最小スイング サイズ (ポイント単位)。
Backstep 3 連続するピボット間に必要なバーの距離。
BreakoutDistancePoints 2 注文を発行する前のピボットからの距離 (ポイント単位)。
MaxTradesPerDay 1 暦日あたりの最大エントリ数。元の newday フラグをミラーリングします。
CloseOnOppositePivot true スラローム ジグザグが逆のスイングを生成したら、オープン ポジションを閉じます。

すべてのポイントベースのパラメータは、Security.PriceStep を使用して価格単位に変換されます。楽器の場合 価格ステップが設定されていない場合、テスト中に戦略を機能し続けるために値 1 が使用されます。

使用上の注意

  • この戦略は成行注文 (BuyMarket / SellMarket) で動作します。独自のリスク ルールを追加する または、より厳格なリスク管理が必要な場合はストップロスヘルパーが必要です。
  • 両方の ZigZag インジケーターが同じローソク足ストリームを共有しているため、選択された CandleType が データアダプターでサポートされています。
  • MaxTradesPerDay = 1 は「1 日あたり 1 回の取引」の動作を再現します。必要に応じて値を増やします 同じセッション中の複数のエントリ。
  • CloseOnOppositePivot = false を設定すると、世界的なトレンドが反転するまでポジションを保持し続けるようになります。 あらゆる短期的なスイングに反応します。

MT4 エキスパートアドバイザーとの違い

  • MetaTrader バージョンには保留中の制限矢印が配置されました。 StockSharp ポートはブレークアウトを実行します 即時成行注文は高レベル API 内に収まるようにします。
  • リスク管理、ロットサイジング、部分的なクローズは意図的に省略されています。 StockSharp の位置を使用する 高度な資本管理が必要な場合は、サイジングヘルパーを使用します。
  • インジケーター バッファー 4/5/6 は、直接戦略ロジックとチャートの注釈に置き換えられます。 DrawIndicatorDrawOwnTrades

推奨される拡張機能

  • ATR または最近の ZigZag スイングに関連付けられたストップロスとテイクプロフィットのパラメーターを追加します。
  • 元のインジケーターを BreakoutDistancePoints = 0 でオーバーレイして、生のピボット ラダーを視覚化します。
  • セッションフィルター (IsFormedAndOnlineAndAllowTrading) と組み合わせて、取引時間を制限します。
namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

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

/// <summary>
/// ZigAndZagScalpel translation that trades on breakouts from short-term pivots confirmed by a long-term ZigZag trend.
/// </summary>
public class ZigAndZagScalpelStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maxTradesPerDay;
	private readonly StrategyParam<bool> _closeOnOppositePivot;

	private decimal _previousMajorPivot;
	private decimal _lastMajorPivot;
	private decimal _previousMinorPivot;
	private decimal _lastMinorPivot;
	private DateTime _currentDay = DateTime.MinValue;
	private int _tradesToday;
	private bool _trendUp;
	private PivotTypes _lastMinorPivotType = PivotTypes.None;
	private bool _minorPivotUsed;

	/// <summary>
	/// Initializes a new instance of the <see cref="ZigAndZagScalpelStrategy"/> class.
	/// </summary>
	public ZigAndZagScalpelStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe for all calculations", "General");

		_maxTradesPerDay = Param(nameof(MaxTradesPerDay), 1)
			.SetDisplay("Max Trades Per Day", "Daily limit matching the original expert advisor", "Trading");

		_closeOnOppositePivot = Param(nameof(CloseOnOppositePivot), true)
			.SetDisplay("Close On Opposite Pivot", "Exit when the entry ZigZag prints the opposite swing", "Risk");
	}

	/// <summary>
	/// Candle type used by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Maximum number of trades allowed per trading day.
	/// </summary>
	public int MaxTradesPerDay
	{
		get => _maxTradesPerDay.Value;
		set => _maxTradesPerDay.Value = value;
	}

	/// <summary>
	/// Determines whether open positions should be closed on the opposite entry pivot.
	/// </summary>
	public bool CloseOnOppositePivot
	{
		get => _closeOnOppositePivot.Value;
		set => _closeOnOppositePivot.Value = value;
	}

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

		_previousMajorPivot = 0m;
		_lastMajorPivot = 0m;
		_previousMinorPivot = 0m;
		_lastMinorPivot = 0m;
		_currentDay = DateTime.MinValue;
		_tradesToday = 0;
		_trendUp = false;
		_lastMinorPivotType = PivotTypes.None;
		_minorPivotUsed = false;
	}

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

		var majorZigZag = new ZigZag { Deviation = 0.02m };
		var minorZigZag = new ZigZag { Deviation = 0.005m };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindWithEmpty(majorZigZag, minorZigZag, ProcessCandle)
			.Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal? majorValue, decimal? minorValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		UpdateDailyCounter(candle.OpenTime);

		if (majorValue is not null)
			UpdateMajorTrend(majorValue.Value);

		if (minorValue is not null)
			UpdateMinorPivot(minorValue.Value);

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		ManageExistingPosition();

		if (Position != 0)
			return;

		if (_minorPivotUsed)
			return;

		if (_lastMinorPivotType == PivotTypes.None)
			return;

		if (_tradesToday >= MaxTradesPerDay)
			return;

		var navel = CalculateNavel(candle);

		if (_lastMinorPivotType == PivotTypes.Low && _trendUp)
		{
			if (navel > _lastMinorPivot)
			{
				BuyMarket();
				_minorPivotUsed = true;
				_tradesToday++;
			}
		}
		else if (_lastMinorPivotType == PivotTypes.High && !_trendUp)
		{
			if (navel < _lastMinorPivot)
			{
				SellMarket();
				_minorPivotUsed = true;
				_tradesToday++;
			}
		}
	}

	private void UpdateDailyCounter(DateTime time)
	{
		var date = time.Date;
		if (date == _currentDay)
			return;

		_currentDay = date;
		_tradesToday = 0;
	}

	private void UpdateMajorTrend(decimal majorValue)
	{
		if (_lastMajorPivot == 0m)
		{
			_lastMajorPivot = majorValue;
			_previousMajorPivot = majorValue;
			return;
		}

		if (majorValue == _lastMajorPivot)
			return;

		_previousMajorPivot = _lastMajorPivot;
		_lastMajorPivot = majorValue;
		_trendUp = _lastMajorPivot < _previousMajorPivot;
	}

	private void UpdateMinorPivot(decimal minorValue)
	{
		if (_lastMinorPivot == 0m)
		{
			_lastMinorPivot = minorValue;
			_previousMinorPivot = minorValue;
			_lastMinorPivotType = PivotTypes.Low;
			_minorPivotUsed = false;
			return;
		}

		if (minorValue == _lastMinorPivot)
			return;

		_previousMinorPivot = _lastMinorPivot;
		_lastMinorPivot = minorValue;
		_lastMinorPivotType = _lastMinorPivot < _previousMinorPivot ? PivotTypes.Low : PivotTypes.High;
		_minorPivotUsed = false;
	}

	private void ManageExistingPosition()
	{
		if (Position > 0)
		{
			if (!_trendUp || (CloseOnOppositePivot && _lastMinorPivotType == PivotTypes.High))
				SellMarket(Position);
		}
		else if (Position < 0)
		{
			if (_trendUp || (CloseOnOppositePivot && _lastMinorPivotType == PivotTypes.Low))
				BuyMarket(Position.Abs());
		}
	}

	private static decimal CalculateNavel(ICandleMessage candle)
	{
		return (5m * candle.ClosePrice + 2m * candle.OpenPrice + candle.HighPrice + candle.LowPrice) / 9m;
	}

	private enum PivotTypes
	{
		None,
		Low,
		High
	}
}