La Estrategia de Cobertura de Cualquier Posición es una conversión directa del expert MQL5 original Hedge any positions (barabashkakvn's edition). La versión de StockSharp mantiene la idea central intacta: monitorea cada pata abierta creada por la estrategia y, una vez que una pata pierde un número definido de pips, inmediatamente abre una posición opuesta con un tamaño de lote amplificado. La implementación se basa en la API de alto nivel de StockSharp, por lo que las órdenes de hedge se colocan mediante órdenes de mercado y el seguimiento de posiciones se maneja internamente sin código de enrutamiento de órdenes personalizado.
La estrategia puede opcionalmente colocar un trade inicial cuando comienza. Después simplemente reacciona a movimientos adversos de precio y construye una escalera de trades de cobertura, marcando cada pata como cubierta para que la misma posición no pueda desencadenar múltiples entradas opuestas.
Flujo de trabajo de cobertura
Feed de velas – un CandleType configurable impulsa la estrategia. Solo se procesan velas terminadas.
Cálculo de pérdidas – en cada cierre de vela la estrategia verifica si el precio de cierre se movió contra cualquier pata abierta al menos LosingPips multiplicado por el tamaño de pip calculado.
Ejecución del hedge – si se encuentra una pata perdedora, se envía una orden de mercado en la dirección opuesta. El volumen de la orden equivale al volumen de la pata original multiplicado por LotCoefficient, redondeado al paso de volumen del instrumento y limitado al volumen mínimo/máximo permitido.
Actualización de estado – una vez que se despacha una orden opuesta, la pata original se marca como cubierta y el trade recién abierto se almacena como una nueva pata que a su vez puede ser cubierta más tarde si el precio se revierte nuevamente.
Parámetros
Parámetro
Descripción
Predeterminado
CandleType
Marco temporal usado para evaluar los movimientos de precio y desencadenar coberturas.
Velas de 1 minuto
LosingPips
Número de pips que el precio debe moverse contra una pata antes de abrir una cobertura.
5
LotCoefficient
Multiplicador aplicado al volumen original al enviar la orden de cobertura.
2.0
AutoPlaceInitialTrade
Cuando está habilitado la estrategia envía el primer trade automáticamente al iniciar.
Deshabilitado
InitialVolume
Tamaño de la orden usado por el trade inicial opcional. Redondeado al paso de volumen del instrumento.
0.10
InitialDirection
Lado (compra o venta) usado para el trade inicial opcional.
Compra
Nota: Establecer la propiedad Strategy.Volume al tamaño base de orden que se desea que use la estrategia. Los parámetros anteriores solo controlan el comportamiento específico de cobertura.
Pautas de uso
Asignar un Security, Portfolio y Volume base deseado antes de iniciar la estrategia.
Ajustar LosingPips y LotCoefficient para reflejar la volatilidad y la tolerancia al riesgo del instrumento seleccionado.
Habilitar AutoPlaceInitialTrade si se desea que la versión de StockSharp cree la primera posición automáticamente; de lo contrario, abrir manualmente una pata inicial o dejar que otro componente lo haga.
Debido a que la API de alto nivel de StockSharp trabaja con posiciones netas, la lista de patas interna se usa para emular la estructura de hedge. Monitorear la exposición de la cuenta al ejecutar en cuentas de netting.
Revisar los reportes de ejecución: cada cobertura se coloca con una orden de mercado (BuyMarket o SellMarket).
Diferencias del expert original
La validación de margen, las comprobaciones de slippage y el registro detallado de resultados fueron eliminados; StockSharp ya reporta los problemas de ejecución a través de los eventos de estrategia.
La conversión usa velas terminadas en lugar de datos tick a tick. Elegir un marco temporal suficientemente pequeño si se necesitan tiempos de reacción más rápidos.
El redondeo de lotes ahora se basa en Security.VolumeStep, Security.MinVolume y Security.MaxVolume para mantenerse en conformidad con las reglas de trading del instrumento.
Las alertas, notificaciones y el trade inicial aleatorio solo para el tester de la versión MQL fueron intencionalmente omitidos. El parámetro de entrada automática opcional reemplaza ese comportamiento.
Mejoras recomendadas
Combinar el módulo de cobertura con una estrategia de entrada separada que define cuándo debe crearse la primera posición.
Agregar reglas de cierre basadas en capital o límites de profundidad máxima para prevenir cadenas de cobertura sin límite.
Integrar monitoreo a nivel de portafolio para asegurar que los requerimientos de margen se mantengan dentro de límites aceptables.
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>
/// Hedge Any Positions strategy using ATR-based mean reversion.
/// Enters long when price drops below EMA by ATR threshold, enters short on rally above.
/// Uses stop-loss and take-profit for risk management.
/// </summary>
public class HedgeAnyPositionsStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _ema;
private AverageTrueRange _atr;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// EMA period.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for entry threshold.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="HedgeAnyPositionsStrategy"/> class.
/// </summary>
public HedgeAnyPositionsStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period for mean price", "Indicator");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation period", "Indicator");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 300)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_atr = null;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
_atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_ema, _atr, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_atr.IsFormed)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
var threshold = atrValue * AtrMultiplier;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
}
// Mean reversion: buy when price drops below EMA by threshold
if (close < emaValue - threshold && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
// Mean reversion: sell when price rallies above EMA by threshold
else if (close > emaValue + threshold && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
}
}
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 ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class hedge_any_positions_strategy(Strategy):
"""
Hedge Any Positions: ATR-based mean reversion.
Enters long when price drops below EMA by ATR*multiplier.
Enters short when price rallies above EMA by ATR*multiplier.
Uses SL/TP for risk management with cooldown.
"""
def __init__(self):
super(hedge_any_positions_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 100) \
.SetDisplay("EMA Period", "EMA period for mean price", "Indicator")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR calculation period", "Indicator")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 300) \
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hedge_any_positions_strategy, self).OnReseted()
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(hedge_any_positions_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self._process_candle).Start()
def _process_candle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
close = float(candle.ClosePrice)
ema = float(ema_val)
atr = float(atr_val)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
threshold = atr * self._atr_multiplier.Value
if self.Position > 0 and self._entry_price > 0:
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
return
elif self.Position < 0 and self._entry_price > 0:
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if close < ema - threshold and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif close > ema + threshold and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
def CreateClone(self):
return hedge_any_positions_strategy()