The AH HM MFI strategy trades hammer and hanging man candlestick patterns that are confirmed by the Money Flow Index (MFI). When a bullish hammer appears in a short-term downtrend and the MFI stays below an oversold threshold, the strategy opens a long position. When a bearish hanging man forms in an uptrend while the MFI is above an overbought threshold, it opens a short position. Protective exits are triggered when the MFI crosses predefined upper or lower boundaries.
Core Logic
Subscribe to the configured time-frame candles and calculate two indicators:
Money Flow Index with a configurable period (default: 47).
Simple Moving Average of closing prices to approximate the trend filter from the original MQL strategy (default length: 5).
Detect hammer and hanging man patterns:
Candle body located in the upper third of the range.
Long lower shadow relative to the real body.
Gap in the direction of the trend compared with the previous candle.
Trend confirmation using the midpoint of the previous candle versus the moving average.
Confirm entries with MFI thresholds:
Enter long if a hammer is detected and the MFI is at or below the configured oversold level (default: 40).
Enter short if a hanging man is detected and the MFI is at or above the configured overbought level (default: 60).
Manage exits using MFI crossings:
Close short positions when the MFI crosses upward above the lower or upper exit levels (defaults: 30 and 70).
Close long positions when the MFI crosses upward above the upper exit level or downward below the lower exit level.
Start the built-in risk protection module to handle emergency stops.
Parameters
Name
Description
Default
CandleType
Candle data type and timeframe used for pattern detection.
30-minute time frame
MfiPeriod
Lookback period for the MFI calculation.
47
MaPeriod
Length of the SMA applied to closing prices for trend confirmation.
5
HammerEntryThreshold
Maximum MFI value allowed before entering on a hammer signal.
40
HangingEntryThreshold
Minimum MFI value required before entering on a hanging man signal.
60
MfiUpperExitLevel
Upper MFI boundary; crossing above it closes any open position.
70
MfiLowerExitLevel
Lower MFI boundary; crossing below it closes long positions, while crossing above it closes shorts.
30
Notes
The strategy evaluates only finished candles to avoid acting on incomplete information.
Hammer and hanging man detection is conservative: both a long lower shadow and a body located near the candle high are required.
The moving average replaces the MetaTrader 5 CloseAvg filter from the original expert advisor, ensuring that entries align with the broader trend.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Hammer/Hanging Man + MFI strategy.
/// Buys on hammer with low MFI (oversold), sells on hanging man with high MFI (overbought).
/// </summary>
public class AhHmMfiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _mfiPeriod;
private readonly StrategyParam<decimal> _mfiLow;
private readonly StrategyParam<decimal> _mfiHigh;
private readonly StrategyParam<int> _signalCooldownCandles;
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 MfiLow { get => _mfiLow.Value; set => _mfiLow.Value = value; }
public decimal MfiHigh { get => _mfiHigh.Value; set => _mfiHigh.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public AhHmMfiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_mfiPeriod = Param(nameof(MfiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("MFI Period", "MFI period", "Indicators");
_mfiLow = Param(nameof(MfiLow), 35m)
.SetDisplay("MFI Low", "MFI oversold threshold for buy", "Signals");
_mfiHigh = Param(nameof(MfiHigh), 65m)
.SetDisplay("MFI High", "MFI overbought threshold for sell", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 8)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candlesSinceTrade = SignalCooldownCandles;
var rsi = new RelativeStrengthIndex { Length = MfiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal mfiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var range = candle.HighPrice - candle.LowPrice;
if (range <= 0 || body <= 0) return;
var upperShadow = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);
var lowerShadow = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
var isHammer = lowerShadow > body * 2.5m && upperShadow < body * 0.5m;
var isHangingMan = upperShadow > body * 2.5m && lowerShadow < body * 0.5m;
if (isHammer && mfiValue < MfiLow && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (isHangingMan && mfiValue > MfiHigh && 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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class ah_hm_mfi_strategy(Strategy):
def __init__(self):
super(ah_hm_mfi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._mfi_period = self.Param("MfiPeriod", 14)
self._mfi_low = self.Param("MfiLow", 35.0)
self._mfi_high = self.Param("MfiHigh", 65.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 8)
self._candles_since_trade = 8
@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 MfiLow(self):
return self._mfi_low.Value
@MfiLow.setter
def MfiLow(self, value):
self._mfi_low.Value = value
@property
def MfiHigh(self):
return self._mfi_high.Value
@MfiHigh.setter
def MfiHigh(self, value):
self._mfi_high.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(ah_hm_mfi_strategy, self).OnReseted()
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(ah_hm_mfi_strategy, self).OnStarted2(time)
self._candles_since_trade = self.SignalCooldownCandles
rsi = RelativeStrengthIndex()
rsi.Length = self.MfiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, 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)
body = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
rng = float(candle.HighPrice) - float(candle.LowPrice)
if rng <= 0 or body <= 0:
return
upper_shadow = float(candle.HighPrice) - max(float(candle.OpenPrice), float(candle.ClosePrice))
lower_shadow = min(float(candle.OpenPrice), float(candle.ClosePrice)) - float(candle.LowPrice)
is_hammer = lower_shadow > body * 2.5 and upper_shadow < body * 0.5
is_hanging_man = upper_shadow > body * 2.5 and lower_shadow < body * 0.5
if is_hammer and mfi_val < self.MfiLow and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif is_hanging_man and mfi_val > self.MfiHigh and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return ah_hm_mfi_strategy()