IU Bigger Than Range Strategy
Breakout strategy that opens trades when the candle body is larger than the previous range of recent candles.
The system compares the current candle body with the range between the highest open/close and lowest open/close over a configurable lookback period. If the body exceeds the previous range, it enters in the candle direction and manages risk via configurable stop methods.
Details
- Entry Criteria: Candle body larger than previous range; direction based on candle body.
- Long/Short: Both.
- Exit Criteria: Stop loss or take profit.
- Stops: Previous candle, ATR or swing levels.
- Default Values:
LookbackPeriod= 22RiskToReward= 3StopLossMethod= PreviousHighLowAtrLength= 14AtrFactor= 2mSwingLength= 10CandleType= TimeSpan.FromMinutes(1)
- Filters:
- Category: Breakout
- Direction: Both
- Indicators: Highest, Lowest, ATR
- Stops: Yes
- Complexity: Medium
- Timeframe: Any
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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>
/// Breakout strategy that enters when the candle body exceeds the previous range.
/// </summary>
public class IuBiggerThanRangeStrategy : Strategy
{
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<int> _riskToReward;
private readonly StrategyParam<decimal> _atrFactor;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRangeSize;
private decimal _prevCandleHigh;
private decimal _prevCandleLow;
private decimal _stopPrice;
private decimal _targetPrice;
private decimal _entryPrice;
private int _barCount;
/// <summary>
/// Lookback period for range calculation.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Risk to reward ratio.
/// </summary>
public int RiskToReward
{
get => _riskToReward.Value;
set => _riskToReward.Value = value;
}
/// <summary>
/// ATR multiplier factor.
/// </summary>
public decimal AtrFactor
{
get => _atrFactor.Value;
set => _atrFactor.Value = value;
}
/// <summary>
/// Type of candles to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="IuBiggerThanRangeStrategy"/> class.
/// </summary>
public IuBiggerThanRangeStrategy()
{
_lookbackPeriod = Param(nameof(LookbackPeriod), 22)
.SetDisplay("Lookback Period", "Length for range calculation.", "Parameters");
_riskToReward = Param(nameof(RiskToReward), 3)
.SetDisplay("Risk To Reward", "Risk to reward ratio.", "Parameters");
_atrFactor = Param(nameof(AtrFactor), 2m)
.SetDisplay("ATR Factor", "ATR multiplier.", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Type of candles.", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRangeSize = 0m;
_prevCandleHigh = 0m;
_prevCandleLow = 0m;
_stopPrice = 0m;
_targetPrice = 0m;
_entryPrice = 0m;
_barCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRangeSize = 0m;
_prevCandleHigh = 0m;
_prevCandleLow = 0m;
_stopPrice = 0m;
_targetPrice = 0m;
_entryPrice = 0m;
_barCount = 0;
var atr = new AverageTrueRange { Length = LookbackPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
var rangeSize = candle.HighPrice - candle.LowPrice;
var candleBody = Math.Abs(candle.ClosePrice - candle.OpenPrice);
if (_barCount < LookbackPeriod)
{
_prevRangeSize = rangeSize;
_prevCandleHigh = candle.HighPrice;
_prevCandleLow = candle.LowPrice;
return;
}
// Exit logic first
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || candle.ClosePrice >= _targetPrice)
{
SellMarket();
_stopPrice = 0m;
_targetPrice = 0m;
_entryPrice = 0m;
}
}
else if (Position < 0)
{
if (candle.HighPrice >= _stopPrice || candle.ClosePrice <= _targetPrice)
{
BuyMarket();
_stopPrice = 0m;
_targetPrice = 0m;
_entryPrice = 0m;
}
}
// Entry logic
var isBodyStrong = candleBody >= _prevRangeSize && candleBody >= atrValue * 0.8m;
if (Position == 0 && isBodyStrong)
{
if (candle.ClosePrice > candle.OpenPrice && candle.ClosePrice > _prevCandleHigh)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - atrValue * AtrFactor;
_targetPrice = _entryPrice + (_entryPrice - _stopPrice) * RiskToReward;
}
else if (candle.ClosePrice < candle.OpenPrice && candle.ClosePrice < _prevCandleLow)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + atrValue * AtrFactor;
_targetPrice = _entryPrice - (_stopPrice - _entryPrice) * RiskToReward;
}
}
_prevRangeSize = rangeSize;
_prevCandleHigh = candle.HighPrice;
_prevCandleLow = candle.LowPrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class iu_bigger_than_range_strategy(Strategy):
def __init__(self):
super(iu_bigger_than_range_strategy, self).__init__()
self._lookback_period = self.Param("LookbackPeriod", 22) \
.SetDisplay("Lookback Period", "Length for range calculation", "Parameters")
self._risk_to_reward = self.Param("RiskToReward", 3) \
.SetDisplay("Risk To Reward", "Risk to reward ratio", "Parameters")
self._atr_factor = self.Param("AtrFactor", 2.0) \
.SetDisplay("ATR Factor", "ATR multiplier", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(120))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_range_size = 0.0
self._prev_candle_high = 0.0
self._prev_candle_low = 0.0
self._stop_price = 0.0
self._target_price = 0.0
self._entry_price = 0.0
self._bar_count = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(iu_bigger_than_range_strategy, self).OnReseted()
self._prev_range_size = 0.0
self._prev_candle_high = 0.0
self._prev_candle_low = 0.0
self._stop_price = 0.0
self._target_price = 0.0
self._entry_price = 0.0
self._bar_count = 0
def OnStarted2(self, time):
super(iu_bigger_than_range_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self._lookback_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
self._bar_count += 1
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
atr_v = float(atr_val)
range_size = high - low
candle_body = abs(close - open_p)
lookback = self._lookback_period.Value
if self._bar_count < lookback:
self._prev_range_size = range_size
self._prev_candle_high = high
self._prev_candle_low = low
return
rr = self._risk_to_reward.Value
factor = float(self._atr_factor.Value)
if self.Position > 0:
if low <= self._stop_price or close >= self._target_price:
self.SellMarket()
self._stop_price = 0.0
self._target_price = 0.0
self._entry_price = 0.0
elif self.Position < 0:
if high >= self._stop_price or close <= self._target_price:
self.BuyMarket()
self._stop_price = 0.0
self._target_price = 0.0
self._entry_price = 0.0
is_body_strong = candle_body >= self._prev_range_size and candle_body >= atr_v * 0.8
if self.Position == 0 and is_body_strong:
if close > open_p and close > self._prev_candle_high:
self.BuyMarket()
self._entry_price = close
self._stop_price = self._entry_price - atr_v * factor
self._target_price = self._entry_price + (self._entry_price - self._stop_price) * rr
elif close < open_p and close < self._prev_candle_low:
self.SellMarket()
self._entry_price = close
self._stop_price = self._entry_price + atr_v * factor
self._target_price = self._entry_price - (self._stop_price - self._entry_price) * rr
self._prev_range_size = range_size
self._prev_candle_high = high
self._prev_candle_low = low
def CreateClone(self):
return iu_bigger_than_range_strategy()