GitHub で見る

ギャップ戦略

連続するローソク足間の始値ギャップに反応するプライスアクション戦略です。新しいバーが設定可能なpips距離だけ前の高値または安値を超えて始値を開けるのを待ち、 予想される反転方向にエントリーし、固定のストップ、ターゲット、およびオプションの段階的なトレーリングストップでトレードを管理します。

仕組み

  1. 戦略は選択したタイムフレームを使用して単一のシンボルを監視します。
  2. 新しいローソク足が形成されると、前のローソク足と始値を比較します:
    • 始値が前の安値からGapPipsを差し引いた値を下回る場合、戦略は強気の反落を期待してロングポジションに入ります。
    • 始値が前の高値にGapPipsを加えた値を上回る場合、下方修正を予測してショートポジションに入ります。
  3. トレードに入ると、リスク管理は戦略内で完全に処理されます:
    • 固定のストップロスがエントリー価格よりStopLossPips低い(ロングの場合)または高い(ショートの場合)位置に設定されます。
    • 固定のテイクプロフィットがトレードの方向にエントリー価格からTakeProfitPipsの距離に設定されます。
    • トレーリングストップを有効にできます。価格がTrailingStopPips + TrailingStepPipsを超えて進んだ後にのみ移動し、その後最も有利な価格からTrailingStopPipsだけストップを維持して利益を確保します。
  4. 保護レベルは各完了したローソク足で高値/安値の極値を使用して評価されるため、バー内のタッチが確実にエグジットをトリガーします。
  5. 新しいポジションを取る前にオープンな注文がキャンセルされ、ポジションの反転が自動的に反対側をクローズします。

パラメーター

  • OrderVolume = 0.1 — 各新しいポジションのロット単位の取引ボリューム。
  • StopLossPips = 50 — エントリー価格からストップロスレベルまでのpips距離。0に設定するとストップが無効になります。
  • TakeProfitPips = 50 — エントリー価格からテイクプロフィットレベルまでのpips距離。0に設定するとターゲットが無効になります。
  • TrailingStopPips = 5 — トレーリングストップのpipsサイズ。0に設定するとトレーリングをオフにします。
  • TrailingStepPips = 5 — トレーリングストップが再び移動する前に必要な最小価格改善(pips単位)。
  • GapPips = 1 — エントリーシグナルを生成するために必要な最小始値ギャップ(pips単位)。
  • CandleType = 1時間のタイムフレーム — ギャップ検出とポジション管理に使用するローソク足。

実装上の注意

  • pipsベースの入力は、インストルメントのティックサイズを使用して絶対価格距離に変換されます。5桁と3桁のFX 相場は、真のpip値で動作するように自動的に調整されます。
  • TrailingStopPipsが有効な場合、トレーリングストップロジックはTrailingStepPipsが正である必要があります。そうでない場合、戦略は 起動時に例外をスローし、元のMQL検証を反映します。
  • 戦略はStockSharpの高レベルAPIガイドラインに従い、確定済みローソク足でのみリスク管理を評価します。
  • 手動のストップとターゲット管理はマーケットオーダーに依存するため、ブックに個別の保護注文はありません。
  • デフォルト設定はFX商品を想定しています。ボラティリティやティックサイズが異なる資産を取引する場合はpips距離を調整してください。
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Gap trading strategy. Detects price gaps between candles and trades the gap fill.
/// </summary>
public class GapsStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _gapPercent;

	private decimal? _prevClose;

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

	public decimal GapPercent
	{
		get => _gapPercent.Value;
		set => _gapPercent.Value = value;
	}

	public GapsStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_gapPercent = Param(nameof(GapPercent), 0.05m)
			.SetDisplay("Gap Percent", "Minimum gap size as percentage", "Trading");
	}

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

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

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

		_prevClose = null;

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

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

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

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

		if (_prevClose == null)
		{
			_prevClose = candle.ClosePrice;
			return;
		}

		var prevClose = _prevClose.Value;
		var open = candle.OpenPrice;
		var close = candle.ClosePrice;
		_prevClose = close;

		if (prevClose == 0)
			return;

		var gapPct = (open - prevClose) / prevClose * 100;

		// Gap up detected - sell expecting gap fill
		if (gapPct > GapPercent && Position == 0)
		{
			SellMarket();
		}
		// Gap down detected - buy expecting gap fill
		else if (gapPct < -GapPercent && Position == 0)
		{
			BuyMarket();
		}
	}
}