RSI Hook Reversal 戦略
RSI Hook Reversal戦略は、RSIが極端な水準から脱するときの短期的な転換点を捉えることを目的としています。買われすぎや売られすぎの後、インジケーターは価格が反応する前に中央ラインに向けて「フック」することがよくあります。
テストでは平均年間リターンが約163%であることが示されています。株式市場で最もパフォーマンスが優れています。
この戦略は、価格が前の方向に押し続ける間にそのフックを待ちます。RSIが売られすぎから上向きにカールし、価格が新安値をつけると同時にロングエントリーがトリガーされます。一方、RSIが買われすぎから下向きに転じ、新高値が形成されるときにショートがオープンします。
取引はリスクをコントロールするためにシンプルなパーセントストップを使用し、RSIが反対方向にフックすると通常クローズします。
詳細
- エントリー条件: インジケーターシグナル
- ロング/ショート: 両方
- エグジット条件: ストップロスまたは逆シグナル
- ストップ: はい、パーセントベース
- デフォルト値:
CandleType= 15分StopLoss= 2%
- フィルター:
- カテゴリ: リバーサル
- 方向: 両方
- インジケーター: RSI
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// RSI Hook Reversal strategy.
/// Enters long when RSI hooks up from oversold zone.
/// Enters short when RSI hooks down from overbought zone.
/// Exits when RSI reaches neutral zone.
/// Uses cooldown to control trade frequency.
/// </summary>
public class RsiHookReversalStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _oversoldLevel;
private readonly StrategyParam<int> _overboughtLevel;
private readonly StrategyParam<int> _exitLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevRsi;
private int _cooldown;
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Oversold level.
/// </summary>
public int OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Overbought level.
/// </summary>
public int OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// Exit level (neutral zone).
/// </summary>
public int ExitLevel
{
get => _exitLevel.Value;
set => _exitLevel.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public RsiHookReversalStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetRange(7, 21)
.SetDisplay("RSI Period", "Period for RSI", "RSI");
_oversoldLevel = Param(nameof(OversoldLevel), 30)
.SetRange(20, 40)
.SetDisplay("Oversold", "Oversold level", "RSI");
_overboughtLevel = Param(nameof(OverboughtLevel), 70)
.SetRange(60, 80)
.SetDisplay("Overbought", "Overbought level", "RSI");
_exitLevel = Param(nameof(ExitLevel), 50)
.SetRange(45, 55)
.SetDisplay("Exit Level", "Neutral exit zone", "RSI");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_cooldown = 0;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevRsi == 0)
{
_prevRsi = rsiValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevRsi = rsiValue;
return;
}
// RSI hook up from oversold
var oversoldHookUp = _prevRsi < OversoldLevel && rsiValue > _prevRsi;
// RSI hook down from overbought
var overboughtHookDown = _prevRsi > OverboughtLevel && rsiValue < _prevRsi;
if (Position == 0 && oversoldHookUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && overboughtHookDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && rsiValue < ExitLevel)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && rsiValue > ExitLevel)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevRsi = rsiValue;
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class rsi_hook_reversal_strategy(Strategy):
"""
RSI Hook Reversal strategy.
Enters long when RSI hooks up from oversold zone.
Enters short when RSI hooks down from overbought zone.
Exits when RSI reaches neutral zone.
Uses cooldown to control trade frequency.
"""
def __init__(self):
super(rsi_hook_reversal_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14).SetDisplay("RSI Period", "Period for RSI", "RSI")
self._oversold_level = self.Param("OversoldLevel", 30).SetDisplay("Oversold", "Oversold level", "RSI")
self._overbought_level = self.Param("OverboughtLevel", 70).SetDisplay("Overbought", "Overbought level", "RSI")
self._exit_level = self.Param("ExitLevel", 50).SetDisplay("Exit Level", "Neutral exit zone", "RSI")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_rsi = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(rsi_hook_reversal_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(rsi_hook_reversal_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._cooldown = 0
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def _process_candle(self, candle, rsi_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
if self._prev_rsi == 0:
self._prev_rsi = rv
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_rsi = rv
return
cd = self._cooldown_bars.Value
oversold = self._oversold_level.Value
overbought = self._overbought_level.Value
exit_lvl = self._exit_level.Value
# RSI hook up from oversold
oversold_hook_up = self._prev_rsi < oversold and rv > self._prev_rsi
# RSI hook down from overbought
overbought_hook_down = self._prev_rsi > overbought and rv < self._prev_rsi
if self.Position == 0 and oversold_hook_up:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and overbought_hook_down:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and rv < exit_lvl:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and rv > exit_lvl:
self.BuyMarket()
self._cooldown = cd
self._prev_rsi = rv
def CreateClone(self):
return rsi_hook_reversal_strategy()