Issue 1/2001 (No. 9)
AmiBroker Tips weekly newsletter.
Issue 1/2001. 8 January 2001.
Copyright (C)2000 Tomasz Janeczko.
All back issues available from:
http://www.amibroker.com/newsletter/
IN THIS ISSUE
1 Welcome
2 AFL Formula Library: New AFL functions in version 3.45 open new possibilities
3 Tip of the week: An automated way of setting up WebID field
1 Welcome

Welcome to the 1st issue of AmiBroker Tips newsletter in the 2001. Hopefully you enjoy new version (3.45) that has just been released and is available for download from http://www.amibroker.com/download.html. In this issue I will discuss some new AFL functions that are added in this version. Also, in "Tip of the week" section I will present you promised script for importing JSE codes into WebID field. All Sharenet users can use this script without modification, all the others should just replace the data file and use the script whenever they want to update the database with custom WebID codes. If you wonder why would you need to do this just read the previous newsletter.

Just a reminder: if you have any comments/suggestions or article ideas, please don't hesitate to drop a line to newsletter@amibroker.com

2 New AFL functions in version 3.45 open new possibilities

A new version of AmiBroker brings 17 new AFL functions. You can find the description of these new functions in the "AFL Language Reference" section of AmiBroker's guide. Here we will focus on four of them: HighestSince, HighestSinceBars, LowestSince, LowestSinceBars. First, let's take a look at their descriptions taken from the guide:

  1. HIGHESTSINCE
    SYNTAX highestsince( EXPRESSION, ARRAY, Nth = 1 )
    RETURNS ARRAY
    FUNCTION Returns the highest ARRAY value since EXPRESSION was true on the Nth most recent occurrence.
    EXAMPLE highestsince( Cross( macd(), 0 ), Close, 1 ) returns the highest close price since macd() has crossed above zero.
  2. HIGHESTSINCEBARS
    SYNTAX highestsincebars( EXPRESSION, ARRAY, Nth = 1 )
    RETURNS ARRAY
    FUNCTION Returns the number of bars that have passed since highest ARRAY value since EXPRESSION was true on the Nth most recent occurrence.
    EXAMPLE highestsincebars( Cross( macd(), 0 ), Close, 1 ) returns the number of bars passed since the highest close price was detected from the time when macd() has crossed above zero.
  3. LOWESTSINCE
    SYNTAX lowestsince( EXPRESSION, ARRAY, Nth = 1 )
    RETURNS ARRAY
    FUNCTION Returns the lowest ARRAY value since EXPRESSION was true on the Nth most recent occurrence.
    EXAMPLE lowestsince( Cross( macd(), 0 ), Close, 1 ) returns the lowest close price since macd() has crossed above zero.
  4. LOWESTSINCEBARS
    SYNTAX lowestsincebars( EXPRESSION, ARRAY, Nth = 1 )
    RETURNS ARRAY
    FUNCTION Returns the number of bars that have passed since lowest ARRAY value since EXPRESSION was true on the Nth most recent occurrence.
    EXAMPLE lowestsincebars( Cross( macd(), 0 ), Close, 1 ) returns the number of bars passed since the lowest close price was detected from the time when macd() has crossed above zero.

As I will show in some examples below these functions bring new possibilities for writing formulas in AFL.

Here is an excerpt from an e-mail I have received some weeks ago "I was trying to write code for the fund switching system in the October issue of TA OF S&C. It requires one to find the lowest low since the highest high in the last 52 weeks and use that lowest low figure against the current close as a measure of relative strength to rank funds. A buy is triggered in the top 8 funds if the current close is above 28 week EMA." . I answered that this kind of formula would need a LowestSince() function which was not available. But.. now it is - so look what we can do....

/* get the highest high from last 52 weeks */
HighLast52week = LastValue( HHV( High, 5 * 52 ) );

/* now find the bar when this happened */
HighLast52bar = HighLast52week == High;

/* find the lowest low since then */
LowSinceLast52High = LastValue( LowestSince( HighLast52bar, Low ) );

/* dividing close by lowest low will give strength */
Strength = Close / LowSinceLast52High;

/* the detection threshold, here we filter out the stocks that closed less than 10% over the low - this might be used to get only the top 8 */
Threshold = 1.1;

/* a buy is generated when current bar is after the 52 week high, Strength is above threshold and close crossed above moving average */
Buy = Cum( 1 ) > HighLast52Bar AND Strength > Threshold AND Cross( Close , EMA( Close, 28 * 5 ) );

The only drawback of this formula is that you currently can not back-test it. Why? It is simple - in most cases this formula will give you only one buy signal for the last 52 weeks.

A second example will be Gann Swing or rather "Gann Swing indicator". Original Gann Swing is a rectangular line drawn over the price chart. Implementing true Gann Swing chart using AFL is not currently possible because it would require referencing future quotes to find the highest high / lowest low, while AmiBroker functions HighestSince, LowestSince use only bars up to current bar. In most cases this is the correct mode of operation, because in reality you don't know the future :-). So our Gann Swing indicator will give you "1" when Swing Up and "-1" when Swing Down.

/* find the number of bars since last two consecutive highs */
Us = BarsSince( ( H > Ref( H,-1 ) ) AND (Ref(H,-1) > Ref(H,-2) ) );

/* find the number of bars since last two consecutive lows */

Ds = BarsSince( ( L < Ref( L,-1 ) ) AND ( Ref( L, -1) < Ref( L, - 2 ) ) );

/* outside bar is reached then it is treated in the opposite way to the next bar.*/
Sd1 =IIf( Us==0, IIf( Ref(L,-1) != LowestSince( Ds==0, L ), 1, 0),
IIf( Ds==0, IIf( Ref(H,-1) != HighestSince( Us==0, H ), -1, 0),
0));
Sd2 = IIf( Sd1 == 1, IIf( Ref( BarsSince(Sd1==1), -1) > Ref( BarsSince( Sd1 == -1), -1), 1, 0),
IIf( Sd1 == -1, IIf( Ref( BarsSince(Sd1==1),-1) < Ref( BarsSince( Sd1 == -1),-1), -1, 0), 0 ) );

graph0 = ValueWhen( Sd2, Sd2 ); /* sample and hold non-zero values */

Hopefully you will find these examples useful. AFL will continue to grow. I plan to include functions for detecting candlesticks patterns, more built-in indicators and general-purpose functions making writing your formulas easier.

3 Tip of the week: An automated way of setting up WebID field

In the previous issue of AmiBroker Tips newsletter I presented the steps needed to setup AmiBroker's profile view to use its built-in web browser to display your local profiles. As shown in the example of Sharenet site providing profile and fundamental information, it is sometimes necessary to use WebID field (available in Stock->Information window) instead of ticker name. As promised last week, I asked Sharenet to give me the file with tickers and appropriate JSECodes. And here it is. A big thank you goes to Sharenet for supplying this file. Having such file it is no longer a pain to set up all WebID codes in JSE database.

The script itself is very easy - I just adapted Industries.js script (described in the 4/2000 issue of AmiBroker tips newsletter) to save some work :-). The only important difference is that the data file from Sharenet has opposite order of columns: JSECode is the first field and then comes the Ticker. After these changes the script looks like this:

/* change this line according to your data file name */
ImportCodes("JSECodes.txt");

function ImportCodes( filename )
{
var fso, f, r;
var ForReading = 1;
var AmiBroker;
var fields;
var stock;

/* Create AmiBroker app object */
AmiBroker = new ActiveXObject( "Broker.Application" );

/* ... and file system object */
fso = new ActiveXObject( "Scripting.FileSystemObject" );

/* open ASCII file */
f = fso.OpenTextFile( filename, ForReading);

i = 1;
/* read the file line by line */
while ( !f.AtEndOfStream )
{

r = f.ReadLine();

/* split the lines using comma as a separator */

fields = r.split(",");

try
{

/* add a ticker - this is safe operation, in case that */
/* ticker already exists, AmiBroker returns existing one */
/* note that ticker is in the second column */

stock = AmiBroker.Stocks.Add( fields[ 1 ] );

stock.WebID = fields[ 0 ];

}
catch( e )
{

WScript.echo( "There is a problem in line no." + i + ".\nThe line looks as follows:\n'" + r + "'\nIt will be skipped and next lines will be processed as normal" );

}

i++;

}

/* refresh ticker list and windows */
AmiBroker.RefreshAll();

}

A ready-to-use version of the WebID.js script is available here. Please remember to copy the data file to the same directory. Please launch AmiBroker, load JSE database and run the script by double clicking on it (in the Windows Explorer). You will see "Script started" and after one minute or so you will see "Script finished" message. This means that your database has been updated with WebID fields.

Now, if you followed last week description on setting up your profiles for Sharenet site you can just choose View->Profile option from the menu and "Quick company fundamentals" page should appear in the bottom pane of AmiBroker.

You can, of course set up your profiles to display other pages from Sharenet site. For example, if you want to see the listing of the most recent news in the Profile view, you should enter the following URL template:

http://www.sharenet.co.za/free/sens/list_sens.phtml?scode={i}&scheme=default

(Note: because of some minor bug in AmiBroker you will need to enter this URL /or copy via clipboard/, and then select different market from the list and then click OK - otherwise new template URL will not be saved)

If you don't trade on Johannesburg Stock Exchange you might think that this script is not useful for you - but you might be wrong - there are a lot of places on the web that use their custom coding for stocks (not using tickers) and for all these places this script would save you a lot of work, if only you have a text file that maps tickers to custom code.

.... and that's all for this week - hope you enjoyed reading


AmiBroker Tips weekly newsletter. Issue 1/2001. Copyright (C)2001 Tomasz Janeczko. All back issues available from: http://www.amibroker.com/newsletter/