Lunch Break Fade Strategy
Lunch Break Fade targets reversals that develop during the slow midday period. After the morning session, trends often pause or pull back as volume dries up around lunchtime.
Testing indicates an average annual return of about 127%. It performs best in the stocks market.
The strategy fades the morning move around noon, entering counter to the prevailing direction and covering before volume returns.
A percent stop manages risk if the trend resumes instead of fading.
Details
- Entry Criteria: indicator signal
- Long/Short: Both
- Exit Criteria: stop-loss or opposite signal
- Stops: Yes, percent based
- Default Values:
CandleType= 15 minuteStopLoss= 2%
- Filters:
- Category: Intraday
- Direction: Both
- Indicators: Price Action
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Intraday
- Seasonality: No
- 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>
/// Strategy that trades on the price movement fade during the lunch break.
/// Fades the prior trend around midday, with MA confirmation and cooldown.
/// </summary>
public class LunchBreakFadeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _cooldownBars;
private SimpleMovingAverage _ma;
private decimal _prevClose;
private decimal _prevPrevClose;
private int _cooldown;
/// <summary>
/// Data type for candles.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Moving average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="LunchBreakFadeStrategy"/>.
/// </summary>
public LunchBreakFadeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period", "Strategy");
_cooldownBars = Param(nameof(CooldownBars), 30)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma = default;
_prevClose = 0;
_prevPrevClose = 0;
_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 maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var hour = candle.OpenTime.Hour;
if (_prevClose == 0)
{
_prevClose = close;
return;
}
if (_prevPrevClose == 0)
{
_prevPrevClose = _prevClose;
_prevClose = close;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevPrevClose = _prevClose;
_prevClose = close;
return;
}
// Lunch zone: hours 11-14
var isLunchTime = hour >= 11 && hour <= 14;
if (isLunchTime)
{
var priorUptrend = _prevClose > _prevPrevClose;
var priorDowntrend = _prevClose < _prevPrevClose;
var currentBearish = close < candle.OpenPrice;
var currentBullish = close > candle.OpenPrice;
// Fade uptrend at lunch: short
if (priorUptrend && currentBearish && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Fade downtrend at lunch: long
else if (priorDowntrend && currentBullish && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
// Exit on MA cross
if (Position > 0 && close < maValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && close > maValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevPrevClose = _prevClose;
_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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class lunch_break_fade_strategy(Strategy):
"""
Fades the prior trend during lunch break (11-14h), exits on MA cross.
"""
def __init__(self):
super(lunch_break_fade_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 20).SetDisplay("MA Period", "SMA period", "Strategy")
self._cooldown_bars = self.Param("CooldownBars", 30).SetDisplay("Cooldown", "Bars between trades", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(lunch_break_fade_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_prev_close = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(lunch_break_fade_strategy, self).OnStarted2(time)
ma = SimpleMovingAverage()
ma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ma_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
hour = candle.OpenTime.Hour
ma = float(ma_val)
if self._prev_close == 0:
self._prev_close = close
return
if self._prev_prev_close == 0:
self._prev_prev_close = self._prev_close
self._prev_close = close
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_prev_close = self._prev_close
self._prev_close = close
return
is_lunch = hour >= 11 and hour <= 14
if is_lunch:
prior_up = self._prev_close > self._prev_prev_close
prior_down = self._prev_close < self._prev_prev_close
bearish = close < open_p
bullish = close > open_p
if prior_up and bearish and self.Position == 0:
self.SellMarket()
self._cooldown = self._cooldown_bars.Value
elif prior_down and bullish and self.Position == 0:
self.BuyMarket()
self._cooldown = self._cooldown_bars.Value
if self.Position > 0 and close < ma:
self.SellMarket()
self._cooldown = self._cooldown_bars.Value
elif self.Position < 0 and close > ma:
self.BuyMarket()
self._cooldown = self._cooldown_bars.Value
self._prev_prev_close = self._prev_close
self._prev_close = close
def CreateClone(self):
return lunch_break_fade_strategy()