Slope Direction Line 策略
该策略复现 Slope Direction Line 智能交易系统的逻辑。它根据收盘价线性回归的斜率进行交易。当斜率由负转正时开多仓,斜率由正转负时开空仓。每次方向变化时都会平掉相反方向的仓位。通过 StartProtection 设置的止损和止盈百分比来保护仓位。
细节
- 指标 – 使用 StockSharp 的
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()