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

How to Optimize for Expectancy in TS



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}