Optimization 201 – Bruce Robinson
[email protected]
March 11, 2006
Presentation Overview
Optimization 101 Review Signal Forms How the Amibroker Backtester Works Selection, Timing, and Trading Variables Using the Optimize Function How the Intelligent Optimizer works IO Directives and Options Example 1 – tuned sector trading system Example 2 – weighted position portfolio system Methodology and Conclusions
Optimization 101 Review
Why do we model, backtest, and optimize
Model
Back-test
To define an “edge” in timing, selection, money management A model approximates history Accuracy is required, but not exactness To verify that edge, know the odd’s To test across different markets To provide confidence going forward
Optimize
To maximize or minimize those factors that are most important CAR, MDD, Sharpe, UPI, etc. The combination is called “fitness”
Back-testing ???
2001
2003
TODAY
BACKTEST AND OPTIMIZE
Big Question – What percentage of the back-test return, MDD, etc. should you expect going forward ?
The back-fitting sensitivity problem -
Other issues
Back-testing
Different market action vs. recent market action
Number of signal samples - statistical significance
Test over bull, bear, sideways markets
Issue for intermediate term systems
Survivorship bias Bad performing stocks fold, or merge Good funds’ record is bought Bad funds record is buried
Selection bias
AI optimization
AI optimization offers many new capabilities To date we have fitted as much data as possible and “hoped” Mechanization is one of the most important capabilities
Eliminates biases - selection, event knowledge, etc.
Less sensitive solutions can be found for a point in time We can “time travel” to points in the past and look at walk forward performance A “spliced” equity curve can be assembled to look at what would have happened if
Keywords –
Near optimal Robust Out of sample, walk-forward
Near optimality -
Robustness -
Rolling walk forward testing
1998
2000
2002
TODAY
Green segments represent equity that COULD have been achieved !
Anchored walk forward testing
1998
2000
2002
TODAY
Green segments represent equity that COULD have been achieved !
Goals and penalties
Goal assigns a minimum desired value to a fitness field name (AB optimization result column) Values below the goal result in an adjustment factor that is applied to fitness. Two types of scaling – multiplier and exponential Example of multiplier
Fitness is UPI UPI of solution point = 2 CAR = 15, CAR goal = 20 Fitness adjustment is .75 * 2 = 1.5
Sensitivity
Definition - change in fitness with respect to “distance” from solution Rationale – since change in inputs can’t be accurately predicted, approach is to measure change in output based on change in parameters An average percentage is determined based on samples The solution point fitness is adjusted by a sensitivity goal
The sensitivity adjusted fitness -
Signal Forms
Amibroker signals
Trading signal logic in Amibroker involves basic Boolean logic Signals can be in two forms – level and impulse But, it also involves manipulation of signal state This results in many special cases Rather than try to detail these cases, guidelines will be offered to avoid them
Boolean logic Amibroker
There are a few special considerations for Boolean logic in Amibroker In AB, the value 0 is FALSE, any other value is TRUE Boolean logic is most often applied to a vector Boolean logic where Nulls are involved are special cases
True AND Null = False True OR Null = True NOT Null = Null
Why is this important –
It allows indicators derived from tickers that start at different dates to have Boolean
Impulse form examples
One bar event – Cross() function at 50 level of Stochastic
Data by
Impulse form
Impulse form can be thought of as an event Also, think of impulse form as the leading edge of the level form It is generated by functions that detect events such as Cross() It becomes very useful in generating trading signal combinations
The need for state
What is state ?
Main reasons why it is needed ?
It is a form of digital memory It retains that value of the last Buy or Sell impulse signal It is usually in level form Not all signal conditions can be described by Boolean logic In certain situations, “redundant” signals are desirable
Let’s look at examples of each
Example 2 of state requirement
Take the earlier example, but of a stochastic, but Buy on going up thru 60 and sell on going down thru 40 – can you describe the area between 40 and 60 with a Boolean statement ?
Data by
Example 2 of state requirement
For example, the Buy or Sell result of a stochastic of 50 depends on how you got there
Data by
State and Impulse Form
Since there are two forms of signals, how do we get from one form to the other Flip() is used to implement the state “memory” The output of Flip() is a level signal
State and Impulse Form
ExRem( Array1, Array2 ) is used to convert from state form to impulse form From the Amibroker help –
removes excessive signals: returns 1 on the first occurence of "true" signal in Array1 then returns 0 until Array2 is true even if there are "true" signals in Array1
The output of ExRem() is an impulse signal
Guidelines for combining signals
These guidelines have an overall goal of keeping as many redundant signals as possible
Perform the And operation in level/state form Perform the Or operation in impulse form Not operation should be done in level/state form Postpone the Flip() that is used to get state form as long as possible Try to make buy and sell signal combinations symmetric to minimize lockup
How the Amibroker Backtester Works
Backtester(s)
There are really 3 types of current backtesters in Amibroker Individual issue Portfolio trading with signals Portfolio trading with rotation
Plus the “Old backtester” It is important to first decide on your goal
Functional considerations
The AFL program will have at least one execution per issue There may be other executions if parameters are involved, for example. If the custom backtester is enabled, it will have a final post-processing pass that can interface to the backtesting engine and its objects The equity and stat’s results are not available until the backtester is complete
Data flow model Data initialization
Automatic Analysis Code
Back-Tester
RESULTS
All
Current
Stocks
Stock
Filter
Portfolio backtester
The backtester can be used to perform selection and timing on a filter list on input issues Number of positions and position size can be specified The condition of buys for more than the number of positions must be resolved PositionScore can be used to assign priorities Only evaluated at the buy impulse for
www.amibroker.com/gifs/bt_regular.gif
Selection, Timing, and Trading Variables
Boilerplate 1 settings code //EnableRotationalTrading(); BuyPrice = SellPrice = ShortPrice = CoverPrice = Open; SetTradeDelays( 1, 1, 1, 1 ); SetFormulaName("TEST");
Boilerplate 2 settings code SetOption( SetOption( SetOption( ); SetOption( );
"InitialEquity", "MinShares", "MinPosValue",
1000 ); .0001 ); 0
SetOption( SetOption( ); SetOption( SetOption(
"AllowPositionShrinking", "ActivateStopsImmediately",
True
); True
"ReverseSignalForcesExit", "AllowSameBarExit",
True True
); );
"FuturesMode",
False
Boilerplate 2 code considerations
Initial equity needs to be large enough to accommodate MinShare and number of positions AllowPositionShrinking is usually enabled to allow the buy less than the PositionSize and to account for round-off error ActivateStopsImmediately has some interactions with other settings and will be explained in detail later ReverseSignalForcesExit allows for long/short switches without an explicit Sell of Cover AllowSameBarExit can be used for single day trade, but is also useful for special purposes
Boilerplate 3 settings code SetOption( ); SetOption( 0 ); SetOption( 0 ); SetOption( 100 ); SetOption( ); SetOption( ); SetOption( True ); SetOption( );
"CommissionMode",
2
"CommissionAmount", "InterestRate", "MarginRequirement", "MaxOpenPositions",
1
"WorstRankHeld",
4
"PriceBoundChecking", "UsePrevBarEquityForPosSizing",
True
Boilerplate 3 code considerations
Commission mode should be modeled as closely as possible, but is usually no longer a major consideration Percentage commission can be used to model “slippage” Interest rate is fixed for the MM (Sell, Cover) position Margin requirement is the percentage of initial equity required. It is applied when PositionSize <= 100 which may be a modeling consideration MaxOpenPositions applies to the Portfolio backtester
Boilerplate 3 code considerations
WorstRankHeld applies to rotational mode and refers to the rank below which a current position is sold PriceBoundChecking is a special consideration if you want to manipulate the trade price arrays outside of the bar limits UsePrevBarEquityForPosSizing is selfexplanatory, and is the typical mode for re-balancing used in fund portfolios
Boilerplate 4 settings code MaxPos
= 100 * 100 /
RoundLotSize TickSize MarginDeposit PointValue
= 0; = 0; = 0; = 1;
GetOption("MarginRequirement"); PositionSize = -MaxPos / GetOption("MaxOpenPositions"); // 0 for Funds, 100 for Stocks // 0 for no min. size // For futures
ExitAtTradePrice = 0; ExitAtStop = 1; ExitNextBar = 2; ApplyStop( stopTypeLoss, stopModeDisable, 0, ExitAtTradePrice ); ApplyStop( stopTypeProfit, stopModeDisable, 0, ExitAtTradePrice ); ApplyStop( stopTypeTrailing, stopModeDisable, 0, ExitAtTradePrice ); ApplyStop( stopTypeNBar, stopModeDisable, 0,
Boilerplate 4 code considerations
PositionSize is coded to account for margin RoundLotSize is typically 0 for funds and 100 for stocks, although odd-lots are not that un-economical and fractional stock shares are possible at one brokerage A detailed explanation of stops is beyond the scope of this talk
The main point to remember is that they
Using the Optimize Function
The Optimize Statement
The general form of the optimize statement is – variable = Optimize( “description ID”, default, min, max, step )
When multiple optimize statements are executed by the Optimize function –
A pass of the AFL code is made for each possible combination
For example – x = Optimize( “X”, 5, 1, 10, 1 ); y = Optimize( “Y”, 50, 100, 1000, 100 );
Passes – ( 1, 100 ) ( 1, 200 ) etc.
The Optimize Statement
Anything that can be represented by a number can be assigned to an Optimize() function result !
Parameters to functions True/False (1/0) to gate a condition Comparison to Status(“stocknum”) to choose from watchlist SetOption() parameter PositionSize
Optimize() parameters are cached – they are not dynamic
The Optimize Statement
Guidelines
Variables that are unique to tickers in a watchlist must be defined separately Variables have no knowledge on each other
So, there is no way to limit the total of N parameters to a maximum For example, Individual PositionSize variables for each ticker cannot be calculated directly They are coded as weights that may vary from 0 to 100 The sum is calculated The scaled value for each ticker is its position size
How the Intelligent Optimizer Works
What IO does – the details
IO generates a number of “tuples” of variable combinations that are designed to explore the problem space
For example – ( 2, 200 ) ( 3, 200 ) ( 2, 300 ) etc.
It replaces each call to Optimize() with a call to IOptimize() IOptimize() returns the value of each variable in the tuple
What IO does – the big picture
IO must “drive” Amibroker from an outside script Creates a modified version of the AFL to perform housekeeping and call IOptimize() Invoke the Optimize function repeatedly to test a set of tuples Analyze the set results to guide the next search iteration
IO Directives and Options
IO directives for traditional optimization //IO: StatusWindow: N //IO: SaveCancelled: Y
Disable the directive // Remove in final "production" run //xIO: FitnessTime: 300 5 minute //xIO: SenFinalTests: 1000 limit 1000 samples for sensitivity Penalize result < 85% //IO: SenOptGoalPct: 85 sensitivity //IO: SenOptTests: 10 10 samples to determine //IO: Fitness: UPI sensitiviy CAR goal to prevent high //IO: Goal: CAR: 15 UPI, low CAR //xIO: BegISDate: 01/01/1995 //xIO: EndISDate: 01/01/2001 Walk-forward directives //xIO: EndOSDate: 9/15/2005 disabled //xIO: WFAuto: Anchored: Every: Year
Example of a Tuned, Sector Trading System
Sector Trading
Elevator pitch – Buy a sector fund when it reaches its “N” day low Hold for a minimum of “H” days Take profits when it reached “P” percent
This is an oversold strategy Works well in non-bear markets BUT, it is a filter tuned to the swing cycle frequency and the volatility
Sector Trading Code //IO: SenOptGoalPct: 85 //IO: SenOptTests: 10 //IO: Fitness: CAR //xIO: Goal: CAR: 15 //xIO: BegISDate: 01/01/1995 //xIO: EndISDate: 01/01/2001 //xIO: EndOSDate: 9/15/2005 //xIO: WFAuto: Anchored: Every: Year HoldDays
= Optimize( "HoldDays", 10, 2, 12, 1 );
ProfitStop
= Optimize( "ProfitStop", 5, 2, 12, 1 );
Sector Trading Code ExitAtTradePrice = 0; ExitAtStopIntraDay = 1; ExitAtStopNext = 2; ApplyStop(stopTypeLoss, stopModeDisable, 0, ExitAtTradePrice); ApplyStop(stopTypeProfit, stopModePercent, ProfitStop, ExitAtStopNext); // ExitAtTradePrice for same day ApplyStop(stopTypeTrailing, stopModeDisable, 0, ExitAtTradePrice); ApplyStop(stopTypeNBar, stopModeBars, HoldDays, ExitAtTradePrice); //ExitAtStopNext); NumDays
= Optimize( "NumDays", 10, 10, 20, 1 );
LowDays = IIf( LLV( C, NumDays ) == C, 1, 0 ); Buy = LowDays; Sell = 0; // Will be exited by N-bar stop or by Profit target Short = Cover = 0;
Optimization Net Profit from 4/1/03 -
Optimization Net Profit from 4/1/00 with 03 parms -
Optimization Net Profit from 4/1/00 -
Optimization CAR/MDD from 4/1/03 with IO -
Optimization CAR/MDD from 4/1/ 00 with 03 parms and IO -
Sector System Optimization Summary
( HoldDays, ProfitStop, NumDays ) Amibroker optimized from 4/1/03 on Net Profit ( 8, 10, 11 ) Amibroker optimized from 4/1/00 on Net Profit ( 5, 6, 10 ) IO optimized from 4/1/03 on CAR/MDD ( 6, 5, 18 )
Optimization CAR/MDD from 4/1/ 03 –
Optimization CAR/MDD from 4/1/ 03 –
Optimization CAR/MDD from 4/1/ 03 –
Optimization from CAR/MDD 4/1/ 03 with IO -
Sector Trading.afl
Implements this sector trading system Runs against a watchlist of sectors
Profunds Sectors – Japan-Europe.tls
Program and watchlist will be posted to site Play with it !
Example os a Portfolio Optimization System
The main portfolio problem
How to optimize the the PositionSize for each ticker Let’s say the we have 5 long tickers, and we want to optimize the portfolio percentages in a hedge Hedge percentage is 30% Long percentage is 70%
Percentages of 5 long tickers must add to 70% Issue is that variables are independent
A solution
This “design pattern” is known as normalizing First, we optimize weightings fund1wt 100, 1 ); fund2wt 100, 1 ); fund3wt 100, 1 ); fund4wt 100, 1 ); fund5wt 100, 1 );
= Optimize( "fund1wt", 10, 0, = Optimize( "fund2wt", 10, 0, = Optimize( "fund3wt", 10, 0, = Optimize( "fund4wt", 10, 0, = Optimize( "fund5wt", 10, 0,
A solution
Next, we get the sum of the weights fundwtsum = fund1wt + fund2wt + fund3wt + fund4wt + fund5wt;
Then, set the total percentage of the long funds fundpcnt
= 70;
Lastly, we normalize the percentages fund1pcnt fund2pcnt fund3pcnt fund4pcnt fund5pcnt
= fund1wt / fundwtsum * fundpcnt; = fund2wt / fundwtsum * fundpcnt; = fund3wt / fundwtsum * fundpcnt; = fund4wt / fundwtsum * fundpcnt; = fund5wt / fundwtsum * fundpcnt;
Why it works
The weights are all allowed to vary over the same range IO optimizes the weights based on the percentages assigned to PositionSize if ( Name() == fund1 ) PositionSize if ( Name() == fund2 ) PositionSize if ( Name() == fund3 ) PositionSize if ( Name() == fund4 ) PositionSize if ( Name() == fund5 ) PositionSize
= -fund1pcnt; = -fund2pcnt; = -fund3pcnt; = -fund4pcnt; = -fund5pcnt;
PortHedge2IO.afl
Portfolio and hedging optimization model Originally presented in Clearwater in 2005 Has two modes –
Finds near efficient frontier for up to 5 funds Optimizes the mix for an augmented hedge with up to 3 long funds, 3 short funds, and money market
Designed to use IO Uses FastTrack symbols Hedging mode uses FastTrack based signals
Methodology and Conclusions
Steps of the Methodology
Conceptualize –
Code
Describe the system in an “elevator pitch” Be aware of Amibroker backtester considerations Check the trades
Refine and optimize
Select in-sample data appropriate to the style and time frame of trading Identify the parameters and degrees of freedom
Be aware of implicit constraints
Steps of the Methodology
Analyze in-sample results Perform sensitivity analysis Analyze drawdowns and other factors that would result in suspension of trading
Decide if further testing is warranted Don’t be afraid to discard systems if solutions to the identified issues are elusive Remember – discarding an inferior system frees up time to investigate others that may prove superior
System Lifetime
Should systems work over different market characteristics ? If so, compromises are inevitable Is it a viable strategy to target the system to certain market conditions ?
If we can detect the transitions in market types If we have a “circuit breaker” to suspend trading
An approach to detection and suspension is to trade the equity curve
Steps of the Methodology
Perform walk-forward testing
Look for results that are less than optimized, but within reason Walk-forward frequency should be looked at as an additional parameter
Re-optimization frequency is being identified Don’t assume that more is better System stability with respect to re-optimization is an issue
“ The future ain't what it used to be “ – Yogi Berra
Conclusion Tool are now available to optimize, evaluate, and perform a walk-forward analysis – don’t accept less