Graph Style 4th Dimension RSI 戦略
価格変動とRSIレベルを組み合わせた戦略です。
テストでは年間平均約80%のリターンが示されています。ボラティリティの高い市場で優れたパフォーマンスを発揮します。
この戦略は、RSIの極値と直近の価格変動の方向を確認します。RSIが買われすぎ/売られすぎゾーンを離れ、直近の価格変動が動きを確認したときにポジションをオープンします。RSIが中央領域に戻るか、反対のシグナルが現れたときにポジションをクローズします。
詳細
- エントリー条件: RSI極値を伴う価格変動方向。
- ロング/ショート: 両方向。
- エグジット条件: 反対のシグナルまたはRSIが中央に戻る。
- ストップ: パーセントストップロス。
- デフォルト値:
RsiPeriod= 14OverboughtLevel= 70mOversoldLevel= 30mStopLossPercent= 1mCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: モメンタム
- 方向: 両方
- インジケーター: RSI
- ストップ: パーセント
- 複雑さ: 基本
- 時間軸: イントラデイ (5m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Graph Style 4th Dimension RSI strategy.
/// Goes long when RSI leaves the oversold zone upward on a candle that closed higher than the previous one, and short when RSI
/// leaves the overbought zone downward on a candle that closed lower, reversing an opposite position. A long closes when RSI
/// returns to the middle level 50 from below and a short when it returns from above; a percent stop limits the loss.
/// </summary>
public class GraphStyle4thDimensionRSIStrategy : Strategy
{
private const decimal _middleLevel = 50m;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _overboughtLevel;
private readonly StrategyParam<decimal> _oversoldLevel;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevRsi;
private decimal? _prevClose;
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </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>
/// Constructor.
/// </summary>
public GraphStyle4thDimensionRSIStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Period of RSI", "Indicators");
_overboughtLevel = Param(nameof(OverboughtLevel), 70m)
.SetDisplay("Overbought", "RSI overbought level", "Indicators");
_oversoldLevel = Param(nameof(OversoldLevel), 30m)
.SetDisplay("Oversold", "RSI oversold level", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_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();
_prevRsi = null;
_prevClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null;
_prevClose = null;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
var prevRsi = _prevRsi;
var prevClose = _prevClose;
_prevRsi = rsi;
_prevClose = candle.ClosePrice;
if (prevRsi is not decimal lastRsi || prevClose is not decimal lastClose)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var longSignal = lastRsi < OversoldLevel && rsi >= OversoldLevel && close > lastClose;
var shortSignal = lastRsi > OverboughtLevel && rsi <= OverboughtLevel && close < lastClose;
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && rsi >= _middleLevel)
SellMarket(Position);
else if (Position < 0 && rsi <= _middleLevel)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
MIDDLE_LEVEL = 50.0
class graph_style_4th_dimension_rsi_strategy(Strategy):
"""
Graph Style 4th Dimension RSI strategy.
Goes long when RSI leaves the oversold zone upward on a candle that closed higher than the previous one, and short when RSI
leaves the overbought zone downward on a candle that closed lower, reversing an opposite position. A long closes when RSI
returns to the middle level 50 from below and a short when it returns from above; a percent stop limits the loss.
"""
def __init__(self):
super(graph_style_4th_dimension_rsi_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay("RSI Period", "Period of RSI", "Indicators")
self._overbought_level = self.Param("OverboughtLevel", 70.0).SetDisplay("Overbought", "RSI overbought level", "Indicators")
self._oversold_level = self.Param("OversoldLevel", 30.0).SetDisplay("Oversold", "RSI oversold level", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_rsi = None
self._prev_close = None
def OnReseted(self):
super(graph_style_4th_dimension_rsi_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(graph_style_4th_dimension_rsi_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi = float(rsi_value)
close = float(candle.ClosePrice)
last_rsi = self._prev_rsi
last_close = self._prev_close
self._prev_rsi = rsi
self._prev_close = close
if last_rsi is None or last_close is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
oversold = float(self._oversold_level.Value)
overbought = float(self._overbought_level.Value)
long_signal = last_rsi < oversold and rsi >= oversold and close > last_close
short_signal = last_rsi > overbought and rsi <= overbought and close < last_close
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and rsi >= MIDDLE_LEVEL:
self.SellMarket(self.Position)
elif self.Position < 0 and rsi <= MIDDLE_LEVEL:
self.BuyMarket(-self.Position)
def CreateClone(self):
return graph_style_4th_dimension_rsi_strategy()