Move Cross Strategy
Overview
This strategy demonstrates a simplified conversion of the original move_cross.mq4 script. It employs the RAVI (Range Action Verification Index) indicator calculated from two simple moving averages to determine trend direction.
The strategy compares hourly and daily RAVI values:
- Buy when the hourly RAVI is negative while the daily RAVI is positive and rising.
- Sell when the hourly RAVI is positive while the daily RAVI is negative and falling.
Positions are opened at market with optional profit target and stop loss.
Parameters
| Name | Description | Default |
|---|---|---|
| TakeProfit | Profit target in points | 50 |
| StopLoss | Loss limit in points | 100 |
Notes
- The strategy uses two SMA pairs (2 and 24 periods) to compute RAVI on hourly and daily candles.
- It is intended for educational purposes and may require further tuning for live trading.
using System;
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>
/// RAVI based trend strategy.
/// Uses fast and slow SMA to compute RAVI oscillator. Enters on RAVI trending conditions.
/// </summary>
public class MoveCrossStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<decimal> _threshold;
private readonly StrategyParam<DataType> _candleType;
private decimal _raviPrev1;
private decimal _raviPrev2;
private decimal _raviPrev3;
private bool _hasHistory;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public decimal Threshold { get => _threshold.Value; set => _threshold.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MoveCrossStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 24)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicators");
_threshold = Param(nameof(Threshold), 0.5m)
.SetDisplay("Threshold", "RAVI threshold", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_raviPrev1 = 0;
_raviPrev2 = 0;
_raviPrev3 = 0;
_hasHistory = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastSma = new SimpleMovingAverage { Length = FastPeriod };
var slowSma = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastSma, slowSma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastSma);
DrawIndicator(area, slowSma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (slow == 0)
return;
var ravi = (fast - slow) / slow * 100m;
if (_hasHistory)
{
// Buy: RAVI rising for 3 bars and above threshold
var raviRising = ravi > _raviPrev1 && _raviPrev1 > _raviPrev2 && _raviPrev2 > _raviPrev3;
// Sell: RAVI falling for 3 bars and below negative threshold
var raviFalling = ravi < _raviPrev1 && _raviPrev1 < _raviPrev2 && _raviPrev2 < _raviPrev3;
if (raviRising && ravi > Threshold && Position <= 0)
BuyMarket();
else if (raviFalling && ravi < -Threshold && Position >= 0)
SellMarket();
}
_raviPrev3 = _raviPrev2;
_raviPrev2 = _raviPrev1;
_raviPrev1 = ravi;
_hasHistory = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class move_cross_strategy(Strategy):
def __init__(self):
super(move_cross_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 24) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicators")
self._threshold = self.Param("Threshold", 0.5) \
.SetDisplay("Threshold", "RAVI threshold", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._ravi_prev1 = 0.0
self._ravi_prev2 = 0.0
self._ravi_prev3 = 0.0
self._has_history = False
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def threshold(self):
return self._threshold.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(move_cross_strategy, self).OnReseted()
self._ravi_prev1 = 0.0
self._ravi_prev2 = 0.0
self._ravi_prev3 = 0.0
self._has_history = False
def OnStarted2(self, time):
super(move_cross_strategy, self).OnStarted2(time)
fast_sma = SimpleMovingAverage()
fast_sma.Length = self.fast_period
slow_sma = SimpleMovingAverage()
slow_sma.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_sma, slow_sma, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_sma)
self.DrawIndicator(area, slow_sma)
self.DrawOwnTrades(area)
def on_process(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
if slow == 0:
return
ravi = (fast - slow) / slow * 100
if self._has_history:
# Buy: RAVI rising for 3 bars and above threshold
ravi_rising = ravi > self._ravi_prev1 and self._ravi_prev1 > self._ravi_prev2 and self._ravi_prev2 > self._ravi_prev3
# Sell: RAVI falling for 3 bars and below negative threshold
ravi_falling = ravi < self._ravi_prev1 and self._ravi_prev1 < self._ravi_prev2 and self._ravi_prev2 < self._ravi_prev3
if ravi_rising and ravi > self.threshold and self.Position <= 0:
self.BuyMarket()
elif ravi_falling and ravi < -self.threshold and self.Position >= 0:
self.SellMarket()
self._ravi_prev3 = self._ravi_prev2
self._ravi_prev2 = self._ravi_prev1
self._ravi_prev1 = ravi
self._has_history = True
def CreateClone(self):
return move_cross_strategy()