This strategy recreates the MetaTrader expert advisor "Contrarian trade MA" using the StockSharp high-level API. It combines weekly context with a Monday-only entry filter to trade against extremes. The system waits for a new trading week, measures how far the previous week closed relative to the highest high and lowest low over the lookback window, and checks whether price opened the new week on the opposite side of a shifted moving average. If the market finishes the first daily candle of the week outside those thresholds, a contrarian position is opened.
The logic relies on finished candles only. A daily series (default) drives entries and exits, while a weekly series supplies the extreme levels and the moving-average signal. Each time a Monday candle completes, the strategy evaluates whether the previous week ended above the recent high band or below the recent low band, or whether the previous moving average value stands on the other side of the current weekly open. The assumption is that such overextended moves tend to mean-revert during the week.
How it works
Weekly candles feed two indicators:
Highest/Lowest find the extreme high and low over CalcPeriod weeks.
A configurable moving average (MaPeriod, MaMethod, MaShift, AppliedPrice) processes the same weekly candles.
Daily candles (or any selected TradeCandleType) trigger trading decisions once they finish.
On the first completed candle whose OpenTime.DayOfWeek == Monday, the strategy evaluates entry conditions:
Long if the previous weekly close is above the highest high of the lookback or if the previous MA value is greater than the current weekly open (meaning price opened below the MA).
Short if the previous weekly close is below the lowest low of the lookback or if the previous MA value is less than the current weekly open (price opened above the MA).
Orders are sent with BuyMarket or SellMarket using the strategy volume and no averaging. Only one position can be open at a time.
Exit management
A fixed stop-loss distance is calculated as StopLossPips * Security.PriceStep. When enabled (> 0), the strategy monitors daily candle highs and lows; if price touches the stop level within the day, the position is closed at market.
A time-based exit closes any open position once seven days have passed since the entry (604800 seconds in the original EA). The check is performed on each finished daily candle.
The strategy never opens a new trade until the previous one is fully closed.
Indicators and data
Weekly extremes:Highest and Lowest indicators attached to the MaCandleType series (default 1-week candles).
Weekly moving average:Simple, Exponential, Smoothed, or LinearWeighted methods are available. The moving average can be shifted forward by MaShift bars to mimic the MetaTrader setting and can consume different price sources (AppliedPrice).
Primary timeframe:TradeCandleType defines which candles drive trade timing; the default is daily candles so entries are evaluated after the first day of the trading week has closed.
Parameters
Name
Type
Default
Description
CalcPeriod
int
4
Number of higher-timeframe candles used to calculate the highest high and lowest low.
StopLossPips
int
300
Stop-loss distance in price steps. Set to 0 to disable the protective stop.
MaPeriod
int
7
Length of the weekly moving average.
MaShift
int
0
Forward shift of the moving average in bars. Mirrors the MetaTrader MA shift parameter.
MaMethod
MovingAverageMethod
LinearWeighted
Moving average calculation method (Simple, Exponential, Smoothed, LinearWeighted).
AppliedPrice
AppliedPriceType
Weighted
Price source fed into the moving average (Close, Open, High, Low, Median, Typical, Weighted).
TradeCandleType
DataType
TimeSpan.FromMinutes(5).TimeFrame()
Primary timeframe that triggers entries and manages stops/exits.
MaCandleType
DataType
TimeSpan.FromDays(7).TimeFrame()
Higher timeframe used for the moving average and for calculating extremes.
Notes
The stop-loss distance adapts to the instrument by multiplying the pip count by Security.PriceStep. Instruments without a defined step will effectively disable the stop.
Because the strategy evaluates finished candles, entries occur at the close of the Monday bar rather than the very first tick of the week. This keeps behaviour deterministic across backtests.
The logic assumes only one open position; any open trade is closed either by the stop-loss or by the seven-day timeout before a new signal is considered.
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>
/// Contrarian Trade MA Monday strategy using SMA crossover with mean reversion.
/// Buys when fast SMA crosses above slow SMA, sells on reverse.
/// </summary>
public class ContrarianTradeMaMondayStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SimpleMovingAverage _fast;
private SimpleMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast SMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ContrarianTradeMaMondayStrategy"/> class.
/// </summary>
public ContrarianTradeMaMondayStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new SimpleMovingAverage { Length = FastPeriod };
_slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// SMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 contrarian_trade_ma_monday_strategy(Strategy):
def __init__(self):
super(contrarian_trade_ma_monday_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 100) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(contrarian_trade_ma_monday_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(contrarian_trade_ma_monday_strategy, self).OnStarted2(time)
self._fast = SimpleMovingAverage()
self._fast.Length = self.fast_period
self._slow = SimpleMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return contrarian_trade_ma_monday_strategy()