Стратегия повторяет эксперта MetaTrader _HPCS_FastSlowRVIsCrossOver_MT4_EA_V01_WE. Торговые решения принимаются, когда основная линия индикатора Relative Vigor Index (RVI) пересекает свою сигнальную линию в пределах заданного торгового окна. На одну свечу допускается только один вход. Поддерживаются стоп-лосс, тейк-профит и трейлинг-стоп, задаваемые в пунктах.
Логика торговли
Формируются стандартные свечи согласно параметру Candle Type.
Рассчитывается RVI с периодом RVI Period и сигнальная линия в виде простого скользящего среднего по четырём барам.
При пробое сигнальной линии снизу вверх закрываются короткие позиции и открывается/наращивается длинная.
При пробое сверху вниз закрываются длинные позиции и открывается/наращивается короткая.
Сигналы вне интервала Start Time – Stop Time игнорируются.
Защитные ордера выставляются через механизм StartProtection, который также управляет трейлинг-стопом.
Чтобы исключить дублирование сделок, с каждой свечи обрабатывается не более одного сигнала.
Параметры
Название
Описание
RVI Period
Количество баров в расчёте индикатора RVI.
Take Profit (pips)
Дистанция тейк-профита в пунктах. Ноль отключает тейк-профит.
Stop Loss (pips)
Дистанция стоп-лосса в пунктах. Ноль отключает стоп-лосс.
Trailing Stop (pips)
Дистанция трейлинг-стопа в пунктах. Ноль отключает трейлинг.
Trailing Step (pips)
Минимальный благоприятный сдвиг для очередного подтягивания трейлинг-стопа. Работает только при активном трейлинге.
Volume
Объём заявки при входе в позицию.
Candle Type
Тип свечей или таймфрейм для анализа.
Start Time
Начало дневного торгового окна (включительно).
Stop Time
Конец дневного торгового окна (исключительно).
Примечания
Размер пункта автоматически масштабируется исходя из минимального шага цены инструмента (для пяти- и трёхзнаковых котировок применяется множитель ×10).
StartProtection вызывается один раз в OnStarted, что включает штатное сопровождение позиций и трейлинг-стоп.
Все комментарии в коде написаны на английском языке, как требует руководство проекта.
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;
}
}