Estrategia de Vencimiento Trimestral
Las semanas de Vencimiento Trimestral ven cómo los contratos de futuros y opciones se renuevan, creando a menudo volatilidad al cerrarse o rolarse posiciones. Las oscilaciones de precios pueden acelerarse cuando se ajustan las coberturas y la liquidez se reduce temporalmente.
Las pruebas indican un retorno anual promedio de aproximadamente el 115%. Funciona mejor en el mercado de acciones.
La estrategia opera en la dirección de la tendencia predominante al inicio de la semana, saliendo antes del día de liquidación para evitar el caos.
Un stop fijo mantiene el riesgo bajo control si la volatilidad resulta ser demasiado extrema.
Detalles
- Criterios de entrada: activadores de efecto de 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 Quarterly Expiry trading strategy.
/// Trades around monthly expiry dates (3rd Friday area of each month).
/// Buys if above MA in expiry week, sells if below. Exits next week.
/// </summary>
public class QuarterlyExpiryStrategy : 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="QuarterlyExpiryStrategy"/>.
/// </summary>
public QuarterlyExpiryStrategy()
{
_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;
}
// Expiry week zone: day 15-19 (around 3rd Friday of each month)
var isExpiryWeek = dayOfMonth >= 15 && dayOfMonth <= 19;
// Post-expiry exit zone: day 22-25
var isPostExpiry = dayOfMonth >= 22 && dayOfMonth <= 25;
// Start of month entry zone: day 1-5
var isStartOfMonth = dayOfMonth >= 1 && dayOfMonth <= 5;
// Pre-expiry exit: day 12-14
var isPreExpiry = dayOfMonth >= 12 && dayOfMonth <= 14;
// Entry in expiry week: buy if above MA
if (isExpiryWeek && isNewDay && Position == 0 && close > maValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Exit after expiry week
else if (isPostExpiry && isNewDay && Position > 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Short entry at start of month if below MA
else if (isStartOfMonth && isNewDay && Position == 0 && close < maValue)
{
SellMarket();
_cooldown = CooldownBars;
}
// Cover short before expiry
else if (isPreExpiry && 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 quarterly_expiry_strategy(Strategy):
"""
Quarterly Expiry trading strategy.
Trades around monthly expiry dates (3rd Friday area of each month).
Buys if above MA in expiry week, sells if below. Exits next week.
"""
def __init__(self):
super(quarterly_expiry_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(quarterly_expiry_strategy, self).OnReseted()
self._cooldown = 0
self._prev_day_of_month = 0
def OnStarted2(self, time):
super(quarterly_expiry_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
# Expiry week zone: day 15-19 (around 3rd Friday of each month)
is_expiry_week = day_of_month >= 15 and day_of_month <= 19
# Post-expiry exit zone: day 22-25
is_post_expiry = day_of_month >= 22 and day_of_month <= 25
# Start of month entry zone: day 1-5
is_start_of_month = day_of_month >= 1 and day_of_month <= 5
# Pre-expiry exit: day 12-14
is_pre_expiry = day_of_month >= 12 and day_of_month <= 14
# Entry in expiry week: buy if above MA
if is_expiry_week and is_new_day and self.Position == 0 and close > ma:
self.BuyMarket()
self._cooldown = cd
# Exit after expiry week
elif is_post_expiry and is_new_day and self.Position > 0:
self.SellMarket()
self._cooldown = cd
# Short entry at start of month if below MA
elif is_start_of_month and is_new_day and self.Position == 0 and close < ma:
self.SellMarket()
self._cooldown = cd
# Cover short before expiry
elif is_pre_expiry 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 quarterly_expiry_strategy()