E-Ratio Custom Backtester

Hi All,

I'm having difficulty getting E-Ratio custom backtester code to work and I'm guessing
that it may be due to Amibroker version changes since I believe it used to work when originally published in 2012 by Eldar Agalarov. Could anyone care to enlighten why this custom backtester code is not working correctly?


// TRADING SYSTEM //

Buy = C > MA(C,10); // Trade entry to test E-Ratio.

Sell = 5 < 3 ; // Never Sell so that the position is stopped out after N bars instead.

// NDAY STOP //

NDayStop = Optimize("Range Stop Days",10,1,60,1); 

ApplyStop( stopTypeNBar, stopModeBars, NDayStop, 0 );   

// CREATE ATR COMPOSITE //

AvgTrueRange = ATR(20);

AddToComposite(AvgTrueRange, "~ATR_" + Name(), "C", atcFlagDeleteValues | atcFlagCompositeGroup | atcFlagEnableInBacktest);  

// CUSTOM BACKTESTER // 

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", arrATR);

bo.ListTrades();
}

@axismagi there is an old blog post with afl code for the Edge Ratio. If you follow the comment section below the original blog you will find various suggested corrections to the bloggers code.

http://www.automated-trading-system.com/e-ratio-amibroker-code/

And his earlier discussion about this metric.
http://www.automated-trading-system.com/e-ratio-trading-edge/

1 Like

Thanks for your input portfoliobuilder, however that website article is where I found Eldar Agalarov's code (listed above) referenced in the comments section.
Apparently Jez Liberties version of the E-Ratio in that website article has some bugs when run with smaller number of allowed open positions which is why I was interested in getting Eldar Agalarov's code to work.

Cheers!

Code is flawed.

Instead of ATC and Foreign you should rather use static variables to pass array from 1st phase to 2nd phase.

Secondly, to get ATR at entry datetime you should rather use LookUp function.

Also why did you set arrATR as e-ratio?

Then... to disable regular Sell and only apply ApplyStop simply assign zero to Sell.

Note: I did not test further. It is not my job and not of my interest. Just did some obvious modifications.

/// @link http://www.automated-trading-system.com/e-ratio-amibroker-code/
/// some fixes/improvements at 
/// @link https://forum.amibroker.com/t/e-ratio-custom-backtester/14895/4
// TRADING SYSTEM //
Buy = C > MA( C, 10 ); // Trade entry to test E-Ratio.
Sell = 0; // Disable regular Sell so that the position is stopped out after N bars instead.

Short = Cover = 0;

// NDAY STOP //
NDayStop = Optimize( "Range Stop Days", 10, 1, 60, 1 );
ApplyStop( stopTypeNBar, stopModeBars, NDayStop, 0 );

// CREATE ATR COMPOSITE //
AvgTrueRange = ATR( 20 );
StaticVarSet("~ATR_" + Name(), AvgTrueRange );

// CUSTOM BACKTESTER //
SetCustomBacktestProc("");
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;

    for ( trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade() )
    {    
		arrATR = StaticVarGet( "~ATR_" + trade.Symbol );
		absMAE = trade.EntryPrice * trade.GetMAE() * 0.01;
		absMFE = trade.EntryPrice * trade.GetMFE() * 0.01;
		entryATR = Lookup(arrATR, trade.EntryDateTime, 0);
		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, 5 );
	}

	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();
}
1 Like

Many thanks for your help fxshrat ! Code is working now :slight_smile:

Illustrates well the use of static variables rather than create a composite which I previously didn't understand. And also LookUp function at time of trade...

my mistake: I had previously set "e-ratio" to arrATR variable while trying to debug and test to see if arrATR was working and had forgotten when I copied the code to my post.

thanks I thought sell = 0 was the simpler way of doing that but noticed Jez Liberty coded it 5 < 3 which made question it and use his line. Still very much a coding noob but learning.

Cheers!