RVI Histogram Reversal 策略
概述
该策略基于 RVI(相对活力指数),当指标离开超买或超卖区域,或当 RVI 与其信号线交叉时开仓。支持两种信号模式:
- Levels – 当 RVI 穿越预设上限或下限时触发。
- Cross – 当 RVI 与其信号线交叉时触发。
这是一个逆势策略:如果 RVI 高于上限后回落,则开多单;如果 RVI 低于下限后上穿,则开空单。
参数
| 名称 | 说明 |
|---|---|
RviPeriod |
RVI 计算周期。 |
HighLevel |
RVI 上限。 |
LowLevel |
RVI 下限。 |
Mode |
信号模式(Levels 或 Cross)。 |
EnableBuyOpen |
允许开多。 |
EnableSellOpen |
允许开空。 |
EnableBuyClose |
允许平多。 |
EnableSellClose |
允许平空。 |
CandleType |
K 线时间框架。 |
工作原理
- 在每根完成的 K 线上计算 RVI 及其简单移动平均线。
- 根据所选模式,检测:
- RVI 是否离开极值区域,或
- RVI 是否与信号线交叉。
- 出现做多信号时平掉空头并开多;出现做空信号时平掉多头并开空。
默认时间框架为四小时。
备注
- 使用市价单执行。
- 如有需要,可另行加入止损止盈管理。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// RVI histogram reversal strategy.
/// Opens long when RVI Average crosses above Signal.
/// Opens short when RVI Average crosses below Signal.
/// </summary>
public class RviHistogramReversalStrategy : Strategy
{
private readonly StrategyParam<int> _rviPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevAvg;
private decimal? _prevSig;
public int RviPeriod { get => _rviPeriod.Value; set => _rviPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RviHistogramReversalStrategy()
{
_rviPeriod = Param(nameof(RviPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RVI Period", "Period of RVI indicator", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevAvg = null;
_prevSig = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rvi = new RelativeVigorIndex();
rvi.Average.Length = RviPeriod;
rvi.Signal.Length = RviPeriod;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rvi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rvi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
var rviVal = value as IRelativeVigorIndexValue;
if (rviVal?.Average is not decimal avg || rviVal?.Signal is not decimal sig)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevAvg = avg;
_prevSig = sig;
return;
}
if (_prevAvg is decimal pa && _prevSig is decimal ps)
{
// Average crosses above Signal - buy
if (pa <= ps && avg > sig && Position <= 0)
BuyMarket();
// Average crosses below Signal - sell
else if (pa >= ps && avg < sig && Position >= 0)
SellMarket();
}
_prevAvg = avg;
_prevSig = sig;
}
}
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 RelativeVigorIndex
from StockSharp.Algo.Strategies import Strategy
class rvi_histogram_reversal_strategy(Strategy):
def __init__(self):
super(rvi_histogram_reversal_strategy, self).__init__()
self._rvi_period = self.Param("RviPeriod", 14).SetDisplay("RVI Period", "Period of RVI indicator", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles", "General")
self._prev_avg = None
self._prev_sig = None
@property
def rvi_period(self): return self._rvi_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(rvi_histogram_reversal_strategy, self).OnReseted()
self._prev_avg = None
self._prev_sig = None
def OnStarted2(self, time):
super(rvi_histogram_reversal_strategy, self).OnStarted2(time)
rvi = RelativeVigorIndex()
rvi.Average.Length = self.rvi_period
rvi.Signal.Length = self.rvi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rvi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rvi)
self.DrawOwnTrades(area)
def process_candle(self, candle, value):
if candle.State != CandleStates.Finished: return
avg = value.Average
sig = value.Signal
if avg is None or sig is None: return
avg = float(avg)
sig = float(sig)
if self._prev_avg is not None and self._prev_sig is not None:
if self._prev_avg <= self._prev_sig and avg > sig and self.Position <= 0:
self.BuyMarket()
elif self._prev_avg >= self._prev_sig and avg < sig and self.Position >= 0:
self.SellMarket()
self._prev_avg = avg
self._prev_sig = sig
def CreateClone(self): return rvi_histogram_reversal_strategy()