IU Big Body Bar Strategy
This strategy enters when the body of the current candle is several times larger than the average body size of the last 20 candles. A big bullish candle opens a long position, while a big bearish candle opens a short one. Positions are protected with an ATR-based trailing stop.
Details
- Entry Criteria:
- Long: body > average body * BigBodyThreshold and close > open.
- Short: body > average body * BigBodyThreshold and close < open.
- Long/Short: Both.
- Exit Criteria: ATR trailing stop.
- Stops: Trailing stop using ATR * AtrFactor.
- Default Values:
BigBodyThreshold= 4AtrLength= 14AtrFactor= 2CandleType= 5 minute
- Filters:
- Category: Momentum
- Direction: Both
- Indicators: SMA, ATR
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday
- 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>
/// Big body bar strategy with ATR trailing stop.
/// </summary>
public class IuBbbBigBodyBarStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _bigBodyThreshold;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrFactor;
private decimal _sumBody;
private int _bodyCount;
private decimal? _atrStop;
private decimal _entryPrice;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Big body threshold multiplier.
/// </summary>
public decimal BigBodyThreshold { get => _bigBodyThreshold.Value; set => _bigBodyThreshold.Value = value; }
/// <summary>
/// ATR period.
/// </summary>
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <summary>
/// ATR factor for trailing stop.
/// </summary>
public decimal AtrFactor { get => _atrFactor.Value; set => _atrFactor.Value = value; }
/// <summary>
/// Initializes a new instance of <see cref="IuBbbBigBodyBarStrategy"/>.
/// </summary>
public IuBbbBigBodyBarStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use.", "General");
_bigBodyThreshold = Param(nameof(BigBodyThreshold), 1.5m)
.SetDisplay("Big Body Threshold", "Multiplier of average body.", "Parameters");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Period", "ATR indicator period.", "Indicators");
_atrFactor = Param(nameof(AtrFactor), 2m)
.SetDisplay("ATR Factor", "ATR multiplier for trailing stop.", "Risk Management");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sumBody = 0m;
_bodyCount = 0;
_atrStop = null;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sumBody = 0m;
_bodyCount = 0;
_atrStop = null;
_entryPrice = 0m;
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
// Track running average of body sizes
_sumBody += body;
_bodyCount++;
var avgBody = _sumBody / _bodyCount;
if (_bodyCount < 20 || avgBody <= 0m)
return;
var longCond = body > avgBody * BigBodyThreshold && candle.ClosePrice > candle.OpenPrice;
var shortCond = body > avgBody * BigBodyThreshold && candle.ClosePrice < candle.OpenPrice;
// Exit logic first
if (Position > 0)
{
if (_atrStop is null)
_atrStop = _entryPrice - atr * AtrFactor;
else
_atrStop = Math.Max(_atrStop.Value, candle.ClosePrice - atr * AtrFactor);
if (candle.LowPrice <= _atrStop)
{
SellMarket();
_atrStop = null;
}
return;
}
else if (Position < 0)
{
if (_atrStop is null)
_atrStop = _entryPrice + atr * AtrFactor;
else
_atrStop = Math.Min(_atrStop.Value, candle.ClosePrice + atr * AtrFactor);
if (candle.HighPrice >= _atrStop)
{
BuyMarket();
_atrStop = null;
}
return;
}
// Entry logic
if (longCond)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (shortCond)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
}
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_bbb_big_body_bar_strategy(Strategy):
def __init__(self):
super(iu_bbb_big_body_bar_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(240))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._big_body_threshold = self.Param("BigBodyThreshold", 1.5) \
.SetDisplay("Big Body Threshold", "Multiplier of average body", "Parameters")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Period", "ATR indicator period", "Indicators")
self._atr_factor = self.Param("AtrFactor", 2.0) \
.SetDisplay("ATR Factor", "ATR multiplier for trailing stop", "Risk Management")
self._sum_body = 0.0
self._body_count = 0
self._atr_stop = None
self._entry_price = 0.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_bbb_big_body_bar_strategy, self).OnReseted()
self._sum_body = 0.0
self._body_count = 0
self._atr_stop = None
self._entry_price = 0.0
def OnStarted2(self, time):
super(iu_bbb_big_body_bar_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self._atr_length.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.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
atr_v = float(atr_val)
body = abs(close - open_p)
self._sum_body += body
self._body_count += 1
avg_body = self._sum_body / self._body_count
if self._body_count < 20 or avg_body <= 0:
return
threshold = float(self._big_body_threshold.Value)
factor = float(self._atr_factor.Value)
long_cond = body > avg_body * threshold and close > open_p
short_cond = body > avg_body * threshold and close < open_p
if self.Position > 0:
if self._atr_stop is None:
self._atr_stop = self._entry_price - atr_v * factor
else:
new_stop = close - atr_v * factor
if new_stop > self._atr_stop:
self._atr_stop = new_stop
if low <= self._atr_stop:
self.SellMarket()
self._atr_stop = None
return
elif self.Position < 0:
if self._atr_stop is None:
self._atr_stop = self._entry_price + atr_v * factor
else:
new_stop = close + atr_v * factor
if new_stop < self._atr_stop:
self._atr_stop = new_stop
if high >= self._atr_stop:
self.BuyMarket()
self._atr_stop = None
return
if long_cond:
self.BuyMarket()
self._entry_price = close
elif short_cond:
self.SellMarket()
self._entry_price = close
def CreateClone(self):
return iu_bbb_big_body_bar_strategy()