MACD + Stochastic Estrategia de filtro de tendencias
Esta estrategia recrea el comportamiento del asesor experto MetaTrader de la carpeta MQL/7604. El script original se basaba en un oscilador personalizado que producía buffers verdes y rojos. En la práctica, los números (15, 3, 3) coinciden con un oscilador estocástico clásico, por lo tanto, el puerto StockSharp utiliza el indicador incorporado Stochastic para la confirmación de la señal, mientras que MACD y un filtro de tendencia EMA administran la dirección.
La estrategia opera tanto en largo como en corto. Espera un cruce estocástico en la dirección de la operación, requiere que el histograma MACD cruce su línea de señal a una distancia suficiente de cero y exige que la pendiente EMA concuerde con la entrada. La gestión de riesgos refleja la versión MQL: un stop-loss fijo, una toma de ganancias y un trailing stop basado en puntos que refuerza el nivel de protección tan pronto como la operación genera ganancias.
Indicadores
- MovingAverageConvergenceDivergenceSignal con parámetros
fast = 12, slow = 26, signal = 9. El histograma MACD debe cruzar su línea de señal mientras permanece por debajo de cero para configuraciones largas y por encima de cero para configuraciones cortas. Los umbrales adicionales (MacdOpenLevel, MacdCloseLevel) imponen una distancia absoluta mínima desde la línea cero.
- Stochastic oscilador con
(Length = 15, KPeriod = 3, DPeriod = 3). La línea %K desempeña el papel de amortiguador "verde" y debe estar por encima de %D para operaciones largas (por debajo para operaciones cortas). El mismo cruce se utiliza para salir de posiciones.
- ExponentialMovingAverage con período
26. El EMA proporciona un filtro direccional: para una operación larga, el valor actual EMA debe estar por encima del EMA de la barra anterior, y a la inversa, para una operación corta.
Lógica de entrada
- Configuración larga
- Stochastic %K > %D en la vela cerrada actual.
- MACD histograma < 0 y > línea de señal en la barra actual.
- MACD histograma <línea de señal en la barra anterior (es decir, cruce alcista ahora).
|MACD| > MacdOpenLevel * price_step.
- EMA en aumento (} actual > } anterior).
- Configuración corta
- Stochastic %K < %D en la vela actual.
- MACD histograma > 0 y < línea de señal en la barra actual.
- MACD histograma > línea de señal en la barra anterior (cruce bajista ahora).
MACD > MacdOpenLevel * price_step.
- EMA cayendo (} actual < anterior EMA).
Si la cuenta ya tiene una posición, no se generan nuevas órdenes hasta que se cierre la operación abierta.
Salir de la lógica
Mientras una posición está abierta, la estrategia aplica continuamente:
- Salida del indicador
- Las posiciones largas se cierran cuando
%K < %D, MACD > 0, MACD < señal, el MACD anterior estaba por encima de su señal y el histograma absoluto excede MacdCloseLevel * price_step.
- Las posiciones cortas se cierran cuando
%K > %D, MACD < 0, MACD > señal, el MACD anterior estaba por debajo de su señal y |MACD| > MacdCloseLevel * price_step.
- Stop-loss: configurado por
StopLossPoints, convertido en unidades de precio a través del PriceStep del instrumento.
- Take-profit:
TakeProfitPoints multiplicado por PriceStep.
- Trailing stop: una vez que la ganancia excede
TrailingStopPoints * PriceStep, el nivel de parada aumenta (para largos) o baja (para cortos) para que la operación siempre bloquee al menos esa cantidad de ganancias.
Parámetros
| Nombre |
Descripción |
Predeterminado |
TradeVolume |
Tamaño del pedido en lotes |
0.1 |
TakeProfitPoints |
Distancia de toma de ganancias en puntos |
10 |
StopLossPoints |
Distancia de stop-loss en puntos |
50 |
TrailingStopPoints |
Distancia del trailing stop en puntos |
5 |
MacdOpenLevel |
Valor mínimo absoluto MACD para entradas |
3 |
MacdCloseLevel |
Valor mínimo absoluto MACD para salidas |
2 |
MacdFastPeriod |
EMA rápida longitud dentro de MACD |
12 |
MacdSlowPeriod |
Longitud lenta de EMA dentro de MACD |
26 |
MacdSignalPeriod |
MACD señal EMA longitud |
9 |
EmaPeriod |
EMA período para el filtro de tendencias |
26 |
StochasticLength |
Stochastic ventana retrospectiva |
15 |
StochasticKPeriod |
%K suavizado |
3 |
StochasticDPeriod |
%D suavizado |
3 |
CandleType |
Plazo utilizado para los cálculos |
15m |
Notas
- Todos los cálculos utilizan únicamente velas terminadas, que coinciden con el bucle
start() en el EA original.
- El
PriceStep suministrado por el instrumento define un punto. Cuando la seguridad no expone un paso, la estrategia vuelve a 1.
- El código se basa exclusivamente en el nivel alto API de StockSharp: los indicadores están vinculados a través de
SubscribeCandles().BindEx(...), no se crean buffers de historial manuales y los pedidos usan BuyMarket/SellMarket como en la versión MQL.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD strategy with stochastic confirmation and EMA trend filter.
/// Buy when MACD crosses above signal with stochastic K > D and price above EMA.
/// Sell when MACD crosses below signal with stochastic K < D and price below EMA.
/// </summary>
public class MacdStochasticFilterStrategy : Strategy
{
private readonly StrategyParam<int> _macdFastPeriod;
private readonly StrategyParam<int> _macdSlowPeriod;
private readonly StrategyParam<int> _macdSignalPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _stochKLength;
private readonly StrategyParam<int> _stochDLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevMacd;
private decimal? _prevSignal;
/// <summary>
/// Fast EMA period inside MACD.
/// </summary>
public int MacdFastPeriod
{
get => _macdFastPeriod.Value;
set => _macdFastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period inside MACD.
/// </summary>
public int MacdSlowPeriod
{
get => _macdSlowPeriod.Value;
set => _macdSlowPeriod.Value = value;
}
/// <summary>
/// Signal line period for MACD.
/// </summary>
public int MacdSignalPeriod
{
get => _macdSignalPeriod.Value;
set => _macdSignalPeriod.Value = value;
}
/// <summary>
/// Trend EMA period used as directional filter.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Stochastic %K length.
/// </summary>
public int StochKLength
{
get => _stochKLength.Value;
set => _stochKLength.Value = value;
}
/// <summary>
/// Stochastic %D length.
/// </summary>
public int StochDLength
{
get => _stochDLength.Value;
set => _stochDLength.Value = value;
}
/// <summary>
/// Type of candles used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public MacdStochasticFilterStrategy()
{
_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
.SetDisplay("MACD Fast", "Fast EMA period for MACD", "Indicators");
_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
.SetDisplay("MACD Slow", "Slow EMA period for MACD", "Indicators");
_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
.SetDisplay("MACD Signal", "Signal EMA period for MACD", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 26)
.SetDisplay("Trend EMA", "EMA period for trend filter", "Indicators");
_stochKLength = Param(nameof(StochKLength), 14)
.SetDisplay("Stochastic K", "Look-back length for stochastic K", "Indicators");
_stochDLength = Param(nameof(StochDLength), 3)
.SetDisplay("Stochastic D", "Smoothing for D line", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for price data", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = null;
_prevSignal = null;
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastPeriod },
LongMa = { Length = MacdSlowPeriod }
},
SignalMa = { Length = MacdSignalPeriod }
};
var stochastic = new StochasticOscillator
{
K = { Length = StochKLength },
D = { Length = StochDLength }
};
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, stochastic, ema, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue stochValue, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFinal || !stochValue.IsFinal || !emaValue.IsFinal)
return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macdData)
return;
if (macdData.Macd is not decimal macd || macdData.Signal is not decimal signal)
return;
if (stochValue is not StochasticOscillatorValue stochData)
return;
if (stochData.K is not decimal kVal || stochData.D is not decimal dVal)
return;
var emaVal = emaValue.ToDecimal();
if (_prevMacd is not decimal prevMacd || _prevSignal is not decimal prevSignal)
{
_prevMacd = macd;
_prevSignal = signal;
return;
}
// MACD crossover signals
var macdBullishCross = prevMacd <= prevSignal && macd > signal;
var macdBearishCross = prevMacd >= prevSignal && macd < signal;
// Long: MACD bullish cross + stochastic K > D + price above EMA
if (Position <= 0 && macdBullishCross && kVal > dVal && candle.ClosePrice > emaVal)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Short: MACD bearish cross + stochastic K < D + price below EMA
else if (Position >= 0 && macdBearishCross && kVal < dVal && candle.ClosePrice < emaVal)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevMacd = macd;
_prevSignal = signal;
}
}
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,
MovingAverageConvergenceDivergenceSignal,
StochasticOscillator
)
from StockSharp.Algo.Strategies import Strategy
class macd_stochastic_filter_strategy(Strategy):
"""MACD strategy with stochastic confirmation and EMA trend filter.
Buy when MACD crosses above signal with stochastic K > D and price above EMA.
Sell when MACD crosses below signal with stochastic K < D and price below EMA."""
def __init__(self):
super(macd_stochastic_filter_strategy, self).__init__()
self._macd_fast_period = self.Param("MacdFastPeriod", 12) \
.SetDisplay("MACD Fast", "Fast EMA period for MACD", "Indicators")
self._macd_slow_period = self.Param("MacdSlowPeriod", 26) \
.SetDisplay("MACD Slow", "Slow EMA period for MACD", "Indicators")
self._macd_signal_period = self.Param("MacdSignalPeriod", 9) \
.SetDisplay("MACD Signal", "Signal EMA period for MACD", "Indicators")
self._ema_period = self.Param("EmaPeriod", 26) \
.SetDisplay("Trend EMA", "EMA period for trend filter", "Indicators")
self._stoch_k_length = self.Param("StochKLength", 14) \
.SetDisplay("Stochastic K", "Look-back length for stochastic K", "Indicators")
self._stoch_d_length = self.Param("StochDLength", 3) \
.SetDisplay("Stochastic D", "Smoothing for D line", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for price data", "General")
self._prev_macd = None
self._prev_signal = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MacdFastPeriod(self):
return self._macd_fast_period.Value
@property
def MacdSlowPeriod(self):
return self._macd_slow_period.Value
@property
def MacdSignalPeriod(self):
return self._macd_signal_period.Value
@property
def EmaPeriod(self):
return self._ema_period.Value
@property
def StochKLength(self):
return self._stoch_k_length.Value
@property
def StochDLength(self):
return self._stoch_d_length.Value
def OnReseted(self):
super(macd_stochastic_filter_strategy, self).OnReseted()
self._prev_macd = None
self._prev_signal = None
def OnStarted2(self, time):
super(macd_stochastic_filter_strategy, self).OnStarted2(time)
self._prev_macd = None
self._prev_signal = None
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self.MacdFastPeriod
macd.Macd.LongMa.Length = self.MacdSlowPeriod
macd.SignalMa.Length = self.MacdSignalPeriod
stochastic = StochasticOscillator()
stochastic.K.Length = self.StochKLength
stochastic.D.Length = self.StochDLength
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(macd, stochastic, ema, self._process_candle).Start()
def _process_candle(self, candle, macd_value, stoch_value, ema_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFinal or not stoch_value.IsFinal or not ema_value.IsFinal:
return
macd_raw = macd_value.Macd if hasattr(macd_value, 'Macd') else None
signal_raw = macd_value.Signal if hasattr(macd_value, 'Signal') else None
if macd_raw is None or signal_raw is None:
return
macd_main = float(macd_raw)
signal_line = float(signal_raw)
k_raw = stoch_value.K if hasattr(stoch_value, 'K') else None
d_raw = stoch_value.D if hasattr(stoch_value, 'D') else None
if k_raw is None or d_raw is None:
return
k_val = float(k_raw)
d_val = float(d_raw)
ema_val = float(ema_value)
if self._prev_macd is None or self._prev_signal is None:
self._prev_macd = macd_main
self._prev_signal = signal_line
return
prev_macd = self._prev_macd
prev_signal = self._prev_signal
# MACD crossover signals
macd_bullish_cross = prev_macd <= prev_signal and macd_main > signal_line
macd_bearish_cross = prev_macd >= prev_signal and macd_main < signal_line
close = float(candle.ClosePrice)
# Long: MACD bullish cross + stochastic K > D + price above EMA
if self.Position <= 0 and macd_bullish_cross and k_val > d_val and close > ema_val:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Short: MACD bearish cross + stochastic K < D + price below EMA
elif self.Position >= 0 and macd_bearish_cross and k_val < d_val and close < ema_val:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_macd = macd_main
self._prev_signal = signal_line
def CreateClone(self):
return macd_stochastic_filter_strategy()