Source code for backtrader.indicators.trix
#!/usr/bin/env python
"""TRIX Indicator Module - Triple exponential moving average slope.
This module provides the TRIX indicator developed by Jack Hutson in the
1980s to show the rate of change of a triple smoothed moving average.
Classes:
Trix: TRIX indicator (alias: TRIX).
TrixSignal: TRIX with signal line.
Example:
class MyStrategy(bt.Strategy):
def __init__(self):
self.trix = bt.indicators.TRIX(self.data.close, period=15)
def next(self):
if self.trix[0] > 0:
self.buy()
"""
import math
from . import Indicator
from .ema import EMA
[docs]
class Trix(Indicator):
"""
Defined by Jack Hutson in the 80s and shows the Rate of Change (%) or slope
of a triple exponentially smoothed moving average
Formula:
- ema1 = EMA(data, period)
- ema2 = EMA(ema1, period)
- ema3 = EMA(ema2, period)
- trix = 100 * (ema3 - ema3(-1)) / ema3(-1)
The final formula can be simplified to: 100 * (ema3 / ema3(-1) - 1)
The moving average used is the one originally defined by Wilder,
the SmoothedMovingAverage
See:
- https://en.wikipedia.org/wiki/Trix_(technical_analysis)
- http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:trix
"""
alias = ("TRIX",)
lines = ("trix",)
params = (
("period", 15),
("_rocperiod", 1),
("_movav", EMA),
)
plotinfo = {"plothlines": [0.0]}
def _plotlabel(self):
plabels = [self.p.period]
plabels += [self.p._rocperiod] * self.p.notdefault("_rocperiod")
plabels += [self.p._movav] * self.p.notdefault("_movav")
return plabels
def __init__(self):
"""Initialize the TRIX indicator.
Creates triple EMA structure for TRIX calculation.
"""
super().__init__()
self.ema1 = self.p._movav(self.data, period=self.p.period)
self.ema2 = self.p._movav(self.ema1, period=self.p.period)
self.ema3 = self.p._movav(self.ema2, period=self.p.period)
# minperiod = 3 * period + rocperiod
self._minperiod = max(self._minperiod, 3 * self.p.period + self.p._rocperiod - 2)
[docs]
def next(self):
"""Calculate TRIX for the current bar.
Formula: TRIX = 100 * (ema3 / ema3_rocperiod_ago - 1.0)
"""
rocperiod = self.p._rocperiod
ema3_curr = self.ema3[0]
ema3_prev = self.ema3[-rocperiod]
if ema3_prev != 0:
self.lines.trix[0] = 100.0 * (ema3_curr / ema3_prev - 1.0)
else:
self.lines.trix[0] = 0.0
[docs]
def once(self, start, end):
"""Calculate TRIX in runonce mode.
Computes triple EMA rate of change percentage across all bars.
"""
ema3_array = self.ema3.lines[0].array
larray = self.lines.trix.array
rocperiod = self.p._rocperiod
minperiod = 3 * self.p.period + rocperiod - 2
while len(larray) < end:
larray.append(float("nan"))
for i in range(min(minperiod, len(ema3_array))):
if i < len(larray):
larray[i] = float("nan")
for i in range(minperiod, min(end, len(ema3_array))):
ema3_curr = ema3_array[i] if i < len(ema3_array) else 0.0
ema3_prev = (
ema3_array[i - rocperiod]
if i >= rocperiod and i - rocperiod < len(ema3_array)
else 0.0
)
if (
isinstance(ema3_curr, float)
and math.isnan(ema3_curr)
or isinstance(ema3_prev, float)
and math.isnan(ema3_prev)
):
larray[i] = float("nan")
elif ema3_prev != 0:
larray[i] = 100.0 * (ema3_curr / ema3_prev - 1.0)
else:
larray[i] = 0.0
[docs]
class TrixSignal(Trix):
"""
Extension of Trix with a signal line (ala MACD)
Formula:
- trix = Trix(data, period)
- signal = EMA(trix, sigperiod)
See:
- http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:trix
"""
lines = ("signal",)
params = (("sigperiod", 9),)
def __init__(self):
"""Initialize the TRIX Signal indicator.
Sets up signal line EMA smoothing parameters.
"""
super().__init__()
self.signal_alpha = 2.0 / (1.0 + self.p.sigperiod)
self.signal_alpha1 = 1.0 - self.signal_alpha
[docs]
def nextstart(self):
"""Seed TRIX Signal calculation on first valid bar.
Initializes signal line with TRIX value.
"""
super().next()
self.lines.signal[0] = self.lines.trix[0]
[docs]
def next(self):
"""Calculate TRIX and signal line for current bar.
Signal line is EMA of TRIX values.
"""
super().next()
self.lines.signal[0] = (
self.lines.signal[-1] * self.signal_alpha1 + self.lines.trix[0] * self.signal_alpha
)
[docs]
def once(self, start, end):
"""Calculate TRIX Signal in runonce mode.
Computes signal line as EMA of TRIX values across all bars.
"""
super().once(start, end)
trix_array = self.lines.trix.array
signal_array = self.lines.signal.array
signal_alpha = self.signal_alpha
signal_alpha1 = self.signal_alpha1
while len(signal_array) < end:
signal_array.append(float("nan"))
# Find first valid trix value for seed
seed_idx = -1
for i in range(len(trix_array)):
if i < len(trix_array):
val = trix_array[i]
if not (isinstance(val, float) and math.isnan(val)):
seed_idx = i
break
if seed_idx >= 0 and seed_idx < len(signal_array):
prev_signal = trix_array[seed_idx]
signal_array[seed_idx] = prev_signal
for i in range(seed_idx + 1, min(end, len(trix_array))):
trix_val = trix_array[i] if i < len(trix_array) else 0.0
if isinstance(trix_val, float) and math.isnan(trix_val):
signal_array[i] = float("nan")
else:
prev_signal = prev_signal * signal_alpha1 + trix_val * signal_alpha
signal_array[i] = prev_signal