/*
* 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;
namespace QuantConnect.Indicators
{
///
/// 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
///
public enum SwissArmyKnifeTool
{
///
/// Two Pole Gaussian Filter (0)
///
Gauss,
///
/// Two Pole Butterworth Filter (1)
///
Butter,
///
/// High Pass Filter (2)
///
HighPass,
///
/// Two Pole High Pass Filter (3)
///
TwoPoleHighPass,
///
/// BandPass Filter (4)
///
BandPass
}
///
/// Swiss Army Knife indicator by John Ehlers
///
public class SwissArmyKnife : Indicator, IIndicatorWarmUpPeriodProvider
{
private readonly RollingWindow _price;
private readonly RollingWindow _filt;
private readonly int _period;
private readonly double _a0 = 1;
private readonly double _a1 = 0;
private readonly double _a2 = 0;
private readonly double _b0 = 1;
private readonly double _b1 = 0;
private readonly double _b2 = 0;
private readonly double _c0 = 1;
///
/// Swiss Army Knife indicator by John Ehlers
///
///
///
///
public SwissArmyKnife(int period, double delta, SwissArmyKnifeTool tool)
: this($"Swiss({period},{delta},{tool})", period, delta, tool)
{
}
///
/// Swiss Army Knife indicator by John Ehlers
///
///
///
///
///
public SwissArmyKnife(string name, int period, double delta, SwissArmyKnifeTool tool)
: base(name)
{
_filt = new RollingWindow(2) {0, 0};
_price = new RollingWindow(3);
_period = period;
var beta = 2.415 * (1 - Math.Cos(2 * Math.PI / period));
var alpha = -beta + Math.Sqrt(Math.Pow(beta, 2) + 2d * beta);
switch (tool)
{
case SwissArmyKnifeTool.Gauss:
_c0 = alpha * alpha;
_a1 = 2d * (1 - alpha);
_a2 = -(1 - alpha) * (1 - alpha);
break;
case SwissArmyKnifeTool.Butter:
_c0 = alpha * alpha / 4d;
_b1 = 2;
_b2 = 1;
_a1 = 2d * (1 - alpha);
_a2 = -(1 - alpha) * (1 - alpha);
break;
case SwissArmyKnifeTool.HighPass:
alpha = (Math.Cos(2 * Math.PI / period) + Math.Sin(2 * Math.PI / period) - 1) / Math.Cos(2 * Math.PI / period);
_c0 = (1 + alpha) / 2;
_b1 = -1;
_a1 = 1 - alpha;
break;
case SwissArmyKnifeTool.TwoPoleHighPass:
_c0 = (1 + alpha) * (1 + alpha) / 4;
_b1 = -2;
_b2 = 1;
_a1 = 2d * (1 - alpha);
_a2 = -(1 - alpha) * (1 - alpha);
break;
case SwissArmyKnifeTool.BandPass:
beta = Math.Cos(2 * Math.PI / period);
var gamma = (1 / Math.Cos(4 * Math.PI * delta / period));
alpha = gamma - Math.Sqrt(Math.Pow(gamma, 2) - 1);
_c0 = (1 - alpha) / 2d;
_b0 = 1;
_b2 = -1;
_a1 = -beta * (1 - alpha);
_a2 = alpha;
break;
default:
throw new ArgumentOutOfRangeException(nameof(tool), tool, "Invalid SwissArmyKnifeTool");
}
}
///
/// Gets a flag indicating when this indicator is ready and fully initialized
///
public override bool IsReady => Samples >= _period;
///
/// Required period, in data points, for the indicator to be ready and fully initialized.
///
public int WarmUpPeriod => _period;
///
/// 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(IndicatorDataPoint input)
{
_price.Add((double)input.Price);
if (_price.Samples == 1)
{
_price.Add(_price[0]);
_price.Add(_price[0]);
}
var signal = _a0 * _c0 * (_b0 * _price[0] + _b1 * _price[1] + _b2 * _price[2]) + _a0 * (_a1 * _filt[0] + _a2 * _filt[1]);
_filt.Add(signal);
return (decimal)signal;
}
///
/// Resets to the initial state
///
public override void Reset()
{
_price.Reset();
_filt.Reset();
_filt.Add(0);
_filt.Add(0);
base.Reset();
}
}
}