PureBytes Links
Trading Reference Links
|
Some newbies in this group (like me) have problems accessing past
data. Normally one would use the Ref() function of AFL, but this
doesn't work from within a definition like follows:
foo_today = func(foo_yesterday, anothervalue1, anothervalue2,...)
The workaround offered in these cases is the usage of the AMA2
function of AFL, since there is no PREV function like in
Metastock. Unfortunately the AMA2 function is not sufficient in
some cases, but - thanks to JScript and VBScript in AB - there is
an other way. In this text I want to decribe, how referencing past
data from within a functions definition can be done by using
JScript. I will explain it using two examples, a on balance volume
and a ATR based trailing stop.
Assumed you know the definiton of the OBV: How would you code it in
AFL? Simply writing OBV()? Right, but we cannot learn anything
from that, so let's get on to the next possibility using the AMA2
function:
OBV = AMA2(Volume,IIf(Close>Ref(Close,-1),1,IIf(Close<Ref(Close,-
1),-1,0)),1);
This function works as follows: The Volume is firstly multiplied
with an factor (+1,-1 or 0) depending on the close price change.
The result is added to the yesterdays OBV value (which is
multiplied before with the "1" on the far right). But this
yesterdays OBV value is not "known" anyhow to the AMA2 function, it
is again calculated by evaluating the first two arguments of the
AMA2 function but therfore the OBV of the preceding day is needed.
So this calculated as well and so on.... This way the AMA2
function recalculates the OBV from the last bar back to the first.
(For an in depth explaination of the AMA2 function please refer to
the AB help files.)
Ok, this construction works, but honestly, this looks a bit
strange, because the On Balance Volume has nothing to do with an
"Adapting Moving Avarage". Well, its a workaround like so many in
AB. It's not easy to see for a newbie.
Here comes, how this calculation can be done using JScript: In
JScript it is possible calculate the OBV values using a "for" loop
from the first to the last bar. This is the opposite direction of
how it's calculated by the AMA2 function. This difference is not
importend in this OBV example but can be very crucial (see 2nd
example). Please read the comments in the following code:
//ON BALANCE VOLUME with JScript
EnableScript("jscript");
<%
//Close and Volume are required to calculate the OBV
//transfer Volume and Close from AFL into JScript
Vol = VBArray(AFL("Volume")).toArray();
Clo = VBArray(AFL("Close")).toArray();
//create an empty OBV-Array and initiate the first value
jsOBV = new Array();
jsOBV[0]=Vol[0];
//calculating OBV-Data in a loop
for(i=1;i<Clo.length;i++){
if(Clo[i]>Clo[i-1]){
jsOBV[i]=jsOBV[i-1]+Vol[i];}//adding Volume to yesterdays OBV
else{
if(Clo[i]<Clo[i-1]){
jsOBV[i]=jsOBV[i-1]-Vol[i];}//substracting Volume from
yesterdays OBV
else{
jsOBV[i]=jsOBV[i-1];}//OBV not changed
}
}
//transfer jsOBV back to AFL
AFL("myOBV")= jsOBV;
%>
Plot(myOBV,"myOBV",colorRed,styleDots+styleNoLine);
Plot(OBV(),"OBV",colorBlue,styleLine);
Attached you find a picture of this AFL code. the blue line is the
plot of the AFL-OBV() function, the red dots were calculated using
JScript.
Yes, you are right: the code is very long and it makes no sense
creating the OBV that way. This was only to show you the general
principle of coding recursive functions: (1) Create an (empty)
array, (2) [optional] initiate the first value and (3) run a loop
to calculate the remaining values of the array. The point is, that
this methology is more powerful than using the AMA2 function
because the calculation is done from the first to the last bar.
This direction makes it possible that a value is calculated based
on the REAL preceding value (not so in AMA2).
AMA2 can (and should) be used for "ordinary" calculations like +-
*/. But try to use it in the following formula:
foo_today = Max(foo_yesterday,anothervalue).
I was not successful but that was part of my attempt to create ATR
based trailing stops. I wanted them to be calculated as follows.
Initially the distance from the close price to the stop should be
say 3*ATR(9): Close+3*ATR(9) for the short stop and Close-3*ATR(9)
for the long stop. The short stop will only be shifted towards
lower prices, except it gets broken by the close price. Vice versa,
the long stop will only be increased, except it gets broken by the
close.
I think it is impossble to code that in "pure" AFL. (I'd be curious
to see the opposite proven. Anyone?) The reason, I think, is that
there are two comparing conditions to be included in the AMA2
function: was the stop of yesterday broken? and is the todays stop
higher/lower than yesterdays? So my approach was using JScript.
Please read the comments in the following code:
//ATR BASED STOPS with JScript
EnableScript("jscript");
//first the raw stop functions are defined
//based on ATR-depending distance to the close price
multiplier = 3;
Len = 5;
range = multiplier*ATR(Len);
rawlongstop=C-range; //raw long stop
rawshortstop=C+range; //raw short stop
//two JScript functions for the stops are defined
<%
//function for the longstop
function f_longstop(rawstop,baseprice,Length){
//"importing" the AFL functions into JScript
Len = AFL(Length);
Ls=VBArray(AFL(rawstop)).toArray();
bp = VBArray(AFL(baseprice)).toArray();
//the stop is calculated in the loop
for(i=Len+1;i<Ls.length;i++){
if(bp[i]>=Ls[i-1]){Ls[i]=Math.max(Ls[i],Ls[i-1]);}
}
return Ls;
}
//function for the shortstop
function f_shortstop(rawstop,baseprice,Length){
//"importing" the AFL functions into JScript
Len = AFL(Length);
Ss=VBArray(AFL(rawstop)).toArray();
bp = VBArray(AFL(baseprice)).toArray();
//the stop is calculated in the loop
for(i=Len+1;i<Ss.length;i++){
if(bp[i]<=Ss[i-1]){Ss[i]=Math.min(Ss[i],Ss[i-1]);}
}
return Ss;
}
%>//End of JScript
//call the JScript functions to create the AFL stop arrays
script = GetScriptObject();
longstop = script.f_longstop("rawlongstop","Close","Len");
shortstop = script.f_shortstop("rawshortstop","Close","Len");
//End of stop calculations
//A simple trading system is included
//to color the price plot. Since it is a trend following system,
//it will cause losses in non trending markets.
//Please don't apply.
Buy=C>Ref(shortstop,-1);
Sell= C<Ref(longstop,-1);
Buy=ExRem(Buy,Sell);
Sell=ExRem(Sell,Buy);
Short=Sell;
Cover=Buy;
poslong=Flip(Buy,Sell);//position is long
posshort=Flip(Short,Cover);//position is short
Plot(shortstop,"ShortStop",colorLightOrange,styleDots+styleNoLine);
Plot(longstop,"LongStop",colorBlue,styleDots+styleNoLine);
Plot(C,"Close",IIf(poslong,colorGreen,IIf
(posshort,colorRed,colorBlack)),styleBar);
Attached you find a image of that code applied. The price bars were
colored green and red indicating the long or short market
position. The orange dots represent the short stop, wich is only
applied if the price bars are red. Vice versa the blue dots
represent the long stop that gets only applied in the long position
(green).
Again, the two JSCript functions follow the above mentioned basic
principle: first the arrays were defined (Ls=... and Ss=...). The
Initiation was already done by the preciding AFL code
(rawlongstop=... and rawshortstop=...), so there was no need to do
it again in the JScript section, and second the loops were run to
create the remaining data.
Finally pretty easy. In addition, I was really surprised by the
execution speed. I expected it to be a bit slower because of the
JScript sections in the code, but it was not. Just a mouse click
and it is there even with long histories (^DJI back to 1930).
I hope that I could show you an other way to prgramm recursive
formulas in AB. Give the scripting languages a try, they make AB
very powerful.
Best regards,
Stephan
Attachment:
gif00451.gif
Description: GIF image
Attachment:
Attachment:
Description: "Description: GIF image"
Attachment:
Description: "gif00452.gif"
|