For this the simulation runs through many cycles. For every cycle the **optimize** function generates a different value between **min** and **max**. At the end of each cycle, the number of winning trades (bright blue bars below), losing trades (dark blue) and the parameter's
overall performance (red) are plotted in a chart. The best parameter value is then selected and internally stored.

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 the TrainMode variable). Using the value of a very narrow peak or of a single line bears the danger of overfitting the strategy. Once the best parameter value is determined, the optimization process continues for the next parameters.

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 often the script must be run, and the longer will it take. The best value of one parameter is automatically used for optimizing the next parameters. At the end of a multi parameter optimization, the best parameters are displayed in the message window in the order of their optimize calls, and they are stored in the parameter file belonging to the strategy. This file is automatically loaded when the PARAMETERS mode is set.

If the script trades multiple assets or algorithms and contains loop calls, a separate parameter set for each loop is optimized.

The parameter values are stored in a text file in **Data\scriptname.par**. The file can be opened and examined with
the script editor. Each line begins with the asset name and (if any) 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, so it could make sense to modify the range for better results. .

start |
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. 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. 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 introduction can be found under Workshop 5.
- Trained parameters are stored in
**.par**files in the**Data**folder. The files 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. - For determining the performance of a parameter combination, the system normally opens all trades with 1 lot. Trading 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 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**start**parameter of the**optimize**call before further optimizing, testing, or trading the strategy. - When optimizing several parameters, 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 individual parameter values, use an array and optimize its index, f.i.:
**int Periods[5] = { 10, 20, 50, 100, 200 }; int Period = Periods[round(optimize(1,0,4,1),1)];** - For optimizing any combination of two parameters - the "brute force" optimization method, see example below - optimize a single number that is the product of two array sizes. Divide it then back into two separate indices to two parameter arrays. For instance, let
**optimize**return a value from**0**to**99**, and use its first digit (**((int)val)/10**) as an index for the first array, and its second (**((int)val)%10**) for the second array. - 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. This means when a
loop is used, 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(i=0; Name=Assets[i]; i++) ...**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
**start**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); }