Strategie des Monats-im-Jahres-Effekts
Der Monats-im-Jahres-Effekt erfasst Leistungsunterschiede, die in verschiedenen Monaten beobachtet werden. Beispielsweise steigen Aktien häufig im November und Dezember, können jedoch im September schwach sein.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 88%. Es funktioniert am besten auf dem Aktienmarkt.
Das System geht zu Beginn jedes Monats long oder short basierend auf diesen historischen Durchschnittswerten und verlässt die Position am Monatsende.
Stops werden eingesetzt, um das Kapital zu schützen, falls das übliche saisonale Verhalten ausbleibt.
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>
/// Month of Year seasonal trading strategy.
/// Enters long in historically strong months (Nov-Jan) and short in weak months (Feb, May, Sep).
/// Uses MA trend filter and cooldown between trades.
/// </summary>
public class MonthOfYearStrategy : 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 int _lastTradeMonth;
private int _lastTradeHalf;
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 MonthOfYearStrategy()
{
_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), 100)
.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;
_lastTradeMonth = 0;
_lastTradeHalf = 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 ma)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var month = candle.OpenTime.Month;
var half = candle.OpenTime.Day <= 15 ? 1 : 2;
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;
_lastTradeMonth = month;
_lastTradeHalf = half;
}
else if (Position < 0 && close > ma && _prevMa > 0 && _prevClose <= _prevMa)
{
BuyMarket();
_cooldown = CooldownBars;
_lastTradeMonth = month;
_lastTradeHalf = half;
}
// Entry logic: seasonal month-half based
if (Position == 0 && (month != _lastTradeMonth || half != _lastTradeHalf))
{
// First half of month: buy if above MA
if (half == 1 && close > ma)
{
BuyMarket();
_cooldown = CooldownBars;
_lastTradeMonth = month;
_lastTradeHalf = half;
}
// Second half of month: sell if below MA
else if (half == 2 && close < ma)
{
SellMarket();
_cooldown = CooldownBars;
_lastTradeMonth = month;
_lastTradeHalf = half;
}
}
_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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class month_of_year_strategy(Strategy):
"""
Month of Year seasonal trading strategy.
Uses first/second half of month with MA trend filter and cooldown.
"""
def __init__(self):
super(month_of_year_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", 100).SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._prev_ma = 0.0
self._prev_close = 0.0
self._last_trade_month = 0
self._last_trade_half = 0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(month_of_year_strategy, self).OnReseted()
self._prev_ma = 0.0
self._prev_close = 0.0
self._last_trade_month = 0
self._last_trade_half = 0
self._cooldown = 0
def OnStarted2(self, time):
super(month_of_year_strategy, self).OnStarted2(time)
self._prev_ma = 0.0
self._prev_close = 0.0
self._last_trade_month = 0
self._last_trade_half = 0
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)
month = candle.OpenTime.Month
half = 1 if candle.OpenTime.Day <= 15 else 2
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_month = month
self._last_trade_half = half
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_month = month
self._last_trade_half = half
# Entry logic: seasonal month-half based
if self.Position == 0 and (month != self._last_trade_month or half != self._last_trade_half):
# First half of month: buy if above MA
if half == 1 and close > ma:
self.BuyMarket()
self._cooldown = cd
self._last_trade_month = month
self._last_trade_half = half
# Second half of month: sell if below MA
elif half == 2 and close < ma:
self.SellMarket()
self._cooldown = cd
self._last_trade_month = month
self._last_trade_half = half
self._prev_ma = ma
self._prev_close = close
def CreateClone(self):
return month_of_year_strategy()