This strategy reimplements the MetaTrader "AH HM CCI" expert in StockSharp. It watches for hammer and hanging man candlestick
patterns and requires confirmation from the Commodity Channel Index (CCI) before entering a trade. The extra confirmation filters
out weak patterns and helps align entries with the momentum shift signaled by CCI.
The logic runs on completed candles only and uses a short simple moving average (SMA) to define the prevailing trend. The previous
candle must be a hammer in a downtrend with oversold CCI to buy, or a hanging man in an uptrend with overbought CCI to sell. Exits
are managed when CCI crosses configurable trigger levels, replicating the vote-based exit logic from the original expert.
Trading Logic
Trend filter – The midpoint of the previous candle has to be below (for longs) or above (for shorts) an SMA calculated on
closing prices. This mimics the original wizard's moving-average trend check.
Pattern detection – The strategy evaluates the previous bar and checks:
Body entirely in the top third of the candle range.
Gap between the previous candle's open/close and the candle before it.
Directional context (hammer for a downtrend, hanging man for an uptrend).
CCI confirmation – The previous bar's CCI must be below the long threshold or above the short threshold. The default values
match the MetaTrader template (40 for longs and 60 for shorts).
Position exits – Existing positions are closed when CCI crosses either the lower or upper exit thresholds. Crossing from
below closes longs; crossing from above closes shorts.
Parameters
Name
Description
Default
CandleType
Candle type and timeframe used for pattern recognition.
TimeSpan.FromMinutes(15)
CciPeriod
Number of bars used by the Commodity Channel Index.
11
MaPeriod
Number of bars in the SMA trend filter.
5
LongConfirmationThreshold
Maximum CCI value allowed for a hammer signal.
40
ShortConfirmationThreshold
Minimum CCI value allowed for a hanging man signal.
60
ExitUpperThreshold
CCI level that triggers exits after an upward crossing.
70
ExitLowerThreshold
Secondary exit level for early signals.
30
All parameters are available for optimization. The thresholds accept negative values, so you can adapt the strategy to other
markets or noise levels by tightening or loosening the filters.
Order Management
Entries use market orders sized as Volume + |Position|, ensuring reversals are executed in a single trade.
Exits rely purely on the CCI crosses to stay close to the MetaTrader expert. Add StartProtection calls if you need
explicit stop-loss or take-profit levels.
Usage Tips
Apply the strategy on liquid instruments where candlestick gaps and tails are informative.
Experiment with longer CciPeriod and MaPeriod values to smooth out noise when trading higher timeframes.
Lowering LongConfirmationThreshold or raising ShortConfirmationThreshold will reduce the number of trades but improve
selectivity.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Hammer/Hanging Man + CCI strategy.
/// Buys on hammer with negative CCI, sells on hanging man with positive CCI.
/// </summary>
public class HammerHangingManCciStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciLevel;
private readonly StrategyParam<int> _signalCooldownCandles;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public HammerHangingManCciStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period", "Indicators");
_cciLevel = Param(nameof(CciLevel), 100m)
.SetDisplay("CCI Level", "CCI threshold", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.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 cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
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 && cciValue < -CciLevel && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (isHangingMan && cciValue > CciLevel && 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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class hammer_hanging_man_cci_strategy(Strategy):
def __init__(self):
super(hammer_hanging_man_cci_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._cci_period = self.Param("CciPeriod", 14)
self._cci_level = self.Param("CciLevel", 100.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
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 CciPeriod(self):
return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, value):
self._cci_period.Value = value
@property
def CciLevel(self):
return self._cci_level.Value
@CciLevel.setter
def CciLevel(self, value):
self._cci_level.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(hammer_hanging_man_cci_strategy, self).OnReseted()
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(hammer_hanging_man_cci_strategy, self).OnStarted2(time)
self._candles_since_trade = self.SignalCooldownCandles
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cci, self._process_candle).Start()
def _process_candle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
cci_val = float(cci_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 cci_val < -self.CciLevel and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif is_hanging_man and cci_val > self.CciLevel and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
def CreateClone(self):
return hammer_hanging_man_cci_strategy()