View on GitHub

Exp Trend Intensity Index Strategy

This strategy is a StockSharp conversion of the MetaTrader expert Exp_Trend_Intensity_Index. It trades finished candles on a configurable timeframe and uses the Trend Intensity Index (TII) to detect when momentum leaves extreme bullish or bearish zones. When the indicator transitions out of an upper zone the algorithm exits shorts and can start a new long. When the indicator leaves a lower zone the algorithm closes longs and can start a new short.

How the indicator is built

  1. Select the price source (close, open, weighted variants, trend-follow prices, etc.).
  2. Smooth that price stream with a first moving average (PriceMaMethod, PriceMaLength).
  3. Split the difference between price and the smoothed value into positive and negative flows.
  4. Smooth the positive and negative flows independently with a second moving average (SmoothingMethod, SmoothingLength).
  5. Calculate the Trend Intensity Index: TII = 100 * Positive / (Positive + Negative).
  6. Compare the result with the HighLevel and LowLevel thresholds to assign a color state: high zone (0), neutral (1), or low zone (2).

The implementation uses StockSharp moving averages (simple, exponential, smoothed, weighted). Advanced smoothing types from the original MQL library are not available in this port.

Trading logic

  • Signals are processed only when a candle is fully closed (CandleStates.Finished).
  • The SignalBar parameter defines which completed bar is analysed (default one bar back). The strategy also inspects the bar immediately before that, matching the double-buffer lookup in the MQL code.
  • When the older bar belongs to the high zone (color == 0):
    • Close any short position if EnableSellExits is true.
    • If the more recent bar left the high zone and EnableBuyEntries is true, open or reverse into a long.
  • When the older bar belongs to the low zone (color == 2):
    • Close any long position if EnableBuyExits is true.
    • If the more recent bar left the low zone and EnableSellEntries is true, open or reverse into a short.
  • Orders are submitted with BuyMarket and SellMarket. Position reversals use the current position volume plus the configured Volume property.
  • Optional stop-loss and take-profit protection (price units) is configured through StopLossPoints and TakeProfitPoints and implemented with StartProtection.

Parameters

Parameter Description
CandleType Timeframe used for indicator calculation and trading.
PriceMaMethod, PriceMaLength Moving average type and period applied to the base price stream.
SmoothingMethod, SmoothingLength Moving average type and period applied to the positive and negative flows.
AppliedPrice Price source for the indicator (close, open, median, trend-follow variants, Demark, etc.).
HighLevel, LowLevel Upper and lower thresholds that define bullish and bearish zones.
SignalBar Number of completed bars to look back for signal confirmation.
EnableBuyEntries, EnableSellEntries Toggles that allow opening long/short positions.
EnableBuyExits, EnableSellExits Toggles that allow automatic exits when the indicator flips.
StopLossPoints, TakeProfitPoints Optional protective distances expressed in price units for StartProtection.

Differences from the original MQL expert

  • Money-management options (MM, MMMode, Deviation) are replaced with StockSharp's standard volume property and order execution; slippage management is not replicated.
  • Only the moving average types available in StockSharp (simple, exponential, smoothed, weighted) are supported.
  • Phase parameters from the MQL indicator are omitted because StockSharp indicators do not expose equivalent controls.
  • Orders are executed immediately after a signal is confirmed on the finished candle; there is no explicit scheduling for the next bar opening.

These changes keep the trading idea intact while following StockSharp high-level strategy guidelines.

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>
/// Trend Intensity Index strategy. Uses EMA crossover for trend following.
/// </summary>
public class ExpTrendIntensityIndexStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _prevSlow;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public ExpTrendIntensityIndexStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = null;
		_prevSlow = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevFast = null;
		_prevSlow = null;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fast);
			DrawIndicator(area, slow);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fastVal > slowVal;

		_prevFast = fastVal;
		_prevSlow = slowVal;

		if (!prevAbove && currAbove && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (prevAbove && !currAbove && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}