Стратегия Slope Direction Line
Эта стратегия повторяет логику эксперта Slope Direction Line. Она отслеживает наклон линейной регрессии цены закрытия. Если наклон становится положительным после отрицательного, открывается длинная позиция. При переходе наклона в отрицательную область открывается короткая позиция. При каждом изменении направления противоположные позиции закрываются. Защита по прибыли и убытку реализована через StartProtection, где используются задаваемые проценты.
Детали
- Индикатор –
LinearRegression, используется компонентLinearRegSlope. - Сигнал – пересечение наклоном нулевого уровня.
- Вход/выход – закрытие текущей позиции и открытие новой в направлении наклона.
- Риск-контроль –
StartProtectionс параметрами стоп-лосса и тейк-профита в процентах.
Параметры
| Имя | Описание |
|---|---|
CandleType |
Таймфрейм свечей. |
Length |
Количество баров для расчёта регрессии. |
TakeProfitPercent |
Процент для фиксации прибыли. |
StopLossPercent |
Процент для ограничения убытка. |
AllowLong |
Разрешить открытия длинных позиций. |
AllowShort |
Разрешить открытия коротких позиций. |
Использование
- Добавьте стратегию в приложение StockSharp.
- Настройте параметры под выбранный рынок и риск.
- Запустите стратегию и отслеживайте сделки на графике.
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 that trades on changes in the slope of a smoothed trend line.
/// </summary>
public class SlopeDirectionLineStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<bool> _allowLong;
private readonly StrategyParam<bool> _allowShort;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _trend = null!;
private decimal? _prevTrend;
private decimal? _prevSlope;
private int _cooldownRemaining;
/// <summary>
/// Candle type for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Length of the trend calculation.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Take-profit percentage from entry price.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Stop-loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool AllowLong
{
get => _allowLong.Value;
set => _allowLong.Value = value;
}
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool AllowShort
{
get => _allowShort.Value;
set => _allowShort.Value = value;
}
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SlopeDirectionLineStrategy"/> class.
/// </summary>
public SlopeDirectionLineStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
_length = Param(nameof(Length), 20)
.SetGreaterThanZero()
.SetDisplay("Trend Length", "Number of bars in the trend line", "Indicators")
.SetOptimize(10, 30, 5);
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
.SetRange(1m, 5m);
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
.SetRange(0.5m, 5m);
_allowLong = Param(nameof(AllowLong), true)
.SetDisplay("Allow Long", "Enable long entries", "Trading");
_allowShort = Param(nameof(AllowShort), true)
.SetDisplay("Allow Short", "Enable short entries", "Trading");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_trend = null!;
_prevTrend = null;
_prevSlope = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_trend = new ExponentialMovingAverage { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_trend, ProcessCandle)
.Start();
StartProtection(new Unit(TakeProfitPercent, UnitTypes.Percent), new Unit(StopLossPercent, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _trend);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal trendValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (_prevTrend is not decimal prevTrend)
{
_prevTrend = trendValue;
return;
}
var currentSlope = trendValue - prevTrend;
if (_prevSlope is decimal prevSlope)
{
if (currentSlope > 0m && prevSlope <= 0m && _cooldownRemaining == 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0 && AllowLong)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
}
else if (currentSlope < 0m && prevSlope >= 0m && _cooldownRemaining == 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0 && AllowShort)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
}
_prevTrend = trendValue;
_prevSlope = currentSlope;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class slope_direction_line_strategy(Strategy):
def __init__(self):
super(slope_direction_line_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "General")
self._length = self.Param("Length", 20) \
.SetDisplay("Trend Length", "Number of bars in the trend line", "Indicators")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._allow_long = self.Param("AllowLong", True) \
.SetDisplay("Allow Long", "Enable long entries", "Trading")
self._allow_short = self.Param("AllowShort", True) \
.SetDisplay("Allow Short", "Enable short entries", "Trading")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._prev_trend = None
self._prev_slope = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def length(self):
return self._length.Value
@property
def take_profit_percent(self):
return self._take_profit_percent.Value
@property
def stop_loss_percent(self):
return self._stop_loss_percent.Value
@property
def allow_long(self):
return self._allow_long.Value
@property
def allow_short(self):
return self._allow_short.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(slope_direction_line_strategy, self).OnReseted()
self._prev_trend = None
self._prev_slope = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(slope_direction_line_strategy, self).OnStarted2(time)
trend = ExponentialMovingAverage()
trend.Length = self.length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(trend, self.process_candle).Start()
self.StartProtection(
Unit(float(self.take_profit_percent), UnitTypes.Percent),
Unit(float(self.stop_loss_percent), UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, trend)
self.DrawOwnTrades(area)
def process_candle(self, candle, trend_value):
if candle.State != CandleStates.Finished:
return
trend_value = float(trend_value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if self._prev_trend is None:
self._prev_trend = trend_value
return
current_slope = trend_value - self._prev_trend
if self._prev_slope is not None:
if current_slope > 0 and self._prev_slope <= 0 and self._cooldown_remaining == 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0 and self.allow_long:
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif current_slope < 0 and self._prev_slope >= 0 and self._cooldown_remaining == 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0 and self.allow_short:
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_trend = trend_value
self._prev_slope = current_slope
def CreateClone(self):
return slope_direction_line_strategy()