Megabar Breakout (Range & Volume & RSI)
Megabar Breakout detects large candles supported by high volume and RSI confirmation. The strategy enters long on bullish megabars and short on bearish ones.
It multiplies average range and volume to find megabars. RSI moving average filters trades.
Details
- Entry Criteria: Candle body and volume exceed their moving averages by given multipliers. RSI MA above long threshold for buys and below short threshold for sells.
- Long/Short: Both directions.
- Exit Criteria: Stop loss or take profit.
- Stops: Yes.
- Default Values:
CandleType= TimeSpan.FromMinutes(5)VolumeAveragePeriod= 20VolumeMultiplier= 3RangeAveragePeriod= 20RangeMultiplier= 4RsiPeriod= 14RsiMaPeriod= 14LongRsiThreshold= 50ShortRsiThreshold= 70TakeProfit= 400StopLoss= 300FilterTradeHours= false
- Filters:
- Category: Breakout
- Direction: Both
- Indicators: Volume, Range, RSI
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
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;
public class MegabarBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _avgPeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private readonly List<decimal> _volumes = new();
private readonly List<decimal> _ranges = new();
private int _barsFromSignal;
public int AvgPeriod { get => _avgPeriod.Value; set => _avgPeriod.Value = value; }
public decimal Multiplier { get => _multiplier.Value; set => _multiplier.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MegabarBreakoutStrategy()
{
_avgPeriod = Param(nameof(AvgPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Average Period", "Rolling average period", "General");
_multiplier = Param(nameof(Multiplier), 1.8m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "Volume and range multiplier", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 8)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame())
.SetDisplay("Candle Type", "Candles timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_volumes.Clear();
_ranges.Clear();
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_volumes.Clear();
_ranges.Clear();
_barsFromSignal = SignalCooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var vol = candle.TotalVolume;
var range = Math.Abs(candle.ClosePrice - candle.OpenPrice);
_volumes.Add(vol);
_ranges.Add(range);
if (_volumes.Count > AvgPeriod) _volumes.RemoveAt(0);
if (_ranges.Count > AvgPeriod) _ranges.RemoveAt(0);
if (!_rsi.IsFormed || _volumes.Count < AvgPeriod)
return;
var avgVol = _volumes.Average();
var avgRange = _ranges.Average();
var volumeOk = vol > avgVol * Multiplier;
var rangeOk = range > avgRange * Multiplier;
_barsFromSignal++;
if (_barsFromSignal >= SignalCooldownBars && candle.ClosePrice > candle.OpenPrice && volumeOk && rangeOk && rsiValue > 52m && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (_barsFromSignal >= SignalCooldownBars && candle.ClosePrice < candle.OpenPrice && volumeOk && rangeOk && rsiValue < 48m && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class megabar_breakout_strategy(Strategy):
def __init__(self):
super(megabar_breakout_strategy, self).__init__()
self._avg_period = self.Param("AvgPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Average Period", "Rolling average period", "General")
self._multiplier = self.Param("Multiplier", 1.8) \
.SetGreaterThanZero() \
.SetDisplay("Multiplier", "Volume and range multiplier", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "RSI period", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 8) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(10))) \
.SetDisplay("Candle Type", "Candles timeframe", "General")
self._volumes = []
self._ranges = []
self._bars_from_signal = 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(megabar_breakout_strategy, self).OnReseted()
self._volumes = []
self._ranges = []
self._bars_from_signal = 0
def OnStarted2(self, time):
super(megabar_breakout_strategy, self).OnStarted2(time)
self._volumes = []
self._ranges = []
self._bars_from_signal = self._signal_cooldown_bars.Value
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._rsi, self.OnProcess).Start()
def OnProcess(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
vol = float(candle.TotalVolume)
rng = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
ap = self._avg_period.Value
self._volumes.append(vol)
self._ranges.append(rng)
if len(self._volumes) > ap:
self._volumes.pop(0)
if len(self._ranges) > ap:
self._ranges.pop(0)
if not self._rsi.IsFormed or len(self._volumes) < ap:
return
avg_vol = sum(self._volumes) / len(self._volumes)
avg_rng = sum(self._ranges) / len(self._ranges)
mult = float(self._multiplier.Value)
volume_ok = vol > avg_vol * mult
range_ok = rng > avg_rng * mult
rv = float(rsi_value)
close = float(candle.ClosePrice)
opn = float(candle.OpenPrice)
self._bars_from_signal += 1
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal >= cd and close > opn and volume_ok and range_ok and rv > 52.0 and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif self._bars_from_signal >= cd and close < opn and volume_ok and range_ok and rv < 48.0 and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
def CreateClone(self):
return megabar_breakout_strategy()