Esta estrategia reproduce el asesor experto original Universum_3_0 MQL4 utilizando la API de alto nivel de StockSharp.
Combina un modelo de entrada de umbral simple DeMarker con una regla de tamaño de posición similar a una martingala que se adapta
tamaño del lote después de perder operaciones.
Lógica de trading
Indicador: oscilador DeMarker clásico con período configurable.
Generación de señal:
Abra una posición larga cuando DeMarker > 0.5 esté al cierre de una vela terminada.
Abra una posición corta cuando DeMarker < 0.5 esté al cierre de una vela terminada.
Sólo puede haber una posición activa a la vez; Las nuevas señales se ignoran mientras una operación está abierta.
Gestión de salida:
Los niveles protectores de stop-loss y take-profit se fijan utilizando compensaciones de precios absolutas medidas en puntos.
Las posiciones se cierran automáticamente mediante estos niveles de protección; la estrategia no cambia inmediatamente.
Gestión del dinero:
Después de una operación rentable, el volumen se restablece al lote base.
Después de una operación perdedora, el volumen se multiplica por (TakeProfitPoints + StopLossPoints) / (TakeProfitPoints - SpreadPoints).
El valor del diferencial se toma de cotizaciones activas de Nivel 1 y se convierte en "puntos" utilizando precisión de símbolo.
Se cuentan las pérdidas consecutivas; alcanzar el límite detiene la estrategia para emular la protección contra pérdidas original.
La configuración FastOptimize = true deshabilita la regla de tamaño adaptable y siempre usa el lote base, lo que acelera las optimizaciones.
Parámetros
Parámetro
Descripción
Predeterminado
CandleType
Marco de tiempo utilizado para los cálculos de DeMarker.
marco de tiempo de 1 minuto
DemarkerPeriod
Período retrospectivo del oscilador DeMarker.
10
TakeProfitPoints
Distancia de obtención de beneficios expresada en puntos (convertida internamente a precio absoluto).
50
StopLossPoints
Distancia de stop-loss expresada en puntos.
50
BaseVolume
Volumen de operaciones inicial utilizado después de cada operación rentable.
1
LossesLimit
Número máximo de pérdidas consecutivas antes de que se detenga la estrategia.
1,000,000
FastOptimize
Cuando true desactiva el tamaño adaptable para pases de optimización rápidos.
true
Notas de implementación
Se requieren datos de Nivel 1 para estimar el diferencial actual y replicar el multiplicador del lote original.
La normalización del volumen respeta el volumen mínimo, el volumen máximo y el tamaño del paso del instrumento.
Las compensaciones de stop-loss y take-profit se adaptan automáticamente a instrumentos de 3/5 dígitos ajustando el tamaño del punto.
La visualización del gráfico traza velas, el indicador DeMarker y las operaciones ejecutadas para una validación más sencilla.
Consejos de uso
Proporcione datos de oferta/demanda de Nivel 1 además de velas para garantizar que el multiplicador basado en diferenciales funcione correctamente.
Utilice FastOptimize = true durante búsquedas generales de parámetros y luego desactívelo para realizar pruebas retrospectivas precisas y operaciones en vivo.
Supervise el contador de pérdidas consecutivas cuando utilice multiplicadores agresivos para evitar exceder los límites del corredor.
Ajuste TakeProfitPoints y StopLossPoints para que coincidan con el símbolo original o su perfil de riesgo antes de operar en vivo.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Universum30OriginalStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRsi;
private bool _hasPrev;
private int _cooldownRemaining;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Universum30OriginalStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI lookback", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA trend", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 30).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevRsi = rsi; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevRsi = rsi;
return;
}
if (_prevRsi <= 30 && rsi > 30 && close > ema && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevRsi >= 70 && rsi < 70 && close < ema && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevRsi = rsi;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class universum30_original_strategy(Strategy):
def __init__(self):
super(universum30_original_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 30) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_rsi = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def ema_period(self):
return self._ema_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(universum30_original_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(universum30_original_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._has_prev = False
self._cooldown_remaining = 0
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, ema, self.process_candle).Start()
def process_candle(self, candle, rsi, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
rsi_val = float(rsi)
ema_val = float(ema)
if not self._has_prev:
self._prev_rsi = rsi_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_rsi = rsi_val
return
if self._prev_rsi <= 30 and rsi_val > 30 and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_rsi >= 70 and rsi_val < 70 and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_rsi = rsi_val
def CreateClone(self):
return universum30_original_strategy()