Esta estratégia implementa uma abordagem de rompimento baseada no conceito clássico de Darvas Boxes. Ela monitora o movimento do preço dentro de um intervalo dinâmico (caixa) calculado usando o indicador Donchian Channels. Quando o preço fecha acima do limite superior da caixa, uma posição comprada é aberta. Quando o preço fecha abaixo do limite inferior, uma posição vendida é aberta. Níveis opcionais de stop-loss e take-profit fornecem gerenciamento básico de risco.
Como funciona
Para cada candle, o indicador Donchian Channels calcula os limites superior e inferior usando o BoxPeriod especificado.
A estratégia rastreia os valores anteriores dos limites para detectar rompimentos.
Se o preço de fechamento atual cruzar acima do limite superior anterior, a estratégia:
Fecha qualquer posição vendida existente (se permitido).
Abre uma nova posição comprada (se permitido).
Se o preço de fechamento atual cruzar abaixo do limite inferior anterior, a estratégia:
Fecha qualquer posição comprada existente (se permitido).
Abre uma nova posição vendida (se permitido).
As posições ativas são monitoradas para verificar as condições de stop-loss e take-profit.
Parâmetros
BoxPeriod (int): Número de candles usados para construir a caixa de preço. O valor padrão é 20.
StopLoss (decimal): Distância do preço de entrada até o nível de stop-loss. O valor padrão é 1000.
TakeProfit (decimal): Distância do preço de entrada até o nível de take-profit. O valor padrão é 2000.
AllowBuyEntry (bool): Habilita a abertura de posições compradas. O valor padrão é true.
AllowSellEntry (bool): Habilita a abertura de posições vendidas. O valor padrão é true.
AllowBuyExit (bool): Habilita o fechamento de posições compradas em sinais inversos ou eventos de risco. O valor padrão é true.
AllowSellExit (bool): Habilita o fechamento de posições vendidas em sinais inversos ou eventos de risco. O valor padrão é true.
CandleType (DataType): Tipo de candles usados para cálculos. O valor padrão são candles de 4 horas.
Uso
Anexe a estratégia a um ativo e defina os valores de parâmetros desejados.
Inicie a estratégia. Ela irá subscrever a série de candles configurada e processar os dados recebidos.
As operações são executadas com ordens a mercado quando as condições de rompimento são atendidas.
Níveis opcionais de stop-loss e take-profit gerenciam posições abertas.
Notas
A estratégia usa a API de alto nível com BindEx para conectar os valores do indicador e os dados de candles.
Coleções internas são evitadas; os valores do indicador são acessados através do callback de vinculação.
Apenas candles concluídos são processados para garantir sinais confiáveis.
Os comentários dentro do código estão em inglês, conforme exigido.
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()