If You Can’t Go Forward, Then Go Backward [Back To The Future]

Calculate MAE/MFE 30 Bars after A Signal

A very astute reader of this blog brought a snippet of code that looks like EasyLanguage and sort of behaves like it, but not exactly.  This code was presented on the exceptional blog of Quant Trader posted by Kahler Philipp.  He used some of the ideas from  Dave Bergstrom.

Equilla Programming Language

The theory behind the code is quite interesting and I haven’t gotten into it thoroughly, but will do so in the next few days.  The code was derived from Trade-Signal’s Equilla Programming Language.  I looked at the website and it seems to leans heavily on an EasyLanguage like syntax, but unlike EZLang allows you to incorporate indicators right in the Strategy.  It also allows you, and I might be wrong, to move forward in time from a point in the past quite easily.  The code basically was fed a signal (+1,0,-1) and based on this value progressively moved forward one bar at a time  (over a certain time period) and calculated the MAE and MFE (max. adverse/favorable excursion for each bar.  The cumulative MAE/MFE were then stored in a BIN for each bar.  At the end of the data, a chart of the ratio between the MAE and MFE was plotted.

EasyLanguage Version

I tried to replicate the code to the best of my ability by going back in time and recording a trading signal and then moving Back to The Future thirty bars, in this case, to calculated and store the MAE/MFE in the BINS.

Simple Moving Average Cross Over Test

After 100 bars, I looked back 30 bars to determine if the price was either greater than or less than the 21 day moving average.   Let’s assume the close was greater than the 21 day moving average 30 days ago, I then kept going backward until this was not the case.  In other words I found the bar that crossed the moving average.  It could have been 5 or 18 or whatever bars further back.  I stored that close and then started moving forward calculating the MAE/MFE by keeping track of the Highest Close and Lowest Close made during 30 bar holding period.  You will see the calculation in the code.  Every time I got a signal I accumulated the results of the calculations for each bar in the walk forward period.  At the end of the chart or test I divided each bars MFE by its MAE and plotted the results.  A table was also created in the Print Log.  This code is barely beta, so let me know if you see any apparent errors in logic or calculations.


inputs: ilb(30); //ilb - initial lookback
vars: lb(0),signal(0),btf(0),mf(0),ma(0),hh(0),ll(99999999),arrCnt(0),numSigs(0);
arrays : mfe[40](0),mae[40](0);
lb = ilb;
if barNumber > 100 then 
begin
	signal = iff(c[ilb] > average(c[ilb],21),1,-1);
//	print(d," signal ",signal," ",ilb);
	if  signal <> signal[1] then
	begin	
		numSigs = numSigs + 1; // keep track of number of signals
//		print("Inside loop ", date[ilb]," ",c[ilb]," ",average(c[ilb],21));
		if signal = 1 then // loop further back to get cross over
		begin
//			print("Inside signal = 1 ",date[lb]," ",c[lb]," ",average(c[lb],21));
			while c[lb] > average(c[lb],21)
			begin
				lb = lb + 1;
			end;
//			print("lb = ",lb);
		end;
		
		if signal = -1 then // loop further back to get cross over
		begin
//			print("Inside signal = -1 ",date[lb]," ",c[lb]," ",average(c[lb],21));
			while c[lb] < average(c[lb],21)
			begin
				lb = lb + 1;
			end;
		end;
		lb = lb - 1;
		
		hh = 0;
		ll = 999999999;
		
		arrCnt = 0;
		for btf = lb downto (lb - ilb) //btf BACK TO FUTURE INDEX
		begin
			mf=0;
			ma=0;
			hh=maxList(c[btf],hh);
//			print("inside inner loop ",btf," hh ",hh," **arrCnt ",arrCnt);
			ll=minList(c[btf],ll);	
			if signal>0 then 
			begin
				mf=iff(hh>c[lb],(hh-c[lb])/c[lb],0); // mf long signal
				ma=iff(ll<c[lb],(c[lb]-ll)/c[lb],0); // ma long signal
			end;
			if signal<0 then begin
				ma=iff(hh>c[lb],(hh-c[lb])/c[lb],0); // ma after short signal
				mf=iff(ll<c[lb],(c[lb]-ll)/c[lb],0); // mf after short signal
			end;
//			print(btf," signal ",signal," mf ",mf:0:5," ma ",ma:0:5," hh ",hh," ll ",ll," close[lb] ",c[lb]);
			mfe[arrCnt]=mfe[arrCnt]+absValue(signal)*mf;
			mae[arrCnt]=mae[arrCnt]+absValue(signal)*ma;
			arrCnt = arrCnt + 1;
		end;
	end;
end;

if lastBarOnChart then
begin
    print(" ** MFE / MAE ** ");
	for arrCnt = 1 to 30
	begin
		print("Bar # ",arrCnt:1:0," mfe / mae ",(mfe[arrCnt]/mae[arrCnt]):0:5);
	end;
	
	for arrCnt = 30 downto 1
	begin
		plot1[arrCnt](mfe[31-arrCnt]/mae[31-arrCnt]," mfe/mae ");
	end;
end;
Back to The Future - going backward then forward

Here is an output at the end of a test on Crude Oil

 ** MFE / MAE ** 
Bar # 1 mfe / mae 0.79828
Bar # 2 mfe / mae 0.81267
Bar # 3 mfe / mae 0.82771
Bar # 4 mfe / mae 0.86606
Bar # 5 mfe / mae 0.87927
Bar # 6 mfe / mae 0.90274
Bar # 7 mfe / mae 0.93169
Bar # 8 mfe / mae 0.97254
Bar # 9 mfe / mae 1.01002
Bar # 10 mfe / mae 1.03290
Bar # 11 mfe / mae 1.01329
Bar # 12 mfe / mae 1.01195
Bar # 13 mfe / mae 0.99963
Bar # 14 mfe / mae 1.01301
Bar # 15 mfe / mae 1.00513
Bar # 16 mfe / mae 1.00576
Bar # 17 mfe / mae 1.00814
Bar # 18 mfe / mae 1.00958
Bar # 19 mfe / mae 1.02738
Bar # 20 mfe / mae 1.01948
Bar # 21 mfe / mae 1.01208
Bar # 22 mfe / mae 1.02229
Bar # 23 mfe / mae 1.02481
Bar # 24 mfe / mae 1.00820
Bar # 25 mfe / mae 1.00119
Bar # 26 mfe / mae 0.99822
Bar # 27 mfe / mae 1.01343
Bar # 28 mfe / mae 1.00919
Bar # 29 mfe / mae 0.99960
Bar # 30 mfe / mae 0.99915
Ratio Values over 30 Bins

Using Arrays for Bins

When  newcomers  start to program EasyLanguage and encounter arrays it sometimes scares them away.  They are really easy and in many cases necessary to complete a project.  In this code I used two 40 element or bins arrays MFE and MAE.  I only use the first 30 of the bins to store my information.  You can change this to 30 if you like, and when you start using a fixed array it is best to define them with the exact number you need, so that TradeStation will tell you if you step out of bounds (assign value to a bin outside the length of the array).  To learn more about arrays just search my blog.  The cool thing about arrays is  you control what data goes in and what you do with that data afterwards.  Anyways play with the code, and I will be back with a more thorough explanation of the theory behind it.