Ver en GitHub

Estrategia de Fortaleza de Fin de Mes

La Fortaleza de Fin de Mes observa que las acciones a menudo repuntan durante los últimos días de negociación a medida que los gestores de cartera ajustan sus posiciones. La presión compradora vinculada al window dressing puede crear un sesgo alcista confiable antes del cierre mensual.

Las pruebas indican un rendimiento anual promedio de aproximadamente el 94%. Funciona mejor en el mercado de acciones.

La estrategia compra cerca de los últimos días del mes y sale en el primer día de negociación del nuevo mes para capturar esa tendencia.

Los stops se colocan por debajo del soporte reciente para protegerse contra una debilidad inesperada.

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 minute
    • DaysBeforeMonthEnd = 3
    • StopLossPercent = 2 Los días son días UTC, ya que el mercado opera las 24 horas; las entradas ocurren al cierre de la primera vela del día. La compra se abre en cada uno de los últimos DaysBeforeMonthEnd días del mes si no hay posición y se cierra en la primera vela del mes nuevo.
  • 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>
/// End of Month Strength strategy.
/// Days are UTC days of a market that trades around the clock.
/// It buys at the close of the first candle of each of the month's last DaysBeforeMonthEnd days while flat and sells
/// at the close of the first candle of the new month; a percent stop limits the loss.
/// </summary>
public class EndOfMonthStrengthStrategy : Strategy
{
	private readonly StrategyParam<int> _daysBeforeMonthEnd;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private DateTime? _day;

	/// <summary>
	/// How many final days of the month to hold.
	/// </summary>
	public int DaysBeforeMonthEnd
	{
		get => _daysBeforeMonthEnd.Value;
		set => _daysBeforeMonthEnd.Value = value;
	}

	/// <summary>
	/// Stop-loss percentage.
	/// </summary>
	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Constructor.
	/// </summary>
	public EndOfMonthStrengthStrategy()
	{
		_daysBeforeMonthEnd = Param(nameof(DaysBeforeMonthEnd), 3)
			.SetGreaterThanZero()
			.SetDisplay("Days Before Month End", "How many final days of the month to hold", "Calendar");

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetNotNegative()
			.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_day = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_day = null;
		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);

		// The stop has to see prices between candles, not only at their close.
		foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
		{
			var quotes = new Subscription(DataType.Level1, Security);
			quotes.MarketData.BuildField = field;
			SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
		}

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ObserveProtectionQuote(Level1ChangeMessage quote)
	{
		// The high-level handler activates native protection before this callback, also between signal bars.
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var day = candle.OpenTime.Date;
		var firstOfDay = _day != day;

		if (firstOfDay)
		{
			_day = day;
		}

		// The candle is the day's last when the next one would open on another day.
		var frame = CandleType.Arg is TimeSpan tf ? tf : TimeSpan.Zero;
		var lastOfDay = (candle.OpenTime + frame).Date != day;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (Position != 0)
		{
			if (firstOfDay && day.Day == 1)
			{
				if (Position > 0)
					SellMarket(Position);
				else
					BuyMarket(-Position);
			}

			return;
		}

		if (!(firstOfDay && day.Day > DateTime.DaysInMonth(day.Year, day.Month) - DaysBeforeMonthEnd))
			return;

		if ((1) > 0)
			BuyMarket(Volume);
		else
			SellMarket(Volume);
	}
}