El Barómetro de Enero establece que el comportamiento del mercado en enero marca la pauta para el resto del año. Esta estrategia invierte en un ETF de renta variable durante el resto del año únicamente si enero cierra en positivo; de lo contrario, permanece en un proxy de efectivo. La decisión de asignación se toma una vez al año y se mantiene hasta final de año.
En el primer día hábil de febrero, el algoritmo mide el rendimiento total del ETF de renta variable durante enero. Si el rendimiento es positivo y el valor de la orden supera el umbral mínimo, compra el ETF de renta variable y lo mantiene hasta diciembre. Si enero fue negativo, mantiene el ETF de efectivo en su lugar. El proceso se repite cada año.
Detalles
Criterios de entrada:
En el primer día hábil de febrero, calcular el rendimiento total de enero de EquityETF.
Comprar EquityETF si el rendimiento es positivo y el tamaño de la orden >= MinTradeUsd; de lo contrario, mantener CashETF.
Largo/Corto: Solo largos en renta variable o efectivo.
Criterios de salida: Cerrar la posición en renta variable en el último día hábil del año.
Stops: Ninguno.
Valores predeterminados:
EquityETF – ETF que representa el mercado de renta variable.
CashETF – ETF proxy de efectivo.
CandleType = 1 día.
MinTradeUsd – valor mínimo de operación.
Filtros:
Categoría: Estacional.
Dirección: Solo largos.
Marco temporal: Largo plazo.
Rebalanceo: Anual.
using System;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// January barometer strategy generalized to any month.
/// Measures the return over the first N candles of each evaluation period,
/// then goes long if bullish or short if bearish for the remainder.
/// Re-evaluates at the start of each new period.
/// </summary>
public class JanuaryBarometerStrategy : Strategy
{
private readonly StrategyParam<int> _measureCandles;
private readonly StrategyParam<int> _periodCandles;
private readonly StrategyParam<DataType> _candleType;
private int _candleCount;
private decimal _periodOpen;
private decimal _measureClose;
private bool _measured;
/// <summary>
/// Number of candles in the measurement (barometer) window.
/// </summary>
public int MeasureCandles
{
get => _measureCandles.Value;
set => _measureCandles.Value = value;
}
/// <summary>
/// Total candles per evaluation period before resetting.
/// </summary>
public int PeriodCandles
{
get => _periodCandles.Value;
set => _periodCandles.Value = value;
}
/// <summary>
/// The type of candles to use for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public JanuaryBarometerStrategy()
{
_measureCandles = Param(nameof(MeasureCandles), 50)
.SetGreaterThanZero()
.SetDisplay("Measure Candles", "Number of candles for barometer measurement", "General");
_periodCandles = Param(nameof(PeriodCandles), 200)
.SetGreaterThanZero()
.SetDisplay("Period Candles", "Total candles per evaluation period", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candleCount = 0;
_periodOpen = 0m;
_measureClose = 0m;
_measured = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_candleCount++;
// Start of a new period
if (_candleCount == 1)
{
_periodOpen = candle.OpenPrice;
_measured = false;
}
// End of measurement window
if (_candleCount == MeasureCandles && !_measured)
{
_measureClose = candle.ClosePrice;
_measured = true;
if (_periodOpen > 0m)
{
var barometerReturn = (_measureClose - _periodOpen) / _periodOpen;
var bullish = barometerReturn > 0m;
// Enter position based on barometer reading
if (bullish && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (!bullish && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
}
// End of period: close position and reset for next period
if (_candleCount >= PeriodCandles)
{
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
_candleCount = 0;
_periodOpen = 0m;
_measureClose = 0m;
_measured = false;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class january_barometer_strategy(Strategy):
"""
January barometer strategy generalized to any month.
Measures the return over the first N candles of each evaluation period,
then goes long if bullish or short if bearish for the remainder.
Re-evaluates at the start of each new period.
"""
def __init__(self):
super(january_barometer_strategy, self).__init__()
self._measure_candles = self.Param("MeasureCandles", 50) \
.SetGreaterThanZero() \
.SetDisplay("Measure Candles", "Number of candles for barometer measurement", "General")
self._period_candles = self.Param("PeriodCandles", 200) \
.SetGreaterThanZero() \
.SetDisplay("Period Candles", "Total candles per evaluation period", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._candle_count = 0
self._period_open = 0
self._measure_close = 0
self._measured = False
@property
def MeasureCandles(self):
return self._measure_candles.Value
@property
def PeriodCandles(self):
return self._period_candles.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(january_barometer_strategy, self).OnReseted()
self._candle_count = 0
self._period_open = 0
self._measure_close = 0
self._measured = False
def OnStarted2(self, time):
super(january_barometer_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
self._candle_count += 1
# Start of a new period
if self._candle_count == 1:
self._period_open = candle.OpenPrice
self._measured = False
# End of measurement window
if self._candle_count == self.MeasureCandles and not self._measured:
self._measure_close = candle.ClosePrice
self._measured = True
if self._period_open > 0:
barometer_return = (self._measure_close - self._period_open) / self._period_open
bullish = barometer_return > 0
# Enter position based on barometer reading
if bullish and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif not bullish and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
# End of period: close position and reset for next period
if self._candle_count >= self.PeriodCandles:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._candle_count = 0
self._period_open = 0
self._measure_close = 0
self._measured = False
def CreateClone(self):
return january_barometer_strategy()