View on GitHub

Harami Strategy

Overview

HaramiStrategy converts the MetaTrader "Harami" expert advisor into StockSharp's high-level API. The strategy combines a bullish/bearish Harami pattern detected on a higher timeframe with momentum expansion and a long-term MACD filter. Only completed candles are processed and all trade management is handled through StockSharp's built-in protection engine.

Data and indicators

  • Base timeframe: configurable (15-minute candles by default) for moving-average trend detection.
  • Higher timeframe: configurable (defaults to one hour) for pattern recognition and momentum confirmation.
  • MACD timeframe: configurable (defaults to 30-day candles) to emulate the original monthly MACD filter.
  • Indicators:
    • Linear Weighted Moving Average (FastMaLength) on the base timeframe.
    • Exponential Moving Average (SlowMaLength) on the base timeframe.
    • Momentum (MomentumPeriod) on the higher timeframe. The strategy uses the absolute distance from the neutral value (100) for the latest three higher-timeframe bars.
    • Moving Average Convergence Divergence (12/26/9) on the MACD timeframe.

Long setup

  1. Slow EMA is above the fast LWMA on the base timeframe, signalling an uptrend.
  2. The higher timeframe forms a bullish Harami sequence: two candles ago was bearish, the previous candle was bullish, and its body is smaller than the earlier bearish body.
  3. Any of the last three higher-timeframe momentum deviations exceed MomentumBuyThreshold.
  4. MACD main line is above the signal line on the MACD timeframe.
  5. No long position is open (Position <= 0).
  6. The strategy sends a market buy order sized to flip any short exposure and add Volume lots.

Short setup

  1. Slow EMA is below the fast LWMA on the base timeframe.
  2. The higher timeframe forms a bearish Harami: two candles ago was bullish, the previous candle was bearish, and the latest body is smaller.
  3. Any of the last three higher-timeframe momentum deviations exceed MomentumSellThreshold.
  4. MACD main line is below the signal line.
  5. No short exposure is open (Position >= 0).
  6. The strategy sends a market sell order large enough to close long positions and open a new short position of size Volume.

Risk management

StartProtection installs stop-loss and take-profit levels (expressed in points). Additional trailing, break-even and money-management features from the original EA are intentionally omitted to keep the StockSharp version concise. Trade direction changes automatically flatten the opposite exposure.

Parameters

Name Description Default
CandleType Primary timeframe for moving averages and signal execution. 15-minute candles
HigherCandleType Timeframe used for Harami and momentum confirmation. 1-hour candles
MacdCandleType Timeframe for the MACD trend filter. 30-day candles
FastMaLength Fast linear weighted MA length. 6
SlowMaLength Slow exponential MA length. 85
MomentumPeriod Momentum lookback on the higher timeframe. 14
MomentumBuyThreshold Minimum momentum deviation for long confirmation. 0.3
MomentumSellThreshold Minimum momentum deviation for short confirmation. 0.3
StopLossPoints Stop-loss distance in points. 40
TakeProfitPoints Take-profit distance in points. 100

Usage tips

  • Align CandleType, HigherCandleType and MacdCandleType with available historical data; ensure the higher timeframe is longer than the base timeframe.
  • Adjust momentum thresholds to match the volatility of the traded instrument.
  • Use StockSharp's optimizer through the provided parameter ranges to tune MA lengths and momentum thresholds.
  • Always backtest with realistic commission/latency settings before deploying live.
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;

public class HaramiStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public HaramiStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}