Day of Week Effect Strategy
The Day of Week Effect exploits tendencies for markets to exhibit recurring behavior on specific weekdays. Some indices show consistent strength midweek while Monday or Friday can be relatively weak.
Testing indicates an average annual return of about 85%. It performs best in the crypto market.
The strategy opens trades based on those historical tendencies, buying or selling at the start of the session and exiting by the close.
A modest stop guards against anomalies, closing the position early if the pattern fails on a given day.
Details
- Entry Criteria: calendar effect triggers
- Long/Short: Both
- Exit Criteria: stop-loss or opposite signal
- Stops: Yes, percent based
- Default Values:
CandleType= 15 minuteStopLoss= 2%
- Filters:
- Category: Seasonality
- Direction: Both
- Indicators: Seasonality
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Intraday
- Seasonality: Yes
- Neural networks: No
- Divergence: No
- Risk level: Medium
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>
/// Day of Week trading strategy.
/// Enters long on Monday and short on Friday, with MA trend filter.
/// Uses daily transitions to limit trade frequency.
/// </summary>
public class DayOfWeekStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _cooldownBars;
private SimpleMovingAverage _ma;
private decimal _prevMa;
private decimal _prevClose;
private DayOfWeek _lastTradeDay;
private int _cooldown;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// MA period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public DayOfWeekStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetDisplay("MA Period", "SMA period", "Indicators")
.SetRange(10, 50);
_cooldownBars = Param(nameof(CooldownBars), 300)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(10, 2000);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma = default;
_prevMa = 0;
_prevClose = 0;
_lastTradeDay = default;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal ma)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var day = candle.OpenTime.DayOfWeek;
if (_cooldown > 0)
{
_cooldown--;
_prevMa = ma;
_prevClose = close;
return;
}
// Exit logic: MA cross
if (Position > 0 && close < ma && _prevMa > 0 && _prevClose >= _prevMa)
{
SellMarket();
_cooldown = CooldownBars;
_lastTradeDay = day;
}
else if (Position < 0 && close > ma && _prevMa > 0 && _prevClose <= _prevMa)
{
BuyMarket();
_cooldown = CooldownBars;
_lastTradeDay = day;
}
// Entry logic: day-of-week based (one trade per day transition)
if (Position == 0 && day != _lastTradeDay)
{
// Monday/Tuesday: buy if above MA
if ((day == DayOfWeek.Monday || day == DayOfWeek.Tuesday) && close > ma)
{
BuyMarket();
_cooldown = CooldownBars;
_lastTradeDay = day;
}
// Thursday/Friday: sell if below MA
else if ((day == DayOfWeek.Thursday || day == DayOfWeek.Friday) && close < ma)
{
SellMarket();
_cooldown = CooldownBars;
_lastTradeDay = day;
}
}
_prevMa = ma;
_prevClose = close;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, DayOfWeek
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class day_of_week_strategy(Strategy):
"""
Day of Week trading strategy.
Enters long on Monday/Tuesday and short on Thursday/Friday, with MA trend filter.
Uses daily transitions to limit trade frequency.
"""
def __init__(self):
super(day_of_week_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._ma_period = self.Param("MaPeriod", 20).SetDisplay("MA Period", "SMA period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 300).SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._prev_ma = 0.0
self._prev_close = 0.0
self._last_trade_day = -1
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(day_of_week_strategy, self).OnReseted()
self._prev_ma = 0.0
self._prev_close = 0.0
self._last_trade_day = -1
self._cooldown = 0
def OnStarted2(self, time):
super(day_of_week_strategy, self).OnStarted2(time)
self._prev_ma = 0.0
self._prev_close = 0.0
self._last_trade_day = -1
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_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, ma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ma = float(ma_val)
day = candle.OpenTime.DayOfWeek
cd = self._cooldown_bars.Value
if self._cooldown > 0:
self._cooldown -= 1
self._prev_ma = ma
self._prev_close = close
return
# Exit logic: MA cross
if self.Position > 0 and close < ma and self._prev_ma > 0 and self._prev_close >= self._prev_ma:
self.SellMarket()
self._cooldown = cd
self._last_trade_day = day
elif self.Position < 0 and close > ma and self._prev_ma > 0 and self._prev_close <= self._prev_ma:
self.BuyMarket()
self._cooldown = cd
self._last_trade_day = day
# Entry logic: day-of-week based (one trade per day transition)
if self.Position == 0 and day != self._last_trade_day:
# Monday/Tuesday: buy if above MA
if (day == DayOfWeek.Monday or day == DayOfWeek.Tuesday) and close > ma:
self.BuyMarket()
self._cooldown = cd
self._last_trade_day = day
# Thursday/Friday: sell if below MA
elif (day == DayOfWeek.Thursday or day == DayOfWeek.Friday) and close < ma:
self.SellMarket()
self._cooldown = cd
self._last_trade_day = day
self._prev_ma = ma
self._prev_close = close
def CreateClone(self):
return day_of_week_strategy()