The Heiken Ashi Smoothed MTF strategy is a port of the "HASNEWJ" MetaTrader expert advisor. It rebuilds the custom smoothed Heiken Ashi indicator on six timeframes (M1, M5, M15, M30, H1, H4) and waits for trend alignment across the higher frames. A trade is opened when the lower M5 stream shows a fresh pullback while the longer-term smoothed candles remain strongly bullish or bearish. Manual stop-loss and take-profit logic replicates the behaviour of the original EA, including the ability to widen the stop slightly after a losing trade.
Indicators and Data
Smoothed Heiken Ashi candles on M1, M5, M15, M30, H1 and H4.
The first smoothing pass applies a configurable moving average method/length to the raw OHLC values.
The second pass smooths the interim Heiken Ashi open/close with another configurable moving average.
Directional counters that track how many one-minute updates each timeframe has stayed bullish or bearish.
Raw close price from the M1 series for risk management checks.
Entry Logic
Update the smoothed Heiken Ashi direction for each timeframe whenever a candle finishes.
On every finished M1 candle, increment or reset the bullish/bearish counters depending on the latest direction of each timeframe.
Buy conditions:
M5 smoothed Heiken Ashi is bullish and the bullish counter is below MaxM5TrendLength (default 10 updates).
M15 smoothed Heiken Ashi is bullish and its bullish counter is above MinM15TrendLength (default 200 updates).
M30, H1 and H4 smoothed Heiken Ashi candles are also bullish.
No long position is currently open (short exposure is allowed and will be flipped).
Sell conditions:
M5 smoothed Heiken Ashi is bearish and the bearish counter is below MaxM5TrendLength.
M15 smoothed Heiken Ashi is bearish and its bearish counter is above MinM15TrendLength.
M30, H1 and H4 smoothed candles are bearish.
No short position is currently open (long exposure is closed or reversed).
The market order volume equals TradeVolume plus the absolute value of the opposite exposure to ensure flips close the prior trade.
Risk Management
A manual stop-loss and take-profit are evaluated on every finished M1 candle using Security.PriceStep.
The take-profit closes the position once price moves TakeProfitPoints steps in favour of the trade.
The stop-loss closes the position once price moves StopLossPoints steps against the trade.
After a losing trade the next entry widens the stop-loss by ExtraStopLossPoints steps, mimicking the EA's "fail" flag.
Trade volume is fixed by TradeVolume; no pyramiding or scaling logic is applied beyond reversing existing exposure.
Parameters
Name
Description
Default
TradeVolume
Base order volume used for entries
0.1
TakeProfitPoints
Take-profit distance in price steps
20
StopLossPoints
Stop-loss distance in price steps
500
ExtraStopLossPoints
Additional stop steps applied after a losing trade
5
FirstMaPeriod
Length of the first smoothing moving average
6
FirstMaMethod
Method of the first smoothing MA (Simple, Exponential, Smoothed, LinearWeighted)
Smoothed
SecondMaPeriod
Length of the second smoothing moving average
2
SecondMaMethod
Method of the second smoothing MA
LinearWeighted
MaxM5TrendLength
Maximum number of M5 updates allowed before cancelling a pullback entry
10
MinM15TrendLength
Minimum number of M15 updates required to confirm the higher trend
200
M1CandleType
Data type for the base one-minute candle stream
TimeFrame(00:01:00)
M5CandleType
Data type for the five-minute confirmation stream
TimeFrame(00:05:00)
M15CandleType
Data type for the fifteen-minute confirmation stream
TimeFrame(00:15:00)
M30CandleType
Data type for the thirty-minute confirmation stream
TimeFrame(00:30:00)
H1CandleType
Data type for the hourly confirmation stream
TimeFrame(01:00:00)
H4CandleType
Data type for the four-hour confirmation stream
TimeFrame(04:00:00)
Usage Notes
The directional counters are updated once per finished M1 candle, which approximates the tick-based counters from MetaTrader while keeping the implementation candle-driven.
Ensure Security.PriceStep is configured; otherwise the strategy falls back to a 0.0001 step when computing stop and target levels.
Both smoothing passes rely on moving averages; experimenting with different combinations of methods and periods can adapt the system to instruments with different volatility profiles.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Heiken Ashi Smoothed MTF: EMA trend with RSI filter and ATR stops.
/// </summary>
public class HeikenAshiSmoothedMtfStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevClose;
private decimal _entryPrice;
public HeikenAshiSmoothedMtfStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0; _entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (_prevClose == 0 || atrVal <= 0) { _prevClose = close; return; }
if (Position > 0)
{
if (close < emaVal || close <= _entryPrice - atrVal * 1.5m || close >= _entryPrice + atrVal * 2.5m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close > emaVal || close >= _entryPrice + atrVal * 1.5m || close <= _entryPrice - atrVal * 2.5m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close > emaVal && _prevClose <= emaVal && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
else if (close < emaVal && _prevClose >= emaVal && rsiVal < 50) { _entryPrice = close; SellMarket(); }
}
_prevClose = close;
}
}
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, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class heiken_ashi_smoothed_mtf_strategy(Strategy):
"""
Heiken Ashi Smoothed MTF: EMA trend with RSI filter and ATR stops.
Buys on EMA cross up with RSI > 50. Sells on EMA cross down with RSI < 50.
Uses ATR for dynamic SL/TP.
"""
def __init__(self):
super(heiken_ashi_smoothed_mtf_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "Trend filter", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_close = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(heiken_ashi_smoothed_mtf_strategy, self).OnReseted()
self._prev_close = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(heiken_ashi_smoothed_mtf_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, rsi, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema = float(ema_val)
rsi = float(rsi_val)
atr = float(atr_val)
if self._prev_close == 0.0 or atr <= 0:
self._prev_close = close
return
if self.Position > 0:
if close < ema or close <= self._entry_price - atr * 1.5 or close >= self._entry_price + atr * 2.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close > ema or close >= self._entry_price + atr * 1.5 or close <= self._entry_price - atr * 2.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > ema and self._prev_close <= ema and rsi > 50:
self._entry_price = close
self.BuyMarket()
elif close < ema and self._prev_close >= ema and rsi < 50:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def CreateClone(self):
return heiken_ashi_smoothed_mtf_strategy()