PureBytes Links
Trading Reference Links
|
A year ago, when I set out to develop my own trading system, Mark
Brown and Mike Higgs both recommended I read _Trade Your Way To
Financial Freedom_ by Van K. Tharp. Mark recommended it to me as a
good book on position sizing. The book also contains an additional
valuable feature: how to measure "quality" of a trading strategy
objectively, in terms of expectancy multiplied by opportunity. Call
this the "expectancy score." Expectancy is how much you expect each
trade to earn for every dollar you risk. Opportunity is how often
your strategy trades. You want to maximize both.
This expectancy score complements position sizing. You have to
make a paradigm shift away from evaluating strategies based on net
profit. Forget the net profit, forget drawdown, forget number
of wins in a row, forget everything else Tradestation shows you
in the Strategy Summary. These things mean nothing for strategy
comparisons, because everyone has a subjective opinion about which
of those measurements matter most.
In your mind you must decouple the entry/exit rules from "net
profit" performance. Instead think of a strategy like this:
Entry and exit rules determine EXPECTANCY and OPPORTUNITY.
Position sizing determines net profit.
So when you're designing the entry/exit rules and their input
parameters, DON'T optimize for net profit.
Optimize for maximum expectancy score, without regard to anything
else. Position sizing takes care of the rest. A good position
sizing strategy will result in greater, more consistent profits on a
high-expectancy strategy than on a low-expectancy strategy, even if
the low expectancy strategy has a higher net profit on a 1-contract
basis!
Now, I know other trading software lets you optimize strategy
parameters based on anything you want. TradeStation gives you only
canned results like net profit, win/loss ratio, drawdown, etc.
For those of us who use TradeStation, I developed something that
lets me optimize my strategies on expectancy score.
It's an EasyLanguage function. You just stick it at the end of your
signal and start the optimizer. Every iteration of the optimizer
will cause a line to be written to an Excel .csv file. Then all you
do is load it into Excel, sort by the last column, and voila! The
parameters for maximum expectancy score are right at the top.
If the attachment doesn't go through, the source code is also at
http://unicorn.us.com/alex/strategyquality.txt
The documentation included with the source code is detailed and
should explain everything more fully. This function can be modified
to use in optimizing anything else you want too.
--
,|___ Alex Matulich -- alex@xxxxxxxxxxxxxx
// +__> Director of Research and Development
// \
// __) Unicorn Research Corporation -- http://unicorn.us.com
{Function SystemQuality
by Alex Matulich, alex@xxxxxxxxxxxxxx (June 2002)
Unicorn Research Corporation
This function allows you to optimize on an objective measure of
quality of strategy performance. The results can be used to
compare objectively the quality of different strategies, or the
same strategy with different input parameters.
TradeStation does not offer a way to optimize a strategy against
some arbitrary user-defined result. It lets you optimize only
against a selection of canned results such as Net Profit or
win/loss ratio. These, unfortunately, are not objective measures
of system quality. This function builds an Excel file while an
optimization is running. The last column contains the quality
score of the strategy.
In the book _Trade Your Way To Financial Freedom_, Van K. Tharp
describes the use of an objective quality score, defined as
quality = expectancy * opportunities
where
expectancy = (AW*PW + AL*PL) / AL
= expected profit per dollar risked
opportunities = NST * (365 / studydays)
= opportunities to trade in a year
AW = average winning trade (excluding maximum win)
PW = probability of winning (total wins / opportunities)
AL = average losing trade (negative, excluding scratch losses)
PW = probability of losing (nonscratch losses/opportunities)
NST = number of non-scratch trades (a scratch trade loses
commission+slippage or less)
studydays = number of days of history being tested
Examples of equivalent-quality systems would be:
* a market maker earning 1 cent per $1 risked, 50 times/day.
* a daytrader earning 10 cents per $1 risked, 5 times/day.
* a position trader earning $1 per $1 risked, 2-3 times/week.
(In reality the market maker earns something like 5 cents per $1
risked, 100 times a day. A market maker's quality score is
sky-high, higher than anything else, which is why they cannot
be beaten.)
To use this function:
0. If your strategy contains position-sizing rules, disable them.
We're not trying for maximum equity growth in this optimization,
but rather risk-normalized performance on constant-quantity
orders. Position sizing rules destroy the measurement. After
finding the optimum quality this way, re-enabling the position
sizing rules will result in better performance than before,
especially if the sizing is a function of trade risk and equity.
1. Just insert a call to this function at the end of your signal,
passing the strategy input parameters of interest, as well as the
file name to output. The file name should have a .CSV extension
so that Excel will read it readily. The file name should not
contain anything TS would interpret as "jump commands" or you'll
get an error. See the warning in the input declaration below.
2. If you use the same strategy in different markets or different
workspaces, be sure to add a filename input parameter to your
strategy's signal, and set all the filenames different, so you
don't have multiple things writing to the same file!
3. Make sure the output file is deleted. Every time your strategy
is recalculated the output file grows. You want to delete it to
start afresh before running an optimization. Also, setting the
filename parameter to "" (empty string) will disabled the output.
4. Run the optimization. Try to set up your parameters so that
less than 50,000 or so results are generated (you will have to
import it all into Excel).
5. When the optimization completes, load your file into Excel and
sort by the last column, the one you want optimized, in descending
order. The top result will be the optimum. The columns in the
file are:
p1, p2,... p9 (input parameters),
#wins, GrossProfit, #losses, GrossLoss, #scratches, ScratchLoss,
MaxIDDrawdown, Expectancy, QualityScore
Be aware that the optimum strategy found this way is objectively
and mathematically optimum. It may not be PSYCHOLOGICALLY
optimum, however. For example, the optimum combination of
parameters giving the highest score may have 25% winning trades.
You may be bothered by losing 3 out of 4 times. If such is the
case for you, you can compromise by multiplying the score by your
personal subjective measure of "psychological quality." In this
case we would multply the score by the percent of winning trades,
and re-sort the spreadsheet. If you're sensitive to drawdown, you
might try multiplying the score by AL/DD. You might find that the
resulting highest score represents a good compromise between your
psychology and the objective best strategy.
See _Trade Your Way To Financial Freedom_ by Van K. Tharp for
a detailed explanation of trading system expectancy. This function
calculates a more conservative version that Tharp's: It ignores
the maximum profit as an outlier, and it uses the average loss
rather than the minimum loss as the standard of risk.
}
inputs:
filename(string), {full pathname for .csv file}
{WARNING: avoid \hb, \he, \pb, \pe, \wb, or \we, in the file
name. TS interprets these as "jump commands." For example,
the filenames "c:\hello.csv" or "c:\data\performance.csv" will
result in an error message because they contain \he and \pe
respectively.}
p1(NumericSimple), {strategy input parameters}
p2(NumericSimple),
p3(NumericSimple),
p4(NumericSimple), p5(NumericSimple), p6(NumericSimple),
p7(NumericSimple), p8(NumericSimple), p9(NumericSimple);
vars: nclosed(0), wins(0), losses(0), scratches(0), scratchloss(0),
scratchtrade(0), studystart(0), studydays(0),
p(0), j(0), k(0), AW(0), PW(0), AL(0), PL(0), expectancy(0);
{Initializations to occur on first bar}
if currentbar <= 1 then begin
nclosed = 0;
wins = 0;
losses = 0;
scratches = 0;
scratchloss = 0;
studystart = DateToJulian(CurrentDate);
{The maximum loss amount for a "scratch" trade will be a
round-turn commission and slippage.
Set Commission and Slippage in Format -> Strategy -> Costs.}
scratchtrade = commission + slippage;
end;
{Do the following whenever a position is closed}
if totaltrades > nclosed then begin
k = totaltrades - nclosed;
for j = 1 to k begin {loop over multiple simultaneous closed trades}
p = PositionProfit(j);
if p <= 0 then begin
if p >= scratchtrade then begin
scratches = scratches + 1;
scratchloss = scratchloss + p;
end else
losses = losses + 1;
end else
wins = wins + 1;
end;
nclosed = totaltrades;
end;
{Finally, at the last bar, output the result to be optimized}
if LastBarOnChart and losses>0 and StrLen(filename)>0 then begin
studydays = DateToJulian(CurrentDate) - studystart;
if studydays < 1 then studydays = 1;
{Append the strategy input parameters to a file}
FileAppend(filename,
numtostr(p1,3)+","+numtostr(p2,3)+","+numtostr(p3,3)+","+
numtostr(p4,3)+","+numtostr(p5,3)+","+numtostr(p6,3)+","+
numtostr(p7,3)+","+numtostr(p8,3)+","+numtostr(p9,3)+",");
{Append win and loss data as desired}
FileAppend(filename,
numtostr(wins,0)+","+numtostr(GrossProfit,2)+","+
numtostr(losses,0)+","+numtostr(GrossLoss,2)+","+
numtostr(scratches,0)+","+numtostr(scratchloss,2)+","+
numtostr(LargestWinTrade,2)+","+numtostr(LargestLosTrade,2)+","+
numtostr(MaxIDDrawDown,2)+",");
{Calculate results to be optimized, and output them}
j = wins - 1 + losses; {total trades excl scratches & max win}
AW = (GrossProfit-LargestWinTrade)/(wins-1); {avg win}
PW = (wins-1) / j; {% wins}
AL = (GrossLoss - scratchloss) / losses; {avg loss}
PL = losses / j; {% losses}
expectancy = (AW*PW + AL*PL) / (-AL);
FileAppend(filename,
numtostr(expectancy, 3) + "," +
numtostr(expectancy * j * 365/studydays, 3));
FileAppend(filename, NewLine); {lastly append a carriage return}
end;
SystemQuality = 1; {dummy return value}
|