La estrategia ABH_BH_MFI es una StockSharp adaptación de alto nivel del MetaTrader asesor experto "Expert_ABH_BH_MFI". El algoritmo combina patrones de velas Harami alcistas y bajistas con la confirmación del Índice de flujo de dinero (MFI). Las operaciones largas se activan cuando se forma un Harami alcista dentro de un mercado en caída mientras la IMF permanece deprimida. Las operaciones cortas requieren un Harami bajista dentro de un mercado en crecimiento y una IMF elevada. La implementación original de MQL se basó en la infraestructura de señal de MetaTrader; esta conversión mantiene la lógica de decisión pero la expresa con las suscripciones de velas, el enlace de indicadores y los asistentes de gestión de posiciones de StockSharp.
Lógica de trading
1. Detección de patrones Harami
La estrategia almacena las dos velas completadas más recientes.
Un Harami alcista requiere:
Hace dos velas había una vela larga negra (bajista) cuyo cuerpo es más grande que la longitud promedio del cuerpo.
La vela más reciente es alcista y su apertura/cierre están envueltos por el cuerpo de la vela bajista anterior.
El punto medio de la vela más antigua se encuentra por debajo del promedio móvil simple de cierres, lo que indica una tendencia bajista predominante.
Un Harami bajista refleja estos requisitos con colores invertidos y el punto medio por encima del promedio móvil para confirmar una tendencia alcista.
2. Confirmación del índice de flujo de dinero
La MFI utiliza el MfiPeriod configurable (predeterminado 37) para replicar la configuración original del oscilador.
Las entradas largas exigen que el último valor de MFI completado se mantenga por debajo de BullishThreshold (predeterminado 40) para garantizar el agotamiento de la entrada de capital.
Las entradas cortas requieren que la IMF se mantenga por encima de BearishThreshold (predeterminado 60) para mostrar el agotamiento de la presión de compra.
3. Reglas de salida a través de cruces de IMF
Las posiciones largas activas se cierran cuando la IMF cruza por encima de ExitLowerLevel (predeterminado 30) o ExitUpperLevel (predeterminado 70), coincidiendo con las MetaTrader condiciones MFI(1) > level && MFI(2) < level.
Las posiciones cortas activas se cierran cuando la IMF cruza hacia abajo desde la zona de sobrecompra o sube por debajo del nivel de sobreventa, reflejando las cláusulas de salida cortas originales.
4. Gestión de riesgos
La estrategia aplica opcionalmente StartProtection con compensaciones de stop-loss y take-profit expresadas en incrementos de precio. Poner a cero el parámetro correspondiente desactiva la distancia de protección, reproduciéndose los valores predeterminados de MetaTrader.
El tamaño de la posición utiliza la propiedad base Volume; Revertir posiciones automáticamente agrega suficientes contratos para aplanarse y reabrirse en la nueva dirección, al igual que el experto en fuentes.
Parámetros
Nombre
Predeterminado
Descripción
CandleType
plazo de 1 hora
Serie de velas primarias analizadas en busca de patrones e IMF.
MfiPeriod
37
Retrospectiva del indicador del índice de flujo de dinero.
BodyAveragePeriod
11
Longitud de las medias móviles simples que miden el tamaño corporal y la tendencia de cierre.
BullishThreshold
40
Valor máximo de MFI permitido antes de abrir operaciones largas.
BearishThreshold
60
Valor mínimo de MFI requerido antes de abrir operaciones cortas.
ExitLowerLevel
30
Nivel de cruce de MFI inferior para salidas de posición.
ExitUpperLevel
70
Nivel de cruce superior de MFI para salidas de posición.
StopLossPoints
0
Distancia de stop-loss opcional en pasos de precio (0 desactiva).
TakeProfitPoints
0
Distancia de toma de ganancias opcional en pasos de precio (0 inhabilitaciones).
Notas de implementación
Los datos de la vela se reciben a través de SubscribeCandles(CandleType) y se procesan solo cuando el estado de la vela es Finished, lo que garantiza la alineación con la lógica de barra cerrada del experto MQL.
El indicador MFI está vinculado directamente con .Bind(_mfi, ProcessCandle) para que el controlador reciba valores decimales listos para usar sin llamar a GetValue.
Dos promedios móviles simples auxiliares replican las funciones auxiliares AvgBody y CloseAvg del código MetaTrader. Sus resultados se almacenan en caché para evitar consultas de indicadores históricos.
Para tomar decisiones de entrada y salida, llame a IsFormedAndOnlineAndAllowTrading() antes de enviar órdenes, manteniendo la coherencia con las comprobaciones de seguridad comerciales recomendadas por StockSharp.
Diferencias con el experto MetaTrader
La gestión del dinero se simplifica al volumen de la estrategia base. El módulo original de "lote fijo" se tradujo al asistente de dimensionamiento de posiciones de StockSharp, que cubre la misma funcionalidad sin clases separadas.
El componente de trailing stop MetaTrader (TrailingNone) no tenía lógica; por lo tanto, la versión StockSharp omite cualquier acción posterior pero mantiene objetivos de riesgo fijos opcionales.
El registro es mínimo de forma predeterminada; puede ampliarlo con llamadas LogInfo si necesita diagnósticos comerciales detallados.
Consejos de uso
Configure la seguridad deseada y asigne el CandleType antes de iniciar la estrategia.
Opcionalmente, ajuste la IMF y los umbrales de salida para adaptarlos a diferentes regímenes de volatilidad.
Proporcionar StopLossPoints/TakeProfitPoints distinto de cero cuando el corredor requiera órdenes de protección explícitas; de lo contrario, déjelos en cero para comerciar sin objetivos concretos.
Supervise los paneles de gráficos creados por la estrategia para visualizar velas, el indicador MFI y las operaciones ejecutadas.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// ABH BH MFI strategy: Harami pattern with MFI confirmation.
/// </summary>
public class AbhBhMfiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _mfiPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _signalCooldownCandles;
private readonly List<ICandleMessage> _candles = new();
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int MfiPeriod { get => _mfiPeriod.Value; set => _mfiPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public AbhBhMfiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_mfiPeriod = Param(nameof(MfiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("MFI Period", "Money Flow Index period", "Indicators");
_oversold = Param(nameof(Oversold), 40m)
.SetDisplay("Oversold", "MFI oversold level", "Signals");
_overbought = Param(nameof(Overbought), 60m)
.SetDisplay("Overbought", "MFI overbought level", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_candlesSinceTrade = SignalCooldownCandles;
var mfi = new MoneyFlowIndex { Length = MfiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(mfi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal mfiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
_candles.Add(candle);
if (_candles.Count > 5) _candles.RemoveAt(0);
if (_candles.Count >= 2)
{
var curr = _candles[^1];
var prev = _candles[^2];
var bullishHarami = prev.OpenPrice > prev.ClosePrice
&& curr.ClosePrice > curr.OpenPrice
&& curr.OpenPrice > prev.ClosePrice
&& curr.ClosePrice < prev.OpenPrice;
var bearishHarami = prev.ClosePrice > prev.OpenPrice
&& curr.OpenPrice > curr.ClosePrice
&& curr.ClosePrice > prev.OpenPrice
&& curr.OpenPrice < prev.ClosePrice;
if (bullishHarami && mfiValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (bearishHarami && mfiValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
}
}
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 MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
class abh_bh_mfi_strategy(Strategy):
def __init__(self):
super(abh_bh_mfi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._mfi_period = self.Param("MfiPeriod", 14)
self._oversold = self.Param("Oversold", 40.0)
self._overbought = self.Param("Overbought", 60.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MfiPeriod(self):
return self._mfi_period.Value
@MfiPeriod.setter
def MfiPeriod(self, value):
self._mfi_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(abh_bh_mfi_strategy, self).OnReseted()
self._candles.clear()
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(abh_bh_mfi_strategy, self).OnStarted2(time)
self._candles.clear()
self._candles_since_trade = self.SignalCooldownCandles
mfi = MoneyFlowIndex()
mfi.Length = self.MfiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(mfi, self._process_candle).Start()
def _process_candle(self, candle, mfi_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
mfi_val = float(mfi_value)
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2:
curr = self._candles[-1]
prev = self._candles[-2]
bullish_harami = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) < float(prev.OpenPrice))
bearish_harami = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) < float(prev.ClosePrice))
if bullish_harami and mfi_val < self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif bearish_harami and mfi_val > self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return abh_bh_mfi_strategy()