Lean  $LEAN_TAG$
SwissArmyKnife.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 using System;
17 
19 {
20  /// <summary>
21  /// The tools of the Swiss Army Knife. Some of the tools lend well to chaining with the "Of" Method, others may be treated as moving averages
22  /// </summary>
23  public enum SwissArmyKnifeTool
24  {
25  /// <summary>
26  /// Two Pole Gaussian Filter (0)
27  /// </summary>
28  Gauss,
29  /// <summary>
30  /// Two Pole Butterworth Filter (1)
31  /// </summary>
32  Butter,
33  /// <summary>
34  /// High Pass Filter (2)
35  /// </summary>
36  HighPass,
37  /// <summary>
38  /// Two Pole High Pass Filter (3)
39  /// </summary>
41  /// <summary>
42  /// BandPass Filter (4)
43  /// </summary>
44  BandPass
45  }
46 
47  /// <summary>
48  /// Swiss Army Knife indicator by John Ehlers
49  /// </summary>
51  {
52  private readonly RollingWindow<double> _price;
53  private readonly RollingWindow<double> _filt;
54  private readonly int _period;
55  private readonly double _a0 = 1;
56  private readonly double _a1 = 0;
57  private readonly double _a2 = 0;
58  private readonly double _b0 = 1;
59  private readonly double _b1 = 0;
60  private readonly double _b2 = 0;
61  private readonly double _c0 = 1;
62 
63  /// <summary>
64  /// Swiss Army Knife indicator by John Ehlers
65  /// </summary>
66  /// <param name="period"></param>
67  /// <param name="delta"></param>
68  /// <param name="tool"></param>
69  public SwissArmyKnife(int period, double delta, SwissArmyKnifeTool tool)
70  : this($"Swiss({period},{delta},{tool})", period, delta, tool)
71  {
72  }
73 
74  /// <summary>
75  /// Swiss Army Knife indicator by John Ehlers
76  /// </summary>
77  /// <param name="name"></param>
78  /// <param name="period"></param>
79  /// <param name="delta"></param>
80  /// <param name="tool"></param>
81  public SwissArmyKnife(string name, int period, double delta, SwissArmyKnifeTool tool)
82  : base(name)
83  {
84  _filt = new RollingWindow<double>(2) {0, 0};
85  _price = new RollingWindow<double>(3);
86  _period = period;
87  var beta = 2.415 * (1 - Math.Cos(2 * Math.PI / period));
88  var alpha = -beta + Math.Sqrt(Math.Pow(beta, 2) + 2d * beta);
89 
90  switch (tool)
91  {
92  case SwissArmyKnifeTool.Gauss:
93  _c0 = alpha * alpha;
94  _a1 = 2d * (1 - alpha);
95  _a2 = -(1 - alpha) * (1 - alpha);
96  break;
97  case SwissArmyKnifeTool.Butter:
98  _c0 = alpha * alpha / 4d;
99  _b1 = 2;
100  _b2 = 1;
101  _a1 = 2d * (1 - alpha);
102  _a2 = -(1 - alpha) * (1 - alpha);
103  break;
104  case SwissArmyKnifeTool.HighPass:
105  alpha = (Math.Cos(2 * Math.PI / period) + Math.Sin(2 * Math.PI / period) - 1) / Math.Cos(2 * Math.PI / period);
106  _c0 = (1 + alpha) / 2;
107  _b1 = -1;
108  _a1 = 1 - alpha;
109  break;
110  case SwissArmyKnifeTool.TwoPoleHighPass:
111  _c0 = (1 + alpha) * (1 + alpha) / 4;
112  _b1 = -2;
113  _b2 = 1;
114  _a1 = 2d * (1 - alpha);
115  _a2 = -(1 - alpha) * (1 - alpha);
116  break;
117  case SwissArmyKnifeTool.BandPass:
118  beta = Math.Cos(2 * Math.PI / period);
119  var gamma = (1 / Math.Cos(4 * Math.PI * delta / period));
120  alpha = gamma - Math.Sqrt(Math.Pow(gamma, 2) - 1);
121  _c0 = (1 - alpha) / 2d;
122  _b0 = 1;
123  _b2 = -1;
124  _a1 = -beta * (1 - alpha);
125  _a2 = alpha;
126  break;
127  default:
128  throw new ArgumentOutOfRangeException(nameof(tool), tool, "Invalid SwissArmyKnifeTool");
129  }
130  }
131 
132  /// <summary>
133  /// Gets a flag indicating when this indicator is ready and fully initialized
134  /// </summary>
135  public override bool IsReady => Samples >= _period;
136 
137  /// <summary>
138  /// Required period, in data points, for the indicator to be ready and fully initialized.
139  /// </summary>
140  public int WarmUpPeriod => _period;
141 
142  /// <summary>
143  /// Computes the next value of this indicator from the given state
144  /// </summary>
145  /// <param name="input">The input given to the indicator</param>
146  /// <returns>A new value for this indicator</returns>
147  protected override decimal ComputeNextValue(IndicatorDataPoint input)
148  {
149  _price.Add((double)input.Price);
150 
151  if (_price.Samples == 1)
152  {
153  _price.Add(_price[0]);
154  _price.Add(_price[0]);
155  }
156 
157  var signal = _a0 * _c0 * (_b0 * _price[0] + _b1 * _price[1] + _b2 * _price[2]) + _a0 * (_a1 * _filt[0] + _a2 * _filt[1]);
158 
159  _filt.Add(signal);
160 
161  return (decimal)signal;
162  }
163 
164  /// <summary>
165  /// Resets to the initial state
166  /// </summary>
167  public override void Reset()
168  {
169  _price.Reset();
170  _filt.Reset();
171  _filt.Add(0);
172  _filt.Add(0);
173  base.Reset();
174  }
175  }
176 }