La Estrategia de Random Hedg es un puerto de alto nivel de StockSharp del experto asesor MetaTrader "Random Hedg". El EA original abre simultáneamente una orden de compra de mercado y una de venta de mercado, luego gestiona ambas posiciones con una combinación de stop loss fijo, take profit, punto de equilibrio y lógica de trailing. La conversión mantiene ese comportamiento central mientras expone cada configuración como parámetro de estrategia para que el bot pueda ajustarse u optimizarse directamente dentro de StockSharp Designer.
Lógica de trading
Cobertura inicial – cuando la estrategia está sin posición, envía de inmediato dos órdenes de mercado (compra y venta) usando el mismo volumen configurable. Ambas posiciones reciben un stop loss y un take profit expresados en pips.
Protección de punto de equilibrio – tras un movimiento de precio favorable por la cantidad configurada de pips, el nivel de stop se desplaza al punto de equilibrio más un desplazamiento opcional (posiciones largas) o menos el desplazamiento (posiciones cortas). Esto imita el interruptor "mover a sin pérdida" del EA.
Trailing stop – una vez que el beneficio supera la distancia de trailing, el stop sigue al precio. Para posiciones largas, el stop sigue el precio más alto menos la distancia de trailing; para cortas, sigue el precio más bajo más la distancia.
Salidas protectoras – cada posición se cierra cuando se toca su take profit o stop loss. Opcionalmente, la estrategia puede liquidar ambas posiciones si una vela cierra por debajo de la Banda de Bollinger inferior, recreando el filtro de salida del código original.
Reinicio de ciclo – una vez que ambas posiciones están cerradas, la estrategia restablece sus rastreadores internos y espera la siguiente vela para abrir un nuevo par cubierto.
Parámetros
HedgeVolume – volumen utilizado para abrir ambas posiciones de cobertura (por defecto 0.1 contratos).
StopLossPips – distancia del stop loss protector (por defecto 200 pips).
TakeProfitPips – distancia del take profit (por defecto 200 pips).
TrailingStopPips – paso de trailing aplicado cuando una posición se vuelve rentable (por defecto 40 pips).
BreakEvenTriggerPips – beneficio requerido antes de mover el stop al punto de equilibrio (por defecto 10 pips).
BreakEvenOffsetPips – beneficio adicional asegurado cuando ocurre el desplazamiento al punto de equilibrio (por defecto 5 pips).
EnableTrailing – activa o desactiva la gestión del trailing stop.
EnableBreakEven – activa o desactiva la función de punto de equilibrio.
EnableExitStrategy – activa el filtro de liquidación basado en Bandas de Bollinger.
BollingerPeriod – período de las Bandas de Bollinger usadas para la salida opcional (por defecto 20 velas).
BollingerWidth – multiplicador de amplitud de las Bandas de Bollinger (por defecto 2).
CandleType – serie de datos de velas usada para ejecutar la lógica (por defecto marco temporal de 30 minutos).
Notas de implementación
La conversión usa la API de alto nivel Strategy con suscripciones a velas y el mecanismo BindEx para calcular las Bandas de Bollinger al vuelo.
El estado interno rastrea el precio de entrada, volumen y niveles protectores dinámicos para cada posición. Esto permite que la versión C# imite los asistentes de gestión monetaria del EA original sin depender de manejadores de órdenes específicos de la plataforma.
Los volúmenes de órdenes pendientes se rastrean por separado para que las ejecuciones puedan clasificarse como entradas o salidas incluso cuando las operaciones de compra y venta ocurren consecutivamente.
La estrategia espera una cuenta con capacidad de cobertura porque mantiene exposición larga y corta al mismo tiempo, igual que el experto asesor fuente.
Las funciones de trailing basado en dinero y take profit por porcentaje del código MQL se omiten intencionalmente. Dependen de datos de balance específicos del broker y rara vez se usaban en la práctica; la versión de StockSharp se centra en la gestión principal de acción del precio.
Archivos
CS/RandomHedgStrategy.cs – implementación principal en C# con comentarios en línea detallados en inglés.
README.md – esta documentación (inglés).
README_ru.md – traducción al ruso.
README_zh.md – traducción al chino simplificado.
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;
public class RandomHedgStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public RandomHedgStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class random_hedg_strategy(Strategy):
def __init__(self):
super(random_hedg_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(random_hedg_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(random_hedg_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return random_hedg_strategy()