How To - Save Custom Defined Parameters' Values

AmiBroker allows me to create user-defined parameters. These parameters are available via a Parameters
dialog for quick and fast adjustment of indicator, backtest formula. When I reload the formula (into the analysis window), my parameters take on their initial default values established in the AFL formula code.

How can I save my custom defined parameter values, so I won’t have to repeatedly reset them to my desired values?

Originally all Param…() functions have been implemented for use in chart panes only!
They are dependent on ChartID.
ChartID return value in all analysis windows is zero (always).

Still if you want to use same parameters (that have been set in chart pane) in analysis window too then you may store them to static variables.

Example for Param() function:

...
uniqueID = "";// uniqueID-> some unique name (which may include chartID)

if ( Status( "action" ) == actionIndicator ) { // if in chart pane
    pval = Param( pname, def, start, end, change);
    StaticVarSet(pname + uniqueID, pval );
} else // call chart pane value (if analysis)
    pval = StaticVarGet( pname + uniqueID );
...

But generally it is recommended to use hardcoded param values in Analysis (exspecially if you do some backtests).

1 Like

The following code snippet accomplishes my desire to save custom parameters for future use. I’m posting this here in case others can improve this code.

//Program: MACDD 50 Over 200_Sandbox.afl - Mainly a simple Backtesting codeto illustrate saving user defined Custom Parameters.
//Author: SwingTradeMonkey
//Date: 20170614.01
//Description: An example for saving User Defined Parameters: In this case, Only the FastMALengthVal is saved.
//How it works: 
//When the formula is initially loaded, the Include file is opened and any saved parameter's values are recalled.  
//Once per run, The Parameter variable names and their assigned values are written to the include file. 
//Improvements to this code sample are welcome.


//In case of an empty include file, provide a list of Variable(s) that will be saved and recalled from within the Include file.
FastMALengthVal = 5;

//Include file should contain the variables (listed above) and their values. Be sure to create this file! 
flname = "C:\\program Files\\AmiBroker\\Formulas\\Custom\\Strategies\\MACD 50 Over 200_Sandbox_Include.afl";
#include_Once FLName

//Setup Parmeters in a normal fashion. User may these values via the Amibroker Built In Parameters 
FastMALength = Param("Fast MA Length",FastMALengthVal,Minimum = 5, Maximum = 25);
SlowMALength = Param("Slow MA Length",26,Minimum = 9, Maximum = 200);

//Perform Work
FastMacd = MA(C,FastMALength);
SlowMacd = MA(C, SlowMALength);
Buy = Cross(FastMacd, SlowMacd);
Sell =Cross(SlowMacd, FastMacd);
Cover = Cross(FastMacd, SlowMacd);
Short =Cross(SlowMacd, FastMacd);

//Save desired parameter values when the backtest is executed. 
//The if statement enables this code block to execute one time.
//The Next time this formula is loaded, it will also retrieve the saved parameters as the new defaults. Nice!
if(Status("stocknum") == 1)
{	
	flHandle=fopen(flname,"w",shared=true);
	fastStr = "FastMALengthVal = " + NumToStr(FastMALength,1.0,separator=False)+";" ;
	fputs(fastSTr, flHandle);
	fclose(flHandle);
	StaticVarSet("ParametersSaved",1);
}
//End of Program

The Include file looks like this. Be sure to create this file as well.

FastMALengthVal = 11;
2 Likes

@SwingTradeMonkey - that is really not good idea. You should rather follow @fxshrat advice.

Can you clarify a little bit on why this may not be a good idea?
For me, using hard coded or static custom parameter values is tedious to maintain for groups of parameter values. By changing the include’s file name, I can save and quickly recall an entire group of parameters rather than having to manually reset in the parameter dialog or the formula.

How is using mentioned Static Var approach being tedious?
The thing is, you do not really know what you are talking about.

// http://forum.amibroker.com/t/how-to-save-custom-defined-parameters-values/724/6
// First apply code in chart pane
// Set your params there
// Note chartID of Chart Title
// Insert that very same chartID into chartID variable of below code (line 18)
// Apply code in analysis and run backtest whatsoever...
//
Version( 6.17 );

function ParamToAnalysis( pname, chartID, def, start, end, change ) {
	if ( Status( "action" ) == actionIndicator ) { // if in chart pane
		pval = Param( pname, def, start, end, change);
		StaticVarSet(StrFormat( "%s%g", pname, GetChartID() ), pval );
	} else // call chart pane value (if in analysis)
		pval = Nz(StaticVarGet( pname + chartID ), 1);
	return pval;
}

chartid = "3554"; // chartID of Chart pane called from analysis (see printf output)

//Setup Parameters in chart pane
FastMALength = ParamToAnalysis( "Fast MA Length", chartid, 5, Minimum = 5, Maximum = 25, 1 );
SlowMALength = ParamToAnalysis( "Slow MA Length", chartid, 26, Minimum = 9, Maximum = 200, 1 );

//Perform Work
FastMacd = MA( C, FastMALength );
SlowMacd = MA( C, SlowMALength );

Buy = Cross( FastMacd, SlowMacd );
Sell = Cross( SlowMacd, FastMacd );
Cover = Cross( FastMacd, SlowMacd );
Short = Cross( SlowMacd, FastMacd );


// ChartID of chart pane to be viewed if chart pane is selected
_N( Title = StrFormat( EncodeColor( colorRed ) + "IMPORTANT: READ COMMENTS IN CODE FIRST!\n\nChartID: %g" + EncodeColor( -1 ) +" -> Param1: %g, Param2: %g", GetChartID(), FastMALength, SlowMALength ) ); 




// ###########################################################
// Optional (NOT mandatory) code to output params in analysis
// ###########################################################
SetCustomBacktestProc(""); 
// Custom-backtest procedure follows  
if( Status("action") == actionPortfolio ) { 
    bo = GetBacktesterObject(); 
    bo.Backtest(1); 

    for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade()) {
       trade.AddCustomMetric( "chartID", chartid );
       trade.AddCustomMetric( "FastMALength", FastMALength, 1 );
       trade.AddCustomMetric( "SlowMALength", SlowMALength, 1 );
    }
    for (trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos()) {
        trade.AddCustomMetric( "FastMALength", FastMALength, 1 );
        trade.AddCustomMetric( "SlowMALength", SlowMALength, 1 );
    }
    bo.ListTrades();
}

Click to enlarge

4 Likes

I am after all, just another :monkey_face: Seriously, thank you @fxshrat for your gracious assistance on this topic. I appreciate and value your time to respond and I look forward to your future posts.

Seems like there are some drawbacks to using the above technique and static variables. Static variables will loose their values when the program is restarted unless they are made persistent, and if they are made persistent they will automatically be reloaded on the next application run.

If you have a back testing strategy with a lot of variables, this could consume a lot of memory. Not only are the persistent variables you want to work with for a particular back test loaded, but all of the persistent variables for all of the back tests ever performed are loaded. Could use up a lot of memory particularly as more and more new back tests are performed over time.

AFL Function Reference - STATICVARSET

Starting from version 5.80 there is a new parameter persist . If it is set to True then static variable will be stored in PersistVars.bin file when AmiBroker is closing and reloaded automatically on next startup, preserving the values of static variables between application runs).

This is the desired result, just need it to be selective and only load the variables for the current back test(s). That is why I think SwingTradeMonkey was trying to use the store in a file approach that only loaded the variables back in for the back test of current interest.

I find it quite cumbersome and error prone having to manually hard code values into a back test strategy to save them, then un-hard code them to test and try something new, then hard again all the while remembering to save at the appropriate times and rename the saved files as necessary.

Seems like there should be a better way.

First of all you do not store arrays but just single numbers to static var.
Also you save per chartID only. And you would store just the current single value per pname argument per function call.

Now still... if you want to keep current chart panes' params only and not want to keep all past ever used ones of all ever created chart panes (and chart IDs) then that is not problem also. Simply remove static vars of all (ever used) panes (or of particular ones) before setting new ones.

// 1. First apply code in chart pane
// 2. Set your params there
// 3. Note chartID of Chart Title
// 4. Insert that very same chartID into chartID variable of below code (line 27)
// 5. Apply code in analysis and run backtest whatsoever...
/// @link http://forum.amibroker.com/t/how-to-save-custom-defined-parameters-values/724/9
indicator = Status( "action" ) == actionIndicator;
procedure ParamToAnalysisRemove(pname) {
	if ( indicator )
		StaticVarRemove(StrFormat("ParamToAnalysis_%s*", pname));
}
function ParamToAnalysis(pname, chartID, def, start, end, change, persist) {
	if ( indicator ) { // if in chart pane		
		result = Param( pname, def, start, end, change);		
		StaticVarSet(StrFormat("ParamToAnalysis_%s_%g", pname, GetChartID()), result, persist);		
	} else // call chart pane value (if in analysis)
		result = Nz(StaticVarGet(StrFormat("ParamToAnalysis_%s_%s", pname, chartID)), def);
	return result;
}

Version( 6.17 );

EnableTextOutput(0);

// chartID of Chart pane called from analysis (see printf output)
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
chartID= "2305"; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

if (Status("stocknum") == 0) {
	// remove all or particular params before setting new one
	ParamToAnalysisRemove("");
	//ParamToAnalysisRemove("Fast_MA_Length");
}

persist = false;// save params of pane persistently -> set to "true"

//SetOption("StaticVarAutoSave", 60); // auto-save persistent variables every n-seconds 

//Setup Parameters in chart pane
FastMALength = ParamToAnalysis( "Fast_MA_Length", chartID, 5, Minimum = 5, Maximum = 25, 1, persist );
SlowMALength = ParamToAnalysis( "Slow_MA_Length", chartID, 26, Minimum = 9, Maximum = 200, 1, persist );

if (indicator) {
	info = StaticVarInfo("ParamToAnalysis_*", "list" );
	printf( "Current chart ID: %g\n\nStatic Params:\n", GetChartID());
	printf( info );
}

//Perform Work
FastMacd = MA( C, FastMALength );
SlowMacd = MA( C, SlowMALength );

Buy = Cross( FastMacd, SlowMacd );
Sell = Cross( SlowMacd, FastMacd );
Cover = Cross( FastMacd, SlowMacd );
Short = Cross( SlowMacd, FastMacd );

// ChartID of chart pane to be viewed if chart pane is selected
_N( Title = StrFormat( EncodeColor( colorRed ) + "IMPORTANT: READ COMMENTS IN CODE FIRST!\n\nChartID: %g" + EncodeColor( -1 ) +" -> Param1: %g, Param2: %g", GetChartID(), FastMALength, SlowMALength ) ); 

// ###########################################################
// Optional (NOT mandatory) code to output params in analysis
// ###########################################################
SetCustomBacktestProc(""); 
// Custom-backtest procedure follows  
if( Status("action") == actionPortfolio ) { 
    bo = GetBacktesterObject(); 
    bo.Backtest(1); 

    for (trade = bo.GetFirstTrade(); trade; trade = bo.GetNextTrade()) {
       trade.AddCustomMetric( "chartID", chartid );
       trade.AddCustomMetric( "FastMALength", FastMALength, 1 );
       trade.AddCustomMetric( "SlowMALength", SlowMALength, 1 );
    }

    for (trade = bo.GetFirstOpenPos(); trade; trade = bo.GetNextOpenPos()) {
        trade.AddCustomMetric( "FastMALength", FastMALength, 1 );
        trade.AddCustomMetric( "SlowMALength", SlowMALength, 1 );
    }
    bo.ListTrades();
}

2 Likes

fxshrat,

You are correct, the static variables used for back testing are almost always not arrays. However static variables can be arrays.

The point is using Param functions is really handy when doing back testing, but not an easy way to save and keep the values after experimenting while back testing. What you suggest could work, but over time if the variables are made persistent so they will be there the next time the program is started eventually you will end up a lot of persistent, static variables because they will continue to be there and accumulating year upon year.

Maybe other people use charts and scripts differently, but I am often experimenting, trying new things, creating and deleting charts as I go. So I don't usually know the chart ID of a chart I deleted and can't therefore selectively delete the static variables associated with it. Could purge them all, but then any settings associated with various charts I wanted to keep would be deleted as well.

1 Like