Ver en GitHub

PivotEMA3RLHv4 Strategy

Overview

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

  1. 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.
  2. Heiken Ashi Confirmation – The current Heiken Ashi candle must be bullish (close above open) for longs or bearish (close below open) for shorts.
  3. Momentum Check – The EMA(3) built on closing prices must lead the EMA on opens in the trade direction.
  4. 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.
  5. 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;
	}
}