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()