Sync charts for zoom and scroll

I am lloking for a way to sync 2 charts tiled horizontally such that scrolling one of them scrols the other as well, I tried zoomer ( but it slows the system down. Is there a better way?

1 Like

@waverider I am not sure how to get what you want (or what you actually want) but I am picking up orphaned posts this evening.

Have you tried Symbol link and/or Interval link? Some resources for review that may help.

Imagine two chart windows one has cboe symbol selected and the other one has es symbol selected. Both are of 1 minute interval. Now tile them horizontally. I am looking for a way by which if I scroll the first chart the second chart scrolls along with it.

1 Like

@waverider Maybe it is not exactly what you have asked for, but there is a very simple way in which you can achieve similar result without any hassle or drop in performance.

Instead of opening two (or more) separate charts and tiling them horizontally, you can display the second, third etc. chart in the first chart's pane - like this:


Use SetForeign() or PlotForeign() to plot the chart of another Symbol(s). You should use the most liquid (most traded) Issue as the base symbol, to avoid data holes.

Perfect syncing of all charts guarenteed without any drop of performance!


Thanks for suggesting that. I am using that in my charts with the index instrument as foreign. Although i have only one scale on the right.

That’s because you probably use only one chart pane, not two or more as I suggested. Take a look at these two short videos:


This is very usefull. Thank you.

1 Like

I have the same need, that I am guessing @Jefforey may be requesting.
Essentially, a synchronized zoom and scroll across multiple chart WINDOWS, (not panes).
Amibroker already supports syncing Symbol and Intervals across charts. Why not Zoom?

So for example, the date ranges displayed across all displayed charts would remain in sync.


Thanks to @PanoS and a somewhat ancient piece of Amibroker code, I discovered the AFL formula that enables a user to Synchronize Charts for Zoom and Scroll. The formula has been included in the Sync charts +zoom posting.

Here is the code that can be used to Synchronize charts and zoom.

Put this AFL in the Include Folder.
Add below 2 lines into your AFL
  UseZoomer = ParamToggle("Use Zoomer?", "No|Yes", 0);
  #include_once <>
  Sourcecode downloaded from:

function ZqZoomSync( force )
    // All variables are made local to guarantee naming collisions or side effects
    local LastBarIndex, FirstBarIndex, prevLastBarIndex, prevFirstBarIndex, prevFirstDateTime, DT, BI, LastDateTime, FirstDateTime, LastDateTimestr, FirstDateTimestr;
    local OAB, OAD, dcount, i, OADoc, OAW, OADocWin, res;
    // Get a count of the number of documents
    OAB = CreateObject( "Broker.Application" );
    OAD = OAB.Documents;
    dcount = OAD.Count;
    // Process multiple windows (documents)
    res = False;
    if ( dcount > 1 )
        // Get current and last start and end DateTimes's
        LastBarIndex = Status( "LastVisibleBarIndex" );
        FirstBarIndex = Status( "FirstVisibleBarIndex" );
        //Nblankbar = Status( "LastVisibleBarIndex" ) - BarCount; // [zq] not used !!
        // [zq] BarIndex may always be the same due to QuickAFL, check prevFirstDateTime in addition
        prevLastBarIndex = Nz( StaticVarGet( "_prevLastVisibleBarIndex" ) );
        prevFirstBarIndex = Nz( StaticVarGet( "_prevFirstVisibleBarIndex" ) );
        prevFirstDateTime = Nz( StaticVarGet( "_prevFirstDateTime" ) );
        // [zq] move outside if() statement for checking prevFirstDateTime
        DT = DateTime();
        BI = BarIndex();
        LastDateTime = LastValue( ValueWhen( LastBarIndex == BI, DT ) ); // [zq] LastDateTime could be empty
        FirstDateTime = LastValue( ValueWhen( FirstBarIndex == BI, DT ) );
        // Check for a new date/time range
//        _TRACE(""+FirstBarIndex+", "+LastBarIndex);
        if ( LastBarIndex != prevLastBarIndex OR FirstBarIndex != prevFirstBarIndex OR FirstDateTime != prevFirstDateTime OR force )
            // Set the new last values
            StaticVarSet( "_prevLastVisibleBarIndex", LastBarIndex );
            StaticVarSet( "_prevFirstVisibleBarIndex", FirstBarIndex );
            StaticVarSet( "_prevFirstDateTime", FirstDateTime );
            LastDateTimestr = DateTimeToStr( LastDateTime );
            FirstDateTimestr = DateTimeToStr( FirstDateTime );
//            _TRACE(""+FirstDateTimestr+", "+LastDateTimestr);
            // Loop through the document collection
            for ( i = 0; i < dcount; i++ )
                // If it is not the active document -
                OADoc = OAD.Item( i );
                // NOTE - it doesn't hurt to sync the current window and it makes all
                // windows have no blank bars on the right so they look the same
                // [zq] I think it's reasonable for not syncing ActiveDocument.
                // [zq] Something not belong to the ActiveDocument was shown when not syncing ActiveDocument with multi-threaded charts options disabled.
                if ( OADoc != OAB.ActiveDocument )
                    // Get the document window and zoom to range
                    //_TRACE( " Zoom to range document - " + i + " , " + Curststr + " - " + Curendstr );
                    OADW = OADoc.Windows;
                    // Document window count assumed to be 1
                    OADocWin = OADW.Item( 0 );
                    OADocWin.ZoomToRange( FirstDateTimestr, LastDateTimestr ); // [zq] this function failed to update chart at the right most margin with empty LastDateTimestr. Just minor issue, don't care.
            res = True;
    return res;

//  Call for synchronization
If (UseZoomer)
     ZqZoomSync( False ); // [zq] set True will enter infinite loop if we also update ActiveDocument

Thanks Milosz,

I have plotted two panes, say an Index in the upper pane (with chart style = candlestick), and say a breath indicator in the lower pane. The problem with this indicator, is it only has one value per day, so it doesn't plot a candle, just a single 'dash' each day. How could I change the default style for this symbol to "line" so that it at least plots a line chart rather than a series of red horizontal close lines?

If I click View / Price Chart Style and change to line, it changes BOTH panes to line, but of course I would prefer the upper pane to remain as a candlestick, and the lower to be a line.


You should post the code of the indicator and specify which plot function is used.

@ezekiel If you are using AmiBroker's default PlotForeign() example code (Charts --> Basic Charts --> Plot (foreign) ) - as I used In my axample above

Ticker = ParamStr("Symbol", Name() );
PlotForeign( Ticker, Ticker, ParamColor("Color", colorCycle ), ParamStyle("Style") | GetPriceStyle() ); 

... you can't select StyleLine or StyleStaircase (as you wanted) and you can choose only between below options:


... because this code uses uses ParamStyle() with mask = maskDefault flag.

I quote from:

mask - binary mask that defines which styles should be visible in the drop down list
maskDefault - show thick, dashed, hidden, own scale styles (this is default mask for ParamStyle)
maskAll - show all style flags
maskPrice - show thick, hidden, own scale, candle, bar
maskHistogram - show histogram, thick, hidden, own scale, area

... but you can easily modify this code, for example this way:

Ticker = ParamStr( "Symbol", Name() );
PlotForeign( Ticker, Ticker, ParamColor( "Color", colorCycle ), ParamStyle( "Style", styleLine, maskAll ) );

Now the chart's default style is styleLine and additionally you have many other options - not available before. For example in your case styleStaircase might be a better choice.


Remember to click "Reset all" in the "Properties of: " window after changing the code.


Thanks very much Milosz,

Yes, I'm using the standard 'Plot(foreign)'.

I edited the code as suggested, although I'm not sure where the "Reset All" and "Properties of" windows are that you mentioned?

Note that I do get the options now of "line" but even when selected (see attached) I don't get a line. ??

Thanks for any suggestions


Actually Milosz, I've worked it out, cheers. I was clicking "save as" on the "plot foreign" code, but couldn't get it working. When I just create an entirely fresh 'indicator' with that code you pasted though, then I can safely plot a line chart on the second pane, while the upper pane remains a candle.

Thanks again !


Sorry, there is still one mystery:

If I plot #RUTAD on it's own chart, I can add a Keltner Channel, no problem. If I add it as a foreign, in a sub pane, when I plot the KC, the width of the KC seems far too narrow. What would be causing this?
Capture Capture1

Ah! It's because when I drag Keltner Channel into the lower pane, it uses ATR in the formula, which references the primary (not the foreign) security. This is a real shame. It would be nice if there was a way to simply have two different panes, of two different securities, that are in-sync with cross-hair and scroll together, while otherwise being separate and having their own indicators - as per say, Ninja Trader functionality. That could be an enhancement request : )

@ezekiel when you want to apply an indicator that internally uses some predefined arrays (like the ATR), if your formula for any reason, needs to use some other values, you could use the "trick" explained in this KB article:

But in this case, I do not see why a normal call to SetForeign() will not be enough.


Nonsense, there isn't any enhancement required (besides NT is junk).

All is there already for YEARS! There isn't aynthing new there under the sun just because you are new to AB.

Simply do not use PlotForeign but SetForeign without restoring to price arrays of selected symbol!

Then you can drag&drop any indicator from "Charts" window onto subpane and it will be calculated based on price of set foreign symbol.

SetChartOptions( 0, chartShowDates | chartShowArrows | chartWrapTitle );

Ticker = ParamStr( "Input Symbol", Name() );

/// FOREIGN SYMBOL CALL ###################
SetForeign( Ticker, fixup = 1, 0 );
/// FOREIGN SYMBOL CALL ###################

Plot( C, "", colorDefault, styleCandle );

_N( Title = StrFormat( Name() + " - {{INTERVAL}} - {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%), Vol %g {{VALUES}}",
                           O, H, L, C, SelectedValue( ROC( C, 1 ) ), V ) );

_SECTION_BEGIN("Keltner Bands");
P = ParamField( "Price field", -1 );
Periods = Param( "Periods", 15, 2, 300, 1 );
Width = Param( "Width", 2, 0, 10, 0.05 );
Color = ParamColor( "Color", colorCycle );
Style = ParamStyle( "Style", styleLine | styleNoLabel );

CenterLine = MA( P, Periods );
KTop   = CenterLine + Width * ATR( Periods );
KBot = CenterLine - Width * ATR( Periods );

Plot( KTop, "KBTop" + _PARAM_VALUES(), Color, Style );
Plot( KBot, "KBBot" + _PARAM_VALUES(), Color, Style );