[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Critically-damped Butterworth Lowpass Filter



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