Gap Fill Reversal 戦略
ギャップフィルリバーサル戦略は、翌セッション中に素早く埋められる夜間のギャップを活用します。価格が前日の終値からギャップアップ・ダウンした後、すぐにそのギャップを埋めるために戻る場合、これは多くの場合、最初の動きの枯渇を示しています。
テストでは平均年間リターンが約181%であることが示されています。暗号資産市場で最もパフォーマンスが優れています。
戦略はギャップが完全に閉じられたときにエントリーし、寄り付きとは逆方向への反転を探します。トラップされたトレーダーがポジションを手放す際に起きる急反発を捉えることを目指します。
パーセントベースのストップがリスクを規定し、モメンタムが消えるかストップに達したときにポジションをクローズします。
詳細
- エントリー条件: パターン一致
- ロング/ショート: 両方
- エグジット条件: ストップロスまたは逆シグナル
- ストップ: はい、パーセントベース
- デフォルト値:
CandleType= 15分MinGapPercent= 0.02 ギャップとは、前の足の終値から MinGapPercent 以上離れた始値です。市場は24時間取引のため、ギャップは連続する足の間で測ります。同じ足がその終値まで戻るとギャップが埋まったとみなし、逆のシグナルでポジションをドテンします。StopLossPercent= 2
- フィルター:
- カテゴリ: パターン
- 方向: 両方
- インジケーター: Gap
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Gap Fill Reversal strategy.
/// A gap is an open at least MinGapPercent away from the previous close. When the same candle trades back to the previous close,
/// filling the gap, the position turns against the gap: short after a gap up, long after a gap down. A percent stop limits the loss.
/// </summary>
public class GapFillReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _minGapPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevClose;
/// <summary>
/// Minimum gap between the previous close and the open, in percent.
/// </summary>
public decimal MinGapPercent
{
get => _minGapPercent.Value;
set => _minGapPercent.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GapFillReversalStrategy()
{
_minGapPercent = Param(nameof(MinGapPercent), 0.02m)
.SetGreaterThanZero()
.SetDisplay("Min Gap %", "Minimum gap between the previous close and the open", "Pattern");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var prevClose = _prevClose;
_prevClose = candle.ClosePrice;
if (prevClose is not decimal last || last <= 0 || !IsFormedAndOnlineAndAllowTrading())
return;
var gap = (candle.OpenPrice - last) / last * 100m;
if (gap >= MinGapPercent && candle.LowPrice <= last && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (-gap >= MinGapPercent && candle.HighPrice >= last && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class gap_fill_reversal_strategy(Strategy):
"""
Gap Fill Reversal strategy.
A gap is an open at least MinGapPercent away from the previous close. When the same candle trades back to the previous close,
filling the gap, the position turns against the gap: short after a gap up, long after a gap down. A percent stop limits the loss.
"""
def __init__(self):
super(gap_fill_reversal_strategy, self).__init__()
self._min_gap_percent = self.Param("MinGapPercent", 0.02).SetGreaterThanZero().SetDisplay("Min Gap %", "Minimum gap between the previous close and the open", "Pattern")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_close = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(gap_fill_reversal_strategy, self).OnReseted()
self._prev_close = None
def OnStarted2(self, time):
super(gap_fill_reversal_strategy, self).OnStarted2(time)
self._prev_close = None
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
prev_close = self._prev_close
self._prev_close = candle.ClosePrice
if prev_close is None or prev_close <= 0 or not self.IsFormedAndOnlineAndAllowTrading():
return
gap = (candle.OpenPrice - prev_close) / prev_close * Decimal(100)
min_gap = Decimal(self._min_gap_percent.Value)
if gap >= min_gap and candle.LowPrice <= prev_close and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
elif -gap >= min_gap and candle.HighPrice >= prev_close and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
def CreateClone(self):
return gap_fill_reversal_strategy()