Открыть на GitHub

Стратегия RAVI + Awesome Oscillator

Общее описание

  • Порт советника MetaTrader 5 "Ravi AO (barabashkakvn's edition)" на высокоуровневый API StockSharp.
  • Использует связку индикаторов RAVI (Range Action Verification Index) и Awesome Oscillator для поиска синхронных смен импульса.
  • Подходит для любого таймфрейма и инструмента, все расстояния задаются в пунктах как в оригинальном советнике.

Индикаторы

  • RAVI – рассчитывается по формуле 100 * (FastMA - SlowMA) / SlowMA для выбранного ценового ряда. Доступен выбор метода сглаживания (простое, экспоненциальное, сглаженное, взвешенное среднее), длин и типа цены (close, open, high, low, median, typical, weighted, simple, quarter, trend-follow, Demark).
  • Awesome Oscillator – моментум на основе медианной цены с настраиваемыми коротким и длинным периодами (по умолчанию 5 и 34, как в MT5).

Параметры

Параметр Описание
CandleType Тип свечей/данных, на которые подписывается стратегия.
StopLossPips Размер стоп-лосса в пунктах. Значение 0 отключает стоп.
TakeProfitPips Размер тейк-профита в пунктах. Значение 0 отключает тейк.
TrailingStopPips Базовая дистанция трейлинг-стопа в пунктах. 0 — трейлинг выключен.
TrailingStepPips Минимальное дополнительное движение цены (в пунктах), после которого трейлинг подтягивает стоп. При включенном трейлинге должно быть > 0.
FastMethod / FastLength Метод сглаживания и длина быстрого среднего RAVI.
SlowMethod / SlowLength Метод сглаживания и длина медленного среднего RAVI.
AppliedPrices Формула расчёта цены для обоих средних (close, open, high, low, median, typical, weighted, simple, quarter, trend-follow #1/#2, Demark).
AoShortPeriod / AoLongPeriod Короткий и длинный периоды Awesome Oscillator.

Правила входа

  1. Расчёт индикаторов выполняется по завершённым свечам (CandleStates.Finished).
  2. Лонг открывается, когда:
    • AO два бара назад < 0, а один бар назад > 0, и
    • RAVI два бара назад < 0, а один бар назад > 0.
  3. Шорт открывается, когда:
    • AO два бара назад > 0, а один бар назад < 0, и
    • RAVI два бара назад > 0, а один бар назад < 0.
  4. Одновременно поддерживается только одна позиция; новые сигналы игнорируются, пока позиция открыта.

Управление выходом

  • Стоп-лосс: вычисляется по StopLossPips с учётом Security.PriceStep. Для инструментов с 5 и 3 знаками после запятой шаг умножается на 10, как в MT5. Срабатывает, если минимум/максимум свечи достигает стоп-уровня.
  • Тейк-профит: опциональный уровень, рассчитывается аналогично. Отключается значением 0.
  • Трейлинг-стоп: при включении подтягивает стоп, когда плавающая прибыль превышает TrailingStopPips + TrailingStepPips. Для лонга стоп переносится на ClosePrice - TrailingStopPips, для шорта – на ClosePrice + TrailingStopPips.
  • Все выходы закрывают позицию полностью рыночной заявкой.

Особенности реализации

  • Сделки совершаются по цене закрытия сигнальной свечи. В MT5 версия входила на открытии следующего бара, поэтому возможны расхождения.
  • Используются только стандартные скользящие средние StockSharp, экзотические алгоритмы сглаживания MT5 (JJMA, Jurik, T3 и т. п.) недоступны.
  • Визуальный параметр Shift исходного индикатора влияет только на отображение и в стратегию не включён.
  • Формулы AppliedPrices повторяют определения MetaTrader, включая варианты TrendFollow и Demark.

Рекомендации

  • Стратегия трендовая, рекомендуется комбинировать её с фильтрами старшего таймфрейма или фильтрами волатильности.
  • Подбирайте длины средних и дистанции стопов под каждый инструмент: размер пункта вычисляется через Security.PriceStep.
  • При необходимости брокерских стопов используйте Strategy.StartProtection или внешнее сопровождение позиций.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// RAVI AO strategy. Uses fast/slow EMA difference (RAVI concept) for crossover signals.
/// </summary>
public class RaviAoStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public RaviAoStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 25)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = null;
		_prevSlow = null;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		if (_prevFast == null || _prevSlow == null) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fastVal > slowVal;
		_prevFast = fastVal; _prevSlow = slowVal;
		if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
	}
}