Gold ORB Strategy
The strategy captures the high and low of the Asia session and trades breakouts during the following hours. Stops and targets are derived from the range size with a reward multiplier.
Details
- Entry Criteria:
- During the trade window, go long when the price closes above the recorded Asia high.
- Go short when the price closes below the recorded Asia low.
- Long/Short: Both
- Exit Criteria:
- Stop and target based on the range size and reward multiplier.
- Stops: Yes
- Default Values:
AsiaStart= 00:00AsiaEnd= 06:00TradeStart= 06:00TradeEnd= 10:00RewardMultiplier= 2.0
- Filters:
- Category: Breakout
- Direction: Both
- Indicators: None
- Stops: Yes
- Complexity: Low
- Timeframe: 5m
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Gold opening range breakout strategy.
/// The high and low of the candles opening between AsiaStart and AsiaEnd (UTC) form the Asia range of the day. Between TradeStart and
/// TradeEnd a close above the range high goes long and a close below the range low goes short. The stop is one range size away from the
/// entry and the target RewardMultiplier range sizes away.
/// </summary>
public class GoldOrbStrategy : Strategy
{
private readonly StrategyParam<TimeSpan> _asiaStart;
private readonly StrategyParam<TimeSpan> _asiaEnd;
private readonly StrategyParam<TimeSpan> _tradeStart;
private readonly StrategyParam<TimeSpan> _tradeEnd;
private readonly StrategyParam<decimal> _rewardMultiplier;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _rangeDay;
private decimal? _asiaHigh;
private decimal? _asiaLow;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Start of the Asia session (UTC).
/// </summary>
public TimeSpan AsiaStart
{
get => _asiaStart.Value;
set => _asiaStart.Value = value;
}
/// <summary>
/// End of the Asia session (UTC).
/// </summary>
public TimeSpan AsiaEnd
{
get => _asiaEnd.Value;
set => _asiaEnd.Value = value;
}
/// <summary>
/// Start of the trade window (UTC).
/// </summary>
public TimeSpan TradeStart
{
get => _tradeStart.Value;
set => _tradeStart.Value = value;
}
/// <summary>
/// End of the trade window (UTC).
/// </summary>
public TimeSpan TradeEnd
{
get => _tradeEnd.Value;
set => _tradeEnd.Value = value;
}
/// <summary>
/// Target distance in range sizes.
/// </summary>
public decimal RewardMultiplier
{
get => _rewardMultiplier.Value;
set => _rewardMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GoldOrbStrategy()
{
_asiaStart = Param(nameof(AsiaStart), TimeSpan.Zero)
.SetDisplay("Asia Start", "Start of the Asia session (UTC)", "Session");
_asiaEnd = Param(nameof(AsiaEnd), TimeSpan.FromHours(6))
.SetDisplay("Asia End", "End of the Asia session (UTC)", "Session");
_tradeStart = Param(nameof(TradeStart), TimeSpan.FromHours(6))
.SetDisplay("Trade Start", "Start of the trade window (UTC)", "Session");
_tradeEnd = Param(nameof(TradeEnd), TimeSpan.FromHours(10))
.SetDisplay("Trade End", "End of the trade window (UTC)", "Session");
_rewardMultiplier = Param(nameof(RewardMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Reward Multiplier", "Target distance in range sizes", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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()
{
_rangeDay = null;
_asiaHigh = null;
_asiaLow = null;
_stopPrice = 0m;
_takePrice = 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 timeOfDay = candle.OpenTime.TimeOfDay;
if (_rangeDay != day)
{
_rangeDay = day;
_asiaHigh = null;
_asiaLow = null;
}
if (timeOfDay >= AsiaStart && timeOfDay < AsiaEnd)
{
_asiaHigh = _asiaHigh is decimal h ? Math.Max(h, candle.HighPrice) : candle.HighPrice;
_asiaLow = _asiaLow is decimal l ? Math.Min(l, candle.LowPrice) : candle.LowPrice;
}
if (ManagePosition(candle))
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0 || timeOfDay < TradeStart || timeOfDay >= TradeEnd)
return;
if (_asiaHigh is not decimal high || _asiaLow is not decimal low)
return;
var range = high - low;
if (range <= 0)
return;
var close = candle.ClosePrice;
if (close > high)
{
BuyMarket(Volume);
_stopPrice = close - range;
_takePrice = close + range * RewardMultiplier;
}
else if (close < low)
{
SellMarket(Volume);
_stopPrice = close + range;
_takePrice = close - range * RewardMultiplier;
}
}
// Returns true when the position was closed on this candle.
private bool ManagePosition(ICandleMessage candle)
{
if (Position > 0 && _stopPrice > 0)
{
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
{
SellMarket(Position);
return true;
}
}
else if (Position < 0 && _stopPrice > 0)
{
if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice)
{
BuyMarket(Math.Abs(Position));
return true;
}
}
return false;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class gold_orb_strategy(Strategy):
"""
Gold opening range breakout strategy.
The high and low of the candles opening between AsiaStart and AsiaEnd (UTC) form the Asia range of the day. Between TradeStart and
TradeEnd a close above the range high goes long and a close below the range low goes short. The stop is one range size away from the
entry and the target RewardMultiplier range sizes away.
"""
def __init__(self):
super(gold_orb_strategy, self).__init__()
self._asia_start = self.Param("AsiaStart", TimeSpan.Zero).SetDisplay("Asia Start", "Start of the Asia session (UTC)", "Session")
self._asia_end = self.Param("AsiaEnd", TimeSpan.FromHours(6)).SetDisplay("Asia End", "End of the Asia session (UTC)", "Session")
self._trade_start = self.Param("TradeStart", TimeSpan.FromHours(6)).SetDisplay("Trade Start", "Start of the trade window (UTC)", "Session")
self._trade_end = self.Param("TradeEnd", TimeSpan.FromHours(10)).SetDisplay("Trade End", "End of the trade window (UTC)", "Session")
self._reward_multiplier = self.Param("RewardMultiplier", 2.0).SetGreaterThanZero().SetDisplay("Reward Multiplier", "Target distance in range sizes", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).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._range_day = None
self._asia_high = None
self._asia_low = None
self._stop_price = Decimal(0)
self._take_price = Decimal(0)
def OnReseted(self):
super(gold_orb_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(gold_orb_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
time_of_day = candle.OpenTime.TimeOfDay
if self._range_day is None or self._range_day != day:
self._range_day = day
self._asia_high = None
self._asia_low = None
if self._asia_start.Value <= time_of_day < self._asia_end.Value:
self._asia_high = candle.HighPrice if self._asia_high is None else Math.Max(self._asia_high, candle.HighPrice)
self._asia_low = candle.LowPrice if self._asia_low is None else Math.Min(self._asia_low, candle.LowPrice)
if self._manage_position(candle):
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0 or time_of_day < self._trade_start.Value or time_of_day >= self._trade_end.Value:
return
if self._asia_high is None or self._asia_low is None:
return
high = self._asia_high
low = self._asia_low
rng = high - low
if rng <= 0:
return
close = candle.ClosePrice
multiplier = Decimal(self._reward_multiplier.Value)
if close > high:
self.BuyMarket(self.Volume)
self._stop_price = close - rng
self._take_price = close + rng * multiplier
elif close < low:
self.SellMarket(self.Volume)
self._stop_price = close + rng
self._take_price = close - rng * multiplier
def _manage_position(self, candle):
if self.Position > 0 and self._stop_price > 0:
if candle.LowPrice <= self._stop_price or candle.HighPrice >= self._take_price:
self.SellMarket(self.Position)
return True
elif self.Position < 0 and self._stop_price > 0:
if candle.HighPrice >= self._stop_price or candle.LowPrice <= self._take_price:
self.BuyMarket(abs(self.Position))
return True
return False
def CreateClone(self):
return gold_orb_strategy()