TASC 2008-03 | Measuring Cycle Periods (Ehlers)

Modified on 2010/09/12 11:18 by Eugene — Categorized as: TASC Traders Tips

Traders' Tip text

We’ve coded Ehler’s filter bank to measure the dominant cycle (DC) and the sine and cosine filter components in WealthScript for Wealth-Lab Version 6 (.NET). The CycleFilterDC function plots and returns the DC series and its components, so it’s a trivial matter to make use of them in a trading strategy.

WealthLab_Fig1_March2008

Figure 1. In this daily chart of cyclical price action, you can just about verify the dominant cycle measurement (plotted in lime on the heat map) by visual inspection.

Strategy Code

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;

namespace WealthLab.Strategies { public class MyStrategy : WealthScript { public class ArrayHolder { // current, old, older internal double I, I2, I3; internal double Q, Q2, Q3; internal double R, R2, R3; internal double Im, Im2, Im3; internal double A; internal double dB; } public DataSeries CycleFilterDC(DataSeries ds, out DataSeries sine, out DataSeries cosine) { double twoPi = 2 * Math.PI; // Initialize arrays ArrayHolder[] ah = new ArrayHolder[51]; for( int n = 8; n < 51; n++ ) ah[n] = new ArrayHolder(); Color[] color = new Color[21]; DataSeries[] DB = new DataSeries[51]; double domCycle = 0d; string name = ds.Description; DataSeries result = Close - Close; result.Description = "Dominant Cycle(" + name + ")"; // Create and plot the decibel series - change the colors later ChartPane dbPane = CreatePane(40, false, false ); for( int n = 8; n < 51; n++ ) { DB[n] = result + n; DB[n].Description = "Cycle." + n.ToString(); PlotSeries(dbPane, DB[n], Color.Black, LineStyle.Solid, 6); } // Convert decibels to RGB color for display for( int n = 0; n <= 10; n++ ) // yellow to red: 0 to 10 dB color[n] = Color.FromArgb(255, (int)(255 - (255 * n / 10)), 0); for( int n = 11; n <= 20; n++ ) // red to black: 11 to 20 db color[n] = Color.FromArgb( (int)(255 * (20 - n)/10 ), 0, 0); // Detrend data by High Pass Filtering with a 40 Period cutoff DataSeries HP = result; double alpha = (1 - Math.Sin(twoPi/40)) / Math.Cos(twoPi/40); for(int bar = 1; bar < Bars.Count; bar++) HP[bar] = 0.5 * (1 + alpha)* Momentum.Series(ds, 1)[bar] + alpha * HP[bar-1]; DataSeries smoothHP = FIR.Series(HP, "1,2,3,3,2,1"); for( int bar = 6; bar < Bars.Count; bar++ ) { double maxAmpl = 0; double delta = -0.015 * bar + 0.5; delta = delta < 0.15 ? 0.15 : delta; for( int n = 8; n < 51; n++ ) { double beta = Math.Cos(twoPi / n); double g = 1 / Math.Cos(2 * twoPi * delta / n); double a = g - Math.Sqrt(g * g - 1); ah[n].Q = Momentum.Series(smoothHP, 1)[bar] * n / twoPi; ah[n].I = smoothHP[bar]; ah[n].R = 0.5 * (1 - a) * (ah[n].I - ah[n].I3) + beta * (1 + a) * ah[n].R2 - a * ah[n].R3; ah[n].Im = 0.5 * (1 - a) * (ah[n].Q - ah[n].Q3) + beta * (1 + a) * ah[n].Im2 - a * ah[n].Im3; ah[n].A = ah[n].R * ah[n].R + ah[n].Im * ah[n].Im; maxAmpl = ah[n].A > maxAmpl ? ah[n].A : maxAmpl; } double num = 0; double den = 0; for( int n = 8; n < 51; n++ ) { if( maxAmpl != 0 && ah[n].A / maxAmpl > 0 ) ah[n].dB = 10 * Math.Log10( (1 - 0.99 * ah[n].A / maxAmpl) / 0.01 ); ah[n].dB = ah[n].dB > 20 ? 20 : ah[n].dB; SetSeriesBarColor(bar, DB[n], color[(int)Math.Round(ah[n].dB)]); if( ah[n].dB <= 3 ) { num = num + n * (20 - ah[n].dB); den = den + (20 - ah[n].dB); } if( den > 0 ) domCycle = num/den; result[bar] = domCycle; ah[n].I3 = ah[n].I2; ah[n].I2 = ah[n].I; ah[n].Q3 = ah[n].Q2; ah[n].Q2 = ah[n].Q; ah[n].R3 = ah[n].R2; ah[n].R2 = ah[n].R; ah[n].Im3 = ah[n].Im2; ah[n].Im2 = ah[n].Im; } } result = Median.Series(result, 10); PlotSeries(dbPane, result, Color.Lime, WealthLab.LineStyle.Solid, 2); // sine and cosine components sine = Low - Low; sine.Description = "sine(DC)"; double a2 = 0d; for(int bar = 10; bar < Bars.Count; bar++) { double delta = -0.015 * bar + 0.5; delta = delta < 0.15 ? 0.15 : delta; double beta = Math.Cos(2 * Math.PI / result[bar] ); double g = 1 / Math.Cos(4 * Math.PI * delta / result[bar]); if( g < 1 ) a2 = 0; else a2 = g - Math.Sqrt(g * g - 1); sine[bar] = 0.5 * (1 - a2) * Momentum.Value(bar, smoothHP, 1) + beta * (1 + a2) * sine[bar-1] - a2 * sine[bar-2]; } cosine = ( result / 6.28 ) * ( sine - (sine >> 1) ); cosine.Description = "cosine(DC)"; ChartPane sinePane = CreatePane( 40, false, false ); for(int bar = 0; bar < Bars.Count; bar++) SetPaneBackgroundColor(sinePane, bar, Color.Black); PlotSeries(sinePane, sine, Color.Red, LineStyle.Solid, 1); PlotSeries(sinePane, cosine, Color.Cyan, LineStyle.Solid, 1); return result; } protected override void Execute() { HideVolume(); HidePaneLines(); DataSeries avgPrice = (High + Low) / 2; avgPrice.Description = "Avg Price"; // Get the dominant cycle, sine and cosine, and plot the heat map DataSeries sine, cosine; DataSeries DC = CycleFilterDC(avgPrice, out sine, out cosine); /* Use the DC, sine, and cosine DataSeries in a Trading Strategy here */ } } }