Стратегия анализирует девять таймфреймов свечных данных и строит для каждого линейный регрессионный канал одинаковой длины. Наклон канала на часовом таймфрейме (H1) выступает фильтром тренда: отрицательное значение разрешает только продажи, положительное — только покупки. Каналы на M5 и M1 используются для точного определения зон входа.
Логика работы
Источники данных: свечи на таймфреймах M1, M5, M15, M30, H1, H4, D1, W1 и MN1.
Индикаторы: для каждого потока рассчитывается линейная регрессия. Верхняя и нижняя границы строятся симметрично относительно центральной линии по максимальному отклонению цены закрытия за выбранное окно.
Фильтр тренда: проверяется знак наклона регрессии на H1. Пока наклон отрицательный, стратегия ищет только шорты; при положительном наклоне ищутся только лонги.
Условия входа:
Шорт — экстремумы последней свечи M5 и последней свечи M1 находятся выше верхних границ своих каналов при отрицательном наклоне H1.
Лонг — минимумы последней свечи M5 и последней свечи M1 касаются нижних границ каналов при положительном наклоне H1.
Исполнение: заявки отправляются по рынку с заданным объемом. Стоп-лосс рассчитывается как половина расстояния от верхней/нижней границы канала M5 до центральной линии. Тейк-профит равен самой линии M5.
Выход: на каждом закрытии свечи M1 проверяются уровни стопа и цели. При достижении любого из уровней позиция закрывается рыночной заявкой.
Управление позицией: одновременно может существовать только одна позиция (без усреднений и переворотов).
Параметры
Параметр
Описание
EnableTrading
Включает или выключает возможность выставления заявок.
BarsToCount
Количество свечей в окне регрессионных каналов (по умолчанию 50).
Volume
Объем рыночной заявки в лотах.
Дополнительные замечания
Увеличение BarsToCount делает наклон каналов плавнее, но замедляет реакции на изменение тренда.
Отображение наклонов по всем таймфреймам полезно для визуальной оценки синхронизации, хотя торговые решения принимает только наклон H1.
Стопы и цели пересчитываются на каждой новой свече M5, поэтому риск подстраивается под актуальную ширину канала.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Linear regression channel strategy.
/// Uses LinearReg as the center line with Highest/Lowest to form a channel.
/// Sells at upper channel, buys at lower channel, with trend filter from regression slope.
/// </summary>
public class MultiTimeFrameRegressionStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _regressionLength;
private readonly StrategyParam<int> _channelLength;
private decimal _prevLrValue;
private bool _hasPrev;
public MultiTimeFrameRegressionStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_regressionLength = Param(nameof(RegressionLength), 20)
.SetDisplay("Regression Length", "Period for linear regression.", "Indicators");
_channelLength = Param(nameof(ChannelLength), 20)
.SetDisplay("Channel Length", "Period for highest/lowest channel.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RegressionLength
{
get => _regressionLength.Value;
set => _regressionLength.Value = value;
}
public int ChannelLength
{
get => _channelLength.Value;
set => _channelLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevLrValue = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevLrValue = 0;
_hasPrev = false;
var lr = new LinearReg { Length = RegressionLength };
var highest = new Highest { Length = ChannelLength };
var lowest = new Lowest { Length = ChannelLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(lr, highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, lr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal lrValue, decimal highestValue, decimal lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
// Determine slope direction from regression
var slope = _hasPrev ? lrValue - _prevLrValue : 0m;
// Channel boundaries
var channelMid = (highestValue + lowestValue) / 2m;
var channelWidth = highestValue - lowestValue;
if (channelWidth <= 0)
{
_prevLrValue = lrValue;
_hasPrev = true;
return;
}
// Upper/lower thresholds
var upperThreshold = channelMid + channelWidth * 0.4m;
var lowerThreshold = channelMid - channelWidth * 0.4m;
// Exit conditions
if (Position > 0 && (close >= upperThreshold || slope < 0))
{
SellMarket();
}
else if (Position < 0 && (close <= lowerThreshold || slope > 0))
{
BuyMarket();
}
// Entry conditions
if (Position == 0)
{
if (close <= lowerThreshold && slope >= 0)
{
// Price near lower channel with flat/rising regression
BuyMarket();
}
else if (close >= upperThreshold && slope <= 0)
{
// Price near upper channel with flat/falling regression
SellMarket();
}
}
_prevLrValue = lrValue;
_hasPrev = true;
}
}
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, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class multi_time_frame_regression_strategy(Strategy):
"""
Multi Time Frame Regression: linear regression channel with highest/lowest boundaries.
"""
def __init__(self):
super(multi_time_frame_regression_strategy, self).__init__()
self._regression_length = self.Param("RegressionLength", 20).SetDisplay("Regression", "LinReg period", "Indicators")
self._channel_length = self.Param("ChannelLength", 20).SetDisplay("Channel", "Channel period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_lr = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(multi_time_frame_regression_strategy, self).OnReseted()
self._prev_lr = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(multi_time_frame_regression_strategy, self).OnStarted2(time)
lr = LinearReg()
lr.Length = self._regression_length.Value
highest = Highest()
highest.Length = self._channel_length.Value
lowest = Lowest()
lowest.Length = self._channel_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(lr, highest, lowest, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, lr)
self.DrawOwnTrades(area)
def _process_candle(self, candle, lr_val, high_val, low_val):
if candle.State != CandleStates.Finished:
return
lr = float(lr_val)
h = float(high_val)
l = float(low_val)
close = float(candle.ClosePrice)
slope = lr - self._prev_lr if self._has_prev else 0.0
ch_mid = (h + l) / 2.0
ch_width = h - l
if ch_width <= 0:
self._prev_lr = lr
self._has_prev = True
return
upper = ch_mid + ch_width * 0.4
lower = ch_mid - ch_width * 0.4
if self.Position > 0 and (close >= upper or slope < 0):
self.SellMarket()
elif self.Position < 0 and (close <= lower or slope > 0):
self.BuyMarket()
if self.Position == 0:
if close <= lower and slope >= 0:
self.BuyMarket()
elif close >= upper and slope <= 0:
self.SellMarket()
self._prev_lr = lr
self._has_prev = True
def CreateClone(self):
return multi_time_frame_regression_strategy()