[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: SYSTEM TESTING



PureBytes Links

Trading Reference Links

Erika

> DOES ANYBODY KNOWS HOW I CAN TEST SYSTEM ON 100 STOCKS
> AT ONCE?
> THANKS
> ERKAN

There are any number of proprietary system testing tools available as
add-ons to Metastock, and others will be able to point you to the
appropriate web sites.

I have posted the attached text documents to the StockCentral forum in the
past few days. Exp.txt is the original post (with corrections), and the
other document has a variation that treats open trades slightly differently.
This will give you the ability to do basic long trade testing on 100 stocks,
and I have some other tools that can also probably be adapted to extend the
capabilities of your testing to include entry dependant exit stops.

I have previously posted (on this forum I think) an indicator that could
handle both long and short trades, and such an approach could be appropriate
for 100 or less issues. I don't promise that anything I have posted will do
will meet your specific needs, and how successful you find my approach will
depend upon computer speed, MetaStock version, and your own ability to adapt
the ideas presented.

Roy


Hi All

I have made some changes to 'Trade Equity LE' indicator that I posted at the start of this link. The first change is a correction to both Ea variables. These should read as per the following two lines. The '3' has been changed to '5'. 

Ea:=ValueWhen(1,En,If(No=5 AND N AND X,Np,Max(O,Np)));
Ea:=If(No<5,ValueWhen(1,En,Np),Ea);

The second change is optional and prevents the deduction of exit costs from open trades on the last bar of a chart. Deducting selling costs at that point gives a more realistic equity value, in my view, but the purist may prefer to include the inevitable exit cost in total equity until the trade is actually closed. This modified indicator does that, and the explorations will report on open trades accordingly. This change makes no difference to the visible equity curve. Nor do the explorations require any changes.

I have added comments to a second copy of the indicator to try and explain the function of each variable. I hope this helps remove a little of the mystery.

Roy 


  {Trade Equity LE}
  {*** Start of user input section ***}
No:=Input("Enter 1=O 2=C 3=H 4=L 5=Stop",1,5,2);
Xo:=Input(" Exit 1=O 2=C 3=H 4=L 5=Stop",1,5,2);
Nd:=Input("Entry Delay",0,3,0); {** Capital **}
Xd:=Input(" Exit Delay",0,3,0);     Cp:=5000; 
Cn:=Input("Entry Commission $",0,99,25);
Cx:=Input(" Exit Commission $",0,99,25);
  {For % commission use this and (*) code}
{Cn:=Input("Entry Commission %",0,3,0.5)*Cp/100;
 Cx:=Input(" Exit Commission %",0,3,0.5)/100;}
N:=Cross(C,Mov(C,34,E)) AND Month()>=10 AND Year()=2001;
Ns:=Fml("RL Williams Buy Stop");
X:=Cross(Mov(C,34,E),C);
Xs:=Fml("RL Williams Delayed exit"); 
  {*** End of user input section ***}
  {*** Do not change code below  ***}
Ns:=If(No=5,Ns,0);
Np:=If(No=5,Ns,If(No=1,O,If(No=3,H,If(No=4,L,C))));
N:=N AND Alert(N=0,2);
N:=If(Nd=0,N,Alert(N,Nd+1) AND Alert(N,Nd)=0);
N:=If(No=5 AND Np>0,1,If(No<5,N,0));
Xs:=If(Xo=5,Xs,0);
Xp:=If(Xo=5 AND Xs>0,Xs,If(Xo=1,O,If(Xo=3,H,If(Xo=4,L,C))));
X:=X AND Alert(X=0,2);
X:=If(Xd=0,X,Alert(X,Xd+1) AND Alert(X,Xd)=0);
X:=If(Xo=5,If(Xp>=L AND Xs>0,1,0),X);
Xp:=If(Xo<5 AND No=5 AND X=0,C,Xp);
Xp:=If(Xo=5 AND Xs>0 AND N AND X,Xs,Xp);
Xp:=If(Ref(Xo=5 AND N AND X,-1),Ref(Xp,-1),Xp);
I:=Cum(N>-1 AND X>-1)=1;
Tr:=If(BarsSince(I OR N)>= BarsSince(I OR X),0,1);
Tr:=If(N AND X AND (No=5 OR Xo=5),1,Tr);
Np:=If(I AND N=0,C,Np);
En:=(Tr AND Alert(Tr=0,2)) OR I;
Ex:=Tr=0 AND Alert(Tr,2);
Lb:=(Alert(Tr,2)) AND LastValue(Cum(1)-0)=Cum(1);
Ea:=ValueWhen(1,En,If(No=5 AND N AND X,Np,Max(O,Np)));
Ea:=If(No<5,ValueWhen(1,En,Np),Ea);
En:=Tr AND Alert(Tr=0,2);
N:=If(Ex OR Lb,(Cp-Cn)*(Xp/Ea)-Cp -If(Ex,Cx,0),0);
{*}{N:=If(Ex OR Lb,(Cp-Cn)*(Xp/Ea)-Cp -If(Ex,Cx*(Cp -Cn)*(Xp/Ea),0),0);}
Bt:=If(Alert(Tr,2),1+BarsSince(En OR I),0);
Xs:=If(Alert(Tr,2),(Cp-Cn)*(Xp/Ea)-Cp-If(Ex,Cx,0),0);
{*}{Xs:=If(Alert(Tr,2),(Cp-Cn)*(Xp/Ea)-Cp-If(Ex,Cx*(Cp-Cn)*(Xp/Ea),0),0);}
Nd:=LowestSince(1,I,Xs);
Ns:=HighestSince(1,I,Xs);
X:=Cum(If((Ex OR Lb) AND N> 0,Bt,0));
Xd:=Cum(If((Ex OR Lb) AND N<=0,Bt,0));
Cum(N)+If(Tr AND Lb=0,(Cp-Cn)*(Xp/Ea),Cp);


  {Trade Equity LE} {with comments}
  {*** Start of user input section ***}
No:=Input("Enter 1=O 2=C 3=H 4=L 5=Stop",1,5,2); 
  {trade entry options}
Xo:=Input(" Exit 1=O 2=C 3=H 4=L 5=Stop",1,5,2);
  {trade exit options}
Nd:=Input("Entry Delay",0,3,0); {** Capital **}
  {trade entry delay - does not apply to stop}
Xd:=Input(" Exit Delay",0,3,0);     Cp:=5000; 
  {trade exit delay - does not apply to stop}
Cn:=Input("Entry Commission $",0,99,25);
  {fixed entry commission}
Cx:=Input(" Exit Commission $",0,99,25);
  {fixed exit commission}
  {For % commission use this and (*) code}
{Cn:=Input("Entry Commission %",0,3,0.5)*Cp/100;
Cx:=Input(" Exit Commission %",0,3,0.5)/100;}
  {alternative % commission variables - also change lines marked} {*}
N:=Cross(C,Mov(C,34,E));
  {entry code - preferably use Fml("your entry")}
Ns:=0;
  { entry stop code - preferably use Fml("your entry stop")}
X:=Cross(Mov(C,34,E),C);
  { exit code - preferably use Fml("your exit")}
Xs:=0;
  { exit stop code - preferably use Fml("your exit stop")}
  {*** End of user input section ***}
  {*** Do not change code below  ***}
Ns:=If(No=5,Ns,0);
  {disable entry stop if not entry option 5}
Np:=If(No=5,Ns,If(No=1,O,If(No=3,H,If(No=4,L,C))));
  {select entry price from entry option}
N:=N AND Alert(N=0,2);
  {convert entry binary code to one bar spike}
N:=If(Nd=0,N,Alert(N,Nd+1) AND Alert(N,Nd)=0);
  {add trade entry delay to entry spike}
N:=If(No=5 AND Np>0,1,If(No<5,N,0));
  {force entry true if entry stop enabled and entry stop > 0}
Xs:=If(Xo=5,Xs,0);
  {disable exit stop if not exit option 5}
Xp:=If(Xo=5 AND Xs>0,Xs,If(Xo=1,O,If(Xo=3,H,If(Xo=4,L,C))));
  {select exit price from exit option }
X:=X AND Alert(X=0,2);
  {convert exit binary code to one bar spike }
X:=If(Xd=0,X,Alert(X,Xd+1) AND Alert(X,Xd)=0);
  {add trade exit delay to entry spike }
X:=If(Xo=5,If(Xp>=L AND Xs>0,1,0),X);
  {force exit true if exit stop enabled and exit stop > 0}
Xp:=If(Xo<5 AND No=5 AND X=0,C,Xp);
  {force exit price (and thus equity curve) to follow close if trade entered
  on stop and this is not an exit bar. This is purely cosmetic to modify the
  equity curve for a stop entry followed later by an exit on open. Otherwise
  the entry bar can show an unrealistic loss based on a low open}
Xp:=If(Xo=5 AND Xs>0 AND N AND X,Xs,Xp);
  {sets exit price to stop value if this is an intra-day stop exit}
Xp:=If(Ref(Xo=5 AND N AND X,-1),Ref(Xp,-1),Xp);
  {holds intra-day exit price over to the following day so curve maintains
  the correct price }
I:=Cum(N>-1 AND X>-1)=1;
  {initialization bar signals all external formulas are valid}
Tr:=If(BarsSince(I OR N)>= BarsSince(I OR X),0,1);
  {trade active}
Tr:=If(N AND X AND (No=5 OR Xo=5),1,Tr);
  {prevents trade from toggling off with simultaneous buy and sell signals,
  and forces on for one bar for an intra-day trade}
Np:=If(I AND N=0,C,Np);
  {forces entry price to a known value, i.e. close, when I is true}
En:=(Tr AND Alert(Tr=0,2)) OR I;
  {opening bar of a trade, or the initialization bar}
Ex:=Tr=0 AND Alert(Tr,2);
  {closing bar of a trade}
Lb:=(Alert(Tr,2)) AND LastValue(Cum(1)-0)=Cum(1);
  {open trade and last bar of chart}
Ea:=ValueWhen(1,En,If(No=5 AND N AND X,Np,Max(O,Np)));
  {first calculation of price paid when entering a trade}
Ea:=If(No<5,ValueWhen(1,En,Np),Ea);
  {second calculation of price paid when entering a trade, not a stop entry}
En:=Tr AND Alert(Tr=0,2);
  {adjust opening bar of a trade, initialization bar no longer wanted }
N:=If(Ex OR Lb,(Cp-Cn)*(Xp/Ea)-Cp -If(Ex,Cx,0),0);
{*}{N:=If(Ex OR Lb,(Cp-Cn)*(Xp/Ea)-Cp -If(Ex,Cx*(Cp -Cn)*(Xp/Ea),0),0);}
  {re-used variable, gain or loss as at trade exit or last bar }
Bt:=If(Alert(Tr,2),1+BarsSince(En OR I),0);
  {bars in trade}
Xs:=If(Alert(Tr,2),(Cp-Cn)*(Xp/Ea)-Cp-If(Ex,Cx,0),0);
{*}{Xs:=If(Alert(Tr,2),(Cp-Cn)*(Xp/Ea)-Cp-If(Ex,Cx*(Cp-Cn)*(Xp/Ea),0),0);}
  {gain or loss during trade}
Nd:=LowestSince(1,I,Xs);
  {re-used variable, maximum open trade draw-down}
Ns:=HighestSince(1,I,Xs);
  {re-used variable, maximum open trade profit}
X:=Cum(If((Ex OR Lb) AND N> 0,Bt,0));
  {re-used variable, bars in winning trades}
Xd:=Cum(If((Ex OR Lb) AND N<=0,Bt,0));
  {re-used variable, bars in losing trades}
Cum(N)+If(Tr AND Lb=0,(Cp-Cn)*(Xp/Ea),Cp);
  {trade equity - no variable name required}


This 'Trade Equity' indicator has a number of applications, and when used with the accompanying explorations it can provide a wealth of information about trading system performance over a basket or portfolio of stocks. Copying MS reports to Excel and using simple tools to summarize column data is an easy and effective way to gauge system performance on multiple stocks.

Trade Equity is an 'equity curve' indicator, and for many this will be its only use. Once set up with appropriate defaults and system entry and exit code it can pulled down into any MS window without the fuss of running the System Tester. Multiple copies of the indicator could be created, each with its own system code.

My main aim in presenting Trade Equity is to show that MetaStock can be used to test a trading system's performance across a database of hundreds of stocks. TE is not the 'ultimate' system tester, but it has the advantages of low cost (free), it is able to be run under MS6.52, and it has the potential to be developed further to meet an individual user's specific needs.

TE only supports long trades, only handles fixed equity positions (there is a version that supports compounding equity), and it cannot manage entry related protective exit stops without additional external code. The limited number of variables allowed by MetaStock makes the inclusion of every desirable feature quite impossible without resorting to dll's. Splitting tasks among several indicators provides a partial solution to this problem but it also slows the execution of explorations because of greater processing overheads. Use of the Prev function in any related code can also significantly affect exploration execution speeds. 

By 'recycling' some variables TE is able to 'get around' the 20 variable limitation. The extra variables are used to provide support code for some of the explorations. This approach is not strictly necessary, but excessive FmlVar() calls used within an exploration also slows execution. 

Some features of TE.
a. Can enter and exit trades at intra-day values.
b. Can calculate intra-day trades (though not on consecutive days).
c. No add-on software is required for multiple stock testing.
d. Runs on MS 6.52.
e. No cost.

Construction may look complicated but it is not - it is merely a series of simple building blocks put together logically to create the final structure. Lack of space prevents the use of fully descriptive variable names, though a list is available and has been posted to other forums.

Before attempting to use TE it is necessary to provide entry and exit code for the N, X, Ns and Xs variables. N is for Entry and X is for Exit. It is possible to build simple code right into TE but creating indicators that are accessed using Fml() or FmlVar is recommended. Those formulas should be identical to entry and exit code used by the System Tester, but with optimization removed. N and X are required to be binary inputs, and a single bar spike is suggested but not mandatory (TE will convert it to a single bar spike anyway). One needs to be aware that overlapping buy and sell signals may cause unexpected results. A buy and sell on the same bar would normally be used for an intra-day trade, and if already in a trade then that trade would continue.

Ns and Xs allow entry and exit prices to be specified, and these price spikes must fall within the High/Low range. Setting the entry and/or exit options at other than =5 will disable the respective stop(s) from functioning. It is not possible to use both stops and conventional MS binary code at the same time. However it is quite legitimate to code stop formulas to use O/H/L/C as well as any other intermediate value. When a particular entry of exit is not used then it can be set =0, or a legitimate fml() call can be left in place. One thing to be aware of is that an unused Fml() can affect the timing of the initialization variable and therefore also the actual bars tested. 

Ensure that default values are set correctly before running an exploration. TE should produce an identical curve to the MS System Tester for the first trade. If it doesn't then there are most likely differences in setup.

Some instances of exploration failures have been reported by users of MS Pro, and the only current work-around is to include all required TE code in each exploration column. This is messy first time round but not impossible.

My thanks go to Mark Pyle for the many ideas that he has contributed to this and other projects.

Roy Larsen
rlarsen@xxxxxxxxxxxxxx 


  {Trade Equity LE}
  {*** Start of user input section ***}
No:=Input("Enter 1=O 2=C 3=H 4=L 5=Stop",1,5,2);
Xo:=Input(" Exit 1=O 2=C 3=H 4=L 5=Stop",1,5,2);
Nd:=Input("Entry Delay",0,3,0); {** Capital **}
Xd:=Input(" Exit Delay",0,3,0);     Cp:=5000; 
Cn:=Input("Entry Commission $",0,99,25);
Cx:=Input(" Exit Commission $",0,99,25);
  {For % commission use this and (*) code}
{Cn:=Input("Entry Commission %",0,3,0.5)*Cp/100;
 Cx:=Input(" Exit Commission %",0,3,0.5)/100;}
  {** set binary or stop =0 when not used **}
N:= Fml("Your binary long entry code");
Ns:=Fml("Your enter long stop price spike");
X:= Fml("Your binary long close code");
Xs:=Fml("Your close long stop price spike"); 
  {*** End of user input section ***}
  {*** Do not change code below  ***}
Ns:=If(No=5,Ns,0);
Np:=If(No=5,Ns,If(No=1,O,If(No=3,H,If(No=4,L,C))));
N:=N AND Alert(N=0,2);
N:=If(Nd=0,N,Alert(N,Nd+1) AND Alert(N,Nd)=0);
N:=If(No=5 AND Np>0,1,If(No<5,N,0));
Xs:=If(Xo=5,Xs,0);
Xp:=If(Xo=5 AND Xs>0,Xs,If(Xo=1,O,If(Xo=3,H,If(Xo=4,L,C))));
X:=X AND Alert(X=0,2);
X:=If(Xd=0,X,Alert(X,Xd+1) AND Alert(X,Xd)=0);
X:=If(Xo=5,If(Xp>=L AND Xs>0,1,0),X);
Xp:=If(Xo<5 AND No=5 AND X=0,C,Xp);
Xp:=If(Xo=5 AND Xs>0 AND N AND X,Xs,Xp);
Xp:=If(Ref(Xo=5 AND N AND X,-1),Ref(Xp,-1),Xp);
I:=Cum(N>-1 AND X>-1)=1;
Tr:=If(BarsSince(I OR N)>= BarsSince(I OR X),0,1);
Tr:=If(N AND X AND (No=5 OR Xo=5),1,Tr);
Np:=If(I AND N=0,C,Np);
En:=(Tr AND Alert(Tr=0,2)) OR I;
Ex:=Tr=0 AND Alert(Tr,2);
Lb:=(Alert(Tr,2)) AND LastValue(Cum(1)-0)=Cum(1);
Ea:=ValueWhen(1,En,If(No=3 AND N AND X,Np,Max(O,Np)));
Ea:=If(No<3,ValueWhen(1,En,Np),Ea);
En:=Tr AND Alert(Tr=0,2);
N:=If(Ex OR Lb,(Cp-Cn)*(Xp/Ea)-Cp-Cx,0);
{*}{N :=If(Ex OR Lb,(Cp-Cn)*(Xp/Ea)-Cp-Cx*(Cp -Cn)* (Xp/Ea),0);}
Bt:=If(Alert(Tr,2),1+BarsSince(En OR I),0);
Xs:=If(Alert(Tr,2),(Cp-Cn)*(Xp/Ea)-Cp-If(Ex OR Lb,Cx,0),0);
{*}{Xs:=If(Alert(Tr,2),(Cp-Cn)*(Xp/Ea)-Cp-If(Ex OR Lb,Cx*(Cp-Cn)* (Xp/Ea),0),0);}
Nd:=LowestSince(1,I,Xs);
Ns:=HighestSince(1,I,Xs);
X:=Cum(If((Ex OR Lb) AND N> 0,Bt,0));
Xd:=Cum(If((Ex OR Lb) AND N<=0,Bt,0));
Cum(N)+If(Tr AND Lb=0,(Cp-Cn)*(Xp/Ea),Cp)+
If(Tr AND Lb,Cx,0);
{*}{Cum(N)+If(Tr AND Lb=0,(Cp-Cn)*(Xp/Ea),Cp)+
If(Tr AND Lb,Cx*(Cp-Cn)* (Xp/Ea),0);}


{Trade Equity Exploration 1}
ColumnA: $ Profit
  {Net profit}
CallFml:=Fml("Trade Equity LE");
Gain:=FmlVar("Trade Equity LE","N");
Cum(Gain);
ColumnB: $ Win
  {Amount of winning trades}
Gain:=FmlVar("Trade Equity LE","N");
Cum(If(Gain>0,Gain,0));
ColumnC: $ Loss
  {Amount of losing trades}
Gain:=FmlVar("Trade Equity LE","N");
Cum(If(Gain<0,Gain,0));
ColumnD: # Win
  {Number of winning long trades}
Gain:=FmlVar("Trade Equity LE","N");
Cum(If(Gain>0,1,0));
ColumnE: # Loss
  {Number of losing long trades}
N:=FmlVar("Trade Equity LE","N");
Ex:=FmlVar("Trade Equity LE","Ex");
Lb:=FmlVar("Trade Equity LE","Lb");
Cum(If((Ex OR Lb) AND N<=0,1,0));
ColumnF: # BarsIn
  {Total bars in all trades}
Trade:=FmlVar("Trade Equity LE","Tr");
Cum(If(Alert(Trade<>0,2),1,0));

{Trade Equity Exploration 2}
ColumnA: #BarsOut
  {Bars out}
CallFml:=Fml("Trade Equity LE");
Tr:=FmlVar("Trade Equity LE","Tr");
I:=FmlVar("Trade Equity LE","I");
Cum(If(I>-1,1,0))-Cum(If(Alert(Tr,2),1,0));
ColumnB: #TotBars
  {Explored bars total}
I:=FmlVar("Trade Equity LE","I");
Cum(If(I>-1,1,0));
ColumnC: $ Costs
  {Commissions paid}
En:=FmlVar("Trade Equity LE","En");
Ex:=FmlVar("Trade Equity LE","Ex");
Cm:=FmlVar("Trade Equity LE","Cn");
Cx:=FmlVar("Trade Equity LE","Cx");
Cum(If(En,Cm,If(Ex,Cx,0)));
ColumnD: Buy/Hold
  {Buy & hold profit/loss}
In:=FmlVar("Trade Equity LE","I");
Ip:=Cum(If(In,C,0));
Lp:=Cum(If(LastValue(Cum(1))=Cum(1),C,0));
Cp:=FmlVar("Trade Equity LE","Cp");
((Cp/Ip)*Lp)-Cp;
ColumnE: P Index
  {Profit index}
Sg:=FmlVar("Trade Equity LE","N");
Gn:=Cum(If(Sg>0,Sg,0));
Ls:=Cum(If(Sg<0,Sg,0));
PREC(100*(Gn+Ls)/Gn,0);
ColumnF: In Trade
  {Currently in a trade}
Tr:=FmlVar("Trade Equity LE","Tr");
Lb:=Cum(LastValue(Cum(1))=Cum(1));
Tr AND Lb;

{Trade Equity Exploration 3}
ColumnA: $Max DD
  {Maximum intra-trade drawdown} 
CallFml:=Fml("Trade Equity LE");
FmlVar("Trade Equity LE","Nd");
ColumnB: $MaxProf
  {Maximum intra-trade profit} 
FmlVar("Trade Equity LE","Ns");
ColumnC: Win Bars
  {Total bars in winning trades} 
FmlVar("Trade Equity LE","X");
ColumnD: LoseBars
  {Total bars in losing trades} 
FmlVar("Trade Equity LE","Xd");
ColumnE: Max Win
  {Maximum consecutive winning trades}
Gn:=FmlVar("Trade Equity LE","N");
Ex:=FmlVar("Trade Equity LE","Ex");
I:=Cum(Ex>-1)<=2; Up:=Gn>0; Dn:=Gn<=0 AND Ex;
Ca:=If(BarsSince(I OR Up)>=BarsSince(I OR Dn),0,1);
Ct:=Cum(Cross(Up,0.5))-ValueWhen(1,i OR (Ca AND 
Alert(Ca=0,2)) OR (Ca=0 AND 
Alert(Ca,2)),Cum(Cross(Up,0.5)));
Cup:=If(Ca,Ct+1,Ct); HighestSince(1,I,Cup);
ColumnF: Max Lose
  {Maximum consecutive losing trades}
Gn:=FmlVar("Trade Equity LE","N");
Ex:=FmlVar("Trade Equity LE","Ex");
I:=Cum(Ex>-1)<3; Up:=Gn>0; Dn:=(Gn<=0 AND Ex);
Ca:=If(BarsSince(I OR Dn)>=BarsSince(I OR Up),0,1);
Ct:=Cum(Cross(Dn,0.5))-ValueWhen(1,i OR (Ca AND 
Alert(Ca=0,2)) OR (Ca=0 AND 
Alert(Ca,2)),Cum(Cross(Dn,0.5)));
Cdn:=If(Ca,Ct+1,Ct); HighestSince(1,I,Cdn);