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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, UnitTypes, Unit
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import SimpleMovingAverage, AwesomeOscillator
from indicator_extensions import *
class ravi_iao_strategy(Strategy):
def __init__(self):
super(ravi_iao_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(10))) \
.SetDisplay("Candle Type", "Time-frame for analysis", "General")
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast Length", "Fast SMA period inside RAVI", "RAVI")
self._slow_length = self.Param("SlowLength", 72) \
.SetDisplay("Slow Length", "Slow SMA period inside RAVI", "RAVI")
self._threshold = self.Param("Threshold", 0.3) \
.SetDisplay("RAVI Threshold", "Minimum absolute RAVI value to confirm the trend", "Signals")
self._stop_loss_points = self.Param("StopLossPoints", 500.0) \
.SetDisplay("Stop Loss", "Stop-loss distance in price units", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 500.0) \
.SetDisplay("Take Profit", "Take-profit distance in price units", "Risk")
self._prev_ravi = None
self._prev_prev_ravi = None
self._prev_ac = None
self._prev_prev_ac = None
self._ao_average = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastLength(self):
return self._fast_length.Value
@property
def SlowLength(self):
return self._slow_length.Value
@property
def Threshold(self):
return self._threshold.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
def OnStarted2(self, time):
super(ravi_iao_strategy, self).OnStarted2(time)
fast_ma = SimpleMovingAverage()
fast_ma.Length = self.FastLength
slow_ma = SimpleMovingAverage()
slow_ma.Length = self.SlowLength
ao = AwesomeOscillator()
self._ao_average = SimpleMovingAverage()
self._ao_average.Length = 5
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(fast_ma, slow_ma, ao, self.ProcessCandle).Start()
tp = Unit(float(self.TakeProfitPoints), UnitTypes.Absolute) if float(self.TakeProfitPoints) > 0 else None
sl = Unit(float(self.StopLossPoints), UnitTypes.Absolute) if float(self.StopLossPoints) > 0 else None
self.StartProtection(tp, sl)
def ProcessCandle(self, candle, fast_val, slow_val, ao_val):
if candle.State != CandleStates.Finished:
return
if fast_val.IsEmpty or slow_val.IsEmpty or ao_val.IsEmpty:
return
fast_value = float(fast_val)
slow_value = float(slow_val)
ao_value = float(ao_val)
ao_avg_result = process_float(self._ao_average, ao_value, candle.OpenTime, True)
if ao_avg_result.IsEmpty:
return
ao_avg_value = float(ao_avg_result)
ac = ao_value - ao_avg_value
if slow_value == 0:
self._update_history(None, ac)
return
ravi = 100.0 * (fast_value - slow_value) / slow_value
if (self._prev_ravi is not None and
self._prev_prev_ravi is not None and
self._prev_ac is not None and
self._prev_prev_ac is not None and
self.Position == 0 and
self.IsFormedAndOnlineAndAllowTrading()):
threshold = float(self.Threshold)
bullish = (self._prev_ac > self._prev_prev_ac and self._prev_prev_ac > 0
and self._prev_ravi > self._prev_prev_ravi and self._prev_ravi > threshold)
bearish = (self._prev_ac < self._prev_prev_ac and self._prev_prev_ac < 0
and self._prev_ravi < self._prev_prev_ravi and self._prev_ravi < -threshold)
if bullish:
self.BuyMarket(self.Volume)
elif bearish:
self.SellMarket(self.Volume)
self._update_history(ravi, ac)
def _update_history(self, ravi, ac):
self._prev_prev_ravi = self._prev_ravi
self._prev_ravi = ravi
self._prev_prev_ac = self._prev_ac
self._prev_ac = ac
def OnReseted(self):
super(ravi_iao_strategy, self).OnReseted()
self._prev_ravi = None
self._prev_prev_ravi = None
self._prev_ac = None
self._prev_prev_ac = None
def CreateClone(self):
return ravi_iao_strategy()