Esta estrategia es una adaptación de C# del MetaTrader 4 experto Frbestexp02_1_maloma_mod.mq4. Combina el impulso de OsMA, reversiones fractales, confirmación de volumen de ticks y un filtro de pivote diario para atenuar los movimientos agotados en el marco temporal M15.
Lógica comercial
Pivote de sesión: un punto de pivote móvil se calcula a partir del máximo más alto, el mínimo más bajo y el cierre más antiguo dentro de una ventana configurable (96 velas de forma predeterminada, equivalente a un día de negociación en M15). Sólo se permiten operaciones que coincidan con el sesgo de pivote: ventas cortas por encima del pivote y posiciones largas por debajo de él.
Patrón fractal: la estrategia espera un fractal confirmado de Bill Williams tres velas atrás. Los fractales hacia abajo (mínimos) permiten posiciones cortas, mientras que los fractales hacia arriba (máximos) permiten posiciones largas.
Histograma OsMA: un histograma MACD (rápido 12, lento 26, señal 9 por defecto) debe tener una pendiente mayor hacia territorio negativo para las posiciones cortas y más hacia territorio positivo para las posiciones largas. La lectura anterior del histograma también debe estar en el mismo lado de cero.
Filtro de volumen: el volumen de la vela terminada anterior debe exceder un umbral configurable y ser mayor que el volumen de hace dos velas. Esto reproduce el requisito de pico de volumen de ticks del experto original.
Tiempo de pedido: las operaciones se limitan por un intervalo mínimo (20 segundos de forma predeterminada) entre entradas.
Gestión de riesgos: el stop-loss configurable, el take-profit y el trailing stop opcional se expresan en puntos y se convierten a precios de instrumentos. Las órdenes de protección se actualizan con los ayudantes integrados SetStopLoss/SetTakeProfit.
Parámetros
Nombre
Descripción
Predeterminado
Volume
Volumen de pedidos utilizado para cada entrada.
1
StopLossPoints
Distancia de stop-loss en puntos del instrumento.
1000
TakeProfitPoints
Distancia de toma de ganancias en puntos del instrumento.
1000
TrailingStopPoints
Distancia de parada de seguimiento opcional en puntos (0 desactiva el seguimiento).
0
VolumeThreshold
Volumen mínimo de vela anterior requerido para habilitar una señal.
MACD parámetros utilizados para calcular el histograma OsMA.
12 / 26 / 9
PivotWindow
Número de velas terminadas incluidas en el cálculo del pivote.
96
MinTradeIntervalSeconds
Número mínimo de segundos entre nuevas entradas.
20
CandleType
Marco de tiempo principal (por defecto, velas de 15 minutos).
M15
Diferencias versus el experto MQL4
El código original admitía órdenes de cobertura multiplicadas por kh y una lógica compleja de reciclaje de beneficios. La versión StockSharp ejecuta una posición direccional única y la cierra o la revierte antes de abrir una nueva operación.
El manejo de trailing stop se simplifica al utilizar el asistente estándar SetStopLoss en lugar de modificar manualmente las órdenes por tick.
Se omiten la agregación de beneficios y los bloques de recuperación estilo martingala. La gestión de salida se basa en stop-loss, take-profit o trailing stop.
Todos los cálculos de los indicadores se basan en eventos en velas terminadas. No hay modificación de orden intrabar.
Notas de uso
Adjunte la estrategia a un instrumento que proporcione datos de volumen de ticks si el filtro de volumen debe coincidir con el comportamiento original.
Mantenga el período de tiempo en 15 minutos para reproducir la calibración original de la ventana dinámica y la mirada retrospectiva fractal.
Ajuste los períodos VolumeThreshold y OsMA para que se ajusten a los símbolos con diferentes perfiles de volatilidad o volumen.
Habilite el trailing stop sólo cuando se desee una salida más cerrada; de lo contrario, déjelo en cero para confiar en la parada/objetivo estático.
El código sigue las pautas de alto nivel StockSharp API: suscripciones de velas a través de SubscribeCandles, vinculación de indicadores para el histograma MACD y ejecución segura a través de BuyMarket/SellMarket con órdenes de protección automáticas.
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>
/// FrBestExp02 Maloma Mod strategy - MACD histogram crossover with EMA trend filter.
/// Buys when MACD histogram crosses above zero while price is above EMA.
/// Sells when MACD histogram crosses below zero while price is below EMA.
/// </summary>
public class FrBestExp02MalomaModStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMacd;
private decimal _prevSignal;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FrBestExp02MalomaModStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevMacd = 0m; _prevSignal = 0m; _hasPrev = false; _currentEma = 0m; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var macd = new MovingAverageConvergenceDivergenceSignal();
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessMacd)
.Bind(ema, ProcessEma)
.Start();
}
private decimal _currentEma;
private void ProcessEma(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
_currentEma = ema;
}
private void ProcessMacd(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!value.IsFinal || value.IsEmpty)
return;
var macdVal = value as MovingAverageConvergenceDivergenceSignalValue;
if (macdVal == null)
return;
var macdLine = macdVal.Macd;
var signalLine = macdVal.Signal;
if (macdLine == null || signalLine == null)
return;
var histogram = macdLine.Value - signalLine.Value;
if (!_hasPrev)
{
_prevMacd = macdLine.Value;
_prevSignal = signalLine.Value;
_hasPrev = true;
return;
}
var prevHist = _prevMacd - _prevSignal;
var close = candle.ClosePrice;
// Histogram crosses above zero + bullish trend
if (prevHist <= 0 && histogram > 0 && close > _currentEma && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Histogram crosses below zero + bearish trend
else if (prevHist >= 0 && histogram < 0 && close < _currentEma && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevMacd = macdLine.Value;
_prevSignal = signalLine.Value;
}
}
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, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fr_best_exp02_maloma_mod_strategy(Strategy):
def __init__(self):
super(fr_best_exp02_maloma_mod_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_macd = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._current_ema = 0.0
@property
def ema_period(self):
return self._ema_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fr_best_exp02_maloma_mod_strategy, self).OnReseted()
self._prev_macd = 0.0
self._prev_signal = 0.0
self._has_prev = False
self._current_ema = 0.0
def OnStarted2(self, time):
super(fr_best_exp02_maloma_mod_strategy, self).OnStarted2(time)
self._has_prev = False
macd = MovingAverageConvergenceDivergenceSignal()
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription \
.BindEx(macd, self.process_macd) \
.Bind(ema, self.process_ema) \
.Start()
def process_ema(self, candle, ema):
if candle.State != CandleStates.Finished:
return
self._current_ema = float(ema)
def process_macd(self, candle, value):
if candle.State != CandleStates.Finished:
return
if not value.IsFinal or value.IsEmpty:
return
if value.Macd is None or value.Signal is None:
return
macd_line = float(value.Macd)
signal_line = float(value.Signal)
histogram = macd_line - signal_line
if not self._has_prev:
self._prev_macd = macd_line
self._prev_signal = signal_line
self._has_prev = True
return
prev_hist = self._prev_macd - self._prev_signal
close = float(candle.ClosePrice)
if prev_hist <= 0 and histogram > 0 and close > self._current_ema and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_hist >= 0 and histogram < 0 and close < self._current_ema and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_macd = macd_line
self._prev_signal = signal_line
def CreateClone(self):
return fr_best_exp02_maloma_mod_strategy()