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
- Subscribe to the selected candle type.
- Compute fast and slow TRIX values on each finished candle.
- 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.
- 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.
- Only one position is held at a time.
- 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();
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import TripleExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class trix_crossover_strategy(Strategy):
def __init__(self):
super(trix_crossover_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 9) \
.SetDisplay("Fast TRIX Period", "Period for the fast TRIX indicator", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow TRIX Period", "Period for the slow TRIX indicator", "Indicators")
self._min_trix = self.Param("MinTrix", 0.0005) \
.SetDisplay("Min TRIX", "Minimum TRIX magnitude for signals", "Indicators")
self._take_profit = self.Param("TakeProfit", 1500.0) \
.SetDisplay("Take Profit", "Take profit in absolute price units", "Risk Management")
self._stop_loss = self.Param("StopLoss", 500.0) \
.SetDisplay("Stop Loss", "Stop loss in absolute price units", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._fast_trix_prev1 = 0.0
self._fast_trix_prev2 = 0.0
self._slow_trix_prev = 0.0
self._prev_fast_tema = 0.0
self._prev_slow_tema = 0.0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def min_trix(self):
return self._min_trix.Value
@property
def take_profit(self):
return self._take_profit.Value
@property
def stop_loss(self):
return self._stop_loss.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(trix_crossover_strategy, self).OnReseted()
self._fast_trix_prev1 = 0.0
self._fast_trix_prev2 = 0.0
self._slow_trix_prev = 0.0
self._prev_fast_tema = 0.0
self._prev_slow_tema = 0.0
def OnStarted2(self, time):
super(trix_crossover_strategy, self).OnStarted2(time)
self._fast_trix_prev1 = 0.0
self._fast_trix_prev2 = 0.0
self._slow_trix_prev = 0.0
self._prev_fast_tema = 0.0
self._prev_slow_tema = 0.0
fast_tema = TripleExponentialMovingAverage()
fast_tema.Length = int(self.fast_period)
slow_tema = TripleExponentialMovingAverage()
slow_tema.Length = int(self.slow_period)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_tema, slow_tema, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_tema)
self.DrawIndicator(area, slow_tema)
self.DrawOwnTrades(area)
self.StartProtection(
takeProfit=Unit(float(self.take_profit), UnitTypes.Absolute),
stopLoss=Unit(float(self.stop_loss), UnitTypes.Absolute))
def process_candle(self, candle, fast_tema_value, slow_tema_value):
if candle.State != CandleStates.Finished:
return
fast_tema_value = float(fast_tema_value)
slow_tema_value = float(slow_tema_value)
if self._prev_fast_tema == 0.0 or self._prev_slow_tema == 0.0:
self._prev_fast_tema = fast_tema_value
self._prev_slow_tema = slow_tema_value
return
fast_trix = (fast_tema_value - self._prev_fast_tema) / self._prev_fast_tema if self._prev_fast_tema != 0 else 0.0
slow_trix = (slow_tema_value - self._prev_slow_tema) / self._prev_slow_tema if self._prev_slow_tema != 0 else 0.0
self._prev_fast_tema = fast_tema_value
self._prev_slow_tema = slow_tema_value
prev_fast_trix = self._fast_trix_prev1
self._fast_trix_prev2 = self._fast_trix_prev1
self._fast_trix_prev1 = fast_trix
slow_trix_prev = self._slow_trix_prev
self._slow_trix_prev = slow_trix
if self._fast_trix_prev2 == 0.0 or slow_trix_prev == 0.0:
return
mt = float(self.min_trix)
cross_up = prev_fast_trix <= 0 and fast_trix > 0
cross_down = prev_fast_trix >= 0 and fast_trix < 0
if cross_up and slow_trix > mt and self.Position <= 0:
self.BuyMarket()
elif cross_down and slow_trix < -mt and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return trix_crossover_strategy()