Backtesting ASX Prices in Zorro

William Wu
4 min readJan 19, 2020

--

Written for Quants & Algo Traders in Australia.

Note: There’s always different methods and platforms for analysis, backtesting and forward testing. I’ve just chosen Zorro for its fast backtesting and simplistic language and framework (Lite-C). It doesn’t require any complicated setup, and is able to connect to MT5 platform through plugins.

Now, usually there’s 3 issues when it comes to backtesting an idea or an edge and that is: Data, Format and Platform. Where to source the data? Is it in a usable format and what platform to backtest in?

1. Data

For most people, EOD data is sufficient to find or research an edge, anything less is day trading territory that I don’t believe is necessary for 80–90% of retail traders. All the data is split into one file representing the prices of all companies on the ASX for that day. For some programs like R or Python that data would probably be good enough, however not for Zorro.

2. Format

Zorro utilises its own file formats which means the data will need to be preprocessed into separate pricing data for each company. The following powershell script takes all the data from the EOD files, combines them and sorts the data according to company. All the data is then separated into one file per company containing all the pricing data that was available.

Get-ChildItem -Path "./output" -Recurse -File | Remove-ItemGet-ChildItem -Path "./split" -Recurse -File | Remove-ItemGet-Content ./data/*.txt | Add-Content ./output/merged.txt@("Ticker, Date, Open, High, Low, Close, Volume")  +  (Get-Content ./output/merged.txt) | Set-Content ./output/merged.csv$prop1 = @{Expression='Ticker'; Descending=$true }$prop2 = @{Expression='Date'; Ascending=$true }(Import-Csv ./output/merged.csv) | Sort-Object $prop1, $prop2 | Export-Csv ./output/sorted.csv -NoType(Get-Content ./output/sorted.csv | Select-Object -Skip 1) | Set-Content ./output/sorted.txtImport-Csv ./output/sorted.csv | Group-Object Ticker | Foreach-Object {$_.Group | Select-Object Date, Open, High, Low, Close, Volume  | Export-Csv "./split/$($_.Name).csv" -NoTypeInformation}Get-ChildItem ./split/* | Select-Object -Property BaseName | Export-Csv -NoTypeInformation ./output/list.csv(Get-Content ./output/list.csv | Select-Object -Skip 1) | Set-Content ./output/list.txt(gc ./output/list.txt) -replace '"', "" | Set-Content ./output/cleanlist.txt

3. Platform

Zorro allows users to import their own data into the platform through direct parsing methods. However, the problem that I discovered was keeping 2 files open at the same time. Since the program below is designed to read from files specifiying a list of all company names and then open the file with the same company name; it originally error-ed when it tried to switch back to reading a list of company names. Therefore the workaround was to allocate memory for the complete list of company names. This solution was quick and sufficient to import all the companies into Zorro's data format.

// the symbol that you want to download
string TickerListPath = "D:\\Workspace\\Zorro\\External Raw Data\\ASX\\output\\cleanlist.txt";
string TickerPath = "D:\\Workspace\\Zorro\\External Raw Data\\ASX\\split\\%s.csv";
string Format = "+%Y%m%d,f3,f1,f2,f4,f6";
bool import(string tickerId)
{
string FullPath = strf(TickerPath,tickerId);
printf("\nLoading from: %s", FullPath);

string Content = file_content(FullPath);
if(Content) {
string TempFile = "History\\history.csv";
file_write(TempFile,Content,0);
// convert the CSV to .t8
int N = dataParse(1,Format,TempFile);
if(N) {
// replace '.','/' in tickerId name with '-'
dataSave(1,strf("History\\%s.t6",strxc(strxc(tickerId,'.','-'),'/','-')));
printf("\n%i %s records read",N,tickerId);
return true;
} else {
printf("%s invalid content",TempFile);
return false;
}
} else {
printf("Unable to access %s",Content);
return false;
}
}
void main()
{
string tickerFile = file_content(TickerListPath);
if(tickerFile) {
string delim = "\r\n";
string token;

char* tickers = malloc(strlen(tickerFile));
strcpy(tickers, tickerFile);

// get the first token
token = strtok(tickers, delim);
while( token != NULL ) {
printf("\nToken: %s", token);
if (import(token)) {
// walk through other tokens
token = strtok(NULL, delim);
} else {
break;
}
}
free(tickers);
} else {
printf("Unable to access %s",tickerFile);
return;
}
}

4. Conclusion & Backtesting:

After importing all the data from all the different ASX companies a quick backtest on a single company can be performed. I've chosen CSL at random. One thing to note however was the entire process took almost 30minutes. Not something I would recommend doing every day or even week. I'll be looking to better and quicker platforms to potentially backtest. Afterall, trading on a daily timeframe doesn't really require automated trading anyways. I would only really consider Zorro for the forex markets.

function run() 
{
// Slider Inputs
Capital = slider(1,1000,1,10000,"Capital","Initial capital");
Margin = slider(2,100,0,500,"Margin",0);

StartDate = 20130107;
EndDate = 20200109;

//MaxLong = 3;
//MaxShort = 3;
asset("CSL");
Verbose = 7;
LookBack = 30; // needed for MMI
BarPeriod = 1440; // 1 DAY
set(LOGFILE,PLOTNOW); // log all trades
vars Prices = series(priceClose());
vars Trends = series(LowPass(Prices,30));

Stop = 1.5*ATR(10);

vars MMI_Raws = series(MMI(Prices,30));
vars MMI_Smooths = series(LowPass(MMI_Raws,30));

if(falling(MMI_Smooths))
{
if(valley(Trends))
enterLong();
else if(peak(Trends))
enterShort();
}

plot("MMI_Raw",MMI_Raws,NEW,GREY);
plot("MMI_Smooth",MMI_Smooths,0,BLACK);
}

--

--