Investment Studio > Views > System > Optimization

System Parameters can be automatically optimized with respect to some user-defined performance measure, e.g. final portfolio NAV. This basically involves running the trading system repeatedly, each time with a different parameter value, and keeping track of which value yields the best result.

The range of values to explore [Low, High] is specified individually for each parameter in the Parameters grid, along with the number of equally spaced Steps in which this range is to be traversed. Optimization is possible only if at least one parameter has a non-trivial range (High > Low, Steps > 1) and is not Locked.

To start optimizing, click Optimize in the Parameters section's pop-up menu or click in its toolbar. This opens the Optimizer window:

Enter an expression for the performance measure which you want to either minimize or maximize (known as the "objective function") in the Objective box. Click the spin button in the Objective box for a list of recently used objective functions.

You can use all the symbols available in the Symbols section to create your objective function.

Keep in mind that the value of a custom-defined symbol is only recomputed when there is a change in the value of one or more arguments to the expression which defines the symbol. This means that care must be exercised if you use custom-defined symbols in your objective function.

Consider for instance the expression in the screenshot above,

portfolio_nav(portfolio, to_date)

In the Objective box, this works just fine, since the expression in the Objective box is computed only after the system has finished executing and the portfolio has been updated accordingly. But if you had defined a symbol

_final_nav = portfolio_nav(portfolio, to_date)

in the Symbols grid, it would have been computed at the beginning of the run and then never again, since both arguments (portfolio, to_date) remain the same throughout system execution. Using _final_nav as your objective function would therefore have yielded misleading results!

Also note that final portfolio NAV may not be the most adequate performance measure, since it doesn't take into account how much risk was incurred to achieve that NAV. Two popular risk-adjusted performance measures which address this problem are the Sharpe ratio and the Treynor ratio:

    average portfolio return - average risk-free return
Sharpe  =  ¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾
    standard deviation of portfolio returns
     
    average portfolio return - average risk-free return
Treynor  =  ¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾¾
    portfolio Beta

where the "risk-free" return is typically taken to be the return on US Treasury bills, a money market account or similar "safe" asset.

While it's possible to compute average returns using the XIRR function, it is not necessary to do so for the purpose of optimization. Instead, define symbols

_initial_nav = portfolio_nav(portfolio, from_date)

_rfdc = asset_quotes(riskfree, "c", from_date, to_date)

_rfdc_n = rows(array(_rfdc))

_risk_free_return = index(array(_rfdc), _rfdc_n, 2) / index(array(_rfdc), 1, 2)

(where riskfree stands for the asset symbol of your risk-free asset; substitute the appropriate actual symbol) and then compute the numerator (in the Objective box - see notes above!) as

portfolio_nav(portfolio, to_date) - _initial_nav * _risk_free_return

The standard deviation of (daily) portfolio returns can be computed as

stdev(subarray(roc(portfolio_quotes(portfolio, "c", from_date, to_date), 1), 1, 0, 2, 0))

while the portfolio's Beta is given by

ba(asset_quotes(market, "c", from_date, to_date), portfolio_quotes(portfolio, from_date, to_date))

where market stands for the asset symbol of a market proxy (typically an index like the S&P 500 or the Russell 3000).

The optimal portfolio according to the Sharpe measure can therefore be found by maximizing the objective function

(portfolio_nav(portfolio, to_date) - _initial_nav * _risk_free_return) / stdev(subarray(roc(portfolio_quotes(portfolio, from_date, to_date), 1), 1, 0, 2, 0))

while the optimal portfolio according to the Treynor measure can be found by maximizing the objective function

(portfolio_nav(portfolio, to_date) - _initial_nav * _risk_free_return) / ba(asset_quotes(market, "c", from_date, to_date), portfolio_quotes(portfolio, from_date, to_date))

The controls in the top row of the Test qoutes box determine the date range over which the system is optimized. They are analogous to the date range controls in the Rules section.

The rationale for having two separate sets of date range controls is that sound trading system development always involves the use of at least two non-overlapping date (and data) ranges: one used for optimization, the other for verification of the optimized system. The system should display similar behaviour (number of transactions per time unit, average portfolio return, drawdown, distribution of gains and losses) over both date ranges. Without such verification there is an overwhelming risk of over-fitting, i.e. of creating a system that performs perfectly over the optimization range, but lousy at all other times.

You should therefore only use part of the available historical data for system optimization, and reserve the rest for verification of the optimized system.
The controls in the second row of the Test qoutes box have no effect unless Shuffle is checked.

When Shuffle is checked, the optimizer does not use quotes in the selected date range "as is". Instead, before the system is executed, the quotes in the selected range are converted to daily relative moves which are then randomly reshuffled and converted back to quotes. The result is a set of quotes which, while fictitious, have the same distribution of daily moves, the same starting point and the same end point (and therefore the same overall trend) as the original quotes. All assets and currency quotes are reshuffled together (i.e. all asset and currency moves from day X are relocated to the same day Y) so as to also preserve any cross-correlations between quotes.

Quotes are reshuffled every time the optimizer takes a new step through parameter space, i.e. every time a parameter value is updated. If a value > 1 has been set in the "x" (times) box,

reshuffling and system execution are also iterated the specified number of times for each set of parameter values, whereafter the set's "official" objective function value is computed from the "x" individual results, using the method specified to the right of the "x" box:

The purpose of all this tribulation is to avoid the common trap of overfitting the system to a particular sequence of market events which are not likely to ever repeat again in the same order.

In general, Median is the best choice, since it represents the most likely individual outcome. In contrast, Mean is obtained by averaging over all outcomes, which is of debatable significance to an investor without access to financial markets in parallell universes. Max and Min yield the outliers; consider using them to analyze your system's worst-case performance.

It should be noted that while random shuffling is close in spirit to Monte Carlo optimization, it skips a crucial step necessary to qualify as such. In Monte Carlo methods, a postulated distribution of price moves (e.g. a log-normal distribution) is fitted to the historical quotes, whereafter the fictitious prices are obtained from the fitted distribution rather than directly from the historical data. This extra step can be argued to make Monte Carlo methods more "general" in nature, by better insulating the fictitious quotes from peculiarities of the available historical data; but this insulation is achieved at the cost of postulating a particular underlying distribution, essentially as a matter of faith.

Generally speaking, the more historical data you have at your disposal, the better it will reflect the actual distribution, whatever its nature. On the other hand, old data may have been collected under market conditions so different from current ones as to have limited predictive value.



Click Execute to start the optimization process. This button is enabled only if optimization is not already underway, if an Objective function has been provided, and if Shuffle is unchecked or a number of iterations > 0 has been specified.

Once clicked, the Execute button stays grayed out until all parameter values in the specified range have been stepped through or Stop (6) is clicked. Execution progress and estimated time left (ETA = hours:minutes:seconds left) are displayed next to the button (5); extrema and the latest parameter set evaluated so far are displayed in the result grid below the button (7).

The result grid can be navigated as usual (with scroll bars, mouse, arrow keys etc.) but is read-only.

The top data row displays the largest (Max), smallest (Min) and latest (Last) objective function value ("<OBJECTIVE>") found during the latest optimization run. The rows below it display the corresponding parameter values.

For instance, in the screenshot below,

  the highlighted value (23.9200...) is the smallest objective function value found so far; its coordinates in parameter space are _min_af = 0.384454545... and _max_af = 0.001.

To transfer a set of parameter values (whether Max, Min or Last) from the result grid to the corresponding Value fields in the System view's Parameters grid, select a cell in the desired column, then click OK (12).



If Log is checked, every point in parameter space reported under Last in the result grid (7) during the optimization run will also be written to the CSV (Comma Separated Value) file specified in the log file box (9). Any data previously in the file will be overwritten. The file path can be typed into the box, or you can click to use a standard Save File dialog.

Symbolic directory names can be used in the file path (e.g. "<TEMP>", as in the screenshot above) and will be automatically substituted for actual directory paths wherever possible.

The first row in the CSV file created by the optimizer is a header describing the contents of each column (<OBJECTIVE> value in the first column, symbol names in the rest). Each subsequent row describes a point in parameter space. All values are written in general floating point format (using a decimal period) and can be read using common spreadsheets and charting software.

Click Minimize to reduce Investment Studio to an icon on the Windows task bar during lengthy optimization runs.
Click OK to close the optimizer windows and transfer all parameter values from the selected column (Max, Min or Last) in the result grid (7) to the coresponding Value fields in the System view's Parameters grid.

Click to close the optimizer window without transferring any values to the Parameters grid.