/* * 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)); } } }