Estrategia del Primer Día del Mes
Muchos mercados exhiben un sesgo alcista en el primer día de negociación del mes a medida que nuevo capital fluye hacia los fondos. Los traders intentan adelantarse a este efecto comprando al cierre del mes anterior o al inicio de la sesión.
Las pruebas indican un rendimiento anual promedio de aproximadamente el 97%. Funciona mejor en el mercado de criptomonedas.
La estrategia entra en largo al inicio del mes y sale antes de que comience el segundo día, capturando el típico aumento de la presión compradora.
Un pequeño stop protege contra sorpresas bajistas si la fortaleza esperada no se materializa.
Detalles
- Criterios de entrada: desencadenadores de efecto calendario
- Largo/Corto: Ambos
- Criterios de salida: stop-loss o señal opuesta
- Stops: Sí, basado en porcentaje
- Valores predeterminados:
CandleType= 15 minuteStopLoss= 2%
- Filtros:
- Categoría: Estacionalidad
- Dirección: Ambos
- Indicadores: Estacionalidad
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: Sí
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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 First Day of Month trading strategy.
/// Enters long on the first few days of month, exits around the 5th-10th day.
/// Enters short in mid-month if price below MA.
/// </summary>
public class FirstDayOfMonthStrategy : 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="FirstDayOfMonthStrategy"/>.
/// </summary>
public FirstDayOfMonthStrategy()
{
_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;
}
// First days of month zone: day 1-3
var isFirstDays = dayOfMonth >= 1 && dayOfMonth <= 3;
// Exit zone: day 8-12
var isExitZone = dayOfMonth >= 8 && dayOfMonth <= 12;
// Mid-month zone for shorts: day 15-20
var isMidMonth = dayOfMonth >= 15 && dayOfMonth <= 20;
// End-of-month exit zone for shorts: day 25+
var isEndOfMonth = dayOfMonth >= 25;
// Entry: buy on first days of month
if (isFirstDays && isNewDay && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Exit long around mid-first-week
else if (isExitZone && isNewDay && Position > 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Short entry mid-month if below MA
else if (isMidMonth && isNewDay && Position == 0 && close < maValue)
{
SellMarket();
_cooldown = CooldownBars;
}
// Cover short at end of month
else if (isEndOfMonth && 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 first_day_of_month_strategy(Strategy):
"""
First Day of Month trading strategy.
Enters long on the first few days of month, exits around the 5th-10th day.
Enters short in mid-month if price below MA.
"""
def __init__(self):
super(first_day_of_month_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(first_day_of_month_strategy, self).OnReseted()
self._cooldown = 0
self._prev_day_of_month = 0
def OnStarted2(self, time):
super(first_day_of_month_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_first_days = day_of_month >= 1 and day_of_month <= 3
is_exit_zone = day_of_month >= 8 and day_of_month <= 12
is_mid_month = day_of_month >= 15 and day_of_month <= 20
is_end_of_month = day_of_month >= 25
# Entry: buy on first days of month
if is_first_days and is_new_day and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
# Exit long around mid-first-week
elif is_exit_zone and is_new_day and self.Position > 0:
self.SellMarket()
self._cooldown = cd
# Short entry mid-month if below MA
elif is_mid_month and is_new_day and self.Position == 0 and close < ma:
self.SellMarket()
self._cooldown = cd
# Cover short at end of month
elif is_end_of_month 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 first_day_of_month_strategy()