PivotEMA3RLHv4 is a trend-following strategy that combines the daily pivot level with short-term momentum filters. It tracks a 3-period exponential moving average (EMA) calculated on candle open prices and compares it against the same EMA computed on closing prices. The setup is validated with Heiken Ashi candles to confirm direction and with multiple Average True Range (ATR) measurements to ensure that volatility is expanding. The strategy trades a single instrument on the selected intraday timeframe and always waits for the current candle to finish before making a decision.
Trading Logic
Pivot Filter – The previous EMA(3) of the open price must be below (for longs) or above (for shorts) the daily pivot level, while the current EMA(3) of the open price needs to cross to the opposite side of the pivot.
Heiken Ashi Confirmation – The current Heiken Ashi candle must be bullish (close above open) for longs or bearish (close below open) for shorts.
Momentum Check – The EMA(3) built on closing prices must lead the EMA on opens in the trade direction.
Volatility Expansion – At least one of the ATR(4), ATR(8), ATR(12) or ATR(24) values must increase compared with the previous candle, and the True Range (ATR with length 1) must either increase on this bar or have increased on the previous bar.
Position Management – Only one position is active at a time. Protective stops and targets are simulated internally and executed via market orders when hit.
Exit signals mirror the entry rules: when the opposite conditions appear, the strategy closes the current trade. Additionally, the optional stop-loss, take-profit, and trailing stop mechanisms may close a trade earlier.
Parameters
Parameter
Description
CandleType
Working timeframe for the strategy candles.
StopLossPips
Initial stop distance in pips from the entry price. Set to zero to disable.
TakeProfitPips
Profit target distance in pips. Set to zero to disable.
UseTrailingStop
Enables or disables trailing stop management.
TrailingStopType
Trailing mode: 1 keeps a fixed distance, 2 activates after price moves by TrailingStopPips, 3 uses the multi-stage ladder described below.
TrailingStopPips
Distance (in pips) used by trailing type 2.
FirstMovePips / FirstStopLossPips
Trigger distance and resulting stop offset for the first stage of trailing type 3.
SecondMovePips / SecondStopLossPips
Trigger distance and resulting stop offset for the second stage of trailing type 3.
ThirdMovePips / TrailingStop3Pips
Trigger distance and dynamic trailing distance for the final stage of trailing type 3.
Trailing Stop Modes
Type 1 – Repositions the stop so that it never lags the price by more than the initial stop distance.
Type 2 – Waits for price to move by TrailingStopPips before locking profits with the same distance.
Type 3 – Uses up to three thresholds: the first two move the stop to predefined offsets, while the third turns into a regular trailing stop.
Notes
The strategy subscribes to daily candles to calculate the pivot level from the previous day’s high, low, and close.
Indicators are updated inside the candle handler using finished bars only, which keeps the logic compatible with both online and backtesting environments.
The original MetaTrader version relied on broker-side stops; this port simulates them and exits with market orders when necessary.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Pivot EMA strategy using fast and slow EMA crossover.
/// Buy when fast EMA crosses above slow EMA.
/// Sell when fast EMA crosses below slow EMA.
/// </summary>
public class PivotEma3RlhV4Strategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public PivotEma3RlhV4Strategy()
{
_fastPeriod = Param(nameof(FastPeriod), 3)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
var bullCross = _prevFast <= _prevSlow && fast > slow;
var bearCross = _prevFast >= _prevSlow && fast < slow;
if (Position <= 0 && bullCross)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (Position >= 0 && bearCross)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class pivot_ema3_rlh_v4_strategy(Strategy):
"""Pivot EMA strategy using fast and slow EMA crossover.
Buy when fast EMA crosses above slow EMA.
Sell when fast EMA crosses below slow EMA."""
def __init__(self):
super(pivot_ema3_rlh_v4_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 3) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(pivot_ema3_rlh_v4_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(pivot_ema3_rlh_v4_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast)
slow_val = float(slow)
if not self._has_prev:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
return
bull_cross = self._prev_fast <= self._prev_slow and fast_val > slow_val
bear_cross = self._prev_fast >= self._prev_slow and fast_val < slow_val
if self.Position <= 0 and bull_cross:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self.Position >= 0 and bear_cross:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return pivot_ema3_rlh_v4_strategy()