ギャップフィリング戦略
ギャップフィリング戦略は、新しいセッション開始時の夜間価格ギャップを探します。ギャップが現れると、前日の価格への戻りを期待してギャップに逆らってポジションを建てます。反転モード時はギャップ方向に取引し、ギャップレベルでストップを置きます。
詳細
- データ: 価格ローソク足。
- エントリー条件:
- ロング: 新セッションかつ下方ギャップ(反転時は上方ギャップ)。
- ショート: 新セッションかつ上方ギャップ(反転時は下方ギャップ)。
- エグジット条件:
- ギャップフィル価格到達(利益目標)、または反転時はギャップレベルのストップ。
- ストップ: 前のセッションの価格を目標/ストップとして使用。
- デフォルト値:
CandleType= 1 minuteInvert= falseCloseWhen= NewSession
- フィルター:
- カテゴリ: ギャップ取引
- 方向: ロング & ショート
- インジケーター: なし
- 複雑さ: シンプル
- リスクレベル: 中
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 filling strategy.
/// A session is a UTC calendar day. When its first candle opens away from the previous session's last close, the strategy fades the gap
/// (short an up gap, long a down gap) with the previous close as the profit target. With Invert it trades in the gap direction instead
/// and the previous close becomes the stop. With CloseWhen set to NewSession an open position is also closed when the next session
/// starts; with GapLevel it stays open until the target or stop is reached.
/// </summary>
public class GapFillingStrategy : Strategy
{
/// <summary>
/// When a position is closed besides the gap level.
/// </summary>
public enum CloseModes
{
/// <summary>
/// Close at the start of the next session or at the gap level.
/// </summary>
NewSession,
/// <summary>
/// Close only at the gap level.
/// </summary>
GapLevel,
}
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _invert;
private readonly StrategyParam<CloseModes> _closeWhen;
private DateTime? _sessionDay;
private decimal? _lastClose;
private decimal _gapLevel;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Trade in the gap direction with a stop at the gap level instead of fading it.
/// </summary>
public bool Invert
{
get => _invert.Value;
set => _invert.Value = value;
}
/// <summary>
/// When the position is closed besides the gap level.
/// </summary>
public CloseModes CloseWhen
{
get => _closeWhen.Value;
set => _closeWhen.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GapFillingStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_invert = Param(nameof(Invert), false)
.SetDisplay("Invert", "Trade in the gap direction with a stop at the gap level", "Trading");
_closeWhen = Param(nameof(CloseWhen), CloseModes.NewSession)
.SetDisplay("Close When", "When the position is closed besides the gap level", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_sessionDay = null;
_lastClose = null;
_gapLevel = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
var newSession = _sessionDay is DateTime current && current != day;
var previousClose = _lastClose;
_sessionDay = day;
_lastClose = candle.ClosePrice;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (newSession)
{
if (Position != 0 && CloseWhen != CloseModes.NewSession)
return;
var target = 0m;
if (previousClose is decimal gapLevel && candle.OpenPrice != gapLevel)
{
_gapLevel = gapLevel;
var gapUp = candle.OpenPrice > gapLevel;
target = gapUp != Invert ? -Volume : Volume;
}
// One order both closes the previous session's position and opens the new one.
var diff = target - Position;
if (diff > 0)
BuyMarket(diff);
else if (diff < 0)
SellMarket(-diff);
return;
}
if (Position == 0 || _gapLevel <= 0)
return;
// A faded gap targets the gap level; an inverted trade is stopped there.
if (Position > 0)
{
var hit = Invert ? candle.LowPrice <= _gapLevel : candle.HighPrice >= _gapLevel;
if (hit)
ClosePosition();
}
else
{
var hit = Invert ? candle.HighPrice >= _gapLevel : candle.LowPrice <= _gapLevel;
if (hit)
ClosePosition();
}
}
private void ClosePosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class CloseModes:
"""When a position is closed besides the gap level."""
NewSession = 0
GapLevel = 1
class gap_filling_strategy(Strategy):
"""
Gap filling strategy.
A session is a UTC calendar day. When its first candle opens away from the previous session's last close, the strategy fades the gap
(short an up gap, long a down gap) with the previous close as the profit target. With Invert it trades in the gap direction instead
and the previous close becomes the stop. With CloseWhen set to NewSession an open position is also closed when the next session
starts; with GapLevel it stays open until the target or stop is reached.
"""
def __init__(self):
super(gap_filling_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._invert = self.Param("Invert", False).SetDisplay("Invert", "Trade in the gap direction with a stop at the gap level", "Trading")
self._close_when = self.Param("CloseWhen", CloseModes.NewSession).SetDisplay("Close When", "When the position is closed besides the gap level", "Trading")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._session_day = None
self._last_close = None
self._gap_level = None
def OnReseted(self):
super(gap_filling_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(gap_filling_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
new_session = self._session_day is not None and self._session_day != day
previous_close = self._last_close
self._session_day = day
self._last_close = candle.ClosePrice
if not self.IsFormedAndOnlineAndAllowTrading():
return
invert = self._invert.Value
if new_session:
if self.Position != 0 and self._close_when.Value != CloseModes.NewSession:
return
target = 0
if previous_close is not None and candle.OpenPrice != previous_close:
self._gap_level = previous_close
gap_up = candle.OpenPrice > previous_close
target = -self.Volume if gap_up != invert else self.Volume
# One order both closes the previous session's position and opens the new one.
diff = target - self.Position
if diff > 0:
self.BuyMarket(diff)
elif diff < 0:
self.SellMarket(-diff)
return
if self.Position == 0 or self._gap_level is None:
return
# A faded gap targets the gap level; an inverted trade is stopped there.
if self.Position > 0:
hit = candle.LowPrice <= self._gap_level if invert else candle.HighPrice >= self._gap_level
if hit:
self.SellMarket(self.Position)
else:
hit = candle.HighPrice >= self._gap_level if invert else candle.LowPrice <= self._gap_level
if hit:
self.BuyMarket(abs(self.Position))
def CreateClone(self):
return gap_filling_strategy()