Origen: MetaTrader 4 asesores expertos et4_MTC_v1.mq4 de la colección GlobeInvestFund.
Propósito: Proporcionar una plantilla nativa StockSharp que refleje los asistentes de administración de dinero y las salvaguardas de tiempo del asesor original, al tiempo que deja la lógica de entrada/salida comercial abierta para un mayor desarrollo.
Estilo de negociación: estrategia esqueleto: no se generan entradas automáticas de forma predeterminada. La clase se centra en hacer cumplir las restricciones de tiempo y replicar la interfaz de parámetros del script MQL4 para que pueda servir como base para reglas personalizadas.
Características principales
Paridad de parámetros
Expone propiedades TakeProfit, StopLoss, Slippage, Lots y EnableLogging que se asignan uno a uno con las variables externas del experto.
Agrega TradeCooldown para describir el retraso codificado de 30 segundos entre operaciones en el código fuente.
Publica el contexto de datos del gráfico a través de CandleType para emular el comportamiento del "período de tiempo actual" de los gráficos MetaTrader.
Tamaño de posición basado en el equilibrio
Admite entradas de lotes negativos (valor predeterminado del script original) para derivar el volumen del pedido del saldo de la cuenta: floor((balance / 1000 * |Lots|) / 10) / 10, con un mínimo de 0,1 lote.
Aplicación del enfriamiento del comercio
Bloquea cualquier intento de negociación adicional hasta que transcurra TradeCooldown después de la actividad de orden más reciente (registro, modificación, cancelación o transacción completada). Esto refleja el guardia CurTime() - LastTradeTime < 30 en start().
Detección de nueva vela
Mantiene la semántica CheckLevels marcando IsNewCandle mediante una comparación de tiempo entre velas terminadas posteriores. Si bien la bandera es interna, los enlaces en OpenPosition, ManagePosition y ClosePosition pueden usarla cuando se agrega lógica personalizada.
Uso de alto nivel de StockSharp API
Utiliza SubscribeCandles().Bind(...) para la entrega de datos.
Aplica StartProtection() una vez al inicio, siguiendo las mejores prácticas del marco.
No asigna colecciones personalizadas ni solicita explícitamente el historial de indicadores, alineándose con las pautas de todo el proyecto.
Referencia de parámetros
Propiedad
Predeterminado
Optimizable
Descripción
TakeProfit
150
✔️
Distancia objetivo en puntos (marcador de posición para reglas de salida personalizadas).
Lots
-10
✔️
Lotes fijos cuando ≥ 0; Dimensionamiento proporcional al equilibrio cuando es negativo.
StopLoss
50
✔️
Distancia de parada en puntos, lista para lógica de extensión.
Slippage
3
✖️
Tolerancia de ejecución en puntos; conservado por compatibilidad.
EnableLogging
false
✖️
Imprime mensajes informativos cuando el tiempo de reutilización bloquea los intercambios.
TradeCooldown
30 segundos
✖️
Retraso mínimo entre operaciones consecutivas.
CandleType
Velas con marco de tiempo de 1 minuto
✖️
Suscripción a datos de mercado utilizada para sincronizar las velas.
Flujo de ejecución
Inicio
Calcula el Volume inicial utilizando el asistente de tamaño que tiene en cuenta el saldo.
Se suscribe al flujo de velas configurado e inicia mecanismos de protección.
Al cerrar la vela
Confirma que la vela ha terminado antes de continuar (equivalente al cierre de Time[0] en MT4).
Actualiza el rastreador de nuevas velas (_isNewCandle).
Comprueba IsFormedAndOnlineAndAllowTrading() para respetar el estado del motor.
Se cancela si el tiempo de reutilización del comercio está activo y registra el próximo tiempo disponible cuando está habilitado.
Ejecuta ganchos de marcador de posición (OpenPosition, ManagePosition, ClosePosition) y regresa antes de tiempo cuando cualquier paso realiza una acción.
Devoluciones de llamadas de pedidos e intercambios
OnOrderRegistered, OnOrderChanged, OnOrderCanceled y OnNewMyTrade actualizan _lastTradeTime, lo que garantiza que cada tipo de operación restablezca el tiempo de reutilización tal como lo hicieron las funciones contenedoras (MOrderSend, MOrderModify, etc.) en el código original.
Ampliando la plantilla
Implemente la lógica de entrada dentro de OpenPosition (devuelva true después de enviar órdenes para detener el procesamiento adicional en la misma vela).
Inserte el comportamiento de gestión de parada dentro de ManagePosition utilizando los parámetros conservados.
Complete ClosePosition con reglas de salida. Actualmente, el método devuelve false para coincidir con el comportamiento inactivo del script fuente.
Utilice _isNewCandle si las reglas deben activarse una vez por barra.
Notas de portabilidad
El experto MQL4 envió sin reglas comerciales; sólo estaban presentes rutinas de infraestructura. En consecuencia, la conversión StockSharp prioriza la paridad de las funciones de soporte en lugar de agregar indicadores especulativos.
Todos los comentarios están escritos en inglés, cumpliendo con los estándares del repositorio.
Las tabulaciones se utilizan para que la sangría coincida con las pautas de estilo definidas en AGENTS.md.
La traducción de Python se omite intencionalmente según la solicitud de conversión.
Pasos de uso
Haga referencia a Et4MtcV1Strategy en un proyecto StockSharp y asigne un Security y Portfolio antes de comenzar.
Ajuste Lots u otros parámetros a través de las propiedades proporcionadas o enlaces de interfaz de usuario.
Anule los métodos de marcador de posición o herede de la clase para inyectar una lógica comercial concreta.
Ejecute la estrategia; el protector de enfriamiento garantiza que no haya operaciones consecutivas dentro del intervalo especificado.
Pruebas
No hay pruebas automatizadas que acompañen a esta plantilla porque la fuente ascendente también carecía de reglas ejecutables. Las extensiones de estrategias manuales deberían introducir pruebas relevantes cuando se implemente un comportamiento comercial concreto.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Et4MtcV1Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMom;
private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Et4MtcV1Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var mom = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, mom, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal mom)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevMom = mom; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevMom = mom;
return;
}
if (close > ema && _prevMom <= 0 && mom > 0 && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 2;
}
else if (close < ema && _prevMom >= 0 && mom < 0 && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 2;
}
_prevMom = mom;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, Momentum
from StockSharp.Algo.Strategies import Strategy
class et4_mtc_v1_strategy(Strategy):
"""
EMA + Momentum crossover strategy.
Buys when price > EMA and momentum crosses above 0.
Sells when price < EMA and momentum crosses below 0.
"""
def __init__(self):
super(et4_mtc_v1_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetDisplay("Momentum", "Momentum period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_mom = 0.0
self._has_prev = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(et4_mtc_v1_strategy, self).OnReseted()
self._prev_mom = 0.0
self._has_prev = False
self._cooldown = 0
def OnStarted2(self, time):
super(et4_mtc_v1_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
mom = Momentum()
mom.Length = self._momentum_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, mom, self._process_candle).Start()
def _process_candle(self, candle, ema_val, mom_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
ema_val = float(ema_val)
mom_val = float(mom_val)
if not self._has_prev:
self._prev_mom = mom_val
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_mom = mom_val
return
if close > ema_val and self._prev_mom <= 0 and mom_val > 0 and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume)
self._cooldown = 2
elif close < ema_val and self._prev_mom >= 0 and mom_val < 0 and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume)
self._cooldown = 2
self._prev_mom = mom_val
def CreateClone(self):
return et4_mtc_v1_strategy()