Стратегия реализует пробойный подход на основе классической концепции Darvas Boxes. Для построения динамического ценового диапазона используется индикатор Donchian Channels. При закрытии цены выше верхней границы коробки открывается длинная позиция. При закрытии ниже нижней границы открывается короткая позиция. Дополнительно поддерживаются уровни стоп-лосса и тейк-профита для управления рисками.
Как это работает
Для каждой свечи индикатор Donchian Channels рассчитывает верхнюю и нижнюю границы на основе параметра BoxPeriod.
Стратегия отслеживает предыдущие значения границ для обнаружения пробоя.
Если текущая цена закрытия пересекает сверху предыдущую верхнюю границу:
Закрывается открытая короткая позиция (если разрешено).
Открывается длинная позиция (если разрешено).
Если текущая цена закрытия пересекает снизу предыдущую нижнюю границу:
Закрывается открытая длинная позиция (если разрешено).
Открывается короткая позиция (если разрешено).
Открытые позиции контролируются на предмет достижения стоп-лосса или тейк-профита.
Параметры
BoxPeriod (int): Количество свечей для расчёта коробки. По умолчанию 20.
StopLoss (decimal): Расстояние от цены входа до уровня стоп-лосса. По умолчанию 1000.
TakeProfit (decimal): Расстояние от цены входа до уровня тейк-профита. По умолчанию 2000.
AllowBuyEntry (bool): Разрешить открытие длинных позиций. По умолчанию true.
AllowSellEntry (bool): Разрешить открытие коротких позиций. По умолчанию true.
AllowBuyExit (bool): Разрешить закрытие длинных позиций по обратному сигналу или риску. По умолчанию true.
AllowSellExit (bool): Разрешить закрытие коротких позиций по обратному сигналу или риску. По умолчанию true.
CandleType (DataType): Тип свечей, используемых в расчётах. По умолчанию 4-часовые свечи.
Использование
Подключите стратегию к инструменту и настройте параметры.
Запустите стратегию. Она подпишется на нужную серию свечей и начнёт обработку данных.
Сделки выполняются рыночными ордерами при выполнении условий пробоя.
Стоп-лосс и тейк-профит контролируют открытые позиции.
Примечания
Используется высокоуровневый API с методом BindEx для получения значений индикатора.
Внутренние коллекции не используются; все расчёты основаны на передаваемых значениях.
Обрабатываются только завершённые свечи для надёжных сигналов.
Комментарии в коде приведены на английском языке, как требуется.
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()