I found this great conversation about "Drawing Multiple Lines using GetAsyncKeyState" and thought it would make using VCPs a little easier.

There is only one problem with the code, maybe someone can help me? With this code and also the original code from @PanoS I always have to double click until the line starts to draw. This activates the "Range Maker" which is slightly irritating. Am I doing something wrong? (use the latest AmiBroker version 6.35.1)

```
/**
Calculate Volatility Contractions
by @PatrickDoblerCH, 22.09.2020
Acknowledgement:
Basic idea by RocketPower
Most of the code is from PanoS and Lennon: https://forum.amibroker.com/t/drawing-multiple-lines-using-getasynckeystate-or-any-better-method/12007/18
*/
_SECTION_BEGIN( "Measure volatility contractions" ); // Using GetAsyncKeyState()
nMM = 6; //Max no. of MM lines
bi = BarIndex();
dt = DateTime();
t=0;
pct="";
duration = "";
titel_new = "";
MouseBtn = GetCursorMouseButtons();
LeftJustClkd = MouseBtn & 8;
LeftClkDownRlsd = MouseBtn & 9;
MiddleClkd = MouseBtn & 4;
chartId = Name() + Interval();
x = GetCursorXPosition( 0 );
y = GetCursorYPosition( 0 );
if( GetAsyncKeyState( 48 ) < 0 OR GetAsyncKeyState( 96 ) < 0 AND MiddleClkd )
{
StaticVarRemove( "x1*" );
StaticVarRemove( "y1*" );
StaticVarRemove( "x2*" );
StaticVarRemove( "y2*" );
StaticVarRemove( "pct_t*" );
}
for( i = 49; i <= 48 + nMM; i++ )
{
riMM = i - 48;
DigitPressed = GetAsyncKeyState( i ) < 0 || GetAsyncKeyState( i + 48 ) < 0;
if( DigitPressed )
{
// This elegant technique shared by @Milosz
if( LeftJustClkd )
{
StaticVarSet( "x1" + chartId + riMM, x, 0 );
StaticVarSet( "y1" + chartId + riMM, y, 0 );
//StaticVarSet( "y1_start" + chartId + riMM, bi, 0 );
}
if( LeftClkDownRlsd )
{
StaticVarSet( "x2" + chartId + riMM, x, 0 );
StaticVarSet( "y2" + chartId + riMM, y, 0 );
}
if( MiddleClkd )
{
StaticVarRemove( "x1" + chartId + riMM );
StaticVarRemove( "y1" + chartId + riMM );
StaticVarRemove( "x2" + chartId + riMM );
StaticVarRemove( "y2" + chartId + riMM );
StaticVarRemove( "pct_t" + chartId + riMM );
}
RequestMouseMoveRefresh();
}
}
GfxSetBkMode( 1 );
GfxSetCoordsMode( 1 ); // mode = 1 - bar / price mode where X is expressed in bar index and Y is expressed in price.
for( i = 1; i <= nMM; i++ )
{
x1 = Lookup( bi, StaticVarGet( "x1" + chartId + i ) );
y1 = StaticVarGet( "y1" + chartId + i );
x2 = Lookup( bi, StaticVarGet( "x2" + chartId + i ) );
y2 = StaticVarGet( "y2" + chartId + i );
if( x1 != 0 AND x2 != 0 )
{
t++;
GfxSelectPen( colorGrey50, 2, 1 );
GfxMoveTo( x1, y1 ); GfxLineTo( x2, y2 ); // Diagonal line
GfxSelectFont( "Courier New", 9.5, 700 );
pct0 = NumToStr( abs(y2/y1-1)*100, 1.0, False );
pct1 = NumToStr( abs(y2/y1-1)*100, 1.1, False );
GfxTextOut( " T"+NumToStr( i, 1.0 )+": " +pct1 , x1 , y1 );
StaticVarSetText("pct_t" + chartId + i, pct0, false);
}
}
//lasttrendline = Lookup( dt, StaticVarGet( "x2" + chartId + t ) );
//iterate through total number of contractions (t)
for( i = 1; i <= t; i++ )
{
//append the latest contraction % into a string
pct += StaticVarGetText("pct_t" + chartId + i) + "/";
}
//set date
startT1 = Lookup( dt, StaticVarGet( "x1" + chartId + 1 ) );
if ( t != 0 )
{
//calculate from start to most recent date. result is rounded in weeks
duration = Round(DateTimeDiff(LastValue(dt), startT1)/3600/24/7);
titel_new = "= " + duration + "W " + StrTrim(pct, "/") + " "+ t + "T";
}
Title = "VCP Profile " + titel_new;
_SECTION_END();
```