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

a JScript replacing Metastocks PREV



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"