A ABH_BH_MFI Estratégia é uma versão StockSharp de alto nível do MetaTrader consultor especialista "Expert_ABH_BH_MFI". O algoritmo combina padrões de velas Harami de alta e baixa com confirmação do Money Flow Index (MFI). As negociações longas são acionadas quando um Harami altista se forma dentro de um mercado em queda, enquanto a IMF permanece deprimida. As negociações a descoberto exigem um Harami baixista dentro de um mercado em ascensão e uma IMF elevada. A implementação original do MQL dependia da infraestrutura de sinal do MetaTrader; esta conversão mantém a lógica de decisão, mas a expressa com assinaturas de velas, vinculação de indicadores e auxiliares de gerenciamento de posição de StockSharp.
Lógica de negociação
1. Detecção de padrão Harami
A estratégia armazena as duas velas concluídas mais recentemente.
Um Harami otimista requer:
Duas velas atrás havia uma longa vela preta (de baixa) cujo corpo é maior que o comprimento médio do corpo.
A vela mais recente é de alta e sua abertura/fechamento é engolfada pelo corpo da vela de baixa anterior.
O ponto médio da vela mais antiga está abaixo da média móvel simples de fechamentos, sinalizando uma tendência de baixa predominante.
Um Harami baixista reflete esses requisitos com cores invertidas e o ponto médio acima da média móvel para confirmar uma tendência de alta.
2. Confirmação do Índice de Fluxo de Dinheiro
A MFI usa o configurável MfiPeriod (padrão 37) para replicar as configurações originais do oscilador.
Entradas longas exigem que o último valor da IMF concluída permaneça abaixo de BullishThreshold (padrão 40) para garantir o esgotamento do fluxo de capital.
As entradas curtas exigem que a IMF permaneça acima de BearishThreshold (padrão 60) para mostrar o esgotamento da pressão de compra.
3. Regras de saída através de cruzamentos de IFM
As posições longas ativas são fechadas quando a IMF ultrapassa ExitLowerLevel (padrão 30) ou ExitUpperLevel (padrão 70), correspondendo às condições MetaTrader MFI(1) > level && MFI(2) < level.
As posições curtas ativas são fechadas quando a IMF desce da zona de sobrecompra ou sobe abaixo do nível de sobrevenda, refletindo as cláusulas de saída curtas originais.
4. Gestão de riscos
A estratégia aplica-se opcionalmente StartProtection com compensações de stop-loss e take-profit expressas em etapas de preço. Definir o parâmetro correspondente como zero desativa a distância de proteção, reproduzindo os padrões MetaTrader.
O dimensionamento da posição usa a propriedade base Volume; a reversão de posições adiciona automaticamente contratos suficientes para estabilizar e reabrir na nova direção, assim como o especialista fonte.
Parâmetros
Nome
Padrão
Descrição
CandleType
Período de 1 hora
Série de velas primárias analisadas para padrões e IMF.
MfiPeriod
37
Lookback para o indicador Money Flow Index.
BodyAveragePeriod
11
Comprimento das médias móveis simples que medem o tamanho do corpo e a tendência de fechamento.
BullishThreshold
40
Valor máximo da IMF permitido antes de abrir negociações longas.
BearishThreshold
60
Valor mínimo da IMF exigido antes de abrir negociações curtas.
ExitLowerLevel
30
Nível de cruzamento mais baixo da MFI para saídas de posição.
ExitUpperLevel
70
Nível superior de cruzamento da MFI para saídas de posição.
StopLossPoints
0
Distância de stop-loss opcional em etapas de preço (0 desativa).
TakeProfitPoints
0
Distância opcional de take-profit em etapas de preço (0 desativa).
Notas de implementação
Os dados da vela são recebidos via SubscribeCandles(CandleType) e processados somente quando o estado da vela é Finished, garantindo o alinhamento com a lógica de barra fechada do especialista MQL.
O indicador MFI é vinculado diretamente a .Bind(_mfi, ProcessCandle) para que o manipulador receba valores decimais prontos para uso sem chamar GetValue.
Duas médias móveis simples auxiliares replicam as funções auxiliares AvgBody e CloseAvg do código MetaTrader. Seus resultados são armazenados em cache para evitar consultas de indicadores históricos.
As decisões de saída e entrada chamam IsFormedAndOnlineAndAllowTrading() antes de enviar ordens, permanecendo consistentes com as verificações de segurança comercial recomendadas por StockSharp.
Diferenças do especialista MetaTrader
A gestão do dinheiro é simplificada para o volume da estratégia básica. O módulo original de "lote fixo" traduzido para o auxiliar de dimensionamento de posição de StockSharp, que cobre a mesma funcionalidade sem classes separadas.
O componente de parada móvel MetaTrader (TrailingNone) não tinha lógica; a versão StockSharp, portanto, omite quaisquer ações finais, mas mantém metas de risco fixas opcionais.
O registro em log é mínimo por padrão; você pode estendê-lo com chamadas LogInfo se precisar de diagnósticos comerciais detalhados.
Dicas de uso
Configure a segurança desejada e atribua o CandleType antes de iniciar a estratégia.
Opcionalmente, ajuste os limites da IMF e de saída para se adequar aos diferentes regimes de volatilidade.
Forneça StopLossPoints/TakeProfitPoints diferente de zero quando o corretor exigir ordens de proteção explícitas; caso contrário, deixe-os em zero para negociar sem metas rígidas.
Monitore os painéis do gráfico criados pela estratégia para visualizar velas, o indicador MFI e as negociações executadas.
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()