Convertido del experto MetaTrader 5 Exp_VortexIndicator_MMRec_Duplex.mq5 (MQL ID 23180).
Mantiene dos flujos independientes del indicador Vortex: uno dedicado a operaciones largas y otro a operaciones cortas. Cada flujo tiene su propio marco temporal, longitud y desplazamiento de barra para que la lógica alcista y bajista pueda ajustarse de forma independiente.
Replica el módulo de gestión monetaria de recuperación "MMRec" del EA original. La estrategia rastrea los últimos resultados de operaciones por dirección y cambia temporalmente a un tamaño de orden reducido tras un número configurable de pérdidas.
Lógica de señal
Suscribirse al tipo de vela configurado para cada flujo y calcular el indicador Vortex (VI+ y VI-).
Entradas largas: cuando la barra anterior tenía VI+ por debajo o igual a VI- y la barra actual cierra con VI+ por encima de VI- (cruce alcista). Las entradas solo se permiten si AllowLongEntries está activado.
Salidas largas: cuando VI- sube por encima de VI+ en la barra evaluada, siempre que AllowLongExits esté activado.
Entradas cortas: cuando la barra anterior tenía VI+ por encima o igual a VI- y la barra actual cierra con VI+ por debajo de VI- (cruce bajista), controlado por AllowShortEntries.
Salidas cortas: cuando VI+ sube de nuevo por encima de VI- en la barra evaluada, controlado por AllowShortExits.
Cada dirección mantiene sus propios niveles de stop-loss y take-profit medidos en pasos de precio. Alcanzar cualquiera de ellos cierra inmediatamente la posición y registra el resultado para los contadores de recuperación.
Recuperación de gestión monetaria
El EA original inspecciona una ventana deslizante de operaciones pasadas para decidir si la próxima orden debe usar el volumen normal o el reducido. Este port replica el mismo comportamiento.
Para operaciones largas, la cola almacena hasta LongTotalTrigger resultados de PnL más recientes. Si al menos LongLossTrigger de ellos son operaciones perdedoras, la próxima entrada larga usa LongSmallMoneyManagement; de lo contrario usa LongMoneyManagement.
Las operaciones cortas repiten la misma lógica con ShortTotalTrigger, ShortLossTrigger, ShortSmallMoneyManagement y ShortMoneyManagement.
Cuando los valores de activación son cero, las colas se borran y siempre se usa el volumen base.
Modos de margen
MarginModeOption describe cómo el valor de gestión monetaria se convierte en un volumen ejecutable:
FreeMargin (0): tratar el valor como una fracción del capital (aproximación del modo "margen libre" original).
Balance (1): idéntico a FreeMargin en este port; usa el valor actual de la cartera.
LossFreeMargin (2): arriesgar una fracción del capital usando la distancia de stop-loss configurada. Recurre al dimensionamiento basado en precio si la distancia del stop es cero.
LossBalance (3): igual que LossFreeMargin en esta implementación.
Lot (4): interpretar el valor directamente como volumen de orden.
Todos los tamaños calculados se normalizan usando el paso de volumen del instrumento, así como las restricciones de volumen mínimo y máximo.
Parámetros
Parámetro
Predeterminado
Descripción
LongCandleType
H4
Marco temporal usado para el indicador Vortex del lado largo.
ShortCandleType
H4
Marco temporal usado para el indicador Vortex del lado corto.
LongLength
14
Período del indicador Vortex para señales largas.
ShortLength
14
Período del indicador Vortex para señales cortas.
LongSignalBar
1
Desplazamiento de barra cerrada evaluado para cruces largos (0 = última barra cerrada).
ShortSignalBar
1
Desplazamiento de barra cerrada evaluado para cruces cortos.
AllowLongEntries
true
Habilitar entradas largas cuando aparece el cruce alcista.
AllowLongExits
true
Habilitar cierre de posiciones largas cuando VI- domina a VI+.
AllowShortEntries
true
Habilitar entradas cortas cuando aparece el cruce bajista.
AllowShortExits
true
Habilitar cierre de posiciones cortas cuando VI+ domina a VI-.
LongTotalTrigger
5
Número de operaciones largas recientes inspeccionadas por el contador de recuperación.
LongLossTrigger
3
Operaciones largas perdedoras requeridas antes de cambiar al volumen largo reducido.
LongMoneyManagement
0.1
Valor base de gestión monetaria para operaciones largas.
LongSmallMoneyManagement
0.01
Valor reducido de gestión monetaria tras una racha perdedora larga.
LongMarginMode
Lot
Interpretación del valor de gestión monetaria largo (ver modos anteriores).
LongStopLossSteps
1000
Distancia protectora por debajo de la entrada larga expresada en pasos de precio.
LongTakeProfitSteps
2000
Distancia de take-profit por encima de la entrada larga expresada en pasos de precio.
LongSlippageSteps
10
Tolerancia de deslizamiento informativa para órdenes largas (no usada para dimensionamiento).
ShortTotalTrigger
5
Número de operaciones cortas recientes inspeccionadas por el contador de recuperación.
ShortLossTrigger
3
Operaciones cortas perdedoras requeridas antes de cambiar al volumen corto reducido.
ShortMoneyManagement
0.1
Valor base de gestión monetaria para operaciones cortas.
ShortSmallMoneyManagement
0.01
Valor reducido de gestión monetaria tras una racha perdedora corta.
ShortMarginMode
Lot
Interpretación del valor de gestión monetaria corto.
ShortStopLossSteps
1000
Distancia protectora por encima de la entrada corta expresada en pasos de precio.
ShortTakeProfitSteps
2000
Distancia de take-profit por debajo de la entrada corta expresada en pasos de precio.
ShortSlippageSteps
10
Tolerancia de deslizamiento informativa para órdenes cortas.
Notas de implementación
Construido completamente sobre la API de alto nivel de StockSharp. Las suscripciones de velas impulsan los indicadores Vortex a través de Bind, que entrega barras terminadas antes de tomar cualquier decisión.
La lógica de recuperación de operaciones almacena series de beneficios por dirección en colas y replica las funciones BuyTradeMMRecounterS / SellTradeMMRecounterS de MetaTrader.
Los niveles de stop-loss y take-profit se recalculan en unidades de precio (paso de precio del instrumento × pasos configurados) y se aplican en cada vela entrante.
Los volúmenes de orden se normalizan mediante las restricciones de VolumeStep, MinVolume y MaxVolume del instrumento para evitar envíos inválidos.
Los parámetros de deslizamiento se conservan por motivos de documentación, pero no son utilizados directamente por los manejadores de órdenes de StockSharp.
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>
/// Vortex Indicator MMRec Duplex strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class VortexIndicatorMmrecDuplexStrategy : 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 VortexIndicatorMmrecDuplexStrategy()
{
_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 vortex_indicator_mmrec_duplex_strategy(Strategy):
def __init__(self):
super(vortex_indicator_mmrec_duplex_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(vortex_indicator_mmrec_duplex_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(vortex_indicator_mmrec_duplex_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 vortex_indicator_mmrec_duplex_strategy()