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

Hello all,

If you use Minervini's Volatility Contraction Pattern, you may have seen me post before on how I'm trying to code something to detect them in a chart. It hasn't been easy with my limited time between work and family, but I did create something that makes it easier calculate it.

Minervini counts each contraction, it's depth and the length of the base to get a sense of the stock base's quality.

So for example if you're looking at TDG, it has three contractions since the beginning of a base:
tdgvcp

You are supposed to determine the % amount each contraction, where the quality is judged by each contraction getting smaller over time until it is < 10%. If it breaks out of this last contraction, it is theoretically buyable and you can place your stop at the bottom of the last contraction for a low-risk trade.

It becomes a pain however to pull out your calculator and calculate this percent difference.
So I created some code in my chart to do this all for me. The only thing I have to do is draw each trendline, and then give each one a study ID of "T1", "T2", "T3", and so forth.

T1, for example, just double-click the trendline and enter in the ID:
study

The rest is automatic. Again taking the TDG example:
tdgvcp2

Now you can see the VCP charateristics in the chart title:
7W 9/8/4 3T

This means the length from first to last contraction is 7 weeks long, the first contraction was 9%, then 8%, then 4% and there was a total of 3 contractions.

And there you have it.

Here's the code you put into the chart formula. I place it after the plotting of the bars:

// 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 <=5; 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";

Enjoy,
Mike

9 Likes

Thanks. Was actually just thinking about seeing if I could find code or build it for VCP this weekend.

1 Like

Excellent rocketPower! So let me make sure I understand. So we could, if we have the Minervini's Trend Identification Template coded for exploration, add this to it as well. And then run an exploration finding those stocks that meet the trend qualifications and then from that list, check the charts against the VCP criteria by drawing the trendlines, lable them with a study ID, and then the criteria they meet will be displayed on the chart. Is that about right?

Also, I just copied it and an error came up. What might cause this?

50%20PM

1 Like

@MCassICT, probably @rocketPower in his actually used formula has some extra code to define the Title variable elsewhere, before the code he posted.
You should probably do the same (inserting some code to plot a chart and add an appropriate title).
The formula, as posted, append some text to an alredy defined/existing title.

Anyway, just for the purpose of testing the code, the fix is easy.
Just replace the += with a single = sign.

Title = "                        VCP Profile = " + NumToStr(DateTimeDiff(VCPEnd, VCPStart)/3600/24/7, 2.0, False) + "W " + StrTrim(Pct, "/") + "  " + NumToStr(T, 1.0, False) + "T";

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;
Buy = false;
Buy[BarCount-2] = true;
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

4 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 :slight_smile: 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

Glad I helped!!! :smile:

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 :grin: )

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 :slight_smile:

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