PureBytes Links
Trading Reference Links
|
Butterworth filters exhibit a decent frequency response with a nice
flat passband and reasonably steep roll-off. The only problem is,
like with all infinite impulse response (IIR) filters except for
the basic exponential moving average (EMA), is that it suffers from
overshoot.
In other words, the Butterworth is under-damped (it overshoots its
target). An EMA, on the other hand, is overdamped (doesn't converge
to the price fast enough) and it has lousy noise rejection.
Just a year ago a couple of professors published an algorithm for a
critically-damped 2nd-order IIR filter, which neither overshoots nor
undershoots. While it responds to large transitions more accurately
than the traditional Butterworth, it's not quite as good with noise
rejection, although much better than the EMA, However, the noise
rejection can be improved easily by calculating a filter of a filter
(cascading, or nesting, the filters). Cascading just twice is
sufficient to exceed the high-frequency rejection of something like
the T3 average while maintaining the same lag and having better
response to transitions. The 'passes' parameter controls the number
of cascaded filters, so you don't have to do it yourself.
Here, then, is my function _butterworthLP, which can be set to
calculate the traditional Butterworth or the critically damped
version. The code itself is rather short. The bulk of it is
comments. Use it anywhere you'd want an EMA; it'll respond better,
smooth better, and never overshoot the data.
==================================================================
{Function: _butterworthLP
Critically-damped Butterworth Lowpass filter (2nd order)
Adapted by Alex Matulich 7/2004 from the article by
D. Gordon E. Robertson and James J. Dowling, "Design and responses
of Butterworth and critically damped digital filters," Journal of
Electromyography and Kinesiology 13 (2003) pp 569-573.
http://people.umass.edu/exsci735/Robertson&Dowling.pdf
This function implements a critically-damped Butterworth lowpass
filter when the critical_damping variable is set to True. Setting
critical_damping to False results in the standard 2nd-order lowpass
Butterworth. Normally a Butterworth exhibits a slight amount of
overshoot. A critically damped lowpass filter has no overshoot or
undershoot.
This critically damped filter follows rapid transitions better than
the traditional Butterworth without overshooting or undershooting
the input data. However, the critically damped filter is weaker at
removing noise, so cascading 2 or 3 critically damped filters will
achieve a better rolloff than the Butterworth. This is an extremely
fast calculation (the filter is just one line after the
initializations), so cascading multiple filters shouldn't adversely
affect execution time. The 'passes' parameter sets the number of
cascaded filters to calculate.
Critically damped filter lag:
passes=1: lag = passes-1 + length/5
passes=2: lag = passes-1 + length/5 + length/15
passes=3: lag = passes-1 + length/5 + length/15 + length/20
passes=4: lag = passes-1 + length/5 + length/15 + length/20 + length/25
etc. The above becomes approximate for high number of passes; i.e.
when passes=7 the lag may be off by 1 bar for high length (~100).
Note: The length parameter is also the 3 dB cutoff wavelength.
Other lowpass filters like EMA (i.e. XAverage) and T3 have a cutoff
wavelength at pi*length. To give this filter the same cutoff
wavelength as the EMA, you must first multiply your length
parameter by pi (3.14159265359) before applying this filter.
The lag will end up being midway between EMA and T3.
Recommendation: Set passes=2. Two passes has the equivalent lag
of a T3 moving average when the T3 has the same cutoff wavelength
(i.e. when T3 length = length/pi), yet the critically-damped
filter has better high-frequency rejection and no overshoot,
unlike the T3. It also has far superior noise rejection compared
to an EMA having the same cutoff wavelength.
}
Inputs:
p(NumericSeries), {input series}
length(NumericSimple), {filter length (3 dB cutoff wavelength)}
passes(NumericSimple); {number of times to cascade filter (max 7)}
vars: a0(0), a1(0), a2(0), b1(0), b2(0), j(0), k(0),
aa(0), cc(0), Wc(0), K2(0), critical_damping(true);
array: bw[7](0);
if currentbar < passes+2 then begin
if critical_damping then begin {critically damped filter}
cc = 1 / SquareRoot(Power(2, 0.5/passes) - 1);
aa = 2; {<2 =underdamped, 2 =critically damped, >2 =overdamped}
end else begin {standard 2nd-order Butterworth}
cc = Power(Power(2, 1/passes) - 1, 0.25);
aa = SquareRoot(2);
end;
{Wc=tan(pi*adjcutoff/samplefreq), adjcutoff=cc/length, samplefreq=1}
Wc = tangent(180 * cc / length); {pi = 180 degrees in EL}
K2 = Wc * Wc;
{filter coefficients}
a0 = K2 / (1 + aa*Wc + K2);
a1 = 2 * a0;
a2 = a0;
b1 = a1 * (1/K2 - 1);
b2 = 1 - (a0 + a1 + a2 + b1);
{initialize first filter value}
for j = 1 to 7 begin bw[j] = p; end;
end else begin
{first filter pass}
bw[passes] = a0*p + a1*p[1] + a2*p[2]
+ b1*bw[passes][1] + b2*bw[passes][2];
{cascade additional filters if needed}
for j = passes-1 downto 1 begin
k = j+1;
bw[j] = a0*bw[k][0] + a1*bw[k][1] + a2*bw[k][2]
+ b1*bw[j][1] + b2*bw[j][2];
end;
end;
_butterworthLP = bw[1];
==================================================================
--
,|___ Alex Matulich -- alex@xxxxxxxxxxxxxx
// +__> Director of Research and Development
// \
// __) Unicorn Research Corporation -- http://unicorn.us.com
|