Store Chart Parameter With Symbol

Hi All,

I would like to store chart/afl parameters with each symbol as opposed to each 'Chart ID'. I have read through posts such as #4654 and #4778, and looked in to StaticVarSet and StaticVarGet but cannot see how I could use them in association with the various 'Param' functions to provide a per symbol setting...

Any ideas?

Thank you.

Parameters are always linked to ChartIDs, not symbols.
That way you don't need to set chart parameters each time you change the symbol.

If you want to have separate parameters for different symbol, you have to have different ChartIDs.

You can do so by storing separate layouts for each symbol and quickly switching the LAYOUTS by just double clicking on items in the Layouts list. That would change the chart layout AND the symbol in one click.

Hi Tomasz,

Thank you for your reply.

I guess that's one way I could address the issue but it would certainly result in a lot of layouts to manage.

I was hoping that there would be some form of 'Param' function/overload that incorporated or could be used in conjunction with the StaticVarSet and StaticVarGet functions.

@SippDealer, one alternative is to use some GUI controls bypassing the Params dialog.

See below a skeleton example (not extensively tested...), and if you think it could be applied in your case, please, review the document: Using on-chart GUI controls to include additional controls.

Version (6.25);
GraphXSpace = 20;
prevSymbol = StaticVarGetText("bb_prev_symbol");
symbol = Name();

// Detect changed symbol
resetSlider = False;
if (symbol != prevSymbol) 
{
	resetSlider = True;
	StaticVarSetText("bb_prev_symbol", symbol);
}	
bb_periods = 20;
bb_width = 2;

bb_periods = NZ(StaticVarGet(symbol + "_bb_periods"), bb_periods);
bb_width = NZ(StaticVarGet(symbol + "_bb_width"), bb_width);

pxHeight = Status("pxheight");
pxWidth = Status("pxwidth");

price = ParamField("Price", 3);
topColor = ParamColor("Top band color", colorRed);
botColor = ParamColor("Bottom band color", colorGreen);
slidersXYPos = ParamList("Slider position", "Hide|Top Left|Top Right|Bottom Left|Bottom Right", 3);

bbbot = BBandBot(price, bb_periods, bb_width);
bbTop = BBandTop(price, bb_periods, bb_width);

// Set the "sliders" position on the chart
x1 = 7;
y1 = 27;
switch (slidersXYPos) 
{
	case "Hide":
		x1 = -2000; 
		y1 = -2000;
		break;
	case "Top Right":
		x1 = pxWidth - 400; 
		y1 = 7;
		break;
	case "Bottom Left":
		y1 = pxHeight - 65;
		break;
	case "Bottom Right":
		x1 = pxWidth - 400; 
		y1 = pxHeight - 65;
		break;
}
// Gui controls
idPeriodsSlider = 1001; // each GUI control should have a globally unique ID
idWidthSlider   = 1002;
function CreateGUI()
{
    if( GuiSlider( idPeriodsSlider, x1 + 3, y1 + 3, 300, 28, notifyEditChange ) == guiNew )
    {
        GuiSetValue( idPeriodsSlider,  bb_periods );
        GuiSetRange( idPeriodsSlider, 2, 50, 1, 1 );
    }
    else 
    {
		if (resetSlider)
			GuiSetValue( idPeriodsSlider,  bb_periods );
    }
    
    if( GuiSlider( idWidthSlider, x1 + 3, y1 + 34, 200, 28, notifyEditChange ) == guiNew )
    {
        GuiSetValue( idWidthSlider,  bb_width );
        GuiSetRange( idWidthSlider, 0.5, 5, 0.1, 1 );
    } 
    else 
    {
		if (resetSlider)
			GuiSetValue( idWidthSlider,  bb_width );
    }
}

function HandleEvents()
{
	toSave = False;
    for( n = 0; id = GuiGetEvent( n, 0 ); n++ )  // get the id of the event
    {
		notify = GuiGetEvent( 0, 1 );
        switch( id )
        {
            case idPeriodsSlider:
				if(notify == notifyEditChange ) 
					toSave = True;
                break;
            case idWidthSlider:
				if(notify == notifyEditChange )
					toSave = True;
                break;
            default:
                break;
        }
    }
    if (toSave) {
		bb_periods = GuiGetValue( idPeriodsSlider );
		bb_width = GuiGetValue( idWidthSlider );
		StaticVarSet(symbol + "_bb_periods", bb_periods, true); // Persist
		StaticVarSet(symbol + "_bb_width", bb_width, true); // Persist
		SetOption("StaticVarAutoSave", -1 ); // one-shot auto-save
		bbbot = BBandBot(price, bb_periods, bb_width);
		bbTop = BBandTop(price, bb_periods, bb_width);
	}
}

CreateGUI();
HandleEvents();

// Display GUI values near the sliders
GfxSetCoordsMode(0);
GfxSetBkMode(1);
GfxSelectFont("Tahoma", 10, 400);
GfxSetTextColor(colorWhite);
GfxFillSolidRect(x1, y1, x1 + 393, y1 + 65, colorRGB(64, 64, 64));
GfxTextOut("BB period: " + bb_periods, x1 + 307, y1 + 11);
GfxTextOut("BB width: " + bb_width, x1 + 207, y1 + 40);

Plot(price, "Price", colorDefault, styleCandle);
Plot(bbTop, StrFormat("BB Top(%g, %g)", bb_periods, bb_width), topColor);
Plot(MA(price, bb_periods), "MA", colorGrey40, styleDashed);
Plot(bbBot, StrFormat("BB Bot(%g, %g)", bb_periods, bb_width), botColor);

To learn more about the Gfx and Gui usage there is also a very nice formula written by @alligator in the Members' AFL Library named "GFX Toolkit" where you could find examples to make similar code more flexible using relative coordinates.

Storing many variables permanently may not be a good idea. I think you should consider a way to also delete the saved data if deemed appropriate.

2 Likes

Hi Beppe,

Thank you for your reply.

That is certainly one approach that would work and although it would appear to be the only option at present, in my view it isn't ideal as I don't really want to clutter the chart area with sliders etc. At present I am limited to version 6.28.0. and unless, there is another approach I'll have to wait until I can upgrade.

Many thanks.

@SippDealer you are correct. I apologize: the minimum required version seems to be 6.30 (I copied some part from an old example I had and did not properly check the version.

The code above also includes a way (albeit a bit primitive) to hide the sliders and possibly display them in different places to be able to change the values ​​to be stored.

@SippDealer another idea you could look into is to use an Excel file and check it via OLE (keeping it open while using AB), using its "VLookup" function to locate the active symbol in a given column and read the other associated parameters from other columns (if not found use some defaults).
For each new symbol that needs "customization" it would be enough to simply create a new row (or modify an existing one) by copying the current parameters from the Amibroker chart.
This would also allow you to easily "delete" data you no longer need.

@beppe Thanks - I guess that is another option, but I would imagine I would have to continuously refresh the chart each time I adjusted the corresponding value in the spreadsheet?

@SippDealer probably yes. Although there is a possibility to force automatic refresh in Ambroker, maybe in combination with OLE it might not be a good idea.
Some time ago, to experiment, I had developed something like this but I don't remember exactly what the workflow used was and whether it was practical enough.

@beppe Thank you for all your help. I'm guessing that unless AFL is extended to support the various 'Param' functions having an override to the way their data is stored I'll have to settle on using a 'GuiSlider' control or similar once I've upgraded.

You can also keep your parameters in Notepad (Window->Notepad) that keeps notes on per-symbol basis and read the notes use NoteGet() AFL function
https://www.amibroker.com/f?noteget

@Tomasz Thanks, but the storing of values on a per symbol basis is not really a problem as there appears to be a number of options including your suggestion, with some possibly providing the ability to have the chart indicator auto update to reflect the latest stored value, For me, all options appear to be a compromise and the issue is that the 'Param' functions are hard coded to store their values with a specific 'Chart ID' with no overload option or other to custom store their values elsewhere.

No. You have simply no idea of a big picture.

I don't want to have separate parameters per symbol in AmiBroker because
that would mean that I COULD NOT QUICKLY SCAN thru charts, having to change the parameters of indicators FOR each and every symbol.

That would be counterproductive and slow.

I want to set the params of chart and be able to quickly watch hundreds of symbols.
I want to set chart parameters ONCE for all, not zillions of times.

AmiBroker is a tool for looking for opportunities in ENTIRE MARKET, not for few symbols like primitive online tools.

The idea is to find opportunities in entire market, not to baby-sit every single symbol.

You don't understand entire principle of this software.

DISCOVER -> VALIDATE -> TRADE

AmiBroker is a tool to DISCOVER market inefficiencies, build and VALIDATE trading system based on inefficiencies and then TRADE it.

DISCOVERY is meant to be automatic. It is not meant to be manual. To achieve that, you write explorations and test parameters that are NOT over-optimized and that work on EVERY symbol, not one.

Over-optimization is the root of all evil. Per-symbol params is the first step to over-optimization.

Anyway, if you still want that, I showed a couple of ways how to setup charts with different params per symbol, the per-symbol layouts being recommended and convenient path.

There is also another way to "abuse" the system. You can simply append symbol name to parameter name this way:

// per-symbol parameters
periods = Param( "Periods for " + Name(), 14, 2, 200, 1 );

And you will be able to control parameters for every symbol separately directly from Parameters dialog and it looks like this:

Animation

Note that this is just to show you flexibility of AmiBroker. It is a "HACK" and it is NOT supported officially and the program is NOT designed to be used that way.

3 Likes

Topic was solved already in first reply.