在 GitHub 上查看
快慢 RVI 均线交叉策略
概述
本策略复现 MetaTrader 专家顾问 _HPCS_FastSlowRVIsCrossOver_MT4_EA_V01_WE。当相对活力指数(RVI)主线在设定的交易时间段内与其信号线发生交叉时触发交易。每根蜡烛只允许一次入场,同时支持用点(pip)表示的止损、止盈以及追踪止损距离。
交易逻辑
- 根据 Candle Type 参数生成标准时间蜡烛。
- 使用设定的 RVI Period 计算 RVI,并对其应用 4 周期简单移动平均作为信号线。
- 当 RVI 从下向上突破信号线时,平掉空头仓位并建立/加仓多头。
- 当 RVI 从上向下跌破信号线时,平掉多头仓位并建立/加仓空头。
- 在 Start Time 与 Stop Time 之外忽略所有信号。
- 按照风险参数设置保护单,追踪止损由 StockSharp 保护机制管理。
- 通过仅对每根蜡烛触发一次信号,避免重复入场。
参数
| 名称 |
说明 |
| RVI Period |
RVI 指标使用的周期数。 |
| Take Profit (pips) |
可选止盈距离(点)。设为 0 表示关闭。 |
| Stop Loss (pips) |
可选止损距离(点)。设为 0 表示关闭。 |
| Trailing Stop (pips) |
可选追踪止损距离(点)。设为 0 表示关闭追踪。 |
| Trailing Step (pips) |
每次收紧追踪止损所需的最小有利波动,仅在启用追踪时生效。 |
| Volume |
每次下单的交易量。 |
| Candle Type |
用于分析的时间框架或自定义蜡烛类型。 |
| Start Time |
每日交易窗口的起始时间(包含)。 |
| Stop Time |
每日交易窗口的结束时间(不包含)。 |
备注
- 为兼容 MetaTrader 点值处理,策略会根据合约最小跳动判断是否需要对点值进行 10 倍放大(针对 5 位或 3 位报价品种)。
- 在
OnStarted 中调用一次 StartProtection,即可启用保护性委托与追踪止损管理。
- 源码中的注释全部使用英文,以符合项目规范。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Fast and slow Relative Vigor Index crossover strategy.
/// Opens a long position when the RVI average line crosses above the signal line,
/// and opens a short position on the opposite crossover.
/// </summary>
public class FastSlowRviCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _rviPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal? _previousAverage;
private decimal? _previousSignal;
public int RviPeriod
{
get => _rviPeriod.Value;
set => _rviPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public FastSlowRviCrossoverStrategy()
{
_rviPeriod = Param(nameof(RviPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("RVI Period", "Period for the Relative Vigor Index", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candles used for analysis", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousAverage = null;
_previousSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousAverage = null;
_previousSignal = null;
var rvi = new ExponentialMovingAverage { Length = RviPeriod };
var signal = new ExponentialMovingAverage { Length = RviPeriod * 2 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rvi, signal, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rvi);
DrawIndicator(area, signal);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal avgValue, decimal sigValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_previousAverage.HasValue && _previousSignal.HasValue)
{
var longSignal = _previousAverage.Value <= _previousSignal.Value && avgValue > sigValue;
var shortSignal = _previousAverage.Value >= _previousSignal.Value && avgValue < sigValue;
if (longSignal && Position <= 0)
{
BuyMarket();
}
else if (shortSignal && Position >= 0)
{
SellMarket();
}
}
_previousAverage = avgValue;
_previousSignal = sigValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fast_slow_rvi_crossover_strategy(Strategy):
def __init__(self):
super(fast_slow_rvi_crossover_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._rvi_period = self.Param("RviPeriod", 20)
self._prev_avg = 0.0
self._prev_sig = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RviPeriod(self):
return self._rvi_period.Value
@RviPeriod.setter
def RviPeriod(self, value):
self._rvi_period.Value = value
def OnReseted(self):
super(fast_slow_rvi_crossover_strategy, self).OnReseted()
self._prev_avg = 0.0
self._prev_sig = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(fast_slow_rvi_crossover_strategy, self).OnStarted2(time)
self._prev_avg = 0.0
self._prev_sig = 0.0
self._has_prev = False
rvi = ExponentialMovingAverage()
rvi.Length = self.RviPeriod
signal = ExponentialMovingAverage()
signal.Length = self.RviPeriod * 2
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rvi, signal, self._process_candle).Start()
def _process_candle(self, candle, avg_value, sig_value):
if candle.State != CandleStates.Finished:
return
avg_val = float(avg_value)
sig_val = float(sig_value)
if self._has_prev:
long_signal = self._prev_avg <= self._prev_sig and avg_val > sig_val
short_signal = self._prev_avg >= self._prev_sig and avg_val < sig_val
if long_signal and self.Position <= 0:
self.BuyMarket()
elif short_signal and self.Position >= 0:
self.SellMarket()
self._prev_avg = avg_val
self._prev_sig = sig_val
self._has_prev = True
def CreateClone(self):
return fast_slow_rvi_crossover_strategy()