Esta estrategia implementa un enfoque de ruptura basado en el concepto clásico de Darvas Boxes. Monitorea el movimiento del precio dentro de un rango dinámico (caja) calculado mediante el indicador Donchian Channels. Cuando el precio cierra por encima del límite superior de la caja, se abre una posición larga. Cuando el precio cierra por debajo del límite inferior, se abre una posición corta. Los niveles opcionales de stop-loss y take-profit proporcionan una gestión básica del riesgo.
Cómo funciona
Para cada vela, el indicador Donchian Channels calcula los límites superior e inferior utilizando el BoxPeriod especificado.
La estrategia rastrea los valores anteriores de los límites para detectar rupturas.
Si el precio de cierre actual cruza por encima del límite superior anterior, la estrategia:
Cierra cualquier posición corta existente (si está permitido).
Abre una nueva posición larga (si está permitido).
Si el precio de cierre actual cruza por debajo del límite inferior anterior, la estrategia:
Cierra cualquier posición larga existente (si está permitido).
Abre una nueva posición corta (si está permitido).
Las posiciones activas se monitorizan para verificar las condiciones de stop-loss y take-profit.
Parámetros
BoxPeriod (int): Número de velas utilizadas para construir la caja de precio. El valor predeterminado es 20.
StopLoss (decimal): Distancia desde el precio de entrada hasta el nivel de stop-loss. El valor predeterminado es 1000.
TakeProfit (decimal): Distancia desde el precio de entrada hasta el nivel de take-profit. El valor predeterminado es 2000.
AllowBuyEntry (bool): Habilita la apertura de posiciones largas. El valor predeterminado es true.
AllowSellEntry (bool): Habilita la apertura de posiciones cortas. El valor predeterminado es true.
AllowBuyExit (bool): Habilita el cierre de posiciones largas ante señales inversas o eventos de riesgo. El valor predeterminado es true.
AllowSellExit (bool): Habilita el cierre de posiciones cortas ante señales inversas o eventos de riesgo. El valor predeterminado es true.
CandleType (DataType): Tipo de velas utilizadas para los cálculos. El valor predeterminado son velas de 4 horas.
Uso
Adjunte la estrategia a un valor y establezca los valores de parámetros deseados.
Inicie la estrategia. Se suscribirá a la serie de velas configurada y procesará los datos entrantes.
Las operaciones se ejecutan con órdenes de mercado cuando se cumplen las condiciones de ruptura.
Los niveles opcionales de stop-loss y take-profit gestionan las posiciones abiertas.
Notas
La estrategia utiliza la API de alto nivel con BindEx para conectar los valores del indicador y los datos de velas.
Se evitan las colecciones internas; los valores del indicador se acceden a través del callback de enlace.
Solo se procesan las velas completadas para garantizar señales fiables.
Los comentarios dentro del código están en inglés, como se requiere.
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()