RSI動的買われすぎ/売られすぎ戦略
RSI Dynamic Overbought Oversold戦略は、動的な買われすぎ/売られすぎレベルを持つRSIを中心に構築されています。
テストでは平均年間リターンが約178%であることが示されています。株式市場で最もよいパフォーマンスを発揮します。
買われすぎがイントラデイ(5m)データでトレンド変化を確認したときにシグナルが発動します。これにより、この手法はアクティブトレーダーに適しています。
ストップはATRの倍数とRsiPeriod、MovingAvgPeriodなどの要素に基づいています。デフォルト値を調整してリスクとリワードのバランスを取ってください。
詳細
- エントリー条件: インジケーター条件の実装を参照。
- ロング/ショート: 両方。
- エグジット条件: 反対シグナルまたはストップロジック。
- ストップ: はい、インジケーターに基づく計算を使用。
- デフォルト値:
RsiPeriod = 14MovingAvgPeriod = 50StdDevMultiplier = 2.0mStopLossPercent = 2.0mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: Overbought, Oversold
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// RSI strategy with dynamic overbought and oversold bands derived from the rolling mean and volatility of RSI.
/// </summary>
public class RsiDynamicOverboughtOversoldStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _movingAvgPeriod;
private readonly StrategyParam<decimal> _stdDevMultiplier;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private SimpleMovingAverage _priceSma;
private SimpleMovingAverage _rsiSma;
private StandardDeviation _rsiStdDev;
private int _cooldown;
/// <summary>
/// Period for RSI calculation.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Period for moving averages and RSI volatility.
/// </summary>
public int MovingAvgPeriod
{
get => _movingAvgPeriod.Value;
set => _movingAvgPeriod.Value = value;
}
/// <summary>
/// Multiplier used for the dynamic RSI bands.
/// </summary>
public decimal StdDevMultiplier
{
get => _stdDevMultiplier.Value;
set => _stdDevMultiplier.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Bars to wait after each order.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public RsiDynamicOverboughtOversoldStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetRange(2, 100)
.SetDisplay("RSI Period", "Period for RSI calculation", "Indicators");
_movingAvgPeriod = Param(nameof(MovingAvgPeriod), 34)
.SetRange(5, 200)
.SetDisplay("Average Period", "Period for moving averages and RSI volatility", "Indicators");
_stdDevMultiplier = Param(nameof(StdDevMultiplier), 1.3m)
.SetRange(0.1m, 5m)
.SetDisplay("StdDev Multiplier", "Multiplier for the dynamic RSI bands", "Signals");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetRange(0.5m, 10m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 48)
.SetRange(1, 500)
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for the strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security != null)
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_priceSma = null;
_rsiSma = null;
_rsiStdDev = null;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (Security == null)
throw new InvalidOperationException("Security is not specified.");
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_priceSma = new SimpleMovingAverage { Length = MovingAvgPeriod };
_rsiSma = new SimpleMovingAverage { Length = MovingAvgPeriod };
_rsiStdDev = new StandardDeviation { Length = MovingAvgPeriod };
_cooldown = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, _priceSma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawIndicator(area, _priceSma);
DrawOwnTrades(area);
}
StartProtection(new Unit(0, UnitTypes.Absolute), new Unit(StopLossPercent, UnitTypes.Percent), false);
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal priceSmaValue)
{
if (candle.State != CandleStates.Finished)
return;
var rsiAverageValue = _rsiSma.Process(rsiValue, candle.OpenTime, true).ToDecimal();
var rsiStdDevValue = _rsiStdDev.Process(rsiValue, candle.OpenTime, true).ToDecimal();
if (!_rsi.IsFormed || !_priceSma.IsFormed || !_rsiSma.IsFormed || !_rsiStdDev.IsFormed)
return;
if (ProcessState != ProcessStates.Started)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var dynamicOverbought = Math.Min(rsiAverageValue + StdDevMultiplier * rsiStdDevValue, 85m);
var dynamicOversold = Math.Max(rsiAverageValue - StdDevMultiplier * rsiStdDevValue, 15m);
var price = candle.ClosePrice;
var bullishFilter = price >= priceSmaValue * 0.995m;
var bearishFilter = price <= priceSmaValue * 1.005m;
if (Position == 0)
{
if (rsiValue <= dynamicOversold && bullishFilter)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (rsiValue >= dynamicOverbought && bearishFilter)
{
SellMarket();
_cooldown = CooldownBars;
}
return;
}
if (Position > 0 && (rsiValue >= rsiAverageValue || price < priceSmaValue * 0.995m))
{
SellMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
else if (Position < 0 && (rsiValue <= rsiAverageValue || price > priceSmaValue * 1.005m))
{
BuyMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class rsi_dynamic_overbought_oversold_strategy(Strategy):
"""
RSI strategy with dynamic overbought and oversold bands derived from
the rolling mean and volatility of RSI.
"""
def __init__(self):
super(rsi_dynamic_overbought_oversold_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "Period for RSI calculation", "Indicators")
self._moving_avg_period = self.Param("MovingAvgPeriod", 34) \
.SetDisplay("Average Period", "Period for moving averages and RSI volatility", "Indicators")
self._std_dev_multiplier = self.Param("StdDevMultiplier", 1.3) \
.SetDisplay("StdDev Multiplier", "Multiplier for the dynamic RSI bands", "Signals")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 48) \
.SetDisplay("Cooldown Bars", "Bars to wait after each order", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles for the strategy", "General")
self._rsi = None
self._price_sma = None
self._rsi_sma = None
self._rsi_std_dev = None
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(rsi_dynamic_overbought_oversold_strategy, self).OnReseted()
self._rsi = None
self._price_sma = None
self._rsi_sma = None
self._rsi_std_dev = None
self._cooldown = 0
def OnStarted2(self, time):
super(rsi_dynamic_overbought_oversold_strategy, self).OnStarted2(time)
ma_period = int(self._moving_avg_period.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_period.Value)
self._price_sma = SimpleMovingAverage()
self._price_sma.Length = ma_period
self._rsi_sma = SimpleMovingAverage()
self._rsi_sma.Length = ma_period
self._rsi_std_dev = StandardDeviation()
self._rsi_std_dev.Length = ma_period
self._cooldown = 0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._rsi, self._price_sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._rsi)
self.DrawIndicator(area, self._price_sma)
self.DrawOwnTrades(area)
self.StartProtection(Unit(0, UnitTypes.Absolute), Unit(self._stop_loss_percent.Value, UnitTypes.Percent), False)
def _process_candle(self, candle, rsi_value, price_sma_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
rsi_average_value = float(process_float(self._rsi_sma, Decimal(rv), candle.OpenTime, True))
rsi_std_dev_value = float(process_float(self._rsi_std_dev, Decimal(rv), candle.OpenTime, True))
if not self._rsi.IsFormed or not self._price_sma.IsFormed or not self._rsi_sma.IsFormed or not self._rsi_std_dev.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown > 0:
self._cooldown -= 1
return
sdm = float(self._std_dev_multiplier.Value)
dynamic_overbought = min(rsi_average_value + sdm * rsi_std_dev_value, 85.0)
dynamic_oversold = max(rsi_average_value - sdm * rsi_std_dev_value, 15.0)
price = float(candle.ClosePrice)
psv = float(price_sma_value)
bullish_filter = price >= psv * 0.995
bearish_filter = price <= psv * 1.005
cd = int(self._cooldown_bars.Value)
if self.Position == 0:
if rv <= dynamic_oversold and bullish_filter:
self.BuyMarket()
self._cooldown = cd
elif rv >= dynamic_overbought and bearish_filter:
self.SellMarket()
self._cooldown = cd
return
if self.Position > 0 and (rv >= rsi_average_value or price < psv * 0.995):
self.SellMarket(Math.Abs(self.Position))
self._cooldown = cd
elif self.Position < 0 and (rv <= rsi_average_value or price > psv * 1.005):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown = cd
def CreateClone(self):
return rsi_dynamic_overbought_oversold_strategy()