Estrategia de Control de Cartera de Futuros con Expiración
Descripción general
Esta estrategia reconstruye el asesor experto de MetaTrader 5 Futures Portfolio Control Expiration sobre la API de alto nivel de StockSharp. Mantiene una cartera de futuros de tres tramos, conserva la exposición largo/corto deseada para cada tramo y rota automáticamente cada contrato al siguiente vencimiento cuando la vida útil restante cae por debajo de un umbral configurable.
La implementación replica el flujo de trabajo original:
Identificar el contrato actualmente negociable para cada familia de futuros basándose en un código corto (por ejemplo MXI o BR).
Abrir o ajustar la posición para que el volumen real del portafolio coincida con el valor de lote configurado (positivo = largo, negativo = corto).
Monitorear el tiempo de vencimiento en cada vela finalizada de una suscripción de latidos.
Cerrar el contrato que vence, descubrir el siguiente vencimiento en la misma familia y recrear la exposición objetivo en el nuevo contrato.
Parámetros
Nombre
Descripción
Predeterminado
BoardCode
Tablero de intercambio adjunto a los identificadores de futuros (por ejemplo FORTS). Dejar vacío si el proveedor no requiere un sufijo de tablero.
FORTS
Symbol1, Symbol2, Symbol3
Códigos cortos de las tres familias de futuros. La estrategia itera los vencimientos de futuros construyendo identificadores como CODE-M.YY.
MXI, BR, SBRF
Lot1, Lot2, Lot3
Tamaño de posición objetivo por tramo. Los valores positivos crean exposición larga, los negativos crean exposición corta.
-4, -1, 5
HoursBeforeExpiration
Número de horas antes del vencimiento del contrato cuando debe comenzar la rotación.
25
MonitoringCandleType
Tipo de vela utilizado solo como latido para activar las verificaciones de vencimiento (por ejemplo velas por hora).
Período 1H
Gestión de rotación y posición
Descubrimiento de contratos. Para cada tramo la estrategia escanea hasta doce meses consecutivos del calendario. Prueba múltiples formatos de identificador (CODE-M.YY, CODE-MM.YY, CODEMMYY, CODEMYY) y opcionalmente agrega el BoardCode configurado. Solo los instrumentos con una fecha de vencimiento posterior al tiempo de referencia son elegibles.
Actualizaciones de latido. Una suscripción de velas en cada contrato activo proporciona una devolución de llamada de vela finalizada que re-evalúa los temporizadores de vencimiento y sincroniza la exposición del portafolio.
Lógica de rotación. Cuando la vida útil restante es menor o igual a HoursBeforeExpiration, la estrategia cierra cualquier posición abierta en el contrato actual, localiza el siguiente futuro con un vencimiento posterior, se re-suscribe a las velas de latido y restaura el lote objetivo en el nuevo contrato.
Sincronización de posición. Después de cada latido, la posición real se compara con el lote objetivo. La estrategia aumenta o disminuye la exposición con órdenes de mercado para que la posición en vivo siempre coincida con el volumen solicitado (incluido cero).
Notas de uso
Asegúrese de que el SecurityProvider conozca todos los símbolos de futuros para las familias seleccionadas. Configure BoardCode si su fuente de datos requiere identificadores como Si-9.23@FORTS.
Inicie la estrategia con los parámetros de portafolio deseados. Las posiciones se abren solo cuando la estrategia está en línea y el trading está permitido.
La estrategia registra cada asignación, ajuste y evento de rotación. Use estos mensajes para verificar el mapeo entre códigos cortos y futuros reales.
Dado que la suscripción de latido es solo un temporizador, puede elegir cualquier tipo de vela que esté disponible de manera consistente para los instrumentos negociados.
Detalles de implementación
Los componentes de la API de alto nivel (SubscribeCandles, StrategyParam, BuyMarket/SellMarket) mantienen el código conciso y adhieren a las pautas del proyecto.
No se almacenan colecciones personalizadas de datos históricos; la estrategia solo trabaja con el último evento de vela y el estado de la posición.
Los comentarios en inglés dentro del código describen cada paso importante para un mantenimiento más sencillo.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Monitors position and rebalances to maintain a target exposure.
/// Simplified from the multi-leg futures portfolio controller to single security.
/// </summary>
public class FuturesPortfolioControlExpirationStrategy : Strategy
{
private readonly StrategyParam<int> _targetPosition;
private readonly StrategyParam<int> _rebalancePeriod;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private int _barCount;
/// <summary>
/// Target position size. Positive for long, negative for short.
/// </summary>
public int TargetPosition
{
get => _targetPosition.Value;
set => _targetPosition.Value = value;
}
/// <summary>
/// Number of bars between rebalance checks.
/// </summary>
public int RebalancePeriod
{
get => _rebalancePeriod.Value;
set => _rebalancePeriod.Value = value;
}
/// <summary>
/// Candle type used as heartbeat for monitoring.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public FuturesPortfolioControlExpirationStrategy()
{
_targetPosition = Param(nameof(TargetPosition), 1)
.SetDisplay("Target Position", "Desired position size (positive=long, negative=short)", "Portfolio");
_rebalancePeriod = Param(nameof(RebalancePeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Rebalance Period", "Number of bars between rebalance checks", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle series for monitoring", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_barCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = 20 };
SubscribeCandles(CandleType)
.Bind(_sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormed)
return;
_barCount++;
var price = candle.ClosePrice;
var target = (decimal)TargetPosition;
// Rebalance: ensure position matches target
if (_barCount % RebalancePeriod == 0)
{
var current = Position;
var diff = target - current;
if (diff > 0)
BuyMarket(Math.Abs(diff));
else if (diff < 0)
SellMarket(Math.Abs(diff));
}
// Trend reversal exit and re-entry
if (Position > 0 && price < smaValue)
{
SellMarket(Math.Abs(Position));
}
else if (Position < 0 && price > smaValue)
{
BuyMarket(Math.Abs(Position));
}
else if (Position == 0)
{
if (target > 0 && price > smaValue)
BuyMarket(Math.Abs(target));
else if (target < 0 && price < smaValue)
SellMarket(Math.Abs(target));
else if (target > 0)
BuyMarket(Math.Abs(target));
else if (target < 0)
SellMarket(Math.Abs(target));
}
}
}
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 futures_portfolio_control_expiration_strategy(Strategy):
"""
Futures Portfolio Control Expiration: rebalances to target position
at regular intervals with SMA trend reversal exits.
"""
def __init__(self):
super(futures_portfolio_control_expiration_strategy, self).__init__()
self._target_position = self.Param("TargetPosition", 1) \
.SetDisplay("Target Position", "Desired position size (positive=long, negative=short)", "Portfolio")
self._rebalance_period = self.Param("RebalancePeriod", 10) \
.SetDisplay("Rebalance Period", "Number of bars between rebalance checks", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle series for monitoring", "General")
self._bar_count = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(futures_portfolio_control_expiration_strategy, self).OnReseted()
self._bar_count = 0
def OnStarted2(self, time):
super(futures_portfolio_control_expiration_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = 20
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormed:
return
self._bar_count += 1
price = float(candle.ClosePrice)
sma = float(sma_value)
target = self._target_position.Value
# Rebalance at intervals
if self._bar_count % self._rebalance_period.Value == 0:
current = self.Position
diff = target - current
if diff > 0:
self.BuyMarket()
elif diff < 0:
self.SellMarket()
# Trend reversal exit
if self.Position > 0 and price < sma:
self.SellMarket()
elif self.Position < 0 and price > sma:
self.BuyMarket()
elif self.Position == 0:
if target > 0 and price > sma:
self.BuyMarket()
elif target < 0 and price < sma:
self.SellMarket()
elif target > 0:
self.BuyMarket()
elif target < 0:
self.SellMarket()
def CreateClone(self):
return futures_portfolio_control_expiration_strategy()