在 GitHub 上查看

快慢 RVI 均线交叉策略

概述

本策略复现 MetaTrader 专家顾问 _HPCS_FastSlowRVIsCrossOver_MT4_EA_V01_WE。当相对活力指数(RVI)主线在设定的交易时间段内与其信号线发生交叉时触发交易。每根蜡烛只允许一次入场,同时支持用点(pip)表示的止损、止盈以及追踪止损距离。

交易逻辑

  1. 根据 Candle Type 参数生成标准时间蜡烛。
  2. 使用设定的 RVI Period 计算 RVI,并对其应用 4 周期简单移动平均作为信号线。
  3. 当 RVI 从下向上突破信号线时,平掉空头仓位并建立/加仓多头。
  4. 当 RVI 从上向下跌破信号线时,平掉多头仓位并建立/加仓空头。
  5. Start TimeStop Time 之外忽略所有信号。
  6. 按照风险参数设置保护单,追踪止损由 StockSharp 保护机制管理。
  7. 通过仅对每根蜡烛触发一次信号,避免重复入场。

参数

名称 说明
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;
	}
}