/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace QuantConnect.Indicators
{
///
/// This indicator computes the True Strength Index (TSI).
/// The True Strength Index is calculated as explained here:
/// https://school.stockcharts.com/doku.php?id=technical_indicators:true_strength_index
///
/// Briefly, the calculation has three steps:
/// 1. Smooth the momentum and the absolute momentum by getting an EMA of them (typically of period 25)
/// 2. Double smooth the momentum and the absolute momentum by getting an EMA of their EMA (typically of period 13)
/// 3. The TSI formula itself: divide the double-smoothed momentum over the double-smoothed absolute momentum and multiply by 100
///
/// The signal is typically a 7-to-12-EMA of the TSI.
///
public class TrueStrengthIndex : Indicator, IIndicatorWarmUpPeriodProvider
{
private decimal _prevClose;
private readonly ExponentialMovingAverage _priceChangeEma;
private readonly ExponentialMovingAverage _priceChangeEmaEma;
private readonly ExponentialMovingAverage _absPriceChangeEma;
private readonly ExponentialMovingAverage _absPriceChangeEmaEma;
private readonly IndicatorBase _tsi;
///
/// Gets the signal line for the TSI indicator
///
public IndicatorBase Signal { get; }
///
/// Required period, in data points, for the indicator to be ready and fully initialized.
///
public int WarmUpPeriod { get; }
///
/// Gets a flag indicating when this indicator is ready and fully initialized
///
public override bool IsReady => Samples >= WarmUpPeriod;
///
/// Initializes a new instance of the class using the specified short and long term smoothing periods, and the signal period and type.
///
/// Period used for the first price change smoothing
/// Period used for the second (double) price change smoothing
/// The signal period
/// The type of moving average to use for the signal
public TrueStrengthIndex(int longTermPeriod = 25, int shortTermPeriod = 13, int signalPeriod = 7, MovingAverageType signalType = MovingAverageType.Exponential)
: this($"TSI({longTermPeriod},{shortTermPeriod},{signalPeriod})", longTermPeriod, shortTermPeriod, signalPeriod, signalType)
{
}
///
/// Initializes a new instance of the class using the specified name, the short and long term smoothing periods, and the signal period and type.
///
/// The name of the indicator
/// Period used for the first price change smoothing
/// Period used for the second (double) price change smoothing
/// The signal period
/// The type of moving average to use for the signal
public TrueStrengthIndex(string name, int longTermPeriod = 25, int shortTermPeriod = 13, int signalPeriod = 7, MovingAverageType signalType = MovingAverageType.Exponential)
: base(name)
{
_priceChangeEma = new ExponentialMovingAverage(name + "_PC_EMA", longTermPeriod);
_absPriceChangeEma = new ExponentialMovingAverage(name + "_APC_EMA", longTermPeriod);
_priceChangeEmaEma = new ExponentialMovingAverage(name + "_PC_EMA_EMA", shortTermPeriod).Of(_priceChangeEma, true);
_absPriceChangeEmaEma = new ExponentialMovingAverage(name + "_APC_EMA_EMA", shortTermPeriod).Of(_absPriceChangeEma, true);
_tsi = _priceChangeEmaEma.Over(_absPriceChangeEmaEma).Times(100m);
Signal = signalType.AsIndicator(name + "_Signal", signalPeriod).Of(_tsi, true);
WarmUpPeriod = longTermPeriod + shortTermPeriod;
}
///
/// Computes the next value of this indicator from the given state
///
/// The input given to the indicator
/// A new value for this indicator
protected override decimal ComputeNextValue(IndicatorDataPoint input)
{
if (Samples == 1)
{
_prevClose = input.Price;
return 0m;
}
var priceChange = input.Price - _prevClose;
_prevClose = input.Price;
_priceChangeEma.Update(input.EndTime, priceChange);
_absPriceChangeEma.Update(input.EndTime, Math.Abs(priceChange));
return _tsi.Current.Value;
}
///
/// Resets this indicator to its initial state
///
public override void Reset()
{
_prevClose = 0;
_priceChangeEma.Reset();
_priceChangeEmaEma.Reset();
_absPriceChangeEma.Reset();
_absPriceChangeEmaEma.Reset();
_tsi.Reset();
Signal.Reset();
base.Reset();
}
}
}