Is it possible to add SetSymbol() and SetTimeFrame() native AFL functions?

Review of current functionality:

Outside of AFL programming, I can set the symbol for a chart with either the symbol combobox on the toolbar or by clicking a symbol on a watchlist, for examples. I can change the chart's timeframe by right clicking in the chart and going to the timeframe sub-menu to select the default timeframe setups or by selecting/insertint the timeframe in the timeframe combobox off of a toolbar.

In AFL, I can change the symbol for a chart by going into the Stock COM object to change the symbol there and RefreshAll() and I can do all sorts of various things with TimeFrameSet, TimeFrameExpand, etc.


However, why can't there be just 2 simple native AFL function calls like:

SetSymbol('MSFT')
SetTimeFrame('1m')

The AFL running on the chart would change to either the new symbol or timeframe on its next execution cycle. This is what happens anyway when you select a different symbol or timeframe from the toolbar comboboxes, right? SetSymbol() would do what the toolbar combobox change of symbol does and SetTimeFrame would do what the toolbar combobox change of timeframe does.

Why would I want this functionality?

I can have my own button grids of symbols and timeframes and that is much faster and more convenient to flip though than going to a combobox, right-clicking into a submenu or finding a symbol on a watchlist in a different window. Less distraction all around in real-time day trading situations.

Yes, I currently do the symbol changing with a button grid I created by going into the Amibroker COM object model. It really wasn't designed for this usage though.

Something like a SetSymbol() and SetTimeFrame() are clean, simple from a user-coded AFL perspective and I don't need to do anything more for all my AFL code to behave just as it does when I change the symbol and timeframe from the Amibroker user interface.

Steve --
Functionality you want is already there. Use Help (F1) and search for "chart window pane" (including the " " marks). See the chart there -- line just above the pane, white background, has ^DJI etc. in it. Add your symbols to it. It's a drop down list.
As for interval, next to the drop down arrow is a smaller arrow with line above it. Click on it. Add/Remove buttons> Ticker, then check interval.

@SteveH I understand your request, and I would also welcome such functions, although I suppose there is (as usual) a good reason why such functionalities are available only via OLE.

Having said that, you can take a look at two of my examples showing a possible way of changing the Interval and Symbol programatically. It shouldn't be a problem to replace parameters slider with a gui button or a similar solution (the first example) and selected chart with an active one (the second one).

Best

Short answer is: NO.

No, because data is prepared BEFORE AFL formula is run.

For charts it is even cached already in selected symbol/interval so it does not need to be prepared with each refresh. Each AFL thread has private copy of data so:

  • performance scales correctly with larger number of threads (so threads don't fight for data access - shared resource)
  • you can safely override OHLC arrays in AFL formula without affecting actual database
  • multiple chart panes in the same chart window can utilise prepared data without ANY extra workload

Given how Foreign/SetForeign is abused allowing that would open incredible can of worms (totally destroy performance).

You CAN however change the symbol and interval programmatically using OLE
http://www.amibroker.com/guide/objects.html

2 Likes

I can post the complete code but here are the procedure calls I use to set the symbol and interval. The set_chart_symbol() is working fine. It doesn't matter if I call this in a floating or non-floating chart window.

However, The set_chart_interval() will only be applied to the last active non-floating chart window. If I try to set the interval in a floating window, then the last active non-floating window will have its interval changed. But that's not the ActiveDocument I'm currently in (i.e. the one which has the mouse focus). I've got 6 monitors and I have 5 floating windows. Every one of these 5 floating windows will effect the interval change of the last used non-floating window (i.e., among all of the tabbed windows in the main Amibroker app window).

procedure set_chart_symbol(symbol)
{
	local ab, chart, stock, stocks;
	
	ab = CreateObject("Broker.Application");
							
	stocks = ab.Stocks;
	stocks.Add(symbol);
						
	stock = stocks.Item(symbol);
							
	ab.RefreshAll();
							
	chart = ab.ActiveDocument;
	chart.Name = symbol;
}

procedure set_chart_interval(timeval)
{
	local ab, chart;
	
	ab = CreateObject("Broker.Application");
	
    chart = ab.ActiveDocument;
    chart.Interval = timeval;
}

@SteveH I'm not at home today, so my reply will be short, but your issue has been also discussed in these threads:

Note that all those solutions do not really provide what original poster asked for. Instead they change symbol/interval NOT in current execution (because it is impossible) but cause change of symbol / interval in the NEXT execution/refresh of chart and then force refresh.

I simply provided links to the threads in which I described my observations concerning a similar topic. In my project, tickers in selected chart windows (which were usually in floating mode) were automatically synchronized with the news coming during the session from different sources (Link to the description) It looked like this:

TradingNewsPanelMM

I wrote about pros and cons of such solution. For this reason, I thought that my observations and other users (also Tomasz) replies might be useful for the author of this thread. As I can see, probably they are not. Nevertheless I still don't understand why it is so important to change Symbol/Interval in the current AFL execution? It wasn't a problem for me. If someone really needs, he can always force only one additional refresh by adding:

if (NewTicker) RequestTimedRefresh (0.1, False);

... and do what should be done in the next execution.

Steve might also experiment with linking different charts by Symbol and/or Interval - especially with the last opened chart, which in some cases is synchronized by default.

My project was working quite well although it would be definitely much more robust if we had direct access to floating windows from OLE. Tomasz - you wrote here that it might happen in the future, so let's hope that this will be the case.

Here is the full coding example. I use hard-coding for the symbols simply because I'm only real-time trading these directly off of the chart. This has been pulled from a much bigger code base for my charting for posting as a self-contained example.

I have 2 rows of symbols. Top 1st contains the main futures contracts I follow along plus 2 stocks. The 2nd row contains the micro/mini contracts corresponding to the 1st row (last 2 don't...DAX mini and micro Bitcoin futures). They're all buttons where I match up the backgrounds of the buttons and the chart. In the code, just change the colorBG variable at the top to match what you want. Ctrl-R allows you to move the whole panel around and there's an option to not show the 3rd row (time intervals).

Changing symbols works regardless of whether the chart window is Normal or Floating. Changing time intervals only works for Normal chart windows. I was hoping that since the Name and Interval are both properties of the Document object, the Interval could be changed if the chart windows were Floating. I tried adding ab.RefreshAll() in the set_chart_interval() function too but that didn't change the situation.

If Tomasz says it can't be done, then it can't be done. I tried and here are the best results I can get for what I initially wanted.

[Note: I have an option grid panel similar in form to this one where that I will post later and I think many of you who chart US options will appreciate its usefulness in being able to quickly switch between option contracts for the same symbol. I use it all the time to look at weekly TSLA options. AFL is a true joy to work with.]


_SECTION_BEGIN("Symbol Panel");

SetChartOptions(0, chartShowDates);

idSymBegin = 700;

idsym1 = 701;	// NQ
idsym2 = 702;	// RTY
idsym3 = 703;	// YM
idsym4 = 704;	// ES
idsym5 = 705;	// CL
idsym6 = 706;	// GC
idsym7 = 707;	// HSI
idsym8 = 708;	// HHI
idsym9 = 709;	// TSLA
idsym10 = 710;	// MRNA

idsym15 = 715;	// MNQ
idsym16 = 716;	// M2K
idsym17 = 717;	// MYM
idsym18 = 718;	// MES
idsym19 = 719;	// MCL
idsym20 = 720;	// MGC
idsym21 = 721;	// MHI
idsym22 = 722;	// MCH
idsym23 = 723;	// FDXM
idsym24 = 724;

idtime1 = 725;	// 1 min
idtime2 = 726;	// 2 min
idtime3 = 727;	// 3 min
idtime4 = 728;	// 5 min
idtime5 = 729;	// 15 min
idtime6 = 730;	// 30 min
idtime7 = 731;	// 60 min
idtime8 = 732;	// 240 min

idtime9 = 733;	// 15 sec
idtime10 = 734;	// 30 sec

idSymEnd = 734;

showSymbolGrid = ParamToggle("Show Symbol Grid", "No|Yes", 1);
showTimeIntervalsGrid = ParamToggle("Show Time Intervals", "No|Yes", 1);

sym_x = Param("Start X (Symbol Panel Grid)", 1, 1, 1920);
sym_y = Param("Start Y (Symbol Panel Grid)", 20, 1, 1080);

colorBG = ColorRGB(115, 151, 124);
colorControl = colorBG;

procedure set_chart_symbol(symbol)
{
	local ab, chart, stock, stocks;
	
	ab = CreateObject("Broker.Application");
							
	stocks = ab.Stocks;
	stocks.Add(symbol);
						
	stock = stocks.Item(symbol);
							
	ab.RefreshAll();
							
	chart = ab.ActiveDocument;
	chart.Name = symbol;
}

procedure set_chart_interval(timeval)
{
	local ab, chart;
	
	ab = CreateObject("Broker.Application");
	
    chart = ab.ActiveDocument;
    chart.Interval = timeval;
}

procedure SymbolGrid(x, y)
{
	local id, btn_width, width, height, idx;
	local extra_spacing;
	
	extra_spacing = 0;
	idx = 0;
	btn_width = 50;
	width = 40;
	height = 20;
	
	id = idsym1;
	
	GuiButton(" NQ ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" RTY ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" YM ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" ES ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" CL ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" GC ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" HSI ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" HHI ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" TSLA ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MRNA ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	
	id = idsym15;
	idx = 0;
	y += height + 2;
	
	GuiButton(" MNQ ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" M2K ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MYM ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MES ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MCL ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MGC ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MHI ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MCH ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" FDXM ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	GuiButton(" MBT ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
	
	GuiSetColors(idsym1, idsym10, 1, colorBlack, colorControl, colorBG, colorBlack, colorGold, -1, -1, -1, -1, -1, -1, -1);
	GuiSetColors(idsym15, idsym24, 1, colorBlack, colorControl, colorBG, colorBlack, colorGold, -1, -1, -1, -1, -1, -1, -1);
	
	if (showTimeIntervalsGrid)
	{
		id = idtime1;
		idx = 0;
		y += height + 2;
	
		GuiButton(" 1m", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
		GuiButton(" 2m ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
		GuiButton(" 3m ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
		GuiButton(" 5m ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
		GuiButton(" 15m ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
		GuiButton(" 30m ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
		GuiButton(" 1h ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);
		GuiButton(" 4h ", id++, x + (idx++ * (width + extra_spacing)), y, width, height, notifyClicked);

		GuiSetColors(idtime1, idtime8, 1, colorBlack, colorControl, colorBG, colorBlack, colorGold, -1, -1, -1, -1, -1, -1, -1);
	}
}

procedure SymbolEvents(id, notify)
{	
	local sym, timeval;
	
	switch (id)
	{
		case idsym1:
			if (notify == notifyClicked)
				sym = "NQU1-GLOBEX-FUT";
		break;
		
		case idsym2:
			if (notify == notifyClicked)
				sym = "RTYU1-GLOBEX-FUT";
		break;
		
		case idsym3:
			if (notify == notifyClicked)
				sym = "YM   SEP 21-ECBOT-FUT";
		break;
		
		case idsym4:
			if (notify == notifyClicked)
				sym = "ESU1-GLOBEX-FUT";
		break;
		
		case idsym5:
			if (notify == notifyClicked)
				sym = "CLU1-NYMEX-FUT";
		break;
		
		case idsym6:
			if (notify == notifyClicked)
				sym = "GCZ1-NYMEX-FUT";
		break;
		
		case idsym7:
			if (notify == notifyClicked)
				sym = "HSIQ1-HKFE-F-HKD";
		break;
		
		case idsym8:
			if (notify == notifyClicked)
				sym = "MHIQ1-HKFE-F-HKD";
		break;
		
		case idsym9:
			if (notify == notifyClicked)
				sym = "TSLA";
		break;
		
		case idsym10:
			if (notify == notifyClicked)
				sym = "MRNA";
		break;
		
		case idsym15:
			if (notify == notifyClicked)
				sym = "MNQU1-GLOBEX-FUT";
		break;
		
		case idsym16:
			if (notify == notifyClicked)
				sym = "M2KU1-GLOBEX-FUT";
		break;
		
		case idsym17:
			if (notify == notifyClicked)
				sym = "MYM  SEP 21-ECBOT-FUT";
		break;
		
		case idsym18:
			if (notify == notifyClicked)
				sym = "MESU1-GLOBEX-FUT";
		break;
		
		case idsym19:
			if (notify == notifyClicked)
				sym = "MCLU1-NYMEX-FUT";
		break;
		
		case idsym20:
			if (notify == notifyClicked)
				sym = "MGCQ1-NYMEX-FUT";
				
		case idsym21:
			if (notify == notifyClicked)
				sym = "HHIU1-HKFE-F-HKD";
		break;
		
		case idsym22:
			if (notify == notifyClicked)
				sym = "MCHQ1-HKFE-F-HKD";
		break;
		
		case idsym23:
			if (notify == notifyClicked)
				sym = "FDXM SEP 21-DTB-FUT-EUR";
		break;
		
		case idsym24:
			if (notify == notifyClicked)
				sym = "MBTQ1-CMECRYPTO-FUT";
		break;
		
		// 1 min
		case idtime1:
			if (notify == notifyClicked)
				timeval = in1Minute;
		break;
		
		// 2 min
		case idtime2:
			if (notify == notifyClicked)
				timeval = in1Minute * 2;
		break;
		
		// 3 min
		case idtime3:
			if (notify == notifyClicked)
				timeval = in1Minute * 3;
		break;
		
		// 5 min
		case idtime4:
			if (notify == notifyClicked)
				timeval = in5minute;
		break;
		
		// 15 min
		case idtime5:
			if (notify == notifyClicked)
				timeval = in15Minute;
		break;
		
		// 30 min
		case idtime6:
			if (notify == notifyClicked)
				timeval = in15Minute * 2;
		break;
		
		// 1 hr
		case idtime7:
			if (notify == notifyClicked)
				timeval = inHourly;
		break;
		
		// 4 hr
		case idtime8:
			if (notify == notifyClicked)
				timeval = inHourly * 4;
		break;
	}
	
	if (id >= idsym1 && id <= idsym24)
		set_chart_symbol(sym);
	else if (id >= idtime1 && id <= idtime8)
		set_chart_interval(timeval);
}

function IsMouseInChart()
{
	local x, y, ret;
	local pxchartleft, pxcharttop;
	local pxchartbottom, pxchartright;
	
	pxchartleft = Status("pxchartleft");
	pxcharttop = Status("pxcharttop");
	pxchartbottom = Status("pxchartbottom");
	pxchartright = Status("pxchartright");
	
	ret = 0;
	
	x = GetCursorXPosition(1); 
	y = GetCursorYPosition(1);
	
	if (x > pxchartleft && x < pxchartright && y > pxcharttop && y < pxchartbottom)
		ret = 1;
		
	return ret;
}

procedure HandleEvents()
{	
	local i, id, notify;
	
	if (IsMouseInChart())
	{
		for (i = 0; id = GuiGetEvent(i, 0); i++) 
		{
			notify = GuiGetEvent(i, 1);
			
			if (id >= idSymBegin && id <= idSymEnd)
				SymbolEvents(id, notify);
		}
	}
}

if (showSymbolGrid)
{
	SymbolGrid(sym_x, sym_y); 
}

HandleEvents();

Plot(Close, "", colorBlack, styleCandle);

_SECTION_END();

There's always a solution or at least a workaround.

Your "one click" solution (not working for floating charts):
1 click 1

and no coding "one click" solution working in all cases (for both normal and floating charts):
1 click 2

Unfortunately, there's only one of those sub-toolbars and I've got charts spread out across 6 monitors.

The right click pop-up menu to select the intraday->timeval item in a floating window is the alternative in my case.

Another option - predefined and custom shortcut keys for all intervals:

Shortcuts

Yes, forgot that way. Thanks.

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.