This strategy implements a breakout approach based on the classic Darvas Boxes concept. It monitors price movement within a dynamic price range (box) calculated using the Donchian Channels indicator. When price closes above the upper boundary of the box, a long position is opened. When price closes below the lower boundary, a short position is opened. Optional stop-loss and take-profit levels provide basic risk management.
How It Works
For each candle, the Donchian Channels indicator computes upper and lower boundaries using the specified BoxPeriod.
The strategy tracks the previous upper and lower values to detect breakouts.
If the current close price crosses above the previous upper boundary, the strategy:
Closes any existing short position (if allowed).
Opens a new long position (if allowed).
If the current close price crosses below the previous lower boundary, the strategy:
Closes any existing long position (if allowed).
Opens a new short position (if allowed).
Active positions are monitored for stop-loss and take-profit conditions.
Parameters
BoxPeriod (int): Number of candles used to build the price box. Default is 20.
StopLoss (decimal): Distance from entry price to the stop-loss level. Default is 1000.
TakeProfit (decimal): Distance from entry price to the take-profit level. Default is 2000.
AllowBuyEntry (bool): Enables opening long positions. Default is true.
AllowSellEntry (bool): Enables opening short positions. Default is true.
AllowBuyExit (bool): Enables closing long positions on reverse signals or risk events. Default is true.
AllowSellExit (bool): Enables closing short positions on reverse signals or risk events. Default is true.
CandleType (DataType): Type of candles used for calculations. Default is 4-hour candles.
Usage
Attach the strategy to a security and set desired parameter values.
Start the strategy. It will subscribe to the configured candle series and process incoming data.
Trades are executed using market orders when breakout conditions are met.
Optional stop-loss and take-profit levels manage open positions.
Notes
The strategy uses the high-level API with BindEx to connect indicator values and candle data.
Internal collections are avoided; indicator values are accessed through the binding callback.
Only finished candles are processed to ensure reliable signals.
Comments inside the code are provided in English as required.
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>
/// Darvas Boxes breakout strategy using Donchian Channels.
/// Opens long position when price breaks above the upper box line.
/// Opens short position when price breaks below the lower box line.
/// </summary>
public class DarvasBoxesSystemStrategy : Strategy
{
private readonly StrategyParam<int> _boxPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevUpper;
private decimal _prevLower;
private decimal _prevClose;
public int BoxPeriod
{
get => _boxPeriod.Value;
set => _boxPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public DarvasBoxesSystemStrategy()
{
_boxPeriod = Param(nameof(BoxPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Box Period", "Period for box calculation", "Indicators")
.SetOptimize(10, 40, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevUpper = 0m;
_prevLower = 0m;
_prevClose = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevUpper = 0m;
_prevLower = 0m;
_prevClose = 0m;
var donchian = new DonchianChannels { Length = BoxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(donchian, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, donchian);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (value is not IDonchianChannelsValue box)
return;
if (box.UpperBand is not decimal upper || box.LowerBand is not decimal lower)
return;
if (_prevUpper == 0m)
{
_prevUpper = upper;
_prevLower = lower;
_prevClose = candle.ClosePrice;
return;
}
var isUpBreakout = candle.ClosePrice > _prevUpper && _prevClose <= _prevUpper;
var isDownBreakout = candle.ClosePrice < _prevLower && _prevClose >= _prevLower;
if (isUpBreakout && Position <= 0)
BuyMarket();
else if (isDownBreakout && Position >= 0)
SellMarket();
_prevUpper = upper;
_prevLower = lower;
_prevClose = 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 DonchianChannels
from StockSharp.Algo.Strategies import Strategy
class darvas_boxes_system_strategy(Strategy):
def __init__(self):
super(darvas_boxes_system_strategy, self).__init__()
self._box_period = self.Param("BoxPeriod", 20) \
.SetDisplay("Box Period", "Period for box calculation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_upper = 0.0
self._prev_lower = 0.0
self._prev_close = 0.0
@property
def box_period(self):
return self._box_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(darvas_boxes_system_strategy, self).OnReseted()
self._prev_upper = 0.0
self._prev_lower = 0.0
self._prev_close = 0.0
def OnStarted2(self, time):
super(darvas_boxes_system_strategy, self).OnStarted2(time)
self._prev_upper = 0.0
self._prev_lower = 0.0
self._prev_close = 0.0
donchian = DonchianChannels()
donchian.Length = self.box_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(donchian, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, donchian)
self.DrawOwnTrades(area)
def process_candle(self, candle, value):
if candle.State != CandleStates.Finished:
return
upper = value.UpperBand
lower = value.LowerBand
if upper is None or lower is None:
return
upper = float(upper)
lower = float(lower)
close_price = float(candle.ClosePrice)
if self._prev_upper == 0.0:
self._prev_upper = upper
self._prev_lower = lower
self._prev_close = close_price
return
is_up_breakout = close_price > self._prev_upper and self._prev_close <= self._prev_upper
is_down_breakout = close_price < self._prev_lower and self._prev_close >= self._prev_lower
if is_up_breakout and self.Position <= 0:
self.BuyMarket()
elif is_down_breakout and self.Position >= 0:
self.SellMarket()
self._prev_upper = upper
self._prev_lower = lower
self._prev_close = close_price
def CreateClone(self):
return darvas_boxes_system_strategy()