Esta estrategia es un port en C# del asesor experto de MetaTrader "MACD 1 MIN SCALPER". Combina medias móviles ponderadas con confirmaciones de MACD en múltiples marcos temporales y un filtro de momentum antes de abrir operaciones. El objetivo es operar en la dirección de la tendencia cuando los indicadores de marcos temporales inferior y superior están alineados y el momentum del precio es suficientemente fuerte.
Lógica de trading
Marco temporal base – configurable (por defecto M1). Dos medias móviles ponderadas (WMA) con períodos 50 y 200, calculadas sobre el precio típico (Máximo + Mínimo + Cierre) / 3, definen la tendencia a corto plazo.
Filtro de tendencia de marco temporal superior – se calculan WMAs con los mismos períodos en el marco temporal H1. Las configuraciones largas requieren que ambas WMAs rápidas estén por encima de sus homólogas lentas, las cortas requieren lo opuesto. Si el marco temporal operativo ya es H1, las WMAs base se reutilizan.
Confirmaciones MACD – el MACD (12, 26, 9) debe tener su línea principal por encima de la línea de señal en el marco temporal base, el marco temporal H1 y un marco temporal mensual (aprox. 43200 minutos). Las entradas cortas requieren que los tres MACDs estén por debajo de sus señales.
Filtro de momentum – un indicador de momentum con período 14 opera en un marco temporal superior derivado del período base de MetaTrader (M1→M15, M5→M30, …). La desviación absoluta desde 100 debe superar un umbral configurable en al menos una de las últimas tres barras completadas.
Reglas de entrada – se abre una posición larga cuando se cumplen todas las condiciones alcistas y la estrategia actualmente no tiene exposición larga. Una posición corta requiere las condiciones bajistas reflejadas. Si hay una posición opuesta abierta, el tamaño de la orden incluye automáticamente la cantidad necesaria para cerrarla.
Gestión de riesgo – las distancias opcionales de stop-loss y take-profit se especifican en pips y se convierten a puntos del instrumento al inicio. Las funciones de trailing, breakeven y gestión monetaria del script original se omiten intencionalmente en este port de alto nivel.
Parámetros
Parámetro
Descripción
CandleType
Marco temporal operativo para los indicadores base.
OrderVolume
Volumen enviado con cada entrada de mercado. También se usa para cerrar/voltear posiciones.
FastMaPeriod / SlowMaPeriod
Longitudes de las medias móviles ponderadas rápida y lenta.
Longitud del indicador de momentum en el marco temporal de confirmación.
MomentumThreshold
Desviación absoluta mínima desde 100 requerida para aceptar el momentum.
TakeProfitPips / StopLossPips
Niveles protectores opcionales especificados en pips.
Notas de implementación
La estrategia depende de las suscripciones de velas de alto nivel de StockSharp (SubscribeCandles) y la vinculación de indicadores (Bind / BindEx). No se usan cálculos de indicadores manuales o buffers históricos.
El marco temporal de momentum se deriva del mapeo de MetaTrader: [1,5,15,30,60,240,1440,10080,43200]. Si un valor cae fuera de esta lista, se usa un multiplicador 4× del marco temporal base como respaldo.
StartProtection se lanza solo cuando al menos uno de los parámetros de riesgo es mayor que cero. No hay implementación de trailing stop en este port.
La representación en gráficos está habilitada para las velas base, ambas WMAs y el MACD para facilitar la inspección visual durante la depuración o el trading en vivo.
Consejos de uso
Establezca el parámetro OrderVolume según el tamaño mínimo de lote del instrumento. El ayudante ajusta automáticamente el volumen enviado para que coincida con el paso y las restricciones de mínimo/máximo del símbolo.
Asegúrese de que los datos de marcos temporales superiores (H1 y mensual) estén disponibles en el feed de datos. Sin estas velas, la estrategia no abrirá posiciones porque las señales de confirmación permanecen incompletas.
El filtrado de momentum es sensible al umbral elegido. Los valores más altos exigen impulsos de momentum más fuertes mientras que los valores más bajos resultan en operaciones más frecuentes.
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 Macd1MinScalperStrategy : 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 Macd1MinScalperStrategy()
{
_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_1_min_scalper_strategy(Strategy):
def __init__(self):
super(macd_1_min_scalper_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400).SetNotNegative().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_1_min_scalper_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_1_min_scalper_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_1_min_scalper_strategy()