|Short introduction to creation of Optimizer plugins|
Copyright (C) 2008 Tomasz Janeczko, AmiBroker.com.
All files included in ADK archive are intended only for the
use of AmiBroker registered users.
ANY DISSEMINATION, DISTRIBUTION OR COPYING OF THESE FILES WITHOUT PRIOR CONSENT OF AMIBROKER.COM IS PROHIBITED.
If you are unfamiliar with writing AmiBroker plugin DLLs, before reading this document, please read AmiBroker Development Kit (ADK) documentation: points 1.1 (introduction), 1.2 (interface architecture) and 2.2.1 (data types)
The document assumes that you have working knowledge of C/C++ programming language. Examples are prepared using Microsoft Visual C++ 6.
1 Getting Started
Optimizer plugins are very simple to implement. You just need to get skeleton code (MonteCarlo random optimizer sample is good as a starting point) and add your bits to it.
As every AmiBroker DLL plugin, optimizer plugins require 3 core functions: GetPluginInfo, Init(), Release() that are standard part of AmiBroker plugin interface. They are very staightforward (single-liners in most cases). You can just copy / paste the functions below. The only 2 things that you must change:
The plugin ID code MUST BE UNIQUE. Otherwise it will conflict with other plugins.
For tests I suggest using
'e', 's', '1') changing the last digit if you want to
have more than one test plugin. For list of already used IDs please see : http://www.amibroker.com/plugins.html .
Before releasing your plugin to the public, you must request unique plugin
ID from support at amibroker.com. The plugin ID will be later used to specify
optimizer in AFL code via OptimizerSetEngine() function.
// These are the only
two lines you need to change
#define PLUGIN_NAME "Monte Carlo Optimizer plug-in"
#define VENDOR_NAME "Amibroker.com"
#define PLUGIN_VERSION 10001
#define THIS_PLUGIN_TYPE PLUGIN_TYPE_OPTIMIZER
// Data section
static struct PluginInfo oPluginInfo =
sizeof( struct PluginInfo ),
PIDCODE( 'm', 'o', 'c', 'a'),
// Basic plug-in interface functions exported by DLL
PLUGINAPI int GetPluginInfo( struct PluginInfo *pInfo )
*pInfo = oPluginInfo;
PLUGINAPI int Init(void)
PLUGINAPI int Release(void)
return 1; // default implementation does nothing
2 Optimizer Interface
The optimizer interface consists of 4 simple functions:
And two data structures:
#define MAX_OPTIMIZE_ITEMS 100
int Mode; // 0 - gets defaults, 1 - retrieves settings from formula (setup phase), 2 - optimization phase
int WalkForwardMode; // 0 - none (regular optimization), 1-in-sample, 2 - out of sample
int Engine; // optimization engine selected - 0 means - built-in exhaustive search
int Qty; // number of variables to optimize
BOOL CanContinue; // boolean flag 1 - means optimization can continue,
//0 - means aborted by pressing "Cancel" in progress dialog or other error
BOOL DuplicateCheck; // boolean flag 1 - means that AmiBroker will first
check if same param set wasn't
// and if duplicate is found it won't run backtest, instead will return previously stored value
char *InfoText; // pointer to info text buffer (providing text display in the progress dialog)
int InfoTextSize; // the size (in bytes) of info text buffer
__int64 Step; // current optimization step (used for progress indicator) - automatically increased with each iteration
__int64 NumSteps; // total number of optimization steps (used for progress indicator)
int TargetBestStep; // optimization step in which best was achieved
struct OptimizeItem Items[ MAX_OPTIMIZE_ITEMS ]; // parameters to optimize
2.1 Data structures
The OptimizeParams structure holds all information needed to perform optimization.
The most important part is Items array of OptimizeItem structures. It
holds the array of all parameters specified for optimization using AFL's Optimize()
function. The number of valid parameters is stored in Qty member of OptimizeParams
2.2 OptimizerInit function
PLUGINAPI int OptimizerInit( struct OptimizeParams *pParams )
This function gets called when AmiBroker collected all information about parameters that should be optimized. This information is available in OptimizeParams structure. The optimization engine DLL should use this point to initialize internal data structures. Also the optimizer should set the value of pParams->NumSteps variable to the expected TOTAL NUMBER OF BACKTESTS that are supposed to be done during optimization.
This value is used for two purposes:
1. progress indicator (total progress is expressed as backtest number divided by NumSteps)
2. flow control (by default AmiBroker will continue calling OptimizerRun until number of backtests reaches the NumSteps) - it is possible however to
override that (see below)
Note that sometimes you may not know exact number
of steps (backtests) in advance, in that case provide estimate. Later,
inside OptimizerRun you will be able to adjust it, as tests go by.
1 - initialization complete and OK
0 - init failed
2.3 OptimizerSetOption function
PLUGINAPI int OptimizerSetOption( const char *pszParam, AmiVar newValue )
This function is intended to be used to allow setting additional options
/ parameters of optimizer from the AFL level.
It gets called in two situations:
1. When SetOptimizerEngine() AFL function is called for particular optimizer - then it calls OptimizerSetOption once with pszParam set to NULL
and it means that optimizer should reset parameter values to default values
2. When OptimizerSetOption( "paramname", value ) AFL function is called
1 - OK (set successful)
0 - option does not exist
-1 - wrong type, number expected
-2 - wrong type, string expected
2.4 OptimizerRun function
PLUGINAPI int OptimizerRun( struct OptimizeParams *pParams, double (*pfEvaluateFunc)( void * ), void *pContext )
This function is called multiple times during main optimization loop
There are two basic modes of operations
1. Simple Mode
2. Advanced Mode
In simple optimization mode, AmiBroker calls OptimizerRun before running backtest internally. Inside OptimizationRun the plugin should simply set current values of parameters and return 1 as long as backtest using given parameter set should be performed. AmiBroker internally will
do the remaining job. By default the OptimizerRun will be called pParams->NumSteps times.
In this mode you don't use pfEvaluateFunc argument.
See Monte Carlo (MOCASample) sample optimizer for coding example using simple mode.
In advanced optimization mode, you can trigger multiple "objective function" evaluations during single OptimizerRun call.
There are many algorithms (mostly "evolutionary" ones) that perform optimization by doing multiple runs, with each run consisting of multiple "objective function"/"fitness" evaluations. To allow interfacing such algorithms with AmiBroker's optimizer infrastructure the advanced mode provides access to pfEvaluateFunc pointer that call evaluation function.
In order to properly evaluate objective function you need to call it the following way:
pfEvaluateFunc( pContext );
Passing the pContext pointer is absolutely necessary as it holds internal state of AmiBroker optimizer. The function will crash if you fail to pass the context.
The following things happen inside AmiBroker when you call evaluation function:
a) the backtest with current parameter set (stored in pParams) is performed
b) step counter gets incremented (pParams->Step)
c) progress window gets updated
d) selected optimization target value is calculated and stored in pParams->TargetCurrent and returned as a result of pfEvaluateFunc
Once you call pfEvaluateFunc() from your plugin, AmiBroker will know that you are using advanced mode, and will NOT perform extra backtest after returning from OptimizerRun
By default AmiBroker will continue to call OptimizerRun as long as pParams->Step reaches pParams->NumSteps. You can overwrite this behaviour by returning value other than 1. See Standard Particle Swarm Optimizer (PSOSample) for coding example using advanced mode.
0 - terminate optimization
1 (default value) - optimization should continue until reaching defined number of steps
2 - continue optimization loop regardless of step counter
2.5 OptimizerFinalize function
PLUGINAPI int OptimizerFinalize( struct OptimizeParams *pParams )
This function gets called when AmiBroker has completed the optimization.
The optimization engine should use this point to release internal
1 - finalization complete and OK
0 - finalization failed