Contrarian Trade MA Strategy
A weekly contrarian system that evaluates prior highs, lows and a moving average to open trades at the end of each week. The position is held for one week regardless of direction.
The method is designed for major currency pairs but can be applied to any liquid asset with weekly data.
Details
- Entry Criteria:
- Buy: Previous week's close above the highest high of the lookback period, or the moving average is above the weekly open.
- Sell: Previous week's close below the lowest low of the lookback period, or the moving average is below the weekly open.
- Long/Short: Both.
- Exit Criteria: Position is closed after being held for one week.
- Stops: None.
- Timeframe: Weekly candles.
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>
/// Weekly contrarian strategy using moving average and extreme price levels.
/// </summary>
public class ContrarianTradeMaStrategy : Strategy
{
private readonly StrategyParam<int> _calcPeriod;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private int _barsInPosition;
private decimal _prevHighest;
private decimal _prevLowest;
private decimal _prevSma;
private bool _hasPrev;
public int CalcPeriod { get => _calcPeriod.Value; set => _calcPeriod.Value = value; }
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ContrarianTradeMaStrategy()
{
_calcPeriod = Param(nameof(CalcPeriod), 10)
.SetDisplay("Calc Period", "Lookback period for extremes", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetDisplay("MA Period", "Moving average period", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_barsInPosition = 0;
_prevHighest = 0;
_prevLowest = 0;
_prevSma = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highest = new Highest { Length = CalcPeriod };
var lowest = new Lowest { Length = CalcPeriod };
var sma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, sma, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest, decimal sma)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevClose = candle.ClosePrice;
_prevHighest = highest;
_prevLowest = lowest;
_prevSma = sma;
_hasPrev = true;
return;
}
if (Position == 0)
{
if (_prevHighest < _prevClose && Position <= 0)
{
BuyMarket();
_barsInPosition = 0;
}
else if (_prevLowest > _prevClose && Position >= 0)
{
SellMarket();
_barsInPosition = 0;
}
else if (_prevSma > candle.OpenPrice && Position <= 0)
{
BuyMarket();
_barsInPosition = 0;
}
else if (_prevSma < candle.OpenPrice && Position >= 0)
{
SellMarket();
_barsInPosition = 0;
}
}
else
{
_barsInPosition++;
if (_barsInPosition >= CalcPeriod)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
_barsInPosition = 0;
}
}
_prevClose = candle.ClosePrice;
_prevHighest = highest;
_prevLowest = lowest;
_prevSma = sma;
}
}
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, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class contrarian_trade_ma_strategy(Strategy):
def __init__(self):
super(contrarian_trade_ma_strategy, self).__init__()
self._calc_period = self.Param("CalcPeriod", 10) \
.SetDisplay("Calc Period", "Lookback period for extremes", "General")
self._ma_period = self.Param("MaPeriod", 20) \
.SetDisplay("MA Period", "Moving average period", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._prev_close = 0.0
self._bars_in_position = 0
self._prev_highest = 0.0
self._prev_lowest = 0.0
self._prev_sma = 0.0
self._has_prev = False
@property
def calc_period(self):
return self._calc_period.Value
@property
def ma_period(self):
return self._ma_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(contrarian_trade_ma_strategy, self).OnReseted()
self._prev_close = 0.0
self._bars_in_position = 0
self._prev_highest = 0.0
self._prev_lowest = 0.0
self._prev_sma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(contrarian_trade_ma_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self.calc_period
lowest = Lowest()
lowest.Length = self.calc_period
sma = SimpleMovingAverage()
sma.Length = self.ma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, sma, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def on_process(self, candle, highest, lowest, sma):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_close = candle.ClosePrice
self._prev_highest = highest
self._prev_lowest = lowest
self._prev_sma = sma
self._has_prev = True
return
if self.Position == 0:
if self._prev_highest < self._prev_close and self.Position <= 0:
self.BuyMarket()
self._bars_in_position = 0
elif self._prev_lowest > self._prev_close and self.Position >= 0:
self.SellMarket()
self._bars_in_position = 0
elif self._prev_sma > candle.OpenPrice and self.Position <= 0:
self.BuyMarket()
self._bars_in_position = 0
elif self._prev_sma < candle.OpenPrice and self.Position >= 0:
self.SellMarket()
self._bars_in_position = 0
else:
self._bars_in_position += 1
if self._bars_in_position >= self.calc_period:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._bars_in_position = 0
self._prev_close = candle.ClosePrice
self._prev_highest = highest
self._prev_lowest = lowest
self._prev_sma = sma
def CreateClone(self):
return contrarian_trade_ma_strategy()