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()