/* * 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; namespace QuantConnect.Indicators { /// /// Oscillator indicator that measures momentum and mean-reversion over a specified /// period n. /// Source: Harris, Michael. "Momersion Indicator." Price Action Lab., /// 13 Aug. 2015. Web. http://www.priceactionlab.com/Blog/2015/08/momersion-indicator/. /// public class Momersion : WindowIndicator, IIndicatorWarmUpPeriodProvider { /// /// The minimum observations needed to consider the indicator ready. After that observation /// number is reached, the indicator will continue gathering data until the full period. /// private readonly int? _minPeriod; /// /// The rolling window used to store the momentum. /// private readonly RollingWindow _multipliedDiffWindow; /// /// Initializes a new instance of the class. /// /// The name. /// The minimum period. /// The full period. /// The minimum period should be greater of 3.;minPeriod public Momersion(string name, int? minPeriod, int fullPeriod) : base(name, fullPeriod) { if (minPeriod < 4) { throw new ArgumentException("The minimum period should be 4.", nameof(minPeriod)); } _minPeriod = minPeriod; _multipliedDiffWindow = new RollingWindow(fullPeriod); WarmUpPeriod = (minPeriod + 2) ?? (fullPeriod + 3); } /// /// Initializes a new instance of the class. /// /// The minimum period. /// The full period. public Momersion(int? minPeriod, int fullPeriod) : this($"Momersion({minPeriod},{fullPeriod})", minPeriod, fullPeriod) { } /// /// Initializes a new instance of the class. /// /// The full period. public Momersion(int fullPeriod) : this(null, fullPeriod) { } /// /// Gets a flag indicating when this indicator is ready and fully initialized /// public override bool IsReady { get { if (_minPeriod.HasValue) { return _multipliedDiffWindow.Count >= _minPeriod; } return _multipliedDiffWindow.Samples > _multipliedDiffWindow.Size; } } /// /// Required period, in data points, for the indicator to be ready and fully initialized. /// public int WarmUpPeriod { get; } /// /// Resets this indicator to its initial state /// public override void Reset() { base.Reset(); _multipliedDiffWindow.Reset(); } /// /// 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(IReadOnlyWindow window, IndicatorDataPoint input) { if (window.Count >= 3) { _multipliedDiffWindow.Add((window[0].Value - window[1].Value) * (window[1].Value - window[2].Value)); } // Estimate the indicator if less than 50% of observation are zero. Avoid division by // zero and estimations with few real observations in case of forward filled data. if (IsReady && _multipliedDiffWindow.Count(obs => obs == 0) < 0.5 * _multipliedDiffWindow.Count) { var mc = _multipliedDiffWindow.Count(obs => obs > 0); var mRc = _multipliedDiffWindow.Count(obs => obs < 0); return 100m * mc / (mc + mRc); } return 50m; } } }