View on GitHub

TRIX Crossover Strategy

This strategy uses two TRIX (Triple Exponential Moving Average Oscillator) indicators with different periods to detect potential reversals. A long position is opened when the fast TRIX forms a local bottom while the slow TRIX is rising. A short position is opened when the fast TRIX forms a local top while the slow TRIX is falling.

Parameters

  • Fast TRIX Period – period of the fast TRIX indicator.
  • Slow TRIX Period – period of the slow TRIX indicator.
  • Take Profit – profit target in absolute price units.
  • Stop Loss – maximum loss in absolute price units.
  • Candle Type – timeframe or data type for candles.

Trading Logic

  1. Subscribe to the selected candle type.
  2. Compute fast and slow TRIX values on each finished candle.
  3. Enter long when the fast TRIX value is higher than its previous value, the previous value is lower than the value before it, and the slow TRIX is rising.
  4. Enter short when the fast TRIX value is lower than its previous value, the previous value is higher than the value before it, and the slow TRIX is falling.
  5. Only one position is held at a time.
  6. Stop loss and take profit protections are applied automatically.

Notes

The strategy is an adaptation of an MQL5 script and demonstrates how to work with TRIX indicators within StockSharp.

using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy based on fast and slow TRIX indicator signals.
/// A long position opens when the fast TRIX forms a local bottom and the slow TRIX is rising.
/// A short position opens when the fast TRIX forms a local top and the slow TRIX is falling.
/// </summary>
public class TrixCrossoverStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<decimal> _minTrix;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<DataType> _candleType;

	// Store previous TRIX values for decision making
	private decimal _fastTrixPrev1;
	private decimal _fastTrixPrev2;
	private decimal _slowTrixPrev;
	private decimal _prevFastTema;
	private decimal _prevSlowTema;
	private TripleExponentialMovingAverage _fastTema = null!;
	private TripleExponentialMovingAverage _slowTema = null!;

	/// <summary>
	/// Fast TRIX period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow TRIX period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Minimum TRIX value required for a signal.
	/// </summary>
	public decimal MinTrix
	{
		get => _minTrix.Value;
		set => _minTrix.Value = value;
	}

	/// <summary>
	/// Take profit size in absolute price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Stop loss size in absolute price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes <see cref="TrixCrossoverStrategy"/>.
	/// </summary>
	public TrixCrossoverStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Fast TRIX Period", "Period for the fast TRIX indicator", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow TRIX Period", "Period for the slow TRIX indicator", "Indicators");
		_minTrix = Param(nameof(MinTrix), 0.0005m)
			.SetGreaterThanZero()
			.SetDisplay("Min TRIX", "Minimum TRIX magnitude for signals", "Indicators");
		_takeProfit = Param(nameof(TakeProfit), 1500m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take profit in absolute price units", "Risk Management");
		_stopLoss = Param(nameof(StopLoss), 500m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop loss in absolute price units", "Risk Management");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_fastTrixPrev1 = 0m;
		_fastTrixPrev2 = 0m;
		_slowTrixPrev = 0m;
		_prevFastTema = 0m;
		_prevSlowTema = 0m;
		_fastTema = null!;
		_slowTema = null!;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fastTrixPrev1 = 0m;
		_fastTrixPrev2 = 0m;
		_slowTrixPrev = 0m;
		_prevFastTema = 0m;
		_prevSlowTema = 0m;
		_fastTema = new TripleExponentialMovingAverage { Length = FastPeriod };
		_slowTema = new TripleExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_fastTema, _slowTema, ProcessCandle)
			.Start();

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

		StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));
	}

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

		if (_prevFastTema == 0m || _prevSlowTema == 0m)
		{
			_prevFastTema = fastTemaValue;
			_prevSlowTema = slowTemaValue;
			return;
		}

		var fastTrix = (fastTemaValue - _prevFastTema) / _prevFastTema;
		var slowTrix = (slowTemaValue - _prevSlowTema) / _prevSlowTema;

		_prevFastTema = fastTemaValue;
		_prevSlowTema = slowTemaValue;

		var prevFastTrix = _fastTrixPrev1;
		_fastTrixPrev2 = _fastTrixPrev1;
		_fastTrixPrev1 = fastTrix;

		var slowTrixPrev = _slowTrixPrev;
		_slowTrixPrev = slowTrix;

		if (_fastTrixPrev2 == 0m || slowTrixPrev == 0m)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var crossUp = prevFastTrix <= 0 && fastTrix > 0;
		var crossDown = prevFastTrix >= 0 && fastTrix < 0;

		if (crossUp && slowTrix > MinTrix && Position <= 0)
			BuyMarket();
		else if (crossDown && slowTrix < -MinTrix && Position >= 0)
			SellMarket();
	}
}