Systematic Trading research and development, with a flavour of Trend Following

## Amibroker e-ratio code

#### November 9th, 2009 · 32 Comments · Code, Development, Software

I recently posted about the e-ratio as a tool to measure parts of a trading system (the code files to compute the e-ratio in TradersStudio and Excel are also available). The e-ratio is supposed to be a quick tool to check how signals might add some edge to a trading system. However computing the e-ratio in TradersStudio is slow (4+ hours for one signal over 100 different durations).

So I decided to give the “legendary fast” Amibroker a test to see if it could better TradersStudio’s performance. After some “playing and learning”, I have finalised the code to compute the e-ratio. My big thanks go to the ASX gorilla whose own version forms a large part of my code.

Below is the code explanation and downloadable afl file.

Directly from the ASX Gorilla’s website as a prelude to the code:

My implementation of the Edge Ratio involves two profound Amibroker fudges. The first is the use of the AddToComposite function to create a composite ticker symbol in which to hold the ATR array of a given stock for later retrieval within the Custom Back Tester via the Foreign function.

The second fudge is the use of the VarSet/VarGet function to create a quasi array. This was necessary to overcome the limitation where array elements cannot exceed in number the value of barcount-1.

The first part is to actually code up your Buy signal (in our case a Donchian Channel Breakout). The Sell signals are tested separately:

```//BUY RULES: implemented with Buy Stop on Upper Donchian Channel(17) BuyStop = Ref(HHV(High, 17),-1); Buy = Cross( High, BuyStop ); BuyPrice = Max( BuyStop, Low ); // make sure buy price >= Low```

Exiting positions is done on a fixed duration basis:

```//Never Sell so that the position is stopped out after N bar instead Sell = 3 > 5; //Stop the positon and close it after N bars //(eratio = N that we step from 1 to 100 in optimisation) ApplyStop( stopTypeNBar, stopModeBars, eratio );```

The first fudge mentioned above to store the ATR:

```Normaliser = ATR(17); AddToComposite(Normaliser, "~atr_"+Name(), "C", 1+2+8);```

And the “meat” of the code: the chunk that implements the custom back-testing to:

1. Loop through the signals and store the Entry ATR value.
2. Loop through all trades and retrieve MFE, MAE and ATR.
3. Compute the e-ratio based on values from all trades.
```SetCustomBacktestProc(""); //activate the custom backtester if(Status("action") == actionPortfolio) //called when backtesting/optimising { bo = GetBacktesterObject(); bo.PreProcess(); // run default backtest procedure TradeATR = NumTrades = ATRArr = 0; //init variables for( bar=0; bar < BarCount-1; bar++) { bo.ProcessTradeSignals(bar);   for ( sig=bo.GetFirstSignal(bar); sig; sig=bo.GetNextSignal(bar) ) { if (sig.isEntry()) { NumTrades++; ATRArr = Foreign("~atr_"+sig.Symbol, "C"); VarSet("TradeATR" + NumTrades, ATRArr[bar]);   _TRACE("Symbol " + sig.Symbol + " ATR: " + VarGet("TradeATR" + NumTrades)); } } }   AvgMAE = AccumMAE = AvgMFE = AccumMFE = NumTrades = 0;   // iterate through closed trades for( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() ) { NumTrades++; EntryATR = VarGet ("TradeATR" + NumTrades); if ( EntryATR != 0 ) { _TRACE("EntryATR: " + WriteVal(EntryATR)); _TRACE("AccumMAE : " + WriteVal(AccumMAE)); AccumMAE = AccumMAE + (trade.GetMAE()*trade.EntryPrice/(100*EntryATR)); AccumMFE = AccumMFE + (trade.GetMFE()*trade.EntryPrice/(100*EntryATR)); }   trade.AddCustomMetric("My MAE", trade.GetMAE()*trade.EntryPrice/100); trade.AddCustomMetric("My MFE", trade.GetMFE()*trade.EntryPrice/100); trade.AddCustomMetric("Entry ATR", EntryATR*10000); }   AvgMAE = AccumMAE / NumTrades; AvgMFE = AccumMFE / NumTrades;   _TRACE(WriteVal(AccumMAE )); _TRACE(WriteVal(NumTrades)); _TRACE(WriteVal(AvgMAE));   Eratio = abs(AvgMFE/AvgMAE);   _TRACE(WriteVal(Eratio));   bo.AddCustomMetric( "Avg MAE", AvgMAE ); bo.AddCustomMetric( "Avg MFE", AvgMFE ); bo.AddCustomMetric( "Eratio", Eratio);   bo.PostProcess(); }```

If you want to run this code, you can download the e-ratio “gorilla” afl file and simply update the BUY signals to whatever you fancy testing.

UPDATE: Note the comment from Eldar Agalarov below, reporting that the code above might give problems (when restricting number of open positions). He kindly posted a version of the code that does not have this problem. Thanks Eldar!

The next post will be a direct speed comparison between TradersStudio and Amibroker for computing the e-ratio on the same underlying data and with the same signal. I expect Amibroker to win the fight hands-down as it appeared “way” faster!

### 32 Comments so far ↓

• Good stuff Jez. I’m going to add you to the blogroll to remind myself to check your blog regularly.

I haven’t fully explored the e ratio, but I’m wondering, how is it different from just plotting the avg. % trade over an n-bar exit?

Thanks for the code Jez!

• Thanks woodshedder!

I find that the e-ratio is a better evaluation of the “potential” of a trade/signal in Risk/Reward term as it uses Maximum Excursions (both on upside and downside)
For example you could have both trades that complete with 1% profit when stopping at N days, but one the trades could be dropping to -10% for most of its life while the other one could stay at around +5%. If they both finish at +1%, a simple average would not differentiate and highlight that the second trade/signal offers more potential.
The e-ratio would tell you a bit more about how the trade “behaved” during its life – and the e-ratio profile over different days allows you to get a feel for the best duration of the edge. It is not a “holy grail” metrics but I think a useful “prism” through which you can look at a signal in a back-test.
Below is the link to explain the basics of the e-ratio.
http://www.automated-trading-system.com/e-ratio-trading-edge/

• Cool. That explanation makes sense. I’ll have to plug the code in to AmiBroker and give it a try.
Thanks again!

• Nuanda

it’s not clear to me why you are multiplying by the entry price compared to the original code.

your version
——————-
trade.GetMAE()*trade.EntryPrice/(100*EntryATR)
trade.GetMFE()*trade.EntryPrice/(100*EntryATR)

ASX Gorilla’s version
——————-
trade.GetMAE()/EntryATR
trade.GetMFE()/EntryATR

I have the feeling your version is an enhancement since it’s comparing MAE/MFE in dollar amount versus the ATR which is also espressed in dollar amount. Correct me if I’m wrong.

Regards,

Paolo

• Nuanda,

Well spotted! Very thorough checking ;-)
You are exactly right and I think this was a slight bug in the ASX gorilla version:

The ATR is expressed in dollar terms while the MAE/MFE is expressed as a percentage of price (in Amibroker). As a result you need to calculate the MAE/MFE in dollar terms to “compare apples and apples”; and you do that by multiplying the ATR(pct) with the Entry Price divided by 100.

• David Fitch

Jez,
Working through your AFL . Found it intriguing. While I’m told it “Edge” doesn’t help EOD systems, I’d like to find for myself. To put my questions in context, I’m a long time user of AB but have only recently been drug kicking and screaming into the quant end of things and in particular, CBT.

That said, why are there trades in the _Trace field that are not round numbers, 1,2,3. But instead 1.6551, 1.0570 ?
Just get’n my head around what’s happening here.
Thanks
Dave

• Hi David,

If you look at the code, the following line produces that Debug output:
_TRACE(“Symbol ” + sig.Symbol + ” Number of Trades ” + VarGet(“TradeATR” + NumTrades));

The string is output as “number of trades” but the VarGet function actually retrieves the ATR value, hence not a round number – this was copied from the original code and is slightly misleading so Ihave updated the code linked to in this post.

Cheers and good luck with the “quant end of things” ;-)

Jez

• Dave

Jez,
NumTrades++. I usually use this at end of loop, not the beginning. IE Step #1=Bar 0, not bar 1. In your code its in two places. Changing it from top to bottom of loop does not change ATR values per signal but does change subsequent AccumMAE ,AvgMFE,eratio, and EntryATR in first trade on single ticker test.
Thanks
Dave

• Dave – Thanks for the input. I’ll check it out later

• Dave

Jez,
Follow up on NumTrades++. I suspect reason for putting the expression at start of loop was to get proper denominator in, AvgMAE=AccumMAE/NumTrades and AvgMFE=AccumMFE/NumTrades

But this still gives different values than placing the Numtrades++ at end of both loops and adding 1 to the denominator, ie Numtrades+1.
Thanks
Dave

• Dave – your explanation seems to make sense and as this is part of the code I reused from the Gorilla ASX, I cant confirm on top of my head why this was implemented that way.

I’ll definitely try your suggestion (ie +1) and revert back with code update if required (after my football game that is…)
Thanks again!

• Raj

Looks like ASX Gorilla’s blog is by invitation only. Any idea, how one gets invited to this? Thanks for the wonderful blog. I have you in my Google Reader.

• Hi Raj,
Thanks for the comments. I checked the ASX blog and it appears that – as you mention it – it has changed to an “invite-only” status (ie I cant access it either any more). It actually seemed “abandonned” when I found it (ie last update was from 2007 I believe) so I am not sure what’s up with it. I guess you could request an invite and check it out… ;-)

• Rajiv

Thanks. The only problem is that I am not even sure how to request an invite i.e. he does not even give that option. Thanks again for the fantastic blog. I use Metastock and AB currently. After reading your blog, I may look at TraderStudio. Also, check out StrataSearch. It’s essentially a software designed mostly to mix and match various systems. I am testing it right now, and the only complain I have is that the learning curve seems a bit steep.

• @Rajiv
little warning: TradersStudio does not offer any free trial… Also I have been wondering for a little while now, whether I made the right choice (I was also considering Trading Blox but was put up by the price difference: \$3,000 for Trading Blox) so I might revisit this :(

• Rajiv

Thanks for the heads up. I will do some more research. The only advantage TradersStudio has is that I could use Metastock data. Anyway, there are some other software to consider: Multi Charts (uses Easy Language), Ninja Trader, and Nuro Shell. Multi Charts is a bit pricy though — close to \$1600. Personally, I think that AB of MS is a good enough software. Besides a mechanical system, one needs money management skills and the discipline. I am leaning toward combining at least three different systems — trend, mean reversion and divergence.

• Enzo

Thank you for this afl formula it’s really an interesting approach to test the quality of entry signals. The thing I don’t understand is the normalization, because when you calculate the eRatio as the division of the MAE and MFE averages, the prior normalization is eliminated (as the same # is in both numerator and denominator).
In fact, I rewrote the procedures without the fudges and I get the exactly the same eRatio values. Thanks.

• Enzo – correct about the normalisation. Another reader mentioned the exact same point on another e-ratio post…
in http://www.automated-trading-system.com/e-ratio-trading-edge/
and I’d replied:

Conceptually it is easier to grasp the idea that the e-ratio compares the average MAE to the average MFE, hence that step in the calculation – however, I’ll give you that mathematically this is completely equivalent to comparing (and dividing) Total MAE with Total MFE (and step 3 can be considered “optional”

• Eldar Agalarov

Hello Jez! This e-Ratio code for AmiBroker is buggy and give wrong results.

• Thanks for the heads up Eldar but would you care to elaborate?
I have not used AmiBroker (or this implementation of the e-ratio) since around the time I wrote that post but I seem to remember it was giving me the correct values back then..

• Eldar Agalarov

This code works fine when there is no restriction on the maximum number of open positions.
But if we limit the number of open positions by SetOption(“MaxOpenPositions”, posNum) statement in the trading system code then this code is not working correctly.
The thing is that not all raw signals will end up trading signals. For example if we have a restriction on the 10 max open positions, the AmiBroker will select only the first 10 signals that will have the highest PositionScore number.

The bug is there: VarSet(“TradeATR” + NumTrades, ATRArr[bar]) – when we write to global dynamic variable Entry ATR results by using incremental NumTrades variable.
And bug is there: EntryATR = VarGet (“TradeATR” + NumTrades) – when we read result from flobal variable using NumTrades as index.

If there is a limit, then the variable NumTrades will produce biased (wrong) results.

• Eldar Agalarov

I suggest my correct code:

if (Status(“action”) == actionPortfolio) {
bo = GetBacktesterObject();
bo.Backtest(True);

stAll = bo.GetPerformanceStats(0);
stLong = bo.GetPerformanceStats(1);
stShort = bo.GetPerformanceStats(2);

qtyProfitAll = stAll.GetValue(“AllQty”);
qtyProfitLong = stLong.GetValue(“AllQty”);
qtyProfitShort = stShort.GetValue(“AllQty”);

// e-Ratio
avgMAEAll = avgMAELong = avgMAEShort = 0;
avgMFEAll = avgMFELong = avgMFEShort = 0;
barIndices = BarIndex();
dateTimes = DateTime();
for (trade = bo.GetFirstTrade(); !IsNull(trade); trade = bo.GetNextTrade()) {
arrATR = Foreign(“~ATR_” + trade.Symbol, “Close”);
tradeBarIndex = LastValue(ValueWhen(trade.EntryDateTime == dateTimes, barIndices));
absMAE = trade.EntryPrice * trade.GetMAE() * 0.01;
absMFE = trade.EntryPrice * trade.GetMFE() * 0.01;
entryATR = arrATR[tradeBarIndex];
normMAE = absMAE / entryATR;
normMFE = absMFE / entryATR;
avgMAEAll += normMAE;
avgMFEAll += normMFE;
if (trade.IsLong()) {
avgMAELong += normMAE;
avgMFELong += normMFE;
} else {
avgMAEShort += normMAE;
avgMFEShort += normMFE;
}
trade.AddCustomMetric(“MAE (\$)”, absMAE, 2);
trade.AddCustomMetric(“MFE (\$)”, absMFE, 2);
trade.AddCustomMetric(“Entry ATR”, entryATR, 2);
}
avgMAEAll /= qtyProfitAll;
avgMAELong /= qtyProfitLong;
avgMAEShort /= qtyProfitShort;
avgMFEAll /= qtyProfitAll;
avgMFELong /= qtyProfitLong;
avgMFEShort /= qtyProfitShort;
eRatioAll = avgMFEAll / abs(avgMAEAll);
eRatioLong = avgMFELong / abs(avgMAELong);
eRatioShort = avgMFEShort / abs(avgMAEShort);
bo.AddCustomMetric(“e-Ratio”, eRatioAll, eRatioLong, eRatioShort, 2);

bo.ListTrades();
}

• Thanks very much Eldar! I appreciate you taking the time to do this. I have just updated the post to point to your comment for future readers of this post/code

• alex

how do you get the graph for the e ratio ?

• alex – there is not graph on this post but if you’re referring to the one on that other e-ratio post, the e-ratio was charted with Excel

• alex

Hi Jez,
as far as i understand the codes help calculate e-ratio for the duration of the backtest. what i would like to do is see a graph of the e-ratio to see if a time based exit might be a better option.
Thanks,
Abhi

• Alex,
This code calculates an average e-ratio for all trades in the back-test.
To produce a chart as mentioned above, I actually ran 100 back-tests each with a different trade duration (time-based exit) and simply plotted the average e-ratio as a function of trade duration. But this was done in Excel. Not sure how you could do it in AmiBroker (which I do not use any more).

• alex

Hi Jez,
Thanks for the info. just wanted to avoid the task of doing 100 backtests with different duration :-)
Alex

• Hi,

Thanks for the code will try to test with supertrend indicator. Let your know the results once the testing is completed from my end. Do you have any information on how to perform monte carlo analysis in amibroker?

• Sorry no, Rajandran R – I do not know about MC in AmiBroker as I’ve stopped using it…

• Shane

Jez,
Great blog, thanks for sharing this. I see you’ve mentioned you dont use AB anymore, but if I could ask a couple of questions on your eratio code please, I’m still very green on the coding side of things but learning as much as I can, where I can.
I get great results with this code on certain watchlists that I’ve conducted walkforward runs on, but on the AmiBroker code check function it identifies a future leak, I narrowed it done to this line of code –
AddToComposite(Normaliser, “~atr_”+Name(), “C”, 1+2+8);
When I comment this line out it passes the future leak test.
I have no idea what this line does, but the system performs fine without it from my tests.
Is there a future leak in this system, is it robust?
I want to paper trade this just for the practice and compare the results to AmiBrokers but it doesnt generate sell signals in the scans that I’ve tried, just lots of buy signals, please, how would you papertrade this system? I see no way of setting the sell criteria for scanning, it just seems time based. ( new here remember )
Also I’m looking to buy some good books on MAE/MFE/eRatio to get a better understanding of how to implement this into my systems, any recommendations?
I ve ordered ‘The Way of the Turtle’ from your mention of it elsewhere and looking forward to that, any others please?
Your help here would be very appreciated.