Moving Regression
Стратегия использует полиномиальную скользящую регрессию для прогнозирования следующей цены. Покупает, когда прогноз выше текущего значения, и продаёт, когда ниже.
Детали
- Критерии входа: Направление прогноза.
- Лонг/Шорт: Оба направления.
- Критерии выхода: Противоположный сигнал.
- Стопы: Нет.
- Значения по умолчанию:
Degree= 2Window= 18CandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Тренд
- Направление: Оба
- Индикаторы: Полиномиальная регрессия
- Стопы: Нет
- Сложность: Средняя
- Таймфрейм: Внутридневной (5m)
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Strategy based on moving linear regression slope.
/// </summary>
public class MovingRegressionStrategy : Strategy
{
private readonly StrategyParam<int> _degree;
private readonly StrategyParam<int> _window;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _slopeThresholdPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _prices = new();
private int _barIndex;
private int _lastSignalBar = -1000000;
/// <summary>
/// Degree-like sensitivity multiplier.
/// </summary>
public int Degree
{
get => _degree.Value;
set => _degree.Value = value;
}
/// <summary>
/// Regression window length.
/// </summary>
public int Window
{
get => _window.Value;
set => _window.Value = value;
}
/// <summary>
/// Minimum finished candles between entries.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Minimum absolute slope in percent of close price.
/// </summary>
public decimal SlopeThresholdPercent
{
get => _slopeThresholdPercent.Value;
set => _slopeThresholdPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="MovingRegressionStrategy"/> class.
/// </summary>
public MovingRegressionStrategy()
{
_degree = Param(nameof(Degree), 2)
.SetRange(0, 5)
.SetDisplay("Degree", "Sensitivity multiplier", "General");
_window = Param(nameof(Window), 20)
.SetRange(10, 200)
.SetDisplay("Window", "Regression window length", "General");
_cooldownBars = Param(nameof(CooldownBars), 8)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Finished candles between entries", "General");
_slopeThresholdPercent = Param(nameof(SlopeThresholdPercent), 0.005m)
.SetGreaterThanZero()
.SetDisplay("Slope Threshold %", "Min slope absolute value in percent", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prices.Clear();
_barIndex = 0;
_lastSignalBar = -1000000;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var dummyEma1 = new ExponentialMovingAverage { Length = 10 };
var dummyEma2 = new ExponentialMovingAverage { Length = 20 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(dummyEma1, dummyEma2, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal d1, decimal d2)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
_prices.Add(candle.ClosePrice);
if (_prices.Count > Window)
_prices.RemoveAt(0);
if (_prices.Count < Window)
return;
var slope = CalculateSlope(_prices);
var slopePercent = candle.ClosePrice != 0m
? slope / candle.ClosePrice * 100m
: 0m;
var threshold = SlopeThresholdPercent * (1m + Degree * 0.05m);
var canSignal = _barIndex - _lastSignalBar >= CooldownBars;
if (canSignal && slopePercent > threshold && Position <= 0)
{
BuyMarket();
_lastSignalBar = _barIndex;
}
else if (canSignal && slopePercent < -threshold && Position >= 0)
{
SellMarket();
_lastSignalBar = _barIndex;
}
}
private static decimal CalculateSlope(IReadOnlyList<decimal> prices)
{
var n = prices.Count;
if (n < 2)
return 0m;
double sumX = 0d;
double sumY = 0d;
double sumXX = 0d;
double sumXY = 0d;
for (var i = 0; i < n; i++)
{
var x = (double)i;
var y = (double)prices[i];
sumX += x;
sumY += y;
sumXX += x * x;
sumXY += x * y;
}
var denominator = n * sumXX - sumX * sumX;
if (denominator == 0d)
return 0m;
var slope = (n * sumXY - sumX * sumY) / denominator;
return (decimal)slope;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class moving_regression_strategy(Strategy):
"""
Moving regression: linear regression slope on close prices for trend direction.
"""
def __init__(self):
super(moving_regression_strategy, self).__init__()
self._degree = self.Param("Degree", 2).SetDisplay("Degree", "Sensitivity multiplier", "General")
self._window = self.Param("Window", 20).SetDisplay("Window", "Regression window", "General")
self._cooldown_bars = self.Param("CooldownBars", 8).SetDisplay("Cooldown", "Min bars between entries", "General")
self._slope_threshold = self.Param("SlopeThresholdPercent", 0.005).SetDisplay("Slope Threshold %", "Min slope pct", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prices = []
self._bar_index = 0
self._last_signal_bar = -1000000
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(moving_regression_strategy, self).OnReseted()
self._prices = []
self._bar_index = 0
self._last_signal_bar = -1000000
def OnStarted2(self, time):
super(moving_regression_strategy, self).OnStarted2(time)
ema1 = ExponentialMovingAverage()
ema1.Length = 10
ema2 = ExponentialMovingAverage()
ema2.Length = 20
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema1, ema2, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, d1, d2):
if candle.State != CandleStates.Finished:
return
self._bar_index += 1
close = float(candle.ClosePrice)
self._prices.append(close)
win = self._window.Value
if len(self._prices) > win:
self._prices = self._prices[-win:]
if len(self._prices) < win:
return
slope = self._calc_slope(self._prices)
slope_pct = slope / close * 100.0 if close != 0 else 0.0
threshold = float(self._slope_threshold.Value) * (1.0 + self._degree.Value * 0.05)
can_signal = self._bar_index - self._last_signal_bar >= self._cooldown_bars.Value
if can_signal and slope_pct > threshold and self.Position <= 0:
self.BuyMarket()
self._last_signal_bar = self._bar_index
elif can_signal and slope_pct < -threshold and self.Position >= 0:
self.SellMarket()
self._last_signal_bar = self._bar_index
@staticmethod
def _calc_slope(prices):
n = len(prices)
if n < 2:
return 0.0
sum_x = 0.0
sum_y = 0.0
sum_xx = 0.0
sum_xy = 0.0
for i in range(n):
x = float(i)
y = prices[i]
sum_x += x
sum_y += y
sum_xx += x * x
sum_xy += x * y
denom = n * sum_xx - sum_x * sum_x
if denom == 0:
return 0.0
return (n * sum_xy - sum_x * sum_y) / denom
def CreateClone(self):
return moving_regression_strategy()