Friday, January 23, 2009

The speed of the FRAMA (Part 1)

Earlier, I mentioned the logic behind the FRAMA (Fractal Adaptive Moving Average), and merely referred to John Ehlers'article. Here I wish to examine and discuss a bit more in detail this logic.

John Ehlers recommends to link the speed of an exponential moving average to the fractal dimension by making the coefficient α a function of this one via the following formula:



Let's accept this formula, in a first time, to consider the problematic of whether to apply this modification on an exponential moving average(EMA) or on a simple moving average(SMA).

The purpose of the EMA is to give more weight to the most recent price variations, this is a fair concern for the medium or long-term trader, I feel it is however a much less interesting feature for the intraday trader, who has to cope with a lot of noisy, meaningless fluctuations, and relies on the moving average precisely to avoid being distracted by this noise.
Besides, if we look at what happens for a high fractal dimension (approaching 2), the coefficient α is going to be very small (around 0.01, see the FRAMA article referenced earlier), the EMA will then be slowed down, but then, we also know that such a high fractal dimension coincides with the wildest noise, and therefore very high variations of prices. What is then the point of, on one hand slowing down the EMA, while this one will put a higher weight on the most recent, wildest price variations, thereby reflecting the wildness?

The two ideas clearly seem to conflict, and the resulting signal appears to be an ambiguous compromise where the exponential endeavors to speed up the moving average (by emphasizing the most recent variations) while the fractal dimension endeavors to slow it down.

I therefore prefer, especially as an intraday trader, to fractalise directly a SMA, and therefore get a direct and readable translation of the information implicit in the fractal dimension. This can be easily achieved by simply dividing the period of the SMA by the coefficient α.

Complement following a remark by Cool here:

In reply to Cool remark, here is a graph representing the FRAMA from Elhers in yellow, and this same FRAMA using a more precise calculation of the Fractal Dimension in red. Both FRAMA are exponential MA with a reference period of 10, their only difference is in the way the fractal dimension and therefore the coefficient α is calculated:

Yellow curve:
The fractal dimension is computed from the following equation:

where N1=(HighestPrice – LowestPrice) over the interval from 0 to T, divided by T, N2=(HighestPrice – LowestPrice) over the interval from T to 2T, divided by T and N3= (HighestPrice – LowestPrice) over the entire interval from 0 to 2T, divided by 2T
and


Red Curve:
The fractal Dimension is computed from the following equation:




Here are the two MT4 listings.

For the original Elhers FRAMA (Yellow Curve):
//+------------------------------------------------------------------+
//| FRAMA.mq4 |
//| Rosh |
//| http://www.alpari-idc.ru/ru/experts/articles/ |
//+------------------------------------------------------------------+
#property copyright "Rosh"
#property link "http://www.alpari-idc.ru/ru/experts/articles/"

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 DarkBlue
//---- input parameters
extern int PeriodFRAMA=10;
extern int PriceType=0;
//PRICE_CLOSE 0 Öåíà çàêðûòèÿ
//PRICE_OPEN 1 Öåíà îòêðûòèÿ
//PRICE_HIGH 2 Ìàêñèìàëüíàÿ öåíà
//PRICE_LOW 3 Ìèíèìàëüíàÿ öåíà
//PRICE_MEDIAN 4 Ñðåäíÿÿ öåíà, (high+low)/2
//PRICE_TYPICAL 5 Òèïè÷íàÿ öåíà, (high+low+close)/3
//PRICE_WEIGHTED 6 Âçâåøåííàÿ öåíà çàêðûòèÿ, (high+low+close+close)/4

//---- buffers
double ExtMapBuffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int init()
{
//---- indicators
SetIndexStyle(0,DRAW_LINE);
SetIndexBuffer(0,ExtMapBuffer1);
SetIndexEmptyValue(0,0.0);
//----
return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
//----

//----
return(0);
}
//+------------------------------------------------------------------+
//| âîçâðàùàåò öåíó |
//+------------------------------------------------------------------+
double Price(int shift)
{
//----
double res;
//----
switch (PriceType)
{
case PRICE_OPEN: res=Open[shift]; break;
case PRICE_HIGH: res=High[shift]; break;
case PRICE_LOW: res=Low[shift]; break;
case PRICE_MEDIAN: res=(High[shift]+Low[shift])/2.0; break;
case PRICE_TYPICAL: res=(High[shift]+Low[shift]+Close[shift])/3.0; break;
case PRICE_WEIGHTED: res=(High[shift]+Low[shift]+2*Close[shift])/4.0; break;
default: res=Close[shift];break;
}
return(res);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int start()
{
double Hi1,Lo1,Hi2,Lo2,Hi3,Lo3;
double N1,N2,N3,D;
double ALFA;
int limit;
int counted_bars=IndicatorCounted();
if (counted_bars==0) limit=Bars-2*PeriodFRAMA;
if (counted_bars>0) limit=Bars-counted_bars;
limit--;

//----
for (int i=limit;i>=0;i--)
{
Hi1=High[iHighest(Symbol(),0,MODE_HIGH,PeriodFRAMA,i)];
Lo1=Low[iLowest(Symbol(),0,MODE_LOW,PeriodFRAMA,i)];
Hi2=High[iHighest(Symbol(),0,MODE_HIGH,PeriodFRAMA,i+PeriodFRAMA)];
Lo2=Low[iLowest(Symbol(),0,MODE_LOW,PeriodFRAMA,i+PeriodFRAMA)];
Hi3=High[iHighest(Symbol(),0,MODE_HIGH,2*PeriodFRAMA,i)];
Lo3=Low[iLowest(Symbol(),0,MODE_LOW,2*PeriodFRAMA,i)];
N1=(Hi1-Lo1)/PeriodFRAMA;
N2=(Hi2-Lo2)/PeriodFRAMA;
N3=(Hi3-Lo3)/(2.0*PeriodFRAMA);
D=(MathLog(N1+N2)-MathLog(N3))/MathLog(2.0);
ALFA=MathExp(-4.6*(D-1.0));
ExtMapBuffer1[i]=ALFA*Price(i)+(1-ALFA)*ExtMapBuffer1[i+1];
}
//----
return(0);
}
//+------------------------------------------------------------------+

For the FRAMA modified with a different fractal dimension calculation (Red Curve):

//+------------------------------------------------------------------+
//| FRAMA2.mq4 |
//| Copyright © 2008, MetaQuotes Software Corp. |
//| http://www.metaquotes.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2008, MetaQuotes Software Corp."
#property link "http://www.metaquotes.net"

#property indicator_chart_window

#property indicator_color1 Red
#property indicator_width1 2
//************************************************************
// Input parameters
//************************************************************
extern int e_period =10;
extern int normal_speed =10;
extern int e_type_data =PRICE_CLOSE;
//************************************************************
// Constant
//************************************************************
string INDICATOR_NAME="FRAMA2";
string FILENAME ="FRAMA2.mq4";
double LOG_2;
//************************************************************
// Private vars
//************************************************************
double ExtOutputBuffer[];
int g_period_minus_1;
//+-----------------------------------------------------------------------+
//| FUNCTION : init |
//| Initialization function |
//| Check the user input parameters and convert them in appropriate types.|
//+-----------------------------------------------------------------------+
int init()
{
// Check e_period input parameter
if(e_period < 2 )
{
Alert( "[ 10-ERROR " + FILENAME + " ] input parameter \"e_period\" must be >= 1 (" + e_period + ")" );
return( -1 );
}
if(e_type_data < PRICE_CLOSE || e_type_data > PRICE_WEIGHTED )
{
Alert( "[ 20-ERROR " + FILENAME + " ] input parameter \"e_type_data\" unknown (" + e_type_data + ")" );
return( -1 );
}
IndicatorBuffers( 1 );
SetIndexBuffer( 0, ExtOutputBuffer );
SetIndexStyle( 0, DRAW_LINE, STYLE_SOLID, 2 );
SetIndexDrawBegin( 0, 2 * e_period );
g_period_minus_1=e_period - 1;
LOG_2=MathLog( 2.0 );
//----
return( 0 );
}
//+------------------------------------------------------------------+
//| FUNCTION : deinit |
//| Custor indicator deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
return(0);
}
//+------------------------------------------------------------------+
//| FUNCTION : start |
//| This callback is fired by metatrader for each tick |
//+------------------------------------------------------------------+
int start()
{
int countedBars=IndicatorCounted();
//---- check for possible errors
if(countedBars < 0)
{
return(-1);
}
_computeLastNbBars( Bars - countedBars - 1 );
//----
return( 0 );
}
//+================================================================================================================+
//+=== FUNCTION : _computeLastNbBars ===+
//+=== ===+
//+=== ===+
//+=== This callback is fired by metatrader for each tick ===+
//+=== ===+
//+=== In : ===+
//+=== - lastBars : these "n" last bars must be repainted ===+
//+=== ===+
//+================================================================================================================+
//+------------------------------------------------------------------+
//| FUNCTION : _computeLastNbBars |
//| This callback is fired by metatrader for each tick |
//| In : - lastBars : these "n" last bars must be repainted |
//+------------------------------------------------------------------+
void _computeLastNbBars( int lastBars )
{
int pos;
switch( e_type_data )
{
case PRICE_CLOSE : _computeFRAMA( lastBars, Close ); break;
case PRICE_OPEN : _computeFRAMA( lastBars, Open ); break;
case PRICE_HIGH : _computeFRAMA( lastBars, High ); break;
case PRICE_LOW : _computeFRAMA( lastBars, Low ); break;

default :
Alert( "[ 20-ERROR " + FILENAME + " ] the imput parameter e_type_data <" + e_type_data + "> is unknown" );
}
}
//+------------------------------------------------------------------+
//| FUNCTION : _computeFRASMA |
//| Compute the fractally modified SMA from input data. |
//| In : |
//| - lastBars : these "n" last bars must be repainted |
//| - inputData : data array on which the will be applied |
//| For technical explanations, see my blog: |
//| http://fractalfinance.blogspot.com/ |
//+------------------------------------------------------------------+
void _computeFRAMA( int lastBars, double inputData[] )
{
int pos, iteration;
double diff, priorDiff;
double length;
double priceMax, priceMin;
double fdi,alpha;
int speed;
//----
for( pos=lastBars; pos>=0; pos-- )
{
priceMax=_highest( e_period, pos, inputData );
priceMin=_lowest( e_period, pos, inputData );
length =0.0;
priorDiff=0.0;
//----
for( iteration=0; iteration <= g_period_minus_1; iteration++ )
{
if(( priceMax - priceMin)> 0.0 )
{
diff =(inputData[pos + iteration] - priceMin )/( priceMax - priceMin );
if(iteration > 0 )
{
length+=MathSqrt( MathPow( diff - priorDiff, 2.0)+(1.0/MathPow( e_period, 2.0)) );
}
priorDiff=diff;
}
}
if(length > 0.0 )
{
fdi=1.0 +(MathLog( length)+ LOG_2 )/MathLog( 2 * g_period_minus_1 );
}
else
{
/*
** The FDI algorithm suggests in this case a zero value.
** I prefer to use the previous FDI value.
*/
fdi=0.0;
}

alpha=MathExp(-4.6*(fdi-1)); // This is the recommendation from Elhers, but using fdi as the fractal dimension
ExtOutputBuffer[pos]=alpha*Close[pos]+(1-alpha)*ExtOutputBuffer[pos+1];
}
}
//+------------------------------------------------------------------+
//| FUNCTION : _highest |
//| Search for the highest value in an array data |
//| In : |
//| - n : find the highest on these n data |
//| - pos : begin to search for from this index |
//| - inputData : data array on which the searching for is done |
//| |
//| Return : the highest value | |
//+------------------------------------------------------------------+
double _highest( int n, int pos, double inputData[] )
{
int length=pos + n;
double highest=0.0;
//----
for( int i=pos; i < length; i++ )
{
if(inputData[i] > highest)highest=inputData[i];
}
return( highest );
}
//+------------------------------------------------------------------+
//| FUNCTION : _lowest | ===+
//| Search for the lowest value in an array data |
//| In : |
//| - n : find the hihest on these n data |
//| - pos : begin to search for from this index |
//| - inputData : data array on which the searching for is done |
//| |
//| Return : the highest value |
//+------------------------------------------------------------------+
double _lowest( int n, int pos, double inputData[] )
{
int length=pos + n;
double lowest=9999999999.0;
//----
for( int i=pos; i < length; i++ )
{
if(inputData[i] < lowest)lowest=inputData[i];
}
return( lowest );
}
//+------------------------------------------------------------------+

No comments: