Several optimization methods, such as Ascend, Genetic, Brute Force, are
available through TrainMode. The optimization target can be individually defined with an
**objective** function. The optimization process consists of many cycles. For every cycle the **optimize** function generates a different value between **min** and **max**
and tests its effect on the strategy. At the end of the optimization, a chart is
plotted for any optimized parameter (see below). The chart displays the total number of winning trades (bright blue bars), losing trades (dark blue) and overall performance (red)
of the parameter. The best or most robust parameter values are then selected and stored
in a **.par** file.

The chart above is a typical result of an **optimize** call in [Train] mode. The strategy parameter to be optimized here is used for a buy/sell threshold. It varies from **0.200** to **3.00** in constant steps of **0.1**. We have a small performance peak around **0.90** and the highest peak at **1.60**. The best parameter value however is **1.70**, with a performance of **2.7** and **125**
winning trades. Although its adjacent bars are higher, the optimizer normally
selects the center of the broad peak that represents not the most profitable,
but the most robust parameter value (this behavior can be changed with
TrainMode). Using the value of a very narrow peak or of a single line bears the danger of overfitting the strategy.

By using several **optimize** calls in the code, any number of parameters can be optimized at the same time. The more parameters are optimized, the more
cycles are needed and the longer will it take. Dependent on the method, the best value of
a parameter can affect the optimization of the next parameters. If the script trades multiple assets or algorithms and contains loop calls, a separate parameter set for each loop is optimized.

default |
Default value of the parameter, returned when the PARAMETERS
flag is not set in [Test] or [Trade] mode. This should be the estimated best value of the parameter, normally in the middle of its range. Must be >= 0. |

min, max |
Parameter range, given by its minimum and maximum values (>= 0). |

step |
Optional step width. When 0 or omitted, 10% is added to the parameter for every optimization step
(Ascent optimization only; in Brute Force or Genetic
optimization the range is divided in 10 equal steps). When a positive step value is given, add this constant value every step; when a negative step value is given, add its magnitude as a percentage every step (f.i. -20 adds 20% per step). Adding a percentage instead of a constant value gives a better step resolution at the begin of the range. For best results, select the step value so that every parameter has about
10..15 optimization steps. The
recommended minimum number of steps is 5, the maximum number of steps (MAX_STEPS)
can be found in trading.h. |

bias |
Optional preference of low or high parameter values (Ascent
optimization only). When 0 or omitted, select the optimal parameter. When > 0, prefer higher parameter values even when their rank is lower by the given bias in percent. When < 0, prefer lower parmeter values even when their rank is lower by abs(bias) in percent. Preferring values from the low or high range can make sense f.i. for setting a Stop as tight as possible even when its value is not optimal. |

- Optimization methods are described under Training; an example can be found under Workshop 5.
- Trained parameters are stored in
**.par**files in the**Data**folder. They are plain text files that contain the parameter values in the order of their**optimize**calls. If WFO was used, the file names contain the number of the WFO cycle. If**optimize**was called without calling asset before, the file names contain the asset name from the scrollbox; this way different sets of**.par**files can be generated by selecting different assets. In multi-asset or multi-algo strategies the**.par**files contain the parameters for all asset/algo combinations.

Each line in a .par file begins with the asset name and optional algorithm identifier of the parameter set. The values are listed in the order of the**optimize**calls in the script. A**'+'**before a parameter value indicates that the best value is at the end of its parameter range. In that case it could make sense to extend the range for better results. . - For determining the performance of a parameter combination, the system normally uses fixed trades sizes of 1 lot. Training with individual lot sizes can be set up with the TrainMode variable.
- The parameter range must be positive, i.e. both
**min**and**max**must not be less than**0**. If a strategy parameter has to be negative, multiply it with**-1**or subtract a constant to get a positive range for the**optimize**function. Parameters also should be in a decent number range from 0.1 to 1000, since they are stored in text files. 0.000000123 would be no good parameter value, nor would 1.23e45. Multiply them with a factor when needed. - The performance is calculated in the
**objective**function in**include\default.c**. This function can be replaced by any user provided function in the script that is named**objective**. This way, other performance values can be used for optimizing parameters, for instance the profit/drawdown ratio, or the gross profit, or the Sharpe ratio (see examples below). The**objective**function can access all trade statistics for determining the performance value. By default, the function calculates the pessimistic return ratio (**PRR**) with reduced influence of the biggest win and the biggest loss. - Returned parameter values are not necessarily a multiple of the step width. They can lie anywhere in the optimization range when PEAK mode is not set. Use the round() function when optimizing an integer value.
- The
**bias**value can be used when a high or a low value of the parameter is preferred even though another value gives a slightly better**objective**result. For instance, you might prefer low stop distances to reduce the risk even when higher stop distances give slightly better results. - All
**optimize**calls must be placed either in the run function or in functions that are called from the**run**function. Since parameters are enumerated by the order of their**optimize**calls, the order or number of**optimize**calls must not change between test and training or from one run to the next. Because parameters are individual per component and stored under their component name, the asset and algo must be selected before calling**optimize**. For optimizing global, asset-independent parameters in a portfolio system, call**optimize**outside the asset loop. Make sure to select an asset before calling**optimize**. This asset serves as 'dummy' for the asset-independent parameters. - Parameters that affect the price data array, such as
**BarPeriod**, require special handling for optimization. They can not be changed during the simulation, so they can not be walk forward optimized; for a walk forward test they must be optimized in a separate training run before all other parameters. They can also not be read from the**.par**file, so their optimized value must be entered directly in the script as**default**parameter of the**optimize**call before further optimizing, testing, or trading the strategy. - When optimizing several parameters in Ascent mode, put the most important parameters first.
Parameters are optimized in the order they appear in the code. For optimizing trade entry and exit parameters, optimize entry parameters first and exit parameters afterwards. When your exit system is complex, optimize entry parameters with a simplified exit such as a simple stop. For using a simple exit while the entry parameters are optimized, evaluate the current parameter number (
**ParCycle**), f.i.**if(Train && ParCycle <= NumParameters-3) setExitSimple();**. When parameters affect each other - for instance, two time periods for a crossover of two moving averages - optimize only one time period, and optimize a multiplication factor for the other (see the example below). This way the other time period also changes when the first one is optimized. - Special cases when RULES and PARAMETERS have to be optimized at the same time are described under Training. For optimizing parameters several times, use NumTrainCycles.
- The current optimization cycle can be evaluated with the
**ParCycle**and**StepCycle**variables. Optimization can be aborted by setting StepNext to**0**. This will end optimization after the current cycle. - When optimizing a time period of an indicator, make sure that
**LookBack**is set at least to the maximum period plus the**UnstablePeriod**(f.i.**LookBack = max(LookBack,300);**). Otherwise**LookBack**will automatically adapt to the period of the current parameter value. This can affect the result in an unexpected way because the back test period would then get shorter when the time period gets longer. - For avoiding randomness, slippage is not simulated during optimization.
Rollover is simulated; it can have a large effect on the result and produce long/short asymmetry in parameters and profit factors. For eliminating rollover asymmetry, set
RollLong/Short to
**0**after calling asset in training mode. For eliminating trend asymmetry, detrend the price curve in training mode. Even without rollover and trend, some commodities and stocks still require different parameter sets for long and short trades due to inherent asymmetries in their price curves (long trades are often dominant). - For optimizing in pre-defined steps, use an array and optimize its index.
Example:
**int Periods[5] = { 10, 20, 50, 100, 200 }; int Period = Periods[round(optimize(1,0,4,1),1)];** - For optimizing a global parameter without the
**optimize**function and outside of all WFO cycles, use NumTotalCycles for several training/test runs, use TotalCycle for setting the parameter value, and use the evaluate function to plot a histogram (see example there). - In portfolio strategies, parameters are optimized for any asset individually.
For this a
loop is used, and all
**optimize**calls must be inside that loop. If the parameters are common for all assets, enumerate the assets in a simple for loop, f.i.**for(used_asset) ...**and optimize the parameters outside the loop. Make sure to select all assets before the first optimize call; otherwise the optimizier will assume a single-asset strategy. - When the strategy opens no trades at all in [Train] mode - f.i. when no trade signal is generated or when
Margin or Lots is zero - the
**default**values are stored in the parameter file. - The profit/loss curves of every
**optimize**step can be exported in a file for further evaluation when a Curves file name is given. A curve is only exported when the**objective**function returns a nonzero value. - The height of the parameter charts can be controlled with PlotHeight2.

// trend trading with Moving Averages and optimized parametersfunction run() { set(PARAMETERS); setf(TrainMode,PEAK); var TimeCycle = optimize(30,10,100,5);var TimeFactor = optimize(3,1,5);// allow 3% tolerance for preferring low stop distancesStop = ATR(10) * optimize(3,1,10,0.5,-3);// for optimizing time periods, set the LookBack variable to the // maximum possible value (here, TimeCycle 100 * TimeFactor 5)LookBack = 100*5;

vars Price = series(price(0)); vars MA1 = series(SMA(Price,TimeCycle)); vars MA2 = series(SMA(Price,TimeCycle*TimeFactor)); plot("MA1",*MA1,0,BLUE); plot("MA2",*MA2,0,BLUE); if(crossOver(MA1,MA2)) enterLong(); else if(crossUnder(MA1,MA2)) enterShort(); }

// brute force optimizationfunction run() { set(PARAMETERS); int PeriodsEMA1[4] = { 5, 10, 15, 20 }; int PeriodsEMA2[3] = { 100, 150, 200 }; LookBack = 250; int Index = optimize(1,1,4*3,1) - 1; int PeriodEMA1 = PeriodsEMA1[Index%4]; int PeriodEMA2 = PeriodsEMA2[Index/4]; vars Price = series(price(0)); vars EMA1 = series(EMA(Price,PeriodEMA1)); vars EMA2 = series(EMA(Price,PeriodEMA2)); if(crossOver(EMA1,EMA2)) enterLong(); else if(crossUnder(EMA1,EMA2)) enterShort(); }

// alternative objective function based on Calmar ratiovar objective() { return(WinTotal-LossTotal)/max(1,DrawDownMax); }// alternative objective function based on Sharpe ratiovar objective() { if(!NumWinTotal && !NumLossTotal) return 0.; return ReturnMean/ReturnStdDev; }// alternative objective function for binary optionsvar objective() { return ((var)(NumWinLong+NumWinShort))/max(1,NumLossLong+NumLossShort); }