The Bullish Harami is a two-candle pattern where a small body is contained within the range of the prior bearish candle.
It hints that selling momentum has stalled and buyers may step back in.
Testing indicates an average annual return of about 40%. It performs best in the crypto market.
This strategy enters long once the second candle closes inside the first, expecting follow-through to the upside on the next bar.
A percent stop beneath the pattern provides protection, and the trade exits if price slips back below the setup.
Details
Entry Criteria: pattern match
Long/Short: Both
Exit Criteria: stop-loss or opposite signal
Stops: Yes, percent based
Default Values:
CandleType = 15 minute
StopLossPercent = 2
The strategy only buys. The stop lies StopLossPercent below the lowest low of the pattern and the position closes when a candle closes below it.
Filters:
Category: Pattern
Direction: Both
Indicators: Candlestick
Stops: Yes
Complexity: Intermediate
Timeframe: Intraday
Seasonality: No
Neural networks: No
Divergence: No
Risk level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bullish Harami strategy.
/// While flat it buys after a bearish candle followed by a candle whose smaller body lies within the first candle's range.
/// The stop lies StopLossPercent below the pattern's lowest low, and a close beyond it closes the position.
/// </summary>
public class HaramiBullishStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _candles = [];
private decimal _stopPrice;
/// <summary>
/// Distance of the stop beyond the pattern, in percent.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HaramiBullishStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Distance of the stop beyond the pattern, in percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_stopPrice = default;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_candles.Add(candle);
if (_candles.Count > 2)
_candles.RemoveAt(0);
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if (candle.ClosePrice <= _stopPrice)
SellMarket(Position);
return;
}
if (Position != 0 || _candles.Count < 2)
return;
var c0 = _candles[0];
var c1 = _candles[1];
if (!(c0.ClosePrice < c0.OpenPrice && Math.Abs(c1.ClosePrice - c1.OpenPrice) < c0.OpenPrice - c0.ClosePrice && Math.Min(c1.OpenPrice, c1.ClosePrice) >= c0.LowPrice && Math.Max(c1.OpenPrice, c1.ClosePrice) <= c0.HighPrice))
return;
BuyMarket(Volume);
_stopPrice = Math.Min(c0.LowPrice, c1.LowPrice) * (1 - StopLossPercent / 100m);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class harami_bullish_strategy(Strategy):
"""
Bullish Harami strategy.
While flat it buys after a bearish candle followed by a candle whose smaller body lies within the first candle's range.
The stop lies StopLossPercent below the pattern's lowest low, and a close beyond it closes the position.
"""
def __init__(self):
super(harami_bullish_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Distance of the stop beyond the pattern, in percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._candles = []
self._stop_price = Decimal(0)
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(harami_bullish_strategy, self).OnReseted()
self._candles = []
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(harami_bullish_strategy, self).OnStarted2(time)
self._candles = []
self._stop_price = Decimal(0)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._candles.append(candle)
if len(self._candles) > 2:
self._candles.pop(0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if candle.ClosePrice <= self._stop_price:
self.SellMarket(self.Position)
return
if self.Position != 0 or len(self._candles) < 2:
return
c0, c1 = self._candles
if not (c0.ClosePrice < c0.OpenPrice and Math.Abs(c1.ClosePrice - c1.OpenPrice) < c0.OpenPrice - c0.ClosePrice and Math.Min(c1.OpenPrice, c1.ClosePrice) >= c0.LowPrice and Math.Max(c1.OpenPrice, c1.ClosePrice) <= c0.HighPrice):
return
self.BuyMarket(self.Volume)
self._stop_price = min(c0.LowPrice, c1.LowPrice) * (Decimal(1) - Decimal(self._stop_loss_percent.Value) / Decimal(100))
def CreateClone(self):
return harami_bullish_strategy()