MACD No Sample es un port del asesor experto de MetaTrader 5 MACD No Sample. La estrategia combina una verificación de pendiente de media móvil con cruces de línea de señal MACD mientras aplica una amplitud mínima de MACD expresada en pips. Cuando se confirma una configuración alcista, la exposición corta existente se cierra antes de entrar en largo; las configuraciones bajistas hacen lo contrario. La gestión de riesgo refleja el EA original con lógica de stop-loss, take-profit y trailing basados en pips, más un modo opcional de dimensionamiento de posición por porcentaje de riesgo.
Lógica de la estrategia
Preparación del indicador
Filtro de media móvil – una media móvil configurable (SMA, EMA, SMMA o LWMA) aplicada a un precio de vela seleccionable (cierre, apertura, máximo, mínimo, mediano, típico o ponderado). La pendiente (MA[0] > MA[1] o <) define la dirección de tendencia.
Señal MACD – el MACD se calcula a partir de longitudes de EMA rápida/lenta y longitud de señal independientes, usando el precio aplicado elegido. Las líneas MACD y señal brutas se monitorean para detectar cruces frescos y la magnitud absoluta del MACD se compara contra un umbral basado en pips.
Reglas de entrada
Entradas largas
La media móvil está subiendo en la última vela terminada.
El MACD está por debajo de cero pero acaba de cruzar por encima de la línea de señal (MACD actual > señal actual mientras MACD anterior < señal anterior).
El valor absoluto del MACD supera el umbral pip configurado (convertido a unidades de precio mediante el tamaño de pip detectado).
Las posiciones cortas existentes se cierran antes de colocar una orden larga.
Entradas cortas
La media móvil está bajando en la última vela terminada.
El MACD está por encima de cero pero acaba de cruzar por debajo de la línea de señal (MACD actual < señal actual mientras MACD anterior > señal anterior).
El valor absoluto del MACD supera el umbral pip.
Las posiciones largas existentes se cierran antes de colocar una orden corta.
Gestión de salida
Stop-loss / take-profit fijo – distancias de pips opcionales convertidas a desplazamientos de precio desde el precio de entrada. Establecer cualquier parámetro en 0 desactiva el nivel correspondiente.
Trailing stop – se activa cuando la distancia del trailing stop es positiva. La estrategia rastrea el mejor precio alcanzado desde la entrada, desplazando el stop al menos la distancia del paso de trailing (ambos expresados en pips) sin aflojarlo nunca.
Dimensionamiento basado en riesgo (opcional) – cuando está habilitado, el volumen de la orden se deriva del valor del portafolio, la distancia del stop-loss y el porcentaje de riesgo configurado. Los volúmenes se alinean al VolumeStep del instrumento y se restringen por MinVolume/MaxVolume cuando están disponibles.
Notas de implementación
Usa la API de alto nivel a través de SubscribeCandles() con una tubería de indicadores manual dentro del callback ProcessCandle; no se usan llamadas GetValue de indicadores.
Las entradas del indicador respetan las selecciones de precio aplicado y dependen de las implementaciones de media móvil y MACD de StockSharp.
La detección del tamaño de pip refleja la lógica original del EA multiplicando el paso de precio por diez en instrumentos de tres y cinco dígitos.
La lógica de stop y trailing cierra la posición mediante órdenes de mercado cuando se superan los niveles calculados; no se registran órdenes de stop separadas.
Solo se proporciona la implementación en C#; no hay versión en Python para esta estrategia.
Parámetros
Volume – volumen de operación fijo para órdenes de mercado.
Stop Loss (pips) – distancia de stop protector; 0 lo desactiva.
Take Profit (pips) – distancia de objetivo de beneficio; 0 lo desactiva.
Trailing Stop (pips) – distancia de trailing; 0 desactiva el trailing.
Trailing Step (pips) – mejora mínima en pips antes de que se ajuste el trailing stop.
Position Sizing – elegir entre dimensionamiento de volumen fijo y por porcentaje de riesgo.
Risk Percent – porcentaje del portafolio usado cuando el dimensionamiento por riesgo está activo.
MA Period / Method / Price – configuración para el filtro de media móvil.
MACD Fast / Slow / Signal – longitudes de EMA para el MACD.
MACD Price – precio aplicado usado para el cálculo del MACD.
MACD Level (pips) – magnitud mínima absoluta del MACD para validar una operación.
Candle Type – marco temporal que impulsa las actualizaciones del indicador.
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>
/// MACD No Sample strategy using EMA crossover.
/// </summary>
public class MacdNoSampleStrategy : 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 MacdNoSampleStrategy()
{
_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_no_sample_strategy(Strategy):
"""
MACD No Sample: EMA crossover with manual SL/TP via price steps.
"""
def __init__(self):
super(macd_no_sample_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_no_sample_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_no_sample_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_no_sample_strategy()