/* * 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 QuantConnect.Data; using QuantConnect.Data.Market; namespace QuantConnect.Indicators { /// /// This indicator computes the Slow Stochastics %K and %D. The Fast Stochastics %K is is computed by /// (Current Close Price - Lowest Price of given Period) / (Highest Price of given Period - Lowest Price of given Period) /// multiplied by 100. Once the Fast Stochastics %K is calculated the Slow Stochastic %K is calculated by the average/smoothed price of /// of the Fast %K with the given period. The Slow Stochastics %D is then derived from the Slow Stochastics %K with the given period. /// public class Stochastic : BarIndicator, IIndicatorWarmUpPeriodProvider { private readonly IndicatorBase _maximum; private readonly IndicatorBase _minimum; private readonly IndicatorBase _sumFastK; private readonly IndicatorBase _sumSlowK; /// /// Gets the value of the Fast Stochastics %K given Period. /// public IndicatorBase FastStoch { get; } /// /// Gets the value of the Slow Stochastics given Period K. /// public IndicatorBase StochK { get; } /// /// Gets the value of the Slow Stochastics given Period D. /// public IndicatorBase StochD { get; } /// /// Creates a new Stochastics Indicator from the specified periods. /// /// The name of this indicator. /// The period given to calculate the Fast %K /// The K period given to calculated the Slow %K /// The D period given to calculated the Slow %D public Stochastic(string name, int period, int kPeriod, int dPeriod) : base(name) { _maximum = new Maximum(name + "_Max", period); _minimum = new Minimum(name + "_Min", period); _sumFastK = new Sum(name + "_SumFastK", kPeriod); _sumSlowK = new Sum(name + "_SumD", dPeriod); FastStoch = new FunctionalIndicator(name + "_FastStoch", input => ComputeFastStoch(input), fastStoch => _maximum.IsReady ); StochK = new FunctionalIndicator(name + "_StochK", input => ComputeStochK(kPeriod, input), stochK => _sumFastK.IsReady ); StochD = new FunctionalIndicator( name + "_StochD", input => ComputeStochD(dPeriod), stochD => _sumSlowK.IsReady ); // Subtracting 2 since the first value is calculated after 'period' bars, // and each smoothing step adds (kPeriod - 1) and (dPeriod - 1) respectively. WarmUpPeriod = period + kPeriod + dPeriod - 2; } /// /// Creates a new indicator from the specified inputs. /// /// The period given to calculate the Fast %K /// The K period given to calculated the Slow %K /// The D period given to calculated the Slow %D public Stochastic(int period, int kPeriod, int dPeriod) : this($"STO({period},{kPeriod},{dPeriod})", period, kPeriod, dPeriod) { } /// /// Gets a flag indicating when this indicator is ready and fully initialized /// public override bool IsReady => FastStoch.IsReady && StochK.IsReady && StochD.IsReady; /// /// Required period, in data points, for the indicator to be ready and fully initialized. /// public int WarmUpPeriod { get; } /// /// Computes the next value of this indicator from the given state /// /// The input given to the indicator protected override decimal ComputeNextValue(IBaseDataBar input) { _maximum.Update(input.EndTime, input.High); _minimum.Update(input.EndTime, input.Low); FastStoch.Update(input); StochK.Update(input); StochD.Update(input); return FastStoch.Current.Value; } /// /// Computes the Fast Stochastic %K. /// /// The input. /// The Fast Stochastics %K value. private decimal ComputeFastStoch(IBaseDataBar input) { var fastStoch = 0m; // It requires at least 'period' data points to compute Fast %K. if (_maximum.IsReady) { var denominator = _maximum.Current.Value - _minimum.Current.Value; // if there's no range, just return constant zero if (denominator == 0m) { return 0m; } var numerator = input.Close - _minimum.Current.Value; fastStoch = numerator / denominator; _sumFastK.Update(input.EndTime, fastStoch); } return fastStoch * 100; } /// /// Computes the Slow Stochastic %K. /// /// The constant k. /// The input. /// The Slow Stochastics %K value. private decimal ComputeStochK(int constantK, IBaseData input) { var stochK = 0m; // It requires at least 'kPeriod' updates in _sumFastK for calculation. if (_sumFastK.IsReady) { stochK = _sumFastK.Current.Value / constantK; _sumSlowK.Update(input.EndTime, stochK); } return stochK * 100; } /// /// Computes the Slow Stochastic %D. /// /// The constant d. /// The Slow Stochastics %D value. private decimal ComputeStochD(int constantD) { var stochD = 0m; // It requires at least 'dPeriod' updates in _sumSlowK for calculation if (_sumSlowK.IsReady) { stochD = _sumSlowK.Current.Value / constantD; } return stochD * 100; } /// /// Resets this indicator to its initial state /// public override void Reset() { FastStoch.Reset(); StochK.Reset(); StochD.Reset(); _maximum.Reset(); _minimum.Reset(); _sumFastK.Reset(); _sumSlowK.Reset(); base.Reset(); } } }