Stochastic Slope 平均回帰戦略
Stochastic Slope 平均回帰戦略は、Stochasticの極端な値に着目して回帰を利用することに焦点を当てています。通常水準からの大きな乖離が長続きすることはほとんどありません。
指標が平均から大きく乖離した後、反転し始めたときにトレードが発動します。ロングとショートの両方のセットアップに保護的なストップが含まれています。
振動を期待するスウィングトレーダーに適しており、Stochasticが均衡に戻ったときにポジションを決済します。開始パラメータ StochPeriod = 14。
詳細
- エントリー条件: 指標が平均に向かって戻るクロス。
- ロング/ショート: 両方向。
- エグジット条件: 指標が平均に回帰する。
- ストップ: はい。
- デフォルト値:
StochPeriod= 14StochKPeriod= 3StochDPeriod= 3SlopeLookback= 20ThresholdMultiplier= 2mStopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: 平均回帰
- 方向: 両方
- インジケーター: Stochastic
- ストップ: はい
- 複雑さ: 中級
- 時間軸: 短期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Stochastic slope mean reversion.
/// The slope of the smoothed %K is compared with its average: buys when the slope is far below the average
/// and starts turning up, sells when it is far above and starts turning down.
/// Exits when the slope returns to its average.
/// </summary>
public class StochasticSlopeMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochKPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<int> _slopeLookback;
private readonly StrategyParam<decimal> _thresholdMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _kSmoothing;
private SimpleMovingAverage _slopeAverage;
private StandardDeviation _slopeStdDev;
private decimal? _prevK;
private decimal? _prevSlope;
/// <summary>
/// Stochastic lookback period.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Smoothing period of %K.
/// </summary>
public int StochKPeriod
{
get => _stochKPeriod.Value;
set => _stochKPeriod.Value = value;
}
/// <summary>
/// Period of %D.
/// </summary>
public int StochDPeriod
{
get => _stochDPeriod.Value;
set => _stochDPeriod.Value = value;
}
/// <summary>
/// Lookback period for slope statistics.
/// </summary>
public int SlopeLookback
{
get => _slopeLookback.Value;
set => _slopeLookback.Value = value;
}
/// <summary>
/// Standard deviation multiplier for extreme slope.
/// </summary>
public decimal ThresholdMultiplier
{
get => _thresholdMultiplier.Value;
set => _thresholdMultiplier.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="StochasticSlopeMeanReversionStrategy"/>.
/// </summary>
public StochasticSlopeMeanReversionStrategy()
{
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Stochastic lookback period", "Stochastic");
_stochKPeriod = Param(nameof(StochKPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch %K Period", "Smoothing period of %K", "Stochastic");
_stochDPeriod = Param(nameof(StochDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch %D Period", "Period of %D", "Stochastic");
_slopeLookback = Param(nameof(SlopeLookback), 20)
.SetGreaterThanZero()
.SetDisplay("Slope Lookback", "Period for slope statistics", "Strategy");
_thresholdMultiplier = Param(nameof(ThresholdMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Threshold Multiplier", "Standard deviation multiplier for extreme slope", "Strategy");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_kSmoothing = null;
_slopeAverage = null;
_slopeStdDev = null;
_prevK = null;
_prevSlope = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochPeriod;
stochastic.D.Length = StochDPeriod;
_kSmoothing = new SimpleMovingAverage { Length = StochKPeriod };
_slopeAverage = new SimpleMovingAverage { Length = SlopeLookback };
_slopeStdDev = new StandardDeviation { Length = SlopeLookback };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stochastic, ProcessCandle)
.Start();
StartProtection(
takeProfit: null,
stopLoss: StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : null
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var stochArea = CreateChartArea();
if (stochArea != null)
DrawIndicator(stochArea, stochastic);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal rawK)
return;
var k = _kSmoothing.Process(rawK, candle.ServerTime, true).ToDecimal();
if (!_kSmoothing.IsFormed)
return;
if (_prevK is not decimal prevK)
{
_prevK = k;
return;
}
_prevK = k;
var slope = k - prevK;
var avgSlope = _slopeAverage.Process(slope, candle.ServerTime, true).ToDecimal();
var stdSlope = _slopeStdDev.Process(slope, candle.ServerTime, true).ToDecimal();
var prevSlope = _prevSlope;
_prevSlope = slope;
if (!_slopeAverage.IsFormed || !_slopeStdDev.IsFormed || prevSlope is not decimal prev)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Extreme reading that has started to turn back toward the average.
if (slope < avgSlope - ThresholdMultiplier * stdSlope && slope > prev && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
}
else if (slope > avgSlope + ThresholdMultiplier * stdSlope && slope < prev && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
}
else if (Position > 0 && slope >= avgSlope)
{
SellMarket(Position);
}
else if (Position < 0 && slope <= avgSlope)
{
BuyMarket(-Position);
}
}
}
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 StochasticOscillator, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class stochastic_slope_mean_reversion_strategy(Strategy):
"""
Stochastic slope mean reversion.
The slope of the smoothed %K is compared with its average: buys when the slope is far below the average
and starts turning up, sells when it is far above and starts turning down.
Exits when the slope returns to its average.
"""
def __init__(self):
super(stochastic_slope_mean_reversion_strategy, self).__init__()
self._stoch_period = self.Param("StochPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic Period", "Stochastic lookback period", "Stochastic")
self._stoch_k_period = self.Param("StochKPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %K Period", "Smoothing period of %K", "Stochastic")
self._stoch_d_period = self.Param("StochDPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %D Period", "Period of %D", "Stochastic")
self._slope_lookback = self.Param("SlopeLookback", 20) \
.SetGreaterThanZero() \
.SetDisplay("Slope Lookback", "Period for slope statistics", "Strategy")
self._threshold_multiplier = self.Param("ThresholdMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Threshold Multiplier", "Standard deviation multiplier for extreme slope", "Strategy")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._k_smoothing = None
self._slope_average = None
self._slope_std_dev = None
self._prev_k = None
self._prev_slope = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(stochastic_slope_mean_reversion_strategy, self).OnReseted()
self._k_smoothing = None
self._slope_average = None
self._slope_std_dev = None
self._prev_k = None
self._prev_slope = None
def OnStarted2(self, time):
super(stochastic_slope_mean_reversion_strategy, self).OnStarted2(time)
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_period.Value
stochastic.D.Length = self._stoch_d_period.Value
self._k_smoothing = SimpleMovingAverage()
self._k_smoothing.Length = self._stoch_k_period.Value
self._slope_average = SimpleMovingAverage()
self._slope_average.Length = self._slope_lookback.Value
self._slope_std_dev = StandardDeviation()
self._slope_std_dev.Length = self._slope_lookback.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stochastic, self._process_candle).Start()
stop = float(self._stop_loss_percent.Value)
self.StartProtection(None, Unit(stop, UnitTypes.Percent) if stop > 0 else None)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
stoch_area = self.CreateChartArea()
if stoch_area is not None:
self.DrawIndicator(stoch_area, stochastic)
def _process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
raw_k = stoch_value.K
if raw_k is None:
return
k = float(process_float(self._k_smoothing, float(raw_k), candle.ServerTime, True))
if not self._k_smoothing.IsFormed:
return
if self._prev_k is None:
self._prev_k = k
return
slope = k - self._prev_k
self._prev_k = k
avg_slope = float(process_float(self._slope_average, slope, candle.ServerTime, True))
std_slope = float(process_float(self._slope_std_dev, slope, candle.ServerTime, True))
prev = self._prev_slope
self._prev_slope = slope
if not self._slope_average.IsFormed or not self._slope_std_dev.IsFormed or prev is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
m = float(self._threshold_multiplier.Value)
# Extreme reading that has started to turn back toward the average.
if slope < avg_slope - m * std_slope and slope > prev and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif slope > avg_slope + m * std_slope and slope < prev and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and slope >= avg_slope:
self.SellMarket(self.Position)
elif self.Position < 0 and slope <= avg_slope:
self.BuyMarket(-self.Position)
def CreateClone(self):
return stochastic_slope_mean_reversion_strategy()