Linking the TDash Window with the Main Chart

The most important requirement for a Trading Dashboard is to be able to place orders by dragging price markers and have these prices reflected on your main chart. To accomplish this relationship, the Main Chart and TDash windows must be bi-directionally linked so that changing prices in one window are being tracked in the other.

If both the Main chart and Trading Dashboard window had an identical price range and window size, this could simply be done by sharing the price and pixel ranges using Static Variables. However, to make this work in a multi window layout where windows may not be aligned accurately, or when the TDash window is located on another monitor, is a little more complex.

The problem can be solved by aligning the upper edge of the windows with the upper edge of the AmiBroker window, and use this as the common reference to calculate prices and pixel values.

The code below demonstrates how the Y-Pixel position and prices, between the Trading Dashboard window and the main chart, can be made to track each other. To apply this technique in an actual application you would want to add graphical price markers that you can drag, start up initialization, and provide a fine-adjustment to set prices exactly. Some of this will be covered in the next post.

To test the code, create a two-window Layout, arrange them horizontally, apply the TDashLinkDemo code in the right window, and apply the MainChartLinkDemo code in the left window. Since this test code is not initialized at start-up, you need to click in the TDash window to set an initial price and make the price-line appear.

Click the left mouse button in the TDash window at a new price level to set the price-line to a different value. Click and hold the left mouse button down to drag the price to a new value. Note that the price-line in the main chart window tracks all changes. Price lines would be used to display pending orders and to enable you to set order prices with respect to real-time chart patterns.

When you release the left mouse button in the TDash window it locks in the current price. The only way to change the price is by clicking or dragging it in the TDash window.

If you drag the Y-axis in the main chart the corresponding price-line in the TDash window tracks this movement. This is needed to keep price markers in the TDash window in sync when the main chart y-axis changes.

In a multi-threading environment, where formulas execute asynchronously, we cannot assume that transient conditions saved in a Static Variable in one window will always be detected in another. To ensure reliable mouse-click detection in the Main chart program, the “~LeftButtonRelease” Static Variable, which is set in the control window, is reset in the main chart after it has been detected. This way no triggers will ever be missed.

The short video below illustrates how this works and what it looks like in AmiBroker.

// TDashLinkDemo

function LinkWithMainChart()
{
MX GetCursorXPosition);
MY GetCursorYPosition);
OnTDash = !IsNullMX ) AND !IsNullMY ); // Is cursor in TDash?
LeftClick GetCursorMouseButtons() == AND OnTDash;
LeftDown = ( GetCursorMouseButtons() == OR LeftClick ) AND OnTDash;
PrevLeftDownState NzStaticVarGet"~LeftDown" ) );
LeftButtonRelease LeftDown &ltPrevLeftDownState;
StaticVarSet"~LeftDown"LeftDown );

if ( LeftClick StaticVarSet"~NowDragging"True );
else if ( LeftButtonRelease )
{
StaticVarSet"~NowDragging"False );
StaticVarSet"~LeftButtonRelease"True );
}
if ( NzStaticVarGet"~NowDragging" ) ) ) StaticVarSet"~TDashYPixels"MY );
}

function DrawTDashPriceLine()
{
pxWidth Status"pxWidth" );
TDashYPixels StaticVarGet"~TDashYPixels" );
GfxSetBkMode);
GfxSelectPencolorRed1);
GfxMoveTo0TDashYPixels );
GfxLineTopxwidthTDashYPixels );
}

function TDashDisplayPrice()
{
MY GetCursorYPosition);
TDashYPixles StaticVarGet"~TDashYPixels" );
MAinChartPrice NzStaticVarGet"~MainChartPrice" ) );
GfxSetTextAlign) ;
GfxSelectFont"Lucida Console"12600 );
GfxTextOut"TDash price: $" NumToStrMAinChartPrice1.2False ), 0TDashYPixles );
GfxTextOut"TDash Pixels: " NumToStrTDashYPixles1.0False ), 0TDashYPixles 20 );
}

RequestTimedRefresh0.1 );
GfxSetOverlayMode2);
LinkWithMainChart();
DrawTDashPriceLine();
TDashDisplayPrice(); // Test only

// MainChartLinkDemo

function LinkWithTDash()
{
pxWidth Status"pxWidth" );
Miny Status"axisminy" );
Maxy Status"axismaxy" );
Pricerange MaxY MinY;
pxchartbottom Status"pxchartbottom" );
pxcharttop Status"pxcharttop" );
PxChartRange Status"pxchartheight" );
pxheight Status"pxheight" );

if ( NzStaticVarGet"~LeftButtonRelease" ) ) )
{
TDashYPixels StaticVarGet"~TDashYPixels" );
PricePerPixel Pricerange PxChartRange;
MAinChartPrice Maxy - ( PricePerPixel * ( TDashYPixels ) );
StaticVarSet"~LeftButtonRelease"False );
StaticVarSet"~MainChartPrice"MAinChartPrice );
}
else
if ( NzStaticVarGet"~NowDragging" ) ) == )
{
MAinChartPrice NzStaticVarGet"~MainChartPrice" ) );
PixelPerprice PxChartRange Pricerange;
PriceToPixelvalue = ( MaxY MAinChartPrice ) * Pixelperprice 5;
StaticVarSet"~TDashYPixels"PriceToPixelvalue );
}
}

function DrawTDashPriceLine()
{
pxWidth Status"pxWidth" );
TDashYPixels StaticVarGet"~TDashYPixels" );
GfxSetBkMode);
GfxSelectPencolorRed1);
GfxMoveTo0TDashYPixels );
GfxLineTopxwidthTDashYPixels );
}

function MainDisplayPrice()
{
pxWidth Status"pxWidth" );
TDashYPixels StaticVarGet"~TDashYPixels" );
MAinChartPrice NzStaticVarGet"~MainChartPrice" ) );
GfxSetTextAlign) ;
GfxSelectFont"Lucida Console"12600 );
GfxTextOut"  Main price: $" NumToStrMAinChartPrice1.2False ), pxwidthTDashYPixels );
GfxTextOut"Main Pixels: " NumToStrTDashYPixels1.0False ), pxwidthTDashYPixels 20 );
}

RequestTimedRefresh0.1 );
GfxSetOverlayMode);
LinkWithTDash();
DrawTDashPriceLine();
MainDisplayPrice(); // Test only

PlotC""colorBlackstyleBar );

Comments are closed.