Este ejemplo convierte el MetaTrader 4 asesor experto original MACD_v1.mq4 en una estrategia de alto nivel StockSharp. El algoritmo rastrea los cruces de convergencia y divergencia del promedio móvil (MACD) y opera en la dirección de la nueva tendencia. Las salidas protectoras opcionales replican el comportamiento del asesor original: un stop-loss, una toma de ganancias distante y un objetivo de ganancias parcial que liquida la mitad de la posición actual.
Lógica de trading
Fuente de datos: la estrategia se suscribe a la serie de velas configuradas (velas de 5 minutos de forma predeterminada) y vincula un indicador MovingAverageConvergenceDivergenceSignal.
Condiciones de entrada:
Ingrese long cuando la línea MACD cruce por encima de la línea de señal. Si una posición corta está activa, se cierra antes de abrir la larga.
Ingrese short cuando la línea MACD cruce por debajo de la línea de señal. Si existe una posición larga, se cierra primero.
Condiciones de salida:
El cruce opuesto MACD cierra la posición actual y abre una nueva posición en la dirección opuesta.
Una toma de ganancias y un stop-loss configurables administrados por StartProtection reflejan los parámetros originales de TP/SL (medidos en puntos del instrumento).
Un objetivo de beneficio parcial cierra la mitad de la posición abierta una vez que el precio avanza una cantidad específica desde el nivel de entrada. La salida parcial se activa sólo una vez por posición.
Parámetros
Nombre
Predeterminado
Descripción
Período rápido
23
Longitud rápida de EMA para el cálculo MACD (refleja el parámetro MQL a = 2300).
Período lento
40
Longitud lenta de EMA para el cálculo MACD (b = 4000 en la fuente).
Período de señal
8
Longitud de la línea de señal (c = 800 en la fuente).
Obtener ganancias
500
Distancia en puntos de precio para la orden protectora de toma de ganancias. Establezca en 0 para desactivar.
Detener pérdidas
80
Distancia en puntos de precio para la orden protectora de stop-loss. Establezca en 0 para desactivar.
Beneficio parcial
70
Distancia en puntos de precio para cerrar la mitad de la posición abierta. Establezca en 0 para deshabilitar las salidas parciales.
Tipo de vela
marco de tiempo de 5 minutos
Serie de velas utilizadas para los cálculos de indicadores.
Notas
Los parámetros del indicador se escalaron a longitudes típicas de MACD (23/40/8) porque el script MQL los expresaba como centésimas (2300/4000/800).
La estrategia restaura automáticamente la bandera de salida parcial cada vez que se acumula una nueva posición.
Los ayudantes de dibujo de gráficos resaltan velas, valores MACD y las operaciones de la estrategia cuando hay un panel de gráfico disponible.
El manejo de volúmenes se basa en la propiedad de la estrategia base Volume. Ajústelo antes de comenzar la estrategia para que coincida con el tamaño de su instrumento.
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 MacdSignalCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<DataType> _candleType;
private bool _prevMacdAboveSignal;
private bool _hasPrev;
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public MacdSignalCrossoverStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 23);
_slowPeriod = Param(nameof(SlowPeriod), 40);
_signalPeriod = Param(nameof(SignalPeriod), 8);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame());
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacdAboveSignal = false;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastPeriod },
LongMa = { Length = SlowPeriod },
},
SignalMa = { Length = SignalPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFinal)
return;
var macdTyped = macdValue as MovingAverageConvergenceDivergenceSignalValue;
if (macdTyped == null)
return;
var macdLine = macdTyped.Macd;
var signalLine = macdTyped.Signal;
if (macdLine == null || signalLine == null)
return;
var isMacdAboveSignal = macdLine.Value > signalLine.Value;
if (!_hasPrev)
{
_hasPrev = true;
_prevMacdAboveSignal = isMacdAboveSignal;
return;
}
var crossedAbove = isMacdAboveSignal && !_prevMacdAboveSignal;
var crossedBelow = !isMacdAboveSignal && _prevMacdAboveSignal;
if (crossedAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (crossedBelow)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevMacdAboveSignal = isMacdAboveSignal;
}
}
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 MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class macd_signal_crossover_strategy(Strategy):
"""Simple MACD/signal line crossover strategy. Buys when MACD crosses above signal,
sells when MACD crosses below signal."""
def __init__(self):
super(macd_signal_crossover_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 23) \
.SetDisplay("Fast Period", "Fast EMA period for MACD", "Indicators")
self._slow_period = self.Param("SlowPeriod", 40) \
.SetDisplay("Slow Period", "Slow EMA period for MACD", "Indicators")
self._signal_period = self.Param("SignalPeriod", 8) \
.SetDisplay("Signal Period", "Signal line period for MACD", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle type used for analysis", "General")
self._prev_macd_above_signal = False
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def SignalPeriod(self):
return self._signal_period.Value
def OnReseted(self):
super(macd_signal_crossover_strategy, self).OnReseted()
self._prev_macd_above_signal = False
self._has_prev = False
def OnStarted2(self, time):
super(macd_signal_crossover_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self.FastPeriod
macd.Macd.LongMa.Length = self.SlowPeriod
macd.SignalMa.Length = self.SignalPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(macd, self._process_candle).Start()
def _process_candle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFinal:
return
macd_line = macd_value.Macd
signal_line = macd_value.Signal
if macd_line is None or signal_line is None:
return
is_macd_above_signal = float(macd_line) > float(signal_line)
if not self._has_prev:
self._has_prev = True
self._prev_macd_above_signal = is_macd_above_signal
return
crossed_above = is_macd_above_signal and not self._prev_macd_above_signal
crossed_below = not is_macd_above_signal and self._prev_macd_above_signal
if crossed_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif crossed_below:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_macd_above_signal = is_macd_above_signal
def CreateClone(self):
return macd_signal_crossover_strategy()