GitHub で見る

フラクタル ジグザグ戦略

この戦略は、MetaTrader 4 エキスパート アドバイザー Fractal ZigZag Expert.mq4 の直接移植です。法案を再構築します Williams フラクタル シーケンスを作成し、最も最近確認された極値をアクティブな市場レッグとして解釈します。最新の有効なフラクタルが スイングが低くなると、システムはロングポジションを開きます。スイングハイが確認されたらショートをオープンします。実装では、 元のパラメータ — フラクタル深度、テイクプロフィット、イニシャルストップとトレーリングストップの距離 — に注文ルーティングを適応させながら、 高レベルの StockSharp API。

この戦略は H1 ローソク足に最適で、MetaTrader バージョンで使用されるデフォルトのチャートを複製します。それにもかかわらず、 CandleType パラメータを使用すると、データ フィードでサポートされている他の時間枠に切り替えることができます。すべての距離は価格で表現されます ポイント(商品価格ステップ)。これは、MetaTrader が Point 定数を使用する方法を反映しています。

取引ルール

  • 信号検出
    • アルゴリズムは完成した各ローソクをスキャンし、2 * Level + 1 要素を含むローリング ウィンドウを構築します。
    • 中央のローソク足がそのウィンドウ内で最高の高さを示す場合、高いフラクタルが確認されます。低フラクタルには最低の値が必要です 低い。
    • 最新に確認されたフラクタルのみが方向を制御します。低値では内部トレンドが 2 (強気) に設定され、高値では内部トレンドが に設定されます。 1 (弱気)。
  • エントリー
    • 内部トレンドが 2 に等しく、オープン ポジションがない場合、成行買いは Lots のボリュームを使用して送信されます。
    • トレンドがポジションなしで 1 に等しい場合、市場売りが送信されます。
    • トレンドが反転していない場合、ポジションを閉じた後、戦略は同じ方向に再エントリーします。
  • 出口とリスク管理
    • すべてのエントリーは、ポイントで定義された初期ストップロスと固定テイクプロフィットを受け取ります。ストップ値 0 は、 それぞれの保護。
    • 設定された距離だけ価格が変動すると、オプションのトレーリングストップ (ポイント単位でも) が有効になります。その後、停留所は次の場所に移動します。 終値からの同じオフセットを維持し、最初の保護ストップを決して超えません。
    • 保護注文は、ローソク足の高値/安値を監視してバー内タッチを近似することによってエミュレートされ、元の値と厳密に一致します。 MQL4 ロジック。

デフォルトパラメータ

パラメータ デフォルト 説明
Level 2 フラクタルを確認するために必要な各辺のキャンドルの数。
TakeProfitPoints 25 価格ポイントでのテイクプロフィットターゲットまでの距離。
InitialStopPoints 20 価格ポイントでの最初のストップロスまでの距離。
TrailingStopPoints 10 価格ポイント単位のトレーリング ストップ距離 (無効にするには 0 に設定します)。
Lots 1 市場エントリーに使用される注文量。
CandleType H1 戦略によって処理されるローソク足のタイムフレーム。

注意事項

  • このストラテジーは起動時に StartProtection() を 1 回呼び出して、必要に応じて StockSharp が緊急ポジションの清算を管理できるようにします。
  • すべてのログとコメントは英語で提供されますが、説明は、要件に応じて各 README バリアントの言語に従います。 変換ガイドライン。
  • この実装ではインジケーターのバッファーを回避し、最小限のローリング ウィンドウのみを維持することで MetaTrader のアプローチを模倣します。 フラクタルを評価するために必要です。
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>
/// Fractal ZigZag: Confirms Bill Williams fractals then trades
/// in the direction of the last confirmed extremum.
/// Bullish after low fractal, bearish after high fractal.
/// </summary>
public class FractalZigZagStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _level;
	private readonly StrategyParam<int> _atrLength;

	private readonly List<(decimal high, decimal low, DateTimeOffset time)> _window = new();
	private int _trend; // 1=bearish (last was high), 2=bullish (last was low)
	private int _prevTrend;
	private decimal _entryPrice;

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

		_level = Param(nameof(Level), 2)
			.SetDisplay("Fractal Depth", "Candles on each side to confirm fractal.", "Signals");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

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

	public int Level
	{
		get => _level.Value;
		set => _level.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_window.Clear();
		_trend = 0;
		_prevTrend = 0;
		_entryPrice = 0;
	}

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

		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		// Update fractal window
		var depth = Math.Max(1, Level);
		var windowSize = depth * 2 + 1;

		_window.Add((candle.HighPrice, candle.LowPrice, candle.OpenTime));
		while (_window.Count > windowSize)
			_window.RemoveAt(0);

		// Evaluate fractals
		if (_window.Count >= windowSize)
		{
			var centerIndex = _window.Count - 1 - depth;
			var center = _window[centerIndex];
			var isHigh = true;
			var isLow = true;

			for (var i = 0; i < _window.Count; i++)
			{
				if (i == centerIndex)
					continue;

				if (_window[i].high >= center.high)
					isHigh = false;
				if (_window[i].low <= center.low)
					isLow = false;

				if (!isHigh && !isLow)
					break;
			}

			if (isHigh)
				_trend = 1; // bearish: last fractal was a high
			if (isLow)
				_trend = 2; // bullish: last fractal was a low
		}

		if (atrVal <= 0 || _trend == 0)
		{
			_prevTrend = _trend;
			return;
		}

		var close = candle.ClosePrice;

		// Exit management
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m || _trend == 1)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m || _trend == 2)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry on trend change
		if (Position == 0 && _prevTrend != 0 && _trend != _prevTrend)
		{
			if (_trend == 2)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_trend == 1)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevTrend = _trend;
	}
}