/*
* 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;
using System.Linq;
using QuantConnect.Data.Market;
namespace QuantConnect.Indicators
{
///
/// This indicator calculates the Swing Index (SI) as defined by Welles Wilder
/// in his book 'New Concepts in Technical Trading Systems'.
///
/// SIₜ = 50 * ( N / R ) * ( K / T )
///
///
/// Where:
///
/// -
/// N
///
/// Equals: Cₜ - Cₜ₋₁ + 0.5 * (Cₜ - Oₜ) + 0.25 * (Cₜ₋₁ - Oₜ₋₁)
///
/// See
///
///
///
/// -
/// R
///
/// Found by selecting the expression with the largest value and
/// then using the corresponding formula.
///
/// Expression => Formula
///
/// -
///
/// |Hₜ - Cₜ₋₁| => |Hₜ - Cₜ| - 0.5 * |Lₜ - Cₜ₋₁| + 0.25 * |Cₜ₋₁ - Oₜ₋₁|
///
///
/// -
///
/// |Lₜ - Cₜ₋₁| => |Lₜ - Cₜ| - 0.5 * |Hₜ - Cₜ₋₁| + 0.25 * |Cₜ₋₁ - Oₜ₋₁|
///
///
/// -
///
/// |Hₜ - Lₜ| => |Hₜ - Lₜ₋₁| + 0.25 * |Cₜ₋₁ - Oₜ₋₁|
///
///
///
///
///
/// See
///
///
///
/// -
/// K
///
/// Found by selecting the larger of the two expressions:
/// |Hₜ - Cₜ₋₁|, |Lₜ - Cₜ₋₁|
///
/// See
///
///
///
/// -
/// T
///
/// The limit move, or the maximum change in price during the time
/// period for the bar. Passed as limitMove via the constructor.
///
/// See
///
///
///
///
///
///
///
public class WilderSwingIndex : TradeBarIndicator, IIndicatorWarmUpPeriodProvider
{
///
/// Holds the bar for the current period.
///
protected IBaseDataBar _currentInput { get; private set; }
///
/// Holds the bar for the previous period.
///
protected IBaseDataBar _previousInput { get; private set; }
///
/// Gets the value for T (the limit move value) set in the constructor.
///
private readonly decimal T;
///
/// Initializes a new instance of the class using the specified name.
///
/// A string for the name of this indicator.
/// A decimal representing the limit move value for the period.
public WilderSwingIndex(string name, decimal limitMove)
: base(name)
{
T = Math.Abs(limitMove);
}
///
/// Initializes a new instance of the class using the default name.
///
/// A decimal representing the limit move value for the period.
public WilderSwingIndex(decimal limitMove)
: this("SI", limitMove)
{
}
///
/// Gets a flag indicating when this indicator is ready and fully initialized.
///
public override bool IsReady => Samples > 1;
///
/// Required period, in data points, for the indicator to be ready and fully initialized.
///
public int WarmUpPeriod => 2;
///
/// 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(TradeBar input)
{
if (!IsReady)
{
_currentInput = input;
return 0m;
}
_previousInput = _currentInput;
_currentInput = input;
var N = GetNValue();
var R = GetRValue();
var K = GetKValue();
if (R == decimal.Zero || T == decimal.Zero)
{
return 0m;
}
return 50m * (N / R) * (K / T);
}
///
/// Gets the value for N.
///
/// N = Cₜ - Cₜ₋₁ + 0.5 * (Cₜ - Oₜ) + 0.25 * (Cₜ₋₁ - Oₜ₋₁)
///
///
private decimal GetNValue()
{
return (_currentInput.Close - _previousInput.Close)
+ (0.5m * (_currentInput.Close - _currentInput.Open))
+ (0.25m * (_previousInput.Close - _previousInput.Open));
}
///
/// Gets the value for R, determined by using the formula corresponding
/// to the expression with the largest value.
///
/// Expressions:
///
/// -
/// |Hₜ - Cₜ₋₁|
///
/// -
/// |Lₜ - Cₜ₋₁|
///
/// -
/// |Hₜ - Lₜ|
///
///
///
///
/// Formulas:
///
/// -
/// |Hₜ - Cₜ₋₁| - 0.5 * |Lₜ - Cₜ₋₁| + 0.25 * |Cₜ₋₁ - Oₜ₋₁|
///
/// -
/// |Lₜ - Cₜ₋₁| - 0.5 * |Hₜ - Cₜ₋₁| + 0.25 * |Cₜ₋₁ - Oₜ₋₁|
///
/// -
/// |Hₜ - Lₜ| + 0.25 * |Cₜ₋₁ - Oₜ₋₁|
///
///
///
///
private decimal GetRValue()
{
var expressions = new decimal[]
{
Math.Abs(_currentInput.High - _previousInput.Close),
Math.Abs(_currentInput.Low - _previousInput.Close),
Math.Abs(_currentInput.High - _currentInput.Low)
};
int expressionIndex = Array.IndexOf(expressions, expressions.Max());
decimal result;
switch (expressionIndex)
{
case 0:
result = Math.Abs(_currentInput.High - _previousInput.Close)
- Math.Abs(0.5m * (_currentInput.Low - _previousInput.Close))
+ Math.Abs(0.25m * (_previousInput.Close - _previousInput.Open));
break;
case 1:
result = Math.Abs(_currentInput.Low - _previousInput.Close)
- Math.Abs(0.5m * (_currentInput.High - _previousInput.Close))
+ Math.Abs(0.25m * (_previousInput.Close - _previousInput.Open));
break;
case 2:
result = Math.Abs(_currentInput.High - _currentInput.Low)
+ Math.Abs(0.25m * (_previousInput.Close - _previousInput.Open));
break;
default:
result = 0m;
break;
}
return result;
}
///
/// Gets the value for K, which is equal to the larger of the two following expressions.
///
/// Expressions:
///
/// -
/// |Hₜ - Cₜ₋₁|
///
/// -
/// |Lₜ - Cₜ₋₁|
///
///
///
///
private decimal GetKValue()
{
var values = new decimal[]
{
_currentInput.High - _previousInput.Close,
_currentInput.Low - _previousInput.Close
};
return values.Max(x => Math.Abs(x));
}
}
}