Lean  $LEAN_TAG$
FractalAdaptiveMovingAverage.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 
17 using System;
18 using System.Linq;
19 
21 {
22  /// <summary>
23  /// The Fractal Adaptive Moving Average (FRAMA) by John Ehlers
24  /// </summary>
26  {
27  private readonly int _n = 16;
28  private readonly double _w = -4.6;
29  private readonly RollingWindow<double> _high;
30  private readonly RollingWindow<double> _low;
31 
32  /// <summary>
33  /// Initializes a new instance of the average class
34  /// </summary>
35  /// <param name="name">The name of the indicator instance</param>
36  /// <param name="n">The window period (must be even). Example value: 16</param>
37  /// <param name="longPeriod">The average period. Example value: 198</param>
38  public FractalAdaptiveMovingAverage(string name, int n, int longPeriod)
39  : base(name)
40  {
41  if (n % 2 > 0)
42  {
43  throw new ArgumentException($"{name}: N must be even, N = {n}", nameof(n));
44  }
45  _n = n;
46  _w = Math.Log(2d / (1 + longPeriod));
47  _high = new RollingWindow<double>(n);
48  _low = new RollingWindow<double>(n);
49  }
50 
51  /// <summary>
52  /// Initializes a new instance of the average class
53  /// </summary>
54  /// <param name="n">The window period (must be even). Example value: 16</param>
55  /// <param name="longPeriod">The average period. Example value: 198</param>
56  public FractalAdaptiveMovingAverage(int n, int longPeriod)
57  : this($"FRAMA({n},{longPeriod})", n, longPeriod)
58  {
59 
60  }
61 
62  /// <summary>
63  /// Initializes a new instance of the average class
64  /// </summary>
65  /// <param name="n">The window period (must be even). Example value: 16</param>
67  : this(n, 198)
68  {
69  }
70 
71  /// <summary>
72  /// Computes the average value
73  /// </summary>
74  /// <param name="input">The data for the calculation</param>
75  /// <returns>The average value</returns>
76  protected override decimal ComputeNextValue(IBaseDataBar input)
77  {
78  var price = (input.High + input.Low) / 2;
79  _high.Add((double)input.High);
80  _low.Add((double)input.Low);
81 
82  // our first data point just return identity
83  if (_high.Samples <= _high.Size)
84  {
85  return price;
86  }
87 
88  var hh = _high.Take(_n / 2).Max();
89  var ll = _low.Take(_n / 2).Min();
90  var n1 = (hh - ll) / (_n / 2);
91 
92  hh = _high.Skip(_n / 2).Take(_n / 2).Max();
93  ll = _low.Skip(_n / 2).Take(_n / 2).Min();
94 
95  var n2 = (hh - ll) / (_n / 2);
96  var n3 = (_high.Max() - _low.Min()) / _n;
97 
98  double dimen = 0;
99 
100  if (n1 + n2 > 0 && n3 > 0)
101  {
102  var log = Math.Log((n1 + n2) / n3);
103  dimen = (double.IsNaN(log) ? 0 : log) / Math.Log(2);
104  }
105 
106  var alpha = Math.Exp(_w * (dimen - 1));
107 
108  if (alpha < .01)
109  {
110  alpha = .01;
111  }
112  if (alpha > 1)
113  {
114  alpha = 1;
115  }
116 
117  return (decimal)alpha * price + (1 - (decimal)alpha) * Current.Value;
118  }
119 
120  /// <summary>
121  /// Returns whether the indicator will return valid results
122  /// </summary>
123  public override bool IsReady => _high.IsReady;
124 
125  /// <summary>
126  /// Required period, in data points, for the indicator to be ready and fully initialized.
127  /// </summary>
128  public int WarmUpPeriod => _high.Size;
129 
130  /// <summary>
131  /// Resets the average to its initial state
132  /// </summary>
133  public override void Reset()
134  {
135  _high.Reset();
136  _low.Reset();
137  base.Reset();
138  }
139  }
140 }