# Quick Volatility Contraction Pattern Calculation (VCP) Using Trendline Studies and AFL

Hello, Title refers to the Title at the top of the top chart pane which usually shows your ticker, candle values, etc.

It's actually a reserved term, it shows up as bold when you use it.

For reference here's my entire code of my chart formula, less the action commentary:

``````// plots
{

thickness = Param( "Bar thickness", -20, -100, 10, 1 );

rc = ROC( C, 1 );
color = IIf( rc >= 0, colorBlue, IIf( rc < 0, colorRed, -1 ) );

style = styleNoTitle | styleBar;

PlotOHLC( Null, H, L, C, "", color, style, Null, Null, 0, 0, thickness );

SetChartOptions( 0, chartShowArrows | chartShowDates );

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

// Calculate VCP characteristics
// Set Default Values
VCPStart = 1e9;
VCPEnd 	 = 0;
T 		 = 0;
Pct      = "";
// Loop through 5 contractions (there's usually less than this but you can adjust as you see fit
for (i = 1; i <=9; i++)
{
// Create the Study ID number, T1, T2, ...
Tstr = "T" + NumToStr(i, 1.0, false);

// Get the trendline with the Study ID given the current chart
trendline = Study(Tstr, GetChartID() );

// extract the x and y values of the trendline
// Credit: https://www.amibroker.com/kb/2006/03/07/getting-x-y-co-ordinates-of-study/
StartX = LastValue( ValueWhen( ExRem( trendline, 0 ), DateTime() ) );
EndX = LastValue( ValueWhen( trendline, DateTime() ) );
StartY = LastValue( ValueWhen( ExRem( trendline, 0 ), trendline ) );
EndY = LastValue( ValueWhen( trendline, trendline ) );

// Set the beginning and end of the base
VCPStart = IIf(i==1, StartX, VCPStart);
VCPEnd	 = IIf(DateTimeDiff(EndX, VCPEnd)>0, EndX, VCPEnd);

// if the trendline exists (nonzero start date), increment the total trendlines found and append the latest contraction % into a string
if (startX > 0)
{
T++;
Pct += numtostr(abs(EndY/StartY-1)*100, 2.0, False) + "/";
}
}
// set the title to add the VCP characteristics using simple string manipulation
Title += "                        VCP Profile = " + NumToStr(DateTimeDiff(VCPEnd, VCPStart)/3600/24/7, 2.0, False) + "W " + StrTrim(Pct, "/") + "  " + NumToStr(T, 1.0, False) + "T";

_SECTION_BEGIN("MA");
P = ParamField("Price field",-1);
Periods = Param("Periods", 15, 2, 300, 1, 10 );
Plot( MA( P, Periods ), _DEFAULT_NAME(), ParamColor( "Color", colorCycle ), ParamStyle("Style") );
_SECTION_END();

_SECTION_BEGIN("MA1");
P = ParamField("Price field",-1);
Periods = Param("Periods", 15, 2, 300, 1, 10 );
Plot( MA( P, Periods ), _DEFAULT_NAME(), ParamColor( "Color", colorCycle ), ParamStyle("Style") );
_SECTION_END();

_SECTION_BEGIN("MA2");
P = ParamField("Price field",-1);
Periods = Param("Periods", 15, 2, 300, 1, 10 );
Plot( MA( P, Periods ), _DEFAULT_NAME(), ParamColor( "Color", colorCycle ), ParamStyle("Style") );
_SECTION_END();

_SECTION_BEGIN("MA3");
P = ParamField("Price field",-1);
Periods = Param("Periods", 15, 2, 300, 1, 10 );
Plot( MA( P, Periods ), _DEFAULT_NAME(), ParamColor( "Color", colorCycle ), ParamStyle("Style") );
_SECTION_END();

_SECTION_BEGIN("MA4");
P = ParamField("Price field",-1);
Periods = Param("Periods", 15, 2, 300, 1, 10 );
Plot( MA( P, Periods ), _DEFAULT_NAME(), ParamColor( "Color", colorCycle ), ParamStyle("Style") );
_SECTION_END();

_SECTION_BEGIN("RS Line");
RSL = C/Foreign("\$SPX", "C")*1000;
RSLHigh 		= ExRem(RSL == HHV(RSL, 69), Buy);
rslb			= RSL + C[BarCount-1]/2.25;
PlotShapes(IIf(RSLHigh, shapeCircle, shapeNone), colorAqua, 0, rslb, 0);
Plot(rslb, "RS Line", colorBlue, styleDashed);
_SECTION_END();
}
``````

Hope this helps!
Mike

5 Likes

That works great! Thanks again rocketPower!

1 Like

Hi, bro. Thank for sharing. I copied and having a error . Please help me. Thank you so much! Presentation1|690x388

@hmht25, Welcome to the forum.

There are several things you, as a new forum member need to do:

From the error you are displaying, we can't see if the error message is correct. Are you missing a semicolon on a previous line?

Most of the very helpful people here like to see that you are a confirmed purchaser of AB, and that you have done some work on this yourself. Post your code (using Code Blocks "</>") so we can run the same thing you are running. Then we should be able to help you out.

2 Likes

@hmht25, I endorse the useful suggestions of @snoopy.pa30, but regarding the error you should simply READ the entire thread!!!!
I gave an answer with a way to handle the error and then @rocketPower, very kindly, posted again his entire formula that works without any error.

4 Likes

@rocketPower

Hi Mike,
thank you very much for all your code for Mark Minervinis SEPA Strategy. I recently read his book "Trade Like A Stock Market Wizard" and have the feeling it is just the right strategy for me. So I finally bought Amibroker (sth. I intended for a long time and finally found a good excuse to do so and trying my first steps with your code.

In your code for the VCP, you are using the RS Line based on the \$SPX. I compared the Line of the symbol TDG to the IBD chart and it does not really fit, though the direction and trend seem to be ok. No wonder according to IBD they create the RS value based on all their stock universe [https://education.investors.com/financial-dictionary/general/rs]. You already used another code of yours to calculate the RS Line in a more complicated manner https://forum.amibroker.com/t/how-to-imitate-ibd-relative-strength-percentile-ranking-of-stocks/6068
Now I am wondering, is this easier SPX solution sufficient for your strategy? As far as I know the value for the final decision is the correct one from IBD eTables anyway. So I guess the RS Line is probably just an little extra.

Best regards
RioL

Hello sir,

You are confusing to very ambiguously-names IBD metrics, which I don't blame anyone for as you'll see why:

Relative Strength: The ranking in percentile of all stocks in your chosen universe, as determine by a weighted price performance over several quarters. This is usually represented as a single value of 1-99

Relative Strengh Line: This is simply a chart of the stock's closing price time-series, divided by the S&P500 closing value time-series. This is represented with a dotted blue line or something similar on IBD chart, with a blue dot appearing at the end when it makes a new high.

So as you can see, these are very different measurements.

As for how my relative strength line looks like on my chart, yes it does look different. I have to scale the resulting line so that it appear on the chart in proximity to the bars. I don't know how the IBD charts do it, but my solution is in the code. But where it is on the chart doesn't matter. And you were on the right path: all that matters is the direction and it's y-axis value in relation to it's previous points.

Hope this helps
Mike

3 Likes

HI Mike,

thanks for the clarification, this is something I totally misunderstood. So far I always thought, the RS Line is just the daily calculated RS Rating.

Best regards
RioL

PS: There is no need to call me Sir. I'm just a regular guy.

2 Likes

I always wondered if a time history of Relative Strength would be a useful indicator for timeliness. You can do it with my code here, if you perform the analysis over a range of dates or your entire time history of your EOD service. May take a while!

Take care
Mike

2 Likes

Hello Mike,

slowly the puzzle pieces come together. For now I will focus on the basic SEPA strategy, combining your eTables Solution with your SEPA screener. And try to figure out if it might work with Tiingo instead of Norgate. After that I'll search again for some fundamental data solution - possibly looking into the Morningstar API for that. Then for european data and after all that I might look into creating a history of the Relative Strength, by then I might even have a more powerful PC (my 10 year old Thinkpad is still going )

RioL

1 Like

Ttingo works well with Amibroker?

I use it with Tiingo EOD and up until recently used Yahoo Fundamental. Overall I have been happy with the data service. I have considered moving to Norgate considering the Yahoo Fundamental isnt working properly with Amiquote currently. Yahoo seems to make changes often which throws things off a bit. I figure the data that we use from eTables eliminates the need for most of anything I would use from Yahoo anyway. If I do end up going with Norgate it will be a few months down the road.

1 Like

We are working on getting our fundamental data out of beta and already sent Tomasz the details

4 Likes

Hi Mike,

as MCassICT already answered the Tiingo question, I took the liberty to not answer until I've done some more work.
I got the trendtemplate working with the Tiingo data, but I have to miss out on these Norgate datasets:

``````/*	// Norgate Data
SF			= GetFnData("SharesFloat");
NPM 		= NorgateFundamentals("ttmnpmgn");			// Net Profit Margin
MarketCap   = NorgateFundamentals("mktcap"); 			// in millions
QRG 		= GetFnData("QtrlyRevenueGrowth");
ES 			= NorgateFundamentals("epssurpriseqprc");	// Earnings Surprise
AE 			= NorgateFundamentals("ttmepschg");			// Annual Earnings
CGR 		= NorgateFundamentals("projltgrowthrate");	// Consensus growth rate
TIT 		= NorgateFundamentals("ttminvturn");		// TTM Inventory Turnover
*/
``````

With the Tiingo fundamental Addon at least the MarketCap and possible some others are accessible too, but I haven't used them to improve the trendtemplate yet. Instead I am using the Addon to create a second ranking based on the historical quarterly data of EPS, RPS, ProfitMargin and SharesWa. For now I do this in a complicated way by creating every day a new batch-file with Amibroker to download the Tiingo fundamental data as *.csv for the tickers of the eTables MasterList. I analyse these *.csv-files with MS Excel Power Query and create a new combined ranking which I then import as a watchlist in Amibroker.
This is all a bit archaic, but for now it works and I'll keep going to improve it.

As for the quality of the Tiingo fundamental data Addon (beta): I checked only a few numbers, which all were correct. Some tickers do have only annually data, but this is also as displayed on www.tiingo.com. I do have to do further checking on this subject and are currently building a pre-buy-checklist, which also involves a manual verification of some of the input data (using the official company statements).

PS: The Morningstar API support never answered, probably to expensive anyway.

1 Like

Nice work! It's never archaic if it works, and you can get through the workflow in a reasonable amount of time!

1 Like

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
titel_new = "= " + duration  + "W  " + StrTrim(pct, "/") + "  "+  t + "T";
}

Title = "VCP Profile " + titel_new;
_SECTION_END();
``````

8 Likes

This is gold @paddy! Thanks for evolving this tool into something that is 100x easier to use!

I don't really ever have time to code deeply, so I rig up something quick and deal with the quirks. So I'm especially grateful when people take my code and make it what I wish it could be!

Take care!
Mike

2 Likes

Hi @paddy i run your code, and in my PC seems that there is not problem at all, i do NOT have to double click.
Are you sure that you first press the button on the keyboard and the you draw the line?

In general, I wish there was a feature to disable those vertical bars from showing up. I never, ever use them.

@Tomasz, is this possible and if not, can it be an option in a future update?

Thanks!
Mike

Thanks for testing! Interesting, which version are you running?
Yes, Iâ€™m sure. For example, number 0+middleBtnClick also works instantly (deleting everything). I also tested clicking middleButton and then leftClick. That also works like a double click

@rocketPower: I was also searching for this to disable. This is what I found.