using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Moving average reversal strategy converted from the MetaTrader MA_Reverse expert advisor.
/// Counts how many consecutive closes remain on one side of the SMA and opens a trade once the streak is long enough.
/// </summary>
public class MaReverseStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _streakThreshold;
private readonly StrategyParam<decimal> _minimumDeviation;
private readonly StrategyParam<DataType> _candleType;
private int _streak;
public MaReverseStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 14)
.SetDisplay("SMA Period", "Number of candles used by the moving average.", "Indicator");
_streakThreshold = Param(nameof(StreakThreshold), 3)
.SetDisplay("Streak Threshold", "Number of consecutive closes required before reversing.", "Logic");
_minimumDeviation = Param(nameof(MinimumDeviation), 0.0001m)
.SetDisplay("Minimum Deviation", "Minimum distance between price and SMA to confirm the reversal.", "Logic");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for SMA calculation.", "General");
}
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
public int StreakThreshold
{
get => _streakThreshold.Value;
set => _streakThreshold.Value = value;
}
public decimal MinimumDeviation
{
get => _minimumDeviation.Value;
set => _minimumDeviation.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_streak = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_streak = 0;
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
var closePrice = candle.ClosePrice;
var deviation = closePrice - smaValue;
if (deviation == 0m)
{
_streak = 0;
return;
}
if (deviation > 0m)
{
// Price above SMA
if (_streak < 0)
_streak = 0;
_streak++;
if (_streak >= StreakThreshold && deviation > MinimumDeviation)
{
// Long streak above SMA => sell (reversal)
if (Position >= 0)
{
SellMarket();
_streak = 0;
}
}
}
else
{
// Price below SMA
if (_streak > 0)
_streak = 0;
_streak--;
if (-_streak >= StreakThreshold && -deviation > MinimumDeviation)
{
// Long streak below SMA => buy (reversal)
if (Position <= 0)
{
BuyMarket();
_streak = 0;
}
}
}
}
}
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 ma_reverse_strategy(Strategy):
"""
MA reversal: counts consecutive closes on one side of SMA.
After streak threshold, opens a reversal trade.
"""
def __init__(self):
super(ma_reverse_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 14).SetDisplay("SMA Period", "SMA period", "Indicators")
self._streak_threshold = self.Param("StreakThreshold", 3).SetDisplay("Streak", "Consecutive closes needed", "Logic")
self._min_deviation = self.Param("MinDeviation", 0.0001).SetDisplay("Min Deviation", "Min distance from SMA", "Logic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._streak = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_reverse_strategy, self).OnReseted()
self._streak = 0
def OnStarted2(self, time):
super(ma_reverse_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._sma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma = float(sma_val)
deviation = close - sma
if deviation == 0:
self._streak = 0
return
if deviation > 0:
if self._streak < 0:
self._streak = 0
self._streak += 1
if self._streak >= self._streak_threshold.Value and deviation > self._min_deviation.Value:
if self.Position >= 0:
self.SellMarket()
self._streak = 0
else:
if self._streak > 0:
self._streak = 0
self._streak -= 1
if -self._streak >= self._streak_threshold.Value and -deviation > self._min_deviation.Value:
if self.Position <= 0:
self.BuyMarket()
self._streak = 0
def CreateClone(self):
return ma_reverse_strategy()