Santa Claus Rally Strategie
Die Santa Claus Rally beschreibt die Tendenz von Aktien, in der letzten Dezemberwoche bis zu den ersten zwei Handelstagen im Januar zu steigen. Urlaubsoptimismus und Jahresend-Positionierungen können diesen kurzen Stärke-Schub antreiben.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 100%. Es funktioniert am besten auf dem Forex-Markt.
Die Strategie kauft zu Beginn des Zeitraums und schließt nach dem zweiten Handelstag des neuen Jahres, um den saisonalen Aufschwung zu erfassen.
Stops werden klein gehalten, um große Verluste zu vermeiden, falls der Markt im Zeitfenster nicht steigt.
Details
- Einstiegskriterien: Kalendereffekt-Auslöser
- Long/Short: Beide
- Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
- Stops: Ja, prozentbasiert
- Standardwerte:
CandleType= 15 minuteStopLoss= 2%
- Filter:
- Kategorie: Saisonalität
- Richtung: Beide
- Indikatoren: Saisonalität
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Ja
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// Implementation of Santa Claus Rally trading strategy.
/// Buys at end of each month (seasonal rally pattern) and exits early next month.
/// Also applies trend following via MA filter for short positions in mid-month.
/// </summary>
public class SantaClausRallyStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private SimpleMovingAverage _ma;
private int _cooldown;
private int _prevDayOfMonth;
/// <summary>
/// Moving average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.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="SantaClausRallyStrategy"/>.
/// </summary>
public SantaClausRallyStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period for trend confirmation", "Strategy");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for strategy", "Strategy");
_cooldownBars = Param(nameof(CooldownBars), 50)
.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;
_cooldown = 0;
_prevDayOfMonth = 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 dayOfMonth = candle.OpenTime.Day;
var isNewDay = dayOfMonth != _prevDayOfMonth;
if (_cooldown > 0)
{
_cooldown--;
_prevDayOfMonth = dayOfMonth;
return;
}
// Rally zone: last week of month (day >= 25)
var isRallyZone = dayOfMonth >= 25;
// Exit zone: first week of month (day 3-7)
var isExitZone = dayOfMonth >= 3 && dayOfMonth <= 7;
// Mid-month short zone: day 12-18
var isShortZone = dayOfMonth >= 12 && dayOfMonth <= 18;
// Buy at end of month for rally
if (isRallyZone && isNewDay && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Exit long in early next month
else if (isExitZone && isNewDay && Position > 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Short mid-month if below MA
else if (isShortZone && isNewDay && Position == 0 && close < maValue)
{
SellMarket();
_cooldown = CooldownBars;
}
// Cover short before rally zone
else if (isRallyZone && isNewDay && Position < 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevDayOfMonth = dayOfMonth;
}
}
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 santa_claus_rally_strategy(Strategy):
"""
Santa Claus Rally trading strategy.
Buys at end of each month and exits early next month.
Short mid-month if below MA.
"""
def __init__(self):
super(santa_claus_rally_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 20).SetDisplay("MA Period", "Moving average period for trend confirmation", "Strategy")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles for strategy", "Strategy")
self._cooldown_bars = self.Param("CooldownBars", 50).SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._cooldown = 0
self._prev_day_of_month = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(santa_claus_rally_strategy, self).OnReseted()
self._cooldown = 0
self._prev_day_of_month = 0
def OnStarted2(self, time):
super(santa_claus_rally_strategy, self).OnStarted2(time)
self._cooldown = 0
self._prev_day_of_month = 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_of_month = candle.OpenTime.Day
cd = self._cooldown_bars.Value
is_new_day = day_of_month != self._prev_day_of_month
if self._cooldown > 0:
self._cooldown -= 1
self._prev_day_of_month = day_of_month
return
is_rally_zone = day_of_month >= 25
is_exit_zone = day_of_month >= 3 and day_of_month <= 7
is_short_zone = day_of_month >= 12 and day_of_month <= 18
# Buy at end of month for rally
if is_rally_zone and is_new_day and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
# Exit long in early next month
elif is_exit_zone and is_new_day and self.Position > 0:
self.SellMarket()
self._cooldown = cd
# Short mid-month if below MA
elif is_short_zone and is_new_day and self.Position == 0 and close < ma:
self.SellMarket()
self._cooldown = cd
# Cover short before rally zone
elif is_rally_zone and is_new_day and self.Position < 0:
self.BuyMarket()
self._cooldown = cd
self._prev_day_of_month = day_of_month
def CreateClone(self):
return santa_claus_rally_strategy()