Auf GitHub ansehen

RAVIiAO Strategy (StockSharp)

Overview

The RAVIiAO Strategy reproduces the MetaTrader 4 expert advisor "RAVIiAO" inside the StockSharp high level API. The system waits for a new candle to close, evaluates the RAVI oscillator slope together with Bill Williams' Acceleration/Deceleration (AC) oscillator, and opens a position immediately at market when both indicators agree on the trend direction. The port keeps the original parameter set – moving average periods, threshold, stop-loss/take-profit distances, and order volume – allowing traders to replicate the legacy behaviour without manual tweaks.

Core Workflow

  1. Candle subscription – the strategy subscribes to a configurable time-frame (30-minute candles by default).
  2. Indicator updates – on each finished candle it updates two simple moving averages to build the RAVI oscillator and feeds the same candle into an Awesome Oscillator + 5-period smoothing pair to obtain the AC value.
  3. Signal preparation – the latest finished candle is stored as "bar 1" while the prior value becomes "bar 2", matching the iCustom(...,1) and iCustom(...,2) calls from MetaTrader.
  4. Entry decision – a long position is opened when both AC and RAVI increase above their previous values and confirm a bullish environment (AC[1] > AC[2] > 0 and RAVI[1] > RAVI[2] > Threshold). Short trades use the mirrored conditions.
  5. Risk management – as soon as an order executes the strategy records static stop-loss and take-profit levels expressed in instrument points (i.e. StopLossPoints * PriceStep). Candles are monitored for intrabar breaches using their high/low prices.
  6. State reset – when a protective level is hit the position is closed with a market order and the internal buffers are reset for the next opportunity.

Trading Rules

  • Long entries
    • Previous AC value above the earlier AC value and both greater than zero.
    • Previous RAVI reading above both the threshold and the earlier RAVI value.
    • No active position at the moment of the signal.
  • Short entries
    • Previous AC value below the earlier AC value and both below zero.
    • Previous RAVI reading below the negative threshold and below the earlier RAVI value.
    • No active position when the signal fires.
  • Position exits
    • Static stop-loss and take-profit levels are expressed in raw points, converted to price offsets via the instrument PriceStep.
    • Breaches are detected with candle extremes (low for long stops, high for short stops, etc.) and closed immediately via market orders to emulate MetaTrader's protective orders.

Parameters

Name Description
CandleType Time-frame used for candle subscription (default 30 minutes).
FastLength Fast moving average length used in the RAVI oscillator.
SlowLength Slow moving average length used in the RAVI oscillator.
Threshold Minimum absolute RAVI percentage to validate a trend continuation.
StopLossPoints Stop-loss distance in instrument points (multiplied by PriceStep).
TakeProfitPoints Take-profit distance in instrument points.
TradeVolume Market order volume for every entry.

Conversion Notes

  • The StockSharp port stores the two most recent indicator values so that the decision at candle n reuses the AC[1] and RAVI[1] values from MetaTrader (i.e. results of the previous bar), preserving the "new bar" execution style of the EA.
  • AC is rebuilt through the difference between the Awesome Oscillator and its 5-period simple moving average, matching the MT4 calculation chain.
  • Stops and targets are evaluated against candle extremes instead of placing pending protective orders; this mirrors the effect of MetaTrader's built-in SL/TP handling while keeping the implementation idiomatic for StockSharp.

Usage Tips

  • Ensure the selected instrument exposes a correct PriceStep; otherwise protective distances will not match the MT4 version.
  • Optimise the Threshold, FastLength, and SlowLength parameters when adapting the strategy to markets with different volatility characteristics.
  • Combine the strategy with StockSharp portfolio- or connector-level protections for additional safety during live trading.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the MetaTrader 4 expert advisor "RAVIiAO" that combines the RAVI oscillator and the Acceleration/Deceleration oscillator.
/// </summary>
public class RaviIaoStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<decimal> _threshold;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;

	private SMA _aoAverage;

	private decimal? _prevRavi;
	private decimal? _prevPrevRavi;
	private decimal? _prevAc;
	private decimal? _prevPrevAc;

	/// <summary>
	/// Type of candles used for indicator calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Fast moving average length for the RAVI oscillator.
	/// </summary>
	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	/// <summary>
	/// Slow moving average length for the RAVI oscillator.
	/// </summary>
	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	/// <summary>
	/// Threshold for bullish or bearish confirmation of the RAVI oscillator (percentage value).
	/// </summary>
	public decimal Threshold
	{
		get => _threshold.Value;
		set => _threshold.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in absolute price units.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in absolute price units.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="RaviIaoStrategy"/>.
	/// </summary>
	public RaviIaoStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame())
			.SetDisplay("Candle Type", "Time-frame for analysis", "General");

		_fastLength = Param(nameof(FastLength), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast Length", "Fast SMA period inside RAVI", "RAVI");

		_slowLength = Param(nameof(SlowLength), 72)
			.SetGreaterThanZero()
			.SetDisplay("Slow Length", "Slow SMA period inside RAVI", "RAVI");

		_threshold = Param(nameof(Threshold), 0.3m)
			.SetDisplay("RAVI Threshold", "Minimum absolute RAVI value to confirm the trend", "Signals");

		_stopLossPoints = Param(nameof(StopLossPoints), 500m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price units", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 500m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price units", "Risk");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevRavi = null;
		_prevPrevRavi = null;
		_prevAc = null;
		_prevPrevAc = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		var fastMa = new SMA { Length = FastLength };
		var slowMa = new SMA { Length = SlowLength };
		var ao = new AwesomeOscillator();
		_aoAverage = new SMA { Length = 5 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(new IIndicator[] { fastMa, slowMa, ao }, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastMa);
			DrawIndicator(area, slowMa);
			DrawOwnTrades(area);
		}

		// Use StartProtection for SL/TP
		var tp = TakeProfitPoints > 0 ? new Unit(TakeProfitPoints, UnitTypes.Absolute) : null;
		var sl = StopLossPoints > 0 ? new Unit(StopLossPoints, UnitTypes.Absolute) : null;
		StartProtection(tp, sl);

		base.OnStarted2(time);
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var fastVal = values[0];
		var slowVal = values[1];
		var aoVal = values[2];

		if (fastVal.IsEmpty || slowVal.IsEmpty || aoVal.IsEmpty)
			return;

		var fastValue = fastVal.ToDecimal();
		var slowValue = slowVal.ToDecimal();
		var aoValue = aoVal.ToDecimal();

		// Compute AC = AO - SMA(AO)
		var aoAvgResult = _aoAverage.Process(aoVal);
		if (aoAvgResult.IsEmpty)
			return;

		var aoAvgValue = aoAvgResult.ToDecimal();
		var ac = aoValue - aoAvgValue;

		if (slowValue == 0m)
		{
			UpdateHistory(null, ac);
			return;
		}

		var ravi = 100m * (fastValue - slowValue) / slowValue;

		if (_prevRavi is decimal prevRavi &&
			_prevPrevRavi is decimal prevPrevRavi &&
			_prevAc is decimal prevAc &&
			_prevPrevAc is decimal prevPrevAc &&
			Position == 0 &&
			IsFormedAndOnlineAndAllowTrading())
		{
			var bullish = prevAc > prevPrevAc && prevPrevAc > 0m && prevRavi > prevPrevRavi && prevRavi > Threshold;
			var bearish = prevAc < prevPrevAc && prevPrevAc < 0m && prevRavi < prevPrevRavi && prevRavi < -Threshold;

			if (bullish)
			{
				BuyMarket(Volume);
			}
			else if (bearish)
			{
				SellMarket(Volume);
			}
		}

		UpdateHistory(ravi, ac);
	}

	private void UpdateHistory(decimal? ravi, decimal ac)
	{
		_prevPrevRavi = _prevRavi;
		_prevRavi = ravi;
		_prevPrevAc = _prevAc;
		_prevAc = ac;
	}
}