Стратегия прорыва регрессионного канала
Стратегия реализует торговую систему по принципу регрессионного канала,
основанную на MQL-скрипте e-Regr. Линейная регрессия строится на заданном
числе последних свечей, а верхняя и нижняя границы формируются на расстоянии
стандартного отклонения. Правила торговли:
- Покупка: минимум свечи касается или пробивает нижнюю границу.
- Продажа: максимум свечи касается или пробивает верхнюю границу.
- Выход: закрытие пересекает линию регрессии в противоположном направлении.
- Трейлинг-стоп: опционально подтягивает уровень стопа после достижения заданной прибыли.
Параметры
| Имя | Описание |
|---|---|
CandleType |
Тип свечей для расчётов. |
Length |
Количество свечей для регрессии и стандартного отклонения. |
Deviation |
Множитель стандартного отклонения для ширины канала. |
UseTrailing |
Включить логику трейлинг-стопа. |
TrailingStart |
Прибыль, после которой запускается трейлинг. |
TrailingStep |
Расстояние между ценой и трейлинг-стопом. |
Стратегия использует высокоуровневый API StockSharp через SubscribeCandles и
Bind для получения свечей и значений индикаторов.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy trading regression channel breakouts.
/// </summary>
public class RegressionChannelBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _deviation;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _useTrailing;
private readonly StrategyParam<decimal> _trailingStart;
private readonly StrategyParam<decimal> _trailingStep;
private LinearReg _regression;
private StandardDeviation _stdev;
private decimal _entryPrice;
private decimal _longStop;
private decimal _shortStop;
public int Length { get => _length.Value; set => _length.Value = value; }
public decimal Deviation { get => _deviation.Value; set => _deviation.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public bool UseTrailing { get => _useTrailing.Value; set => _useTrailing.Value = value; }
public decimal TrailingStart { get => _trailingStart.Value; set => _trailingStart.Value = value; }
public decimal TrailingStep { get => _trailingStep.Value; set => _trailingStep.Value = value; }
public RegressionChannelBreakoutStrategy()
{
_length = Param(nameof(Length), 250)
.SetDisplay("Length", "Number of candles for regression and deviation", "Common")
.SetOptimize(50, 500, 50);
_deviation = Param(nameof(Deviation), 2m)
.SetDisplay("Deviation", "Standard deviation multiplier", "Common")
.SetOptimize(1m, 4m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "Common");
_useTrailing = Param(nameof(UseTrailing), false)
.SetDisplay("Use Trailing", "Enable trailing stop logic", "Trailing");
_trailingStart = Param(nameof(TrailingStart), 30m)
.SetDisplay("Trailing Start", "Profit required before trailing starts", "Trailing");
_trailingStep = Param(nameof(TrailingStep), 30m)
.SetDisplay("Trailing Step", "Distance between price and trailing stop", "Trailing");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetTrailing();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_regression = new LinearReg { Length = Length };
_stdev = new StandardDeviation { Length = Length };
ResetTrailing();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_regression, _stdev, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal reg, decimal dev)
{
if (candle.State != CandleStates.Finished)
return;
var middle = reg;
var upper = reg + Deviation * dev;
var lower = reg - Deviation * dev;
var price = candle.ClosePrice;
if (price >= middle && Position > 0)
{
SellMarket();
ResetTrailing();
}
else if (price <= middle && Position < 0)
{
BuyMarket();
ResetTrailing();
}
else
{
if (candle.LowPrice <= lower && Position <= 0)
{
BuyMarket();
_entryPrice = price;
ResetTrailing();
}
else if (candle.HighPrice >= upper && Position >= 0)
{
SellMarket();
_entryPrice = price;
ResetTrailing();
}
}
if (UseTrailing)
ApplyTrailing(price);
}
private void ApplyTrailing(decimal price)
{
if (Position > 0)
{
if (_entryPrice == 0m)
_entryPrice = price;
var profit = price - _entryPrice;
if (profit >= TrailingStart)
{
var stop = price - TrailingStep;
if (_longStop == 0m || stop > _longStop)
_longStop = stop;
}
if (_longStop != 0m && price <= _longStop)
{
SellMarket();
ResetTrailing();
}
}
else if (Position < 0)
{
if (_entryPrice == 0m)
_entryPrice = price;
var profit = _entryPrice - price;
if (profit >= TrailingStart)
{
var stop = price + TrailingStep;
if (_shortStop == 0m || stop < _shortStop)
_shortStop = stop;
}
if (_shortStop != 0m && price >= _shortStop)
{
BuyMarket();
ResetTrailing();
}
}
else
{
ResetTrailing();
}
}
private void ResetTrailing()
{
_entryPrice = 0m;
_longStop = 0m;
_shortStop = 0m;
}
}
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 LinearReg, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class regression_channel_breakout_strategy(Strategy):
def __init__(self):
super(regression_channel_breakout_strategy, self).__init__()
self._length = self.Param("Length", 250)
self._deviation = self.Param("Deviation", 2.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._use_trailing = self.Param("UseTrailing", False)
self._trailing_start = self.Param("TrailingStart", 30.0)
self._trailing_step = self.Param("TrailingStep", 30.0)
self._entry_price = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
@property
def Length(self):
return self._length.Value
@Length.setter
def Length(self, value):
self._length.Value = value
@property
def Deviation(self):
return self._deviation.Value
@Deviation.setter
def Deviation(self, value):
self._deviation.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def UseTrailing(self):
return self._use_trailing.Value
@UseTrailing.setter
def UseTrailing(self, value):
self._use_trailing.Value = value
@property
def TrailingStart(self):
return self._trailing_start.Value
@TrailingStart.setter
def TrailingStart(self, value):
self._trailing_start.Value = value
@property
def TrailingStep(self):
return self._trailing_step.Value
@TrailingStep.setter
def TrailingStep(self, value):
self._trailing_step.Value = value
def OnStarted2(self, time):
super(regression_channel_breakout_strategy, self).OnStarted2(time)
self._regression = LinearReg()
self._regression.Length = self.Length
self._stdev = StandardDeviation()
self._stdev.Length = self.Length
self._reset_trailing()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._regression, self._stdev, self.ProcessCandle).Start()
def ProcessCandle(self, candle, reg, dev):
if candle.State != CandleStates.Finished:
return
middle = float(reg)
upper = float(reg) + float(self.Deviation) * float(dev)
lower = float(reg) - float(self.Deviation) * float(dev)
price = float(candle.ClosePrice)
if price >= middle and self.Position > 0:
self.SellMarket()
self._reset_trailing()
elif price <= middle and self.Position < 0:
self.BuyMarket()
self._reset_trailing()
else:
if float(candle.LowPrice) <= lower and self.Position <= 0:
self.BuyMarket()
self._entry_price = price
self._reset_trailing()
elif float(candle.HighPrice) >= upper and self.Position >= 0:
self.SellMarket()
self._entry_price = price
self._reset_trailing()
if self.UseTrailing:
self._apply_trailing(price)
def _apply_trailing(self, price):
if self.Position > 0:
if self._entry_price == 0.0:
self._entry_price = price
profit = price - self._entry_price
if profit >= float(self.TrailingStart):
stop = price - float(self.TrailingStep)
if self._long_stop == 0.0 or stop > self._long_stop:
self._long_stop = stop
if self._long_stop != 0.0 and price <= self._long_stop:
self.SellMarket()
self._reset_trailing()
elif self.Position < 0:
if self._entry_price == 0.0:
self._entry_price = price
profit = self._entry_price - price
if profit >= float(self.TrailingStart):
stop = price + float(self.TrailingStep)
if self._short_stop == 0.0 or stop < self._short_stop:
self._short_stop = stop
if self._short_stop != 0.0 and price >= self._short_stop:
self.BuyMarket()
self._reset_trailing()
else:
self._reset_trailing()
def _reset_trailing(self):
self._entry_price = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
def OnReseted(self):
super(regression_channel_breakout_strategy, self).OnReseted()
self._reset_trailing()
def CreateClone(self):
return regression_channel_breakout_strategy()