NY ブレイクアウト戦略
13:00から13:30 UTCの間に形成されたレンジのブレイクアウトを取引する戦略。ウィンドウが閉じた後、価格がセッションの高値または安値をブレイクした際にエントリーし、レンジの2倍を目標にし、反対側にストップを置く。
詳細
- エントリー条件:
- 13:30 UTC以降の最初のバーがセッション高値を超えて終値をつける -> ロング。
- 13:30 UTC以降の最初のバーがセッション安値を下回って終値をつける -> ショート。
- ロング/ショート: 両サイド。
- エグジット条件:
RewardRisk倍のレンジでの利益確定。- 反対側のレンジ境界でのストップ。
- ストップ: はい。
- デフォルト値:
RewardRisk= 2CandleType= TimeSpan.FromMinutes(1)
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: なし
- ストップ: はい
- 複雑さ: 基本
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// NY breakout strategy.
/// The high and low of the candles opening between 13:00 and 13:30 UTC form the session range. The first candle after 13:30 goes
/// long if it closes above the range high and short if it closes below the range low. The stop is the opposite range boundary
/// and the target lies RewardRisk times the range height from the entry.
/// </summary>
public class NyBreakoutStrategy : Strategy
{
private static readonly TimeSpan _rangeStart = new(13, 0, 0);
private static readonly TimeSpan _rangeEnd = new(13, 30, 0);
private readonly StrategyParam<decimal> _rewardRisk;
private readonly StrategyParam<DataType> _candleType;
private DateTime _currentDay;
private decimal? _rangeHigh;
private decimal? _rangeLow;
private bool _checked;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// Target distance as a multiple of the range height.
/// </summary>
public decimal RewardRisk
{
get => _rewardRisk.Value;
set => _rewardRisk.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public NyBreakoutStrategy()
{
_rewardRisk = Param(nameof(RewardRisk), 2m)
.SetGreaterThanZero()
.SetDisplay("Reward/Risk", "Target distance as a multiple of the range height", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
ResetState();
}
private void ResetState()
{
_currentDay = default;
_rangeHigh = null;
_rangeLow = null;
_checked = false;
_stopPrice = null;
_takePrice = null;
}
/// <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 tod = candle.OpenTime.TimeOfDay;
if (day != _currentDay)
{
_currentDay = day;
_rangeHigh = null;
_rangeLow = null;
_checked = false;
}
if (tod >= _rangeStart && tod < _rangeEnd)
{
_rangeHigh = _rangeHigh is decimal h ? Math.Max(h, candle.HighPrice) : candle.HighPrice;
_rangeLow = _rangeLow is decimal l ? Math.Min(l, candle.LowPrice) : candle.LowPrice;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0 && _stopPrice is decimal longStop && _takePrice is decimal longTake)
{
if (candle.LowPrice <= longStop || candle.HighPrice >= longTake)
{
SellMarket(Position);
_stopPrice = null;
_takePrice = null;
return;
}
}
else if (Position < 0 && _stopPrice is decimal shortStop && _takePrice is decimal shortTake)
{
if (candle.HighPrice >= shortStop || candle.LowPrice <= shortTake)
{
BuyMarket(-Position);
_stopPrice = null;
_takePrice = null;
return;
}
}
if (_checked || tod < _rangeEnd || _rangeHigh is not decimal high || _rangeLow is not decimal low)
return;
// Only the first candle after the window is checked for a breakout.
_checked = true;
var close = candle.ClosePrice;
var range = high - low;
if (close > high && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = low;
_takePrice = close + range * RewardRisk;
}
else if (close < low && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = high;
_takePrice = close - range * RewardRisk;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
RANGE_START = TimeSpan(13, 0, 0)
RANGE_END = TimeSpan(13, 30, 0)
class ny_breakout_strategy(Strategy):
"""
NY breakout strategy.
The high and low of the candles opening between 13:00 and 13:30 UTC form the session range. The first candle after 13:30 goes
long if it closes above the range high and short if it closes below the range low. The stop is the opposite range boundary
and the target lies RewardRisk times the range height from the entry.
"""
def __init__(self):
super(ny_breakout_strategy, self).__init__()
self._reward_risk = self.Param("RewardRisk", 2.0).SetGreaterThanZero().SetDisplay("Reward/Risk", "Target distance as a multiple of the range height", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._current_day = None
self._range_high = None
self._range_low = None
self._checked = False
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(ny_breakout_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(ny_breakout_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
tod = candle.OpenTime.TimeOfDay
if self._current_day is None or day != self._current_day:
self._current_day = day
self._range_high = None
self._range_low = None
self._checked = False
if tod >= RANGE_START and tod < RANGE_END:
self._range_high = candle.HighPrice if self._range_high is None else max(self._range_high, candle.HighPrice)
self._range_low = candle.LowPrice if self._range_low is None else min(self._range_low, candle.LowPrice)
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0 and self._stop_price is not None and self._take_price is not None:
if candle.LowPrice <= self._stop_price or candle.HighPrice >= self._take_price:
self.SellMarket(self.Position)
self._stop_price = None
self._take_price = None
return
elif self.Position < 0 and self._stop_price is not None and self._take_price is not None:
if candle.HighPrice >= self._stop_price or candle.LowPrice <= self._take_price:
self.BuyMarket(-self.Position)
self._stop_price = None
self._take_price = None
return
if self._checked or tod < RANGE_END or self._range_high is None or self._range_low is None:
return
# Only the first candle after the window is checked for a breakout.
self._checked = True
high = self._range_high
low = self._range_low
close = candle.ClosePrice
rng = high - low
reward_risk = Decimal(self._reward_risk.Value)
if close > high and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = low
self._take_price = close + rng * reward_risk
elif close < low and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = high
self._take_price = close - rng * reward_risk
def CreateClone(self):
return ny_breakout_strategy()