La estrategia Macd Secrets es un sistema multitemporal de seguimiento de momentum inspirado en el asesor experto original "Macd Secrets I" para MetaTrader. La adaptación a StockSharp usa la API de alto nivel y se centra en alinear la dirección MACD en tres marcos temporales, filtrando operaciones con una línea base de media móvil ponderada lineal (LWMA) y una comprobación de desviación de momentum. La estrategia mantiene solo una posición neta en cada momento, ofreciendo un perfil de riesgo simplificado y transparente frente al EA fuente, que podía piramidar múltiples órdenes.
Generación de señales
Configuración larga
La LWMA rápida está por debajo de la LWMA lenta en el marco temporal de ejecución, lo que indica que el precio opera cerca del lado inferior del canal de tendencia (el EA original aplica el mismo filtro).
La línea MACD está por encima de su línea de señal en todos los marcos temporales rastreados: ejecución, confirmación de tendencia y confirmación mensual. Esto refleja la alineación triple de MACD de la versión MQL.
Al menos una de las tres últimas lecturas de momentum en el marco temporal de tendencia se desvía de 100 por el mínimo configurado (predeterminado 0.3). El cálculo de desviación reproduce la lógica MathAbs(100 - Momentum) del EA.
No hay ninguna posición abierta.
Cuando se cumplen las condiciones, se coloca una orden de compra a mercado con el volumen configurado.
Configuración corta
La línea MACD está por debajo de su línea de señal en los marcos temporales de ejecución, tendencia y mensual.
Al menos una de las tres últimas desviaciones de momentum en el marco temporal de tendencia supera el umbral corto configurado.
No hay ninguna posición abierta (la adaptación evita cobertura y escalado).
Si todas las reglas se mantienen, se envía una orden de venta a mercado.
Gestión de operaciones
La estrategia puede iniciar opcionalmente órdenes de protección usando distancias basadas en puntos para stop-loss y take-profit. Estas distancias se multiplican por el paso de precio del valor para convertir puntos en incrementos de precio.
No se incluye lógica de trailing-stop, breakeven ni protección basada en equity del EA original; la protección de StockSharp se aplica una vez al inicio.
Las señales se evalúan solo en velas terminadas para evitar ruido intrabar.
Datos multitemporales
Marco temporal principal: frecuencia de ejecución (predeterminado 15 minutos). MACD y el par de LWMAs se calculan aquí.
Marco temporal de tendencia: confirmación de marco temporal superior (predeterminado 1 hora). Tanto MACD como momentum se ejecutan en esta suscripción. Las desviaciones de momentum se recopilan de las tres últimas velas cerradas.
Marco temporal mensual: confirmación MACD de largo plazo (predeterminado 30 días para aproximar un mes calendario).
La estrategia sobrescribe GetWorkingSecurities para que las tres suscripciones se soliciten al conector desde el principio.
Parámetros
Nombre
Descripción
Predeterminado
OrderVolume
Volumen de operación en lotes. Debe ser positivo.
0.1
TakeProfitPoints
Distancia de take-profit medida en puntos. Establecer en cero para desactivar.
50
StopLossPoints
Distancia de stop-loss en puntos. Establecer en cero para desactivar.
20
FastMaPeriod
Longitud de LWMA rápida en el marco temporal principal.
6
SlowMaPeriod
Longitud de LWMA lenta en el marco temporal principal.
85
MacdFastPeriod
Período de EMA rápida usado por cada instancia MACD.
12
MacdSlowPeriod
Período de EMA lenta usado por cada instancia MACD.
26
MacdSignalPeriod
Período de EMA de señal para MACD.
9
MomentumPeriod
Período retrospectivo de momentum en el marco temporal de tendencia.
14
MomentumBuyThreshold
Desviación absoluta mínima desde 100 requerida para operaciones largas.
0.3
MomentumSellThreshold
Desviación absoluta mínima desde 100 requerida para operaciones cortas.
0.3
PrimaryCandleType
Tipo de vela para ejecución. Predeterminado: marco temporal de 15 minutos.
15m
TrendCandleType
Tipo de vela para confirmación. Predeterminado: marco temporal de 1 hora.
1h
MonthlyCandleType
Tipo de vela para confirmación de largo plazo. Predeterminado: barra de 30 días.
30d
Notas de uso
El filtro LWMA es intencionalmente asimétrico: solo las operaciones largas requieren que la LWMA rápida esté por debajo de la LWMA lenta, coincidiendo con el comportamiento observado en el script MQL.
Como la adaptación opera una sola posición neta, omite el dimensionamiento de posición estilo martingala presente en el código fuente (LotsOptimized). Si se necesita apilamiento, puede reintroducirse rastreando el volumen ejecutado y comparándolo con OrderVolume.
Asegúrese de que el bróker o proveedor de datos conectado pueda proporcionar los tres marcos temporales de velas; de lo contrario, la estrategia permanecerá inactiva esperando la formación de indicadores.
Considere ajustar el marco temporal mensual para mercados donde no haya velas de 30 días disponibles, suministrando un parámetro DataType personalizado.
La estrategia opera completamente sobre velas cerradas y no lee directamente buffers históricos de indicadores, cumpliendo las directrices de uso de indicadores de StockSharp.
Diferencias frente al EA original
No se adaptan trailing-stop, breakeven, salidas basadas en dinero ni protección de equity de toda la cuenta. En su lugar se usa la protección de StockSharp con distancias estáticas.
La piramidación de órdenes y la lógica martingala se omiten por claridad. El dimensionamiento de posición permanece constante.
Las notificaciones (alertas, correos electrónicos, mensajes push) no están implementadas.
Aviso
El trading algorítmico implica un riesgo financiero significativo. Pruebe la estrategia con datos históricos y en un entorno simulado antes de desplegarla con capital real.
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 MacdSecretsStrategy : 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 MacdSecretsStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12).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 macd_secrets_strategy(Strategy):
"""
MACD Secrets: EMA crossover with manual SL/TP via price steps.
"""
def __init__(self):
super(macd_secrets_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12).SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50).SetDisplay("Slow Period", "Slow EMA 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._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_secrets_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(macd_secrets_strategy, self).OnStarted2(time)
self._fast_ind = ExponentialMovingAverage()
self._fast_ind.Length = self._fast_period.Value
self._slow_ind = ExponentialMovingAverage()
self._slow_ind.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ind, self._slow_ind, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
if not self._fast_ind.IsFormed or not self._slow_ind.IsFormed:
self._prev_fast = fast
self._prev_slow = slow
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast
self._prev_slow = slow
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.Value > 0 and close <= self._entry_price - self._stop_loss_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close >= self._entry_price + self._take_profit_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
elif self.Position < 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close >= self._entry_price + self._stop_loss_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close <= self._entry_price - self._take_profit_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._prev_fast <= self._prev_slow and fast > slow 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 < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return macd_secrets_strategy()