The Alli Heik strategy is a conversion of the MetaTrader 5 expert advisor "AlliHeik". It trades the Heiken Ashi Smoothed Oscillator (HASO) originally published by mladen. The indicator builds a custom Heiken Ashi candle by smoothing the raw open, high, low, and close prices with a selectable moving average, applies an additional smoothing pass to the Heiken Ashi midpoint, and then measures the bar-to-bar difference of that smoothed value. A moving average of the difference forms the signal line.
Trading decisions are made on the crossover of the oscillator and signal line evaluated on fully closed candles. The strategy offers an optional reverse mode, the ability to automatically close opposite positions, static stop-loss/take-profit handling, and a trailing stop that mimics the step logic of the original MetaTrader version.
Trading rules
Indicator preparation
Pre-smooth OHLC data with one of SMA, EMA, SMMA, or LWMA.
Build Heiken Ashi candles from the smoothed data and average open/close to obtain a midpoint.
Post-smooth the midpoint and compute the oscillator as the difference between consecutive smoothed values.
Smooth the oscillator with a configurable moving average to create the signal line.
Entry conditions
Normal mode: open a long when the oscillator crosses below the signal line, open a short when it crosses above the signal line (exactly reproducing the MQL logic).
Reverse mode: swap the long and short conditions.
Signals are evaluated on finished candles only. Existing positions can optionally be closed before entering a new trade in the opposite direction.
Exit management
Static stop-loss and take-profit distances are expressed in pips and converted to price using the security tick size and decimals.
A trailing stop becomes active once price advances by TrailingStop + TrailingStep pips in profit. The stop is then shifted to current price - TrailingStop for longs (or current price + TrailingStop for shorts) and only moves if the new stop is at least TrailingStep pips beyond the previous level.
Manual exits are issued if price touches the configured stop or target.
Parameters
Volume – order volume in lots.
Stop Loss (pips) – distance for the protective stop; set to 0 to disable.
Take Profit (pips) – distance for the profit target; set to 0 to disable.
Trailing Stop (pips) – trailing stop distance; set to 0 to disable trailing.
Trailing Step (pips) – minimum advance beyond the trailing stop before the stop is moved (must be positive when trailing is enabled).
Reverse Signals – invert long/short interpretation of the oscillator crossover.
Close Opposite – close an existing position before opening a new trade in the opposite direction.
Pre Smooth Period / Method – moving average period and type used to smooth the raw OHLC data.
Post Smooth Period / Method – moving average parameters for smoothing the Heiken Ashi midpoint.
Signal Period / Method – moving average parameters for the oscillator signal line.
Candle Type – candle source used for calculations (default 15-minute time frame).
Implementation notes
The conversion reproduces the original Heiken Ashi Smoothed Oscillator by chaining StockSharp moving average indicators (SMA, EMA, SMMA, LWMA) to pre-smooth prices, build the Heiken Ashi series, and derive the oscillator difference.
Pip distances are translated to absolute price offsets using the security tick size and decimal precision, matching the 3/5 digit handling from MetaTrader.
Manual stop/target checks and the step-based trailing stop are executed on every finished candle, closely mirroring the behavior of the MQL version.
Signals are processed only when all required values are available; partial indicator states are ignored until enough data has accumulated.
No Python translation is provided in this directory.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Alli Heik strategy.
/// Uses Heikin Ashi candle patterns with EMA filter for trend following.
/// Buys on bullish HA candles when above EMA, sells on bearish HA candles when below EMA.
/// </summary>
public class AlliHeikStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private decimal _prevHaOpen;
private decimal _prevHaClose;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public AlliHeikStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles for the strategy", "General");
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Trend filter EMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHaOpen = 0m;
_prevHaClose = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHaOpen = 0;
_prevHaClose = 0;
_initialized = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Calculate Heikin Ashi values
decimal haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
decimal haOpen;
if (!_initialized)
{
haOpen = (candle.OpenPrice + candle.ClosePrice) / 2m;
_initialized = true;
}
else
{
haOpen = (_prevHaOpen + _prevHaClose) / 2m;
}
var haBullish = haClose > haOpen;
var haBearish = haClose < haOpen;
var prevHaBullish = _prevHaClose > _prevHaOpen;
var prevHaBearish = _prevHaClose < _prevHaOpen;
// Buy only on a bearish-to-bullish HA flip confirmed by the EMA filter.
if (haBullish && prevHaBearish && candle.ClosePrice > emaValue && Position <= 0)
{
BuyMarket();
}
// Sell only on a bullish-to-bearish HA flip confirmed by the EMA filter.
else if (haBearish && prevHaBullish && candle.ClosePrice < emaValue && Position >= 0)
{
SellMarket();
}
_prevHaOpen = haOpen;
_prevHaClose = haClose;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class alli_heik_strategy(Strategy):
def __init__(self):
super(alli_heik_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles for the strategy", "General")
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "Trend filter EMA period", "Indicators")
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaPeriod(self):
return self._ema_period.Value
def OnReseted(self):
super(alli_heik_strategy, self).OnReseted()
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._initialized = False
def OnStarted2(self, time):
super(alli_heik_strategy, self).OnStarted2(time)
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._initialized = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(ema, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
ev = float(ema_value)
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
ha_close = (o + h + l + c) / 4.0
if not self._initialized:
ha_open = (o + c) / 2.0
self._initialized = True
else:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
ha_bullish = ha_close > ha_open
ha_bearish = ha_close < ha_open
prev_ha_bullish = self._prev_ha_close > self._prev_ha_open
prev_ha_bearish = self._prev_ha_close < self._prev_ha_open
if ha_bullish and prev_ha_bearish and c > ev and self.Position <= 0:
self.BuyMarket()
elif ha_bearish and prev_ha_bullish and c < ev and self.Position >= 0:
self.SellMarket()
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
def CreateClone(self):
return alli_heik_strategy()