Lean  $LEAN_TAG$
Momersion.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15 */
16 
17 using System;
18 using System.Linq;
19 
21 {
22  /// <summary>
23  /// Oscillator indicator that measures momentum and mean-reversion over a specified
24  /// period n.
25  /// Source: Harris, Michael. "Momersion Indicator." Price Action Lab.,
26  /// 13 Aug. 2015. Web. http://www.priceactionlab.com/Blog/2015/08/momersion-indicator/.
27  /// </summary>
29  {
30  /// <summary>
31  /// The minimum observations needed to consider the indicator ready. After that observation
32  /// number is reached, the indicator will continue gathering data until the full period.
33  /// </summary>
34  private readonly int? _minPeriod;
35 
36  /// <summary>
37  /// The rolling window used to store the momentum.
38  /// </summary>
39  private readonly RollingWindow<decimal> _multipliedDiffWindow;
40 
41  /// <summary>
42  /// Initializes a new instance of the <see cref="MomersionIndicator"/> class.
43  /// </summary>
44  /// <param name="name">The name.</param>
45  /// <param name="minPeriod">The minimum period.</param>
46  /// <param name="fullPeriod">The full period.</param>
47  /// <exception cref="System.ArgumentException">The minimum period should be greater of 3.;minPeriod</exception>
48  public MomersionIndicator(string name, int? minPeriod, int fullPeriod)
49  : base(name, fullPeriod)
50  {
51  if (minPeriod < 4)
52  {
53  throw new ArgumentException("The minimum period should be 4.", nameof(minPeriod));
54  }
55  _minPeriod = minPeriod;
56  _multipliedDiffWindow = new RollingWindow<decimal>(fullPeriod);
57  WarmUpPeriod = (minPeriod + 2) ?? (fullPeriod + 3);
58  }
59 
60  /// <summary>
61  /// Initializes a new instance of the <see cref="MomersionIndicator"/> class.
62  /// </summary>
63  /// <param name="minPeriod">The minimum period.</param>
64  /// <param name="fullPeriod">The full period.</param>
65  public MomersionIndicator(int? minPeriod, int fullPeriod)
66  : this($"Momersion({minPeriod},{fullPeriod})", minPeriod, fullPeriod)
67  {
68  }
69 
70  /// <summary>
71  /// Initializes a new instance of the <see cref="MomersionIndicator"/> class.
72  /// </summary>
73  /// <param name="fullPeriod">The full period.</param>
74  public MomersionIndicator(int fullPeriod)
75  : this(null, fullPeriod)
76  {
77  }
78 
79  /// <summary>
80  /// Gets a flag indicating when this indicator is ready and fully initialized
81  /// </summary>
82  public override bool IsReady
83  {
84  get
85  {
86  if (_minPeriod.HasValue)
87  {
88  return _multipliedDiffWindow.Count >= _minPeriod;
89  }
90  return _multipliedDiffWindow.Samples > _multipliedDiffWindow.Size;
91  }
92  }
93 
94  /// <summary>
95  /// Required period, in data points, for the indicator to be ready and fully initialized.
96  /// </summary>
97  public int WarmUpPeriod { get; }
98 
99  /// <summary>
100  /// Resets this indicator to its initial state
101  /// </summary>
102  public override void Reset()
103  {
104  base.Reset();
105  _multipliedDiffWindow.Reset();
106  }
107 
108  /// <summary>
109  /// Computes the next value of this indicator from the given state
110  /// </summary>
111  /// <param name="window"></param>
112  /// <param name="input">The input given to the indicator</param>
113  /// <returns>
114  /// A new value for this indicator
115  /// </returns>
117  {
118  if (window.Count >= 3)
119  {
120  _multipliedDiffWindow.Add((window[0].Value - window[1].Value) * (window[1].Value - window[2].Value));
121  }
122 
123  // Estimate the indicator if less than 50% of observation are zero. Avoid division by
124  // zero and estimations with few real observations in case of forward filled data.
125  if (IsReady && _multipliedDiffWindow.Count(obs => obs == 0) < 0.5 * _multipliedDiffWindow.Count)
126  {
127  var mc = _multipliedDiffWindow.Count(obs => obs > 0);
128  var mRc = _multipliedDiffWindow.Count(obs => obs < 0);
129  return 100m * mc / (mc + mRc);
130  }
131  return 50m;
132  }
133  }
134 }