This strategy ports the MetaTrader expert "Expert_AH_HM_Stoch" to the StockSharp high-level API. It combines hammer and hanging man candlestick patterns with stochastic oscillator confirmation to capture reversal setups after extended moves.
The strategy waits for a completed candle before acting, uses the stochastic signal line for filtering, and closes positions when momentum exits the extreme zones.
Details
Entry Criteria:
Long: Bullish hammer candle and stochastic %D (previous bar) below the oversold level.
Short: Bearish hanging man candle and stochastic %D (previous bar) above the overbought level.
Long/Short: Both.
Exit Criteria: Close positions when stochastic %D crosses above/below configurable recovery and extreme levels.
Stops: Enabled through the built-in StartProtection() hook (defaults to account-level protection).
Default Values:
CandleType = TimeSpan.FromHours(1)
StochPeriodK = 15
StochPeriodD = 49
StochPeriodSlow = 25
OversoldLevel = 30
OverboughtLevel = 70
ExitLowerLevel = 20
ExitUpperLevel = 80
MaxBodyRatio = 0.35
LowerShadowMultiplier = 2.5
UpperShadowMultiplier = 0.3
Filters:
Category: Pattern + Oscillator confirmation
Direction: Both
Indicators: Candlestick, Stochastic
Stops: Optional risk controls via StartProtection
Complexity: Intermediate
Timeframe: Swing / Intraday (1h default)
Seasonality: No
Neural Networks: No
Divergence: No
Risk Level: Moderate
How It Works
Subscribes to the configured candle series and stochastic oscillator using the high-level BindEx API.
Detects hammer and hanging man formations based on body and shadow ratios.
Confirms entries with the stochastic %D line using the previous closed bar value.
Manages exits when the stochastic exits the oversold/overbought zones, mirroring the logic of the original MQL expert.
Provides chart visualization for candles, stochastic, and own trades when a chart area is available.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Hammer/Hanging Man + Stochastic strategy.
/// Buys on hammer in oversold, sells on hanging man in overbought.
/// </summary>
public class HammerHangingStochasticStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public HammerHangingStochasticStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stoch Period", "Stochastic K period", "Indicators");
_oversold = Param(nameof(Oversold), 30m)
.SetDisplay("Oversold", "Stochastic oversold level", "Signals");
_overbought = Param(nameof(Overbought), 70m)
.SetDisplay("Overbought", "Stochastic overbought level", "Signals");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(stoch, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished) return;
var stochTyped = stochValue as StochasticOscillatorValue;
if (stochTyped?.K is not decimal kValue) return;
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;
// Hammer: small body at top, long lower shadow
var isHammer = lowerShadow > body * 2 && upperShadow < body;
// Hanging Man: small body at bottom, long upper shadow
var isHangingMan = upperShadow > body * 2 && lowerShadow < body;
if (isHammer && kValue < Oversold && Position <= 0)
BuyMarket();
else if (isHangingMan && kValue > Overbought && Position >= 0)
SellMarket();
}
}
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 StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class hammer_hanging_stochastic_strategy(Strategy):
"""
Hammer/Hanging Man + Stochastic strategy.
Buys on hammer candle in oversold stochastic.
Sells on hanging man candle in overbought stochastic.
"""
def __init__(self):
super(hammer_hanging_stochastic_strategy, self).__init__()
self._stoch_period = self.Param("StochPeriod", 14) \
.SetDisplay("Stoch Period", "Stochastic K period", "Indicators")
self._oversold = self.Param("Oversold", 30.0) \
.SetDisplay("Oversold", "Stochastic oversold level", "Signals")
self._overbought = self.Param("Overbought", 70.0) \
.SetDisplay("Overbought", "Stochastic overbought level", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(hammer_hanging_stochastic_strategy, self).OnStarted2(time)
stoch = StochasticOscillator()
stoch.K.Length = self._stoch_period.Value
stoch.D.Length = 3
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stoch, self._process_candle).Start()
def _process_candle(self, candle, stoch_val):
if candle.State != CandleStates.Finished:
return
k_value = stoch_val.K
if k_value is None:
return
k_value = float(k_value)
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
body = abs(close - open_p)
range_val = high - low
if range_val <= 0 or body <= 0:
return
upper_shadow = high - max(open_p, close)
lower_shadow = min(open_p, close) - low
is_hammer = lower_shadow > body * 2 and upper_shadow < body
is_hanging_man = upper_shadow > body * 2 and lower_shadow < body
if is_hammer and k_value < self._oversold.Value and self.Position <= 0:
self.BuyMarket()
elif is_hanging_man and k_value > self._overbought.Value and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return hammer_hanging_stochastic_strategy()