2007 Jan: Fourier Transform for Traders

ICE Data Services -

TransformedDFT.efs  
EFSLibrary - Discussion Board  

File Name: TransformedDFT.efs

Description:
This study is based on the January 2007 article, Fourier Transform For Traders, by John Ehlers.

Formula Parameters:

  • Show Dominant Cycle: false
  • Dominant Cycle Color: blue
  • Dominant Cycle Size: 2

Notes:
The related article is copyrighted material. If you are not a subscriber of Stocks & Commodities, please visit www.traders.com.

Download File:
TransformedDFT.efs


EFS Code:

/***************************************
Provided By : eSignal (c) Copyright 2006
Description:  Fourier Transform for Traders 
              by John Ehlers

Version 1.0  11/02/2006

Notes:
* Jan 2007 Issue of Stocks and Commodities Magazine
* Study requires version 8.0 or later.


Formula Parameters:                     Default:
Show Dominant Cycle                     false
Dominant Cycle Color                    blue
Dominant Cycle Size                     2
*****************************************************************/

function preMain() {
    setStudyTitle("Transformed DFT ");
    setShowCursorLabel(false);
    setShowTitleParameters(false);    
    // Dominant Cycle properties
    setPlotType(PLOTTYPE_CIRCLE, 51);

    var fp1 = new FunctionParameter("bShowDC", FunctionParameter.BOOLEAN);
        fp1.setName("Show Dominant Cycle");
        fp1.setDefault(false);
    var fp2 = new FunctionParameter("nDC_color", FunctionParameter.COLOR);
        fp2.setName("Dominant Cycle Color");
        fp2.setDefault(Color.blue);
    var fp3 = new FunctionParameter("nDC_thick", FunctionParameter.NUMBER);
        fp3.setName("Dominant Cycle Size");
        fp3.setLowerLimit(1);
        fp3.setDefault(2);    
}

// Global Variables
var bVersion  = null;    // Version flag
var bInit     = false;   // Initialization flag
var xPrice = null;
var xHP = null;
var xCleanData = null;
var nWindow = 50;
var nAlpha = (1 - Math.sin((2*Math.PI)/40)) / Math.cos((2*Math.PI)/40);
var aCosinePart = new Array(51);
var aSinePart = new Array(51);
var aPwr = new Array(51);
var aDB = new Array(51);
var aReturn = new Array(52);  // return array


function main(bShowDC, nDC_color, nDC_thick) {
    if (bVersion == null) bVersion = verify();
    if (bVersion == false) return;    

    var nPeriod = 0;
    var n = 0;
    var nMaxPwr = 0;
    var nNum = 0;
    var nDenom = 0;
    var nDominantCycle = 0;
    var Color1 = null;
    var Color2 = null;

    //Initialization
    if (bInit == false) {
        setDefaultBarFgColor(nDC_color, 51);
        setDefaultBarThickness(nDC_thick, 51);
        xPrice = hl2();
        xHP = efsInternal("calcHP", xPrice);
        xCleanData = efsInternal("calcCleanData", xHP);
        bInit = true;
    }

    var nHP = xHP.getValue(0);
    var nCleanData = xCleanData.getValue(-nWindow);
    if (nHP == null || nCleanData == null) return;
    

    //This is the DFT
    nPeriod = 8;
    n = 0;
    for (nPeriod = 8; nPeriod <= 50; nPeriod++) {
        aCosinePart[nPeriod] = 0;
        aSinePart[nPeriod] = 0;
        
        for (n = 0; n <= nWindow; n++) {
            var nCD = xCleanData.getValue(-n);
            aCosinePart[nPeriod] = aCosinePart[nPeriod] + 
                nCD*Math.cos((2*Math.PI)*n/nPeriod);
            aSinePart[nPeriod] = aSinePart[nPeriod] + 
                nCD*Math.sin((2*Math.PI)*n/nPeriod);
        }
        
        aPwr[nPeriod] = aCosinePart[nPeriod]*aCosinePart[nPeriod] + 
            aSinePart[nPeriod]*aSinePart[nPeriod];
    }


    //Find Maximum Power Level for Normalization
    nPeriod = 8;
    nMaxPwr = aPwr[nPeriod];
    for (nPeriod = 8; nPeriod <= 50; nPeriod++) {
        if (aPwr[nPeriod] > nMaxPwr) nMaxPwr = aPwr[nPeriod];
    }

    //Normalize Power Levels and Convert to Decibels
    nPeriod = 8;
    for (nPeriod = 8; nPeriod <= 50; nPeriod++) {
        if (nMaxPwr > 0 && aPwr[nPeriod] > 0) { 
            aDB[nPeriod] = -10*Math.log(.01 / 
                (1 - .99*aPwr[nPeriod] / nMaxPwr))/Math.log(10);
        }
        if (aDB[nPeriod] > 20) aDB[nPeriod] = 20;
    }
    

    //Find Dominant Cycle using CG algorithm 
    nPeriod = 8;
    for (nPeriod = 8; nPeriod <= 50; nPeriod++) {
        if (aDB[nPeriod] < 3) {
            nNum += nPeriod*(3 - aDB[nPeriod]);
            nDenom += (3 - aDB[nPeriod]);
        }
    }

    if (nDenom != 0) {
        nDominantCycle = nNum/nDenom;
        if (bShowDC == true) {
            // Dominant Cycle
            aReturn[51] = nDominantCycle;
        }
    }

    //Plot the Spectrum as a Heatmap
    nPeriod = 8;
    for (nPeriod = 8; nPeriod <= 50; nPeriod++) {
        //Convert Decibels to RGB Color for Display
        if (aDB[nPeriod] > 10) {
            Color1 = Math.round(255*(2 - aDB[nPeriod]/10));
            Color2 = 0;
        } else if (aDB[nPeriod] <= 10) {
            Color1 = 255;
            Color2 = Math.round(255*(1 - aDB[nPeriod]/10));
        }
        aReturn[nPeriod] = nPeriod;
        setDefaultBarFgColor(Color.RGB(Color1 , Color2, 0), nPeriod);
        setDefaultBarThickness(4, nPeriod);
    }
    
    //  aReturn[51]  // contains the Dominant Cycle value
    
    return aReturn;
}


// calcHP globals
var nPrevHP = null;
var nCurrHP = null;

function calcHP(x) {
    if (getCurrentBarCount() <= 5 ) {
        nCurrHP = x.getValue(0);
        return nCurrHP;
    } else {
        if (x.getValue(-1) == null) return null;
        if (getBarState() == BARSTATE_NEWBAR) nPrevHP = nCurrHP;
        nCurrHP = ( 0.5*(1 + nAlpha)*(x.getValue(0) - x.getValue(-1)) + nAlpha*nPrevHP );
        return nCurrHP;
    }
}

function calcCleanData(x) {
//Get a detrended version of the data by High Pass Filtering 
//with a 40 Period cutoff
    if (getCurrentBarCount() <= 5 ) {
        return x.getValue(0);
    } else {
        return (  x.getValue(0) + 
                  2*x.getValue(-1) + 
                  3*x.getValue(-2) + 
                  3*x.getValue(-3) + 
                  2*x.getValue(-4) + 
                  x.getValue(-5)    ) / 12;
    }
}

function verify() {
    var b = false;
    if (getBuildNumber() < 779) {
        drawTextAbsolute(5, 35, "This study requires version 8.0 or later.", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "error");
        drawTextAbsolute(5, 20, "Click HERE to upgrade.@URL=http://www.esignal.com/download/default.asp", 
            Color.white, Color.blue, Text.RELATIVETOBOTTOM|Text.RELATIVETOLEFT|Text.BOLD|Text.LEFT,
            null, 13, "upgrade");
        return b;
    } else {
        b = true;
    }
    
    return b;
}