Daily Breakpoint Strategy
This strategy trades breakouts from the daily open. At the beginning of each new day the opening price is stored. When the price moves away from this level by a user defined number of points, and the previous bar is within a configurable size range, the strategy enters in the breakout direction.
Entry logic
- If the previous bar is bullish and price rises above the daily open by Break Point points, a long position is opened.
- If the previous bar is bearish and price falls below the daily open by Break Point points, a short position is opened.
- Previous bar size must be between Last Bar Min and Last Bar Max points.
- Breakout level must lie within the previous bar body.
Risk management
- Optional Take Profit and Stop Loss are measured in points from the entry price.
- A trailing stop can be enabled with Trailing Start, Trailing Stop and Trailing Step parameters. When price moves in favour by Trailing Start the stop is set at Trailing Stop points from the entry and then trails by Trailing Step increments.
Parameters
| Name |
Description |
| Candle Type |
Timeframe of processed candles. |
| Break Point |
Distance from daily open to trigger a trade (points). |
| Last Bar Min |
Minimum size of the previous bar (points). |
| Last Bar Max |
Maximum size of the previous bar (points). |
| Trailing Start |
Price move to start trailing stop (points). |
| Trailing Stop |
Initial trailing distance (points). |
| Trailing Step |
Step to move trailing stop (points). |
| Take Profit |
Take profit distance (points). |
| Stop Loss |
Stop loss distance (points). |
Notes
The strategy operates on finished candles only and uses market orders for entries and exits. It stores internal variables for previous bar data and trailing stop level.
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>
/// Daily breakout strategy based on the previous bar size and daily open.
/// </summary>
public class DailyBreakpointStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _breakPointPct;
private readonly StrategyParam<decimal> _lastBarMinPct;
private readonly StrategyParam<decimal> _lastBarMaxPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private decimal _prevOpen;
private decimal _prevClose;
private DateTimeOffset _prevTime;
private bool _hasPrev;
private decimal _dayOpen;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal BreakPointPct { get => _breakPointPct.Value; set => _breakPointPct.Value = value; }
public decimal LastBarMinPct { get => _lastBarMinPct.Value; set => _lastBarMinPct.Value = value; }
public decimal LastBarMaxPct { get => _lastBarMaxPct.Value; set => _lastBarMaxPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public DailyBreakpointStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_breakPointPct = Param(nameof(BreakPointPct), 0.3m)
.SetDisplay("Break Point %", "Breakout offset as % of price", "General");
_lastBarMinPct = Param(nameof(LastBarMinPct), 0.05m)
.SetDisplay("Min Bar %", "Minimal bar size as % of price", "Filter");
_lastBarMaxPct = Param(nameof(LastBarMaxPct), 1.0m)
.SetDisplay("Max Bar %", "Maximum bar size as % of price", "Filter");
_takeProfitPct = Param(nameof(TakeProfitPct), 2m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 1m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevOpen = default;
_prevClose = default;
_prevTime = default;
_hasPrev = default;
_dayOpen = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
isStopTrailing: true,
useMarketOrders: true);
var sub = SubscribeCandles(CandleType);
sub.Bind(Process).Start();
}
private void Process(ICandleMessage c)
{
if (c.State != CandleStates.Finished)
return;
if (!_hasPrev || c.OpenTime.Date != _prevTime.Date)
_dayOpen = c.OpenPrice;
if (Position == 0 && _hasPrev)
{
var price = c.ClosePrice;
var lastSize = Math.Abs(_prevClose - _prevOpen);
var minSize = LastBarMinPct / 100m * price;
var maxSize = LastBarMaxPct / 100m * price;
var offset = BreakPointPct / 100m * price;
var breakBuy = _dayOpen + offset;
var breakSell = _dayOpen - offset;
if (_prevClose > _prevOpen && price - _dayOpen >= offset &&
lastSize >= minSize && lastSize <= maxSize &&
breakBuy >= _prevOpen && breakBuy <= _prevClose)
{
BuyMarket();
}
else if (_prevClose < _prevOpen && _dayOpen - price >= offset &&
lastSize >= minSize && lastSize <= maxSize &&
breakSell <= _prevOpen && breakSell >= _prevClose)
{
SellMarket();
}
}
_prevOpen = c.OpenPrice;
_prevClose = c.ClosePrice;
_prevTime = c.OpenTime;
_hasPrev = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class daily_breakpoint_strategy(Strategy):
def __init__(self):
super(daily_breakpoint_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._break_point_pct = self.Param("BreakPointPct", 0.3) \
.SetDisplay("Break Point %", "Breakout offset as % of price", "General")
self._last_bar_min_pct = self.Param("LastBarMinPct", 0.05) \
.SetDisplay("Min Bar %", "Minimal bar size as % of price", "Filter")
self._last_bar_max_pct = self.Param("LastBarMaxPct", 1.0) \
.SetDisplay("Max Bar %", "Maximum bar size as % of price", "Filter")
self._take_profit_pct = self.Param("TakeProfitPct", 2.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 1.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._prev_open = 0.0
self._prev_close = 0.0
self._prev_time = None
self._has_prev = False
self._day_open = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@property
def break_point_pct(self):
return self._break_point_pct.Value
@property
def last_bar_min_pct(self):
return self._last_bar_min_pct.Value
@property
def last_bar_max_pct(self):
return self._last_bar_max_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
def OnReseted(self):
super(daily_breakpoint_strategy, self).OnReseted()
self._prev_open = 0.0
self._prev_close = 0.0
self._prev_time = None
self._has_prev = False
self._day_open = 0.0
def OnStarted2(self, time):
super(daily_breakpoint_strategy, self).OnStarted2(time)
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
isStopTrailing=True,
useMarketOrders=True)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process).Start()
def process(self, candle):
if candle.State != CandleStates.Finished:
return
if not self._has_prev or candle.OpenTime.Date != self._prev_time.Date:
self._day_open = float(candle.OpenPrice)
if self.Position == 0 and self._has_prev:
price = float(candle.ClosePrice)
last_size = abs(self._prev_close - self._prev_open)
min_size = float(self.last_bar_min_pct) / 100.0 * price
max_size = float(self.last_bar_max_pct) / 100.0 * price
offset = float(self.break_point_pct) / 100.0 * price
break_buy = self._day_open + offset
break_sell = self._day_open - offset
if (self._prev_close > self._prev_open and price - self._day_open >= offset and
last_size >= min_size and last_size <= max_size and
break_buy >= self._prev_open and break_buy <= self._prev_close):
self.BuyMarket()
elif (self._prev_close < self._prev_open and self._day_open - price >= offset and
last_size >= min_size and last_size <= max_size and
break_sell <= self._prev_open and break_sell >= self._prev_close):
self.SellMarket()
self._prev_open = float(candle.OpenPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_time = candle.OpenTime
self._has_prev = True
def CreateClone(self):
return daily_breakpoint_strategy()