ORB 15m – 最初の15分ブレイクアウト戦略(ロング/ショート)
この戦略は、ストックホルム時間のセッション開始後の最初の15分足の終値でエントリーします。最初の足が陽線の場合はロング、陰線の場合はショートを取引します。ポジションサイズはリスク率とストップまでの距離から計算されます。
詳細
- エントリー条件: セッション開始後の最初の15分足で取引;足が始値より高く終値した場合はロング、低く終値した場合はショート。
- エグジット条件: 参照足の反対側の極値にストップロス;オプションで
RMultiple倍のリスクでテイクプロフィット、またはセッション終了時にクローズ。 - ロング/ショート: 両方。
- ストップ: はい。
- デフォルト値:
RiskPct = 1TpTenR = trueRMultiple = 10SessionOpenHour = 15SessionOpenMinute = 30SessionEndHour = 22SessionEndMinute = 0
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: なし
- ストップ: はい
- 複雑さ: 初心者
- 時間軸: イントラデイ
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// ORB 15m first 15 minute breakout strategy.
/// At the close of the first 15 minute bar after the session open (Stockholm time) a bullish bar opens a long and a bearish bar a short.
/// The stop sits at the opposite extreme of that bar, the size risks RiskPct of equity on the stop distance, the optional target is
/// RMultiple times the risk, and any open position is closed at the session end.
/// </summary>
public class Orb15mFirst15minBreakoutStrategy : Strategy
{
private readonly StrategyParam<decimal> _riskPct;
private readonly StrategyParam<bool> _tpTenR;
private readonly StrategyParam<decimal> _rMultiple;
private readonly StrategyParam<int> _sessionOpenHour;
private readonly StrategyParam<int> _sessionOpenMinute;
private readonly StrategyParam<int> _sessionEndHour;
private readonly StrategyParam<int> _sessionEndMinute;
private readonly StrategyParam<DataType> _candleType;
private static readonly TimeZoneInfo _stockholm = TimeZoneInfo.FindSystemTimeZoneById("Europe/Stockholm");
private DateTime _currentDay;
private bool _tradedToday;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// Percent of equity risked per trade.
/// </summary>
public decimal RiskPct
{
get => _riskPct.Value;
set => _riskPct.Value = value;
}
/// <summary>
/// Use the take profit at RMultiple times risk.
/// </summary>
public bool TpTenR
{
get => _tpTenR.Value;
set => _tpTenR.Value = value;
}
/// <summary>
/// Take profit as a multiple of risk.
/// </summary>
public decimal RMultiple
{
get => _rMultiple.Value;
set => _rMultiple.Value = value;
}
/// <summary>
/// Session open hour (Stockholm time).
/// </summary>
public int SessionOpenHour
{
get => _sessionOpenHour.Value;
set => _sessionOpenHour.Value = value;
}
/// <summary>
/// Session open minute (Stockholm time).
/// </summary>
public int SessionOpenMinute
{
get => _sessionOpenMinute.Value;
set => _sessionOpenMinute.Value = value;
}
/// <summary>
/// Session end hour (Stockholm time).
/// </summary>
public int SessionEndHour
{
get => _sessionEndHour.Value;
set => _sessionEndHour.Value = value;
}
/// <summary>
/// Session end minute (Stockholm time).
/// </summary>
public int SessionEndMinute
{
get => _sessionEndMinute.Value;
set => _sessionEndMinute.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public Orb15mFirst15minBreakoutStrategy()
{
_riskPct = Param(nameof(RiskPct), 1m)
.SetGreaterThanZero()
.SetDisplay("Risk %", "Percent of equity risked per trade", "Risk");
_tpTenR = Param(nameof(TpTenR), true)
.SetDisplay("Use Take Profit", "Take profit at RMultiple times risk", "Risk");
_rMultiple = Param(nameof(RMultiple), 10m)
.SetGreaterThanZero()
.SetDisplay("R Multiple", "Take profit as a multiple of risk", "Risk");
_sessionOpenHour = Param(nameof(SessionOpenHour), 15)
.SetRange(0, 23)
.SetDisplay("Session Open Hour", "Session open hour (Stockholm time)", "Session");
_sessionOpenMinute = Param(nameof(SessionOpenMinute), 30)
.SetRange(0, 59)
.SetDisplay("Session Open Minute", "Session open minute (Stockholm time)", "Session");
_sessionEndHour = Param(nameof(SessionEndHour), 22)
.SetRange(0, 23)
.SetDisplay("Session End Hour", "Session end hour (Stockholm time)", "Session");
_sessionEndMinute = Param(nameof(SessionEndMinute), 0)
.SetRange(0, 59)
.SetDisplay("Session End Minute", "Session end minute (Stockholm time)", "Session");
_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();
ResetState();
}
private void ResetState()
{
_currentDay = default;
_tradedToday = 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 local = TimeZoneInfo.ConvertTimeFromUtc(candle.OpenTime.ToUniversalTime(), _stockholm);
var tod = local.TimeOfDay;
var sessionOpen = new TimeSpan(SessionOpenHour, SessionOpenMinute, 0);
var sessionEnd = new TimeSpan(SessionEndHour, SessionEndMinute, 0);
if (local.Date != _currentDay)
{
_currentDay = local.Date;
_tradedToday = false;
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
var closeTime = local + (candle.CloseTime - candle.OpenTime);
if (Position > 0 && (_stopPrice is decimal ls && candle.LowPrice <= ls || _takePrice is decimal lt && candle.HighPrice >= lt || closeTime.TimeOfDay >= sessionEnd))
{
SellMarket(Position);
_stopPrice = null;
_takePrice = null;
}
else if (Position < 0 && (_stopPrice is decimal ss && candle.HighPrice >= ss || _takePrice is decimal st && candle.LowPrice <= st || closeTime.TimeOfDay >= sessionEnd))
{
BuyMarket(-Position);
_stopPrice = null;
_takePrice = null;
}
return;
}
// Only the first bar of the session is the reference bar.
if (_tradedToday || tod != sessionOpen)
return;
_tradedToday = true;
var isLong = candle.ClosePrice > candle.OpenPrice;
var isShort = candle.ClosePrice < candle.OpenPrice;
if (!isLong && !isShort)
return;
var entry = candle.ClosePrice;
var stop = isLong ? candle.LowPrice : candle.HighPrice;
var risk = Math.Abs(entry - stop);
if (risk <= 0)
return;
var volume = Volume;
var equity = Portfolio?.CurrentValue ?? 0m;
if (equity > 0)
{
var riskVolume = equity * RiskPct / 100m / risk;
if (riskVolume > 0)
volume = riskVolume;
}
_stopPrice = stop;
_takePrice = TpTenR ? (isLong ? entry + risk * RMultiple : entry - risk * RMultiple) : null;
if (isLong)
BuyMarket(volume);
else
SellMarket(volume);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, TimeZoneInfo, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class orb_15m_first_15min_breakout_strategy(Strategy):
"""
ORB 15m first 15 minute breakout strategy.
At the close of the first 15 minute bar after the session open (Stockholm time) a bullish bar opens a long and a bearish bar a short.
The stop sits at the opposite extreme of that bar, the size risks RiskPct of equity on the stop distance, the optional target is
RMultiple times the risk, and any open position is closed at the session end.
"""
def __init__(self):
super(orb_15m_first_15min_breakout_strategy, self).__init__()
self._risk_pct = self.Param("RiskPct", 1.0).SetGreaterThanZero().SetDisplay("Risk %", "Percent of equity risked per trade", "Risk")
self._tp_ten_r = self.Param("TpTenR", True).SetDisplay("Use Take Profit", "Take profit at RMultiple times risk", "Risk")
self._r_multiple = self.Param("RMultiple", 10.0).SetGreaterThanZero().SetDisplay("R Multiple", "Take profit as a multiple of risk", "Risk")
self._session_open_hour = self.Param("SessionOpenHour", 15).SetRange(0, 23).SetDisplay("Session Open Hour", "Session open hour (Stockholm time)", "Session")
self._session_open_minute = self.Param("SessionOpenMinute", 30).SetRange(0, 59).SetDisplay("Session Open Minute", "Session open minute (Stockholm time)", "Session")
self._session_end_hour = self.Param("SessionEndHour", 22).SetRange(0, 23).SetDisplay("Session End Hour", "Session end hour (Stockholm time)", "Session")
self._session_end_minute = self.Param("SessionEndMinute", 0).SetRange(0, 59).SetDisplay("Session End Minute", "Session end minute (Stockholm time)", "Session")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._time_zone = None
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._current_day = None
self._traded_today = False
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(orb_15m_first_15min_breakout_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(orb_15m_first_15min_breakout_strategy, self).OnStarted2(time)
self._reset_state()
self._time_zone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Stockholm")
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
local = TimeZoneInfo.ConvertTimeFromUtc(candle.OpenTime.ToUniversalTime(), self._time_zone)
tod = local.TimeOfDay
session_open = TimeSpan(self._session_open_hour.Value, self._session_open_minute.Value, 0)
session_end = TimeSpan(self._session_end_hour.Value, self._session_end_minute.Value, 0)
if self._current_day is None or local.Date != self._current_day:
self._current_day = local.Date
self._traded_today = False
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
close_time = local.Add(candle.CloseTime.Subtract(candle.OpenTime))
session_over = close_time.TimeOfDay.TotalMinutes >= session_end.TotalMinutes
if self.Position > 0:
if (self._stop_price is not None and candle.LowPrice <= self._stop_price) or \
(self._take_price is not None and candle.HighPrice >= self._take_price) or session_over:
self.SellMarket(self.Position)
self._stop_price = None
self._take_price = None
else:
if (self._stop_price is not None and candle.HighPrice >= self._stop_price) or \
(self._take_price is not None and candle.LowPrice <= self._take_price) or session_over:
self.BuyMarket(-self.Position)
self._stop_price = None
self._take_price = None
return
# Only the first bar of the session is the reference bar.
if self._traded_today or tod.TotalMinutes != session_open.TotalMinutes:
return
self._traded_today = True
is_long = candle.ClosePrice > candle.OpenPrice
is_short = candle.ClosePrice < candle.OpenPrice
if not is_long and not is_short:
return
entry = candle.ClosePrice
stop = candle.LowPrice if is_long else candle.HighPrice
risk = abs(entry - stop)
if risk <= 0:
return
volume = self.Volume
equity = self.Portfolio.CurrentValue if self.Portfolio is not None else None
if equity is not None and equity > 0:
risk_volume = equity * Decimal(self._risk_pct.Value) / Decimal(100) / risk
if risk_volume > 0:
volume = risk_volume
r_multiple = Decimal(self._r_multiple.Value)
self._stop_price = stop
if self._tp_ten_r.Value:
self._take_price = entry + risk * r_multiple if is_long else entry - risk * r_multiple
else:
self._take_price = None
if is_long:
self.BuyMarket(volume)
else:
self.SellMarket(volume)
def CreateClone(self):
return orb_15m_first_15min_breakout_strategy()