Abnormal PlotShapes behavior when clicking on chart at different locations

I am quite new at AFL programming.
I wrote this code to identify and plot shapes for the Tom Demark TD Points and TD Double TD Points.
The code runs without bugs.
The TD Points display without any problems. However the TD Double TD Points PlotShapes work also.
But when I click at different locations on the chart the Shapes used for TD Double TD Points appear out of nowhere.
The full code is presented here and is quite well documented within the script.
I would appreciate any help with this weird behavior:

Pierre

SECTION_BEGIN("Price");
GraphXSpace = 25;
	//SetChartOptions(0,chartShowArrows|chartShowDates);
	_N(Title = StrFormat("{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) ));
	//Plot( C, "Close", ParamColor("Color", colorDefault ), styleNoTitle | ParamStyle("Style") | GetPriceStyle() ); 
	Plot(C, "", IIf(O>=C, colorRed, colorGreen), styleBar, Null, Null, 0, 0, 3);
_SECTION_END();

_SECTION_BEGIN ("TD Double TD Points");
// As per Tome Demark book 

pointLevel = Param("Pivot Point Level......", 1, 1,5,1);
showPoints = ParamToggle("Show TD Points............","No|Yes", 0);
showDblPoints = ParamToggle("Show Double TD Points.....", "No|Yes", 0);

//****************************************BUILD Arrays******************************************

//******************************* True Lows and True Highs**************************************
// a true High is the high of the current day or the previous day's Close if higher
// a true Low is the Low of the current day or the previous day's Close if lower

tL = IIf(Ref(C,-1) < L, Ref(C,-1), L);
//tL[0] = L[0];

tH = IIf(Ref(C, -1) > H, Ref(C, -1), H);
//tH[0] = H[0];

//************************ create TD Points arrays - level as per Parameter **********************

//********************************** TD Demand Points
switch (pointLevel)
{
	case 1:
		TDdPoints = IIf((tL < Ref(tL, -1) AND tL < Ref(tL, 1)), 1, 0);
	break;
	
	case 2:
		TDdPoints = IIf((tL < Ref(tL, -1) AND tL < Ref(tL, -2) AND tL < Ref(tL, 1) AND tL < Ref(tL, 2)), 1, 0);
	break;
	
	case 3:
		TDdPoints = IIf((tL < Ref(tL, -1) AND tL < Ref(L, -2) AND tL < Ref(tL, -3) AND tL < Ref(tL, 1) AND tL < Ref(tL, 2) AND tL < Ref(tL, 3)), 1, 0);
	break;
		
	case 4:
		TDdPoints = IIf((tL < Ref(tL, -1) AND tL < Ref(tL, -2) AND tL < Ref(tL, -3) AND tL < Ref(tL,-4) AND tL < Ref(tL, 1) AND tL < Ref(tL, 2) AND tL < Ref(tL, 3) AND tL < Ref(tL, 4)), 1, 0);
	break;
	
	case 5:
		TDdPoints = IIf((tL < Ref(tL,-1) AND tL<Ref(tL,-2) AND tL<Ref(tL,-3) AND tL<Ref(tL,-4) AND tL<Ref(tL,-5) AND tL<Ref(tL,1) AND tL<Ref(tL,2) AND tL<Ref(tL,3) AND tL<Ref(tL,4) AND tL<Ref(tL,5)), 1, 0);
	break;
	
	default:
		TDdPoints = IIf((tL < Ref(tL, -1) AND tL < Ref(tL, 1)),1,0);
	break;
}
//TDdPoints[0] = 0;

//********************************* TD Supply Points	
switch (pointLevel)
{
	case 1:
		TDsPoints = IIf((tH > Ref(tH, -1) AND tH > Ref(tH, 1)), 1, 0);
	break;
	
	case 2:
		TDsPoints = IIf((tH > Ref(tH, -1) AND tH > Ref(tH, -2) AND tH > Ref(tH, 1) AND tH > Ref(tH, 2)), 1, 0);
	break;
	
	case 3:
		TDsPoints = IIf((tH > Ref(tH, -1) AND tH > Ref(tH, -2) AND tH > Ref(tH, -3) AND tH > Ref(tH,1) AND tH > Ref(tH, 2) AND tH > Ref(tH, 3)), 1, 0);
	break;
	
	case 4:
		TDsPoints = IIf((tH > Ref(tH, -1) AND tH > Ref(tH, -2) AND tH > Ref(tH, -3) AND tH > Ref(tH, -4) AND tH > Ref(tH, 1) AND tH > Ref(tH, 2) AND tH > Ref(tH, 3) AND tH > Ref(tH, 4)), 1, 0);
	break;
	
	case 5:
		TDsPoints = IIf((tH > Ref(tH, -1) AND tH > Ref(tH, -2) AND tH > Ref(tH, -3) AND tH > Ref(tH,-4) AND tH > Ref(tH, -5) AND tH > Ref(tH, 1) AND tH > Ref(tH, 2) AND tH > Ref(tH, 3) AND tH > Ref(tH, 4) AND tH > Ref(tH, 5)), 1, 0);
	break;
	
	default:
		TDsPoints = IIf((tH > Ref(tH, -1) AND tH > Ref(tH, 1)), 1, 0);
	break;
}
//TDsPoints[0] = 0;

//***************************** TD Double TD Points ******************************************
/*
If a level 1 TD Point low is defined, 
and subsequently a second level 1 TD Point low is formed at a higher price level, 
then this secondary test of the original inal low generally confirms that the price trend should be up. 
On the other hand, if a level 1 TD Point high is defined, 
and subsequently a second level I TD Point high is formed at a lower price level, 
then this secondary test of the original high usually confirms that the trend should be down.

Research indicates that subsequent to the formation of the second TD Point low, 
once the high of the second TD Point low day is exceeded upside, 
then the trend should be up. 
Conversely, subsequent to the formation of the second TD Point high, 
once the low of the second TD Point high day is exceeded downside, 
then the trend should be down.

page 230-231

Thomas R. DeMark. New Market Timing Techniques: Innovative Studies in Market Rhythm & Price Exhaustion (p. 230). Kindle Edition. 
*/

//******************************** will use True Highs and True Lows to identify TD Points *************************************
firstSpoint = True;
firstDpoint = True;

for (i =  BarCount - 1; i > 0; i--)
{
	if (TDsPoints[i]) 
	{
		if (firstSPoint)
		{
			firstSPoint = False;
		} else if (tH[i] > tH[previousSTDpoint] AND tL[previousSTDpoint] > tL[previousSTDpoint+1])
		{
			TDdTDsPoints[i] = True;
			TDdTDsPoints[previousSTDpoint] = True; 
		} else
		{
			TDdTDsPoints[i] = False;
		}
		previousSTDPoint = i;
	}
}

for (i = BarCount -1; i > 0; i--)
{
	if (TDdPoints[i])
	{
		if (firstDPoint)
		{
			firstDpoint = False;
		}else if(tL[i] < tL[previousDTDPoint] AND tH[previousDTDPoint] < tH[previousDTDPoint+1])
		{
			TDdTDdPoints[i] = True;
			TDdTDdPoints[previousDTDPoint] = True;
		} else
		{
			TDdTDdPoints[i]= False;
		}
		previousDTDPoint = i;
	}
}

//****************************** Show all TD Points **********************************

if (showPoints) 
{
	PlotShapes(IIf(TDsPoints, shapeSmallCircle, shapeNone),colorGreen, 0, H, 20, 0);
	PlotShapes(IIf(TDdPoints, shapesmallCircle, shapeNone), colorRed, 0, L, -20, 0);
}

if (showDblPoints)
{
	PlotShapes(IIf(TDdTDsPoints, shapeHollowCircle, shapeNone),colorGreen, 0, H, 20, 0);
	PlotShapes(IIf(TDdTDdPoints, shapeHollowCircle, shapeNone), colorRed, 0, L, -20, 0);
}

_SECTION_END();

Your code uses loops but is unaware of QuickAFL. Please read the Knowledge Base to ensure your loops work fine in when not all bars are visible

Last but not least:

Your user account isn't marked with License verified badge. Did you purchase AmiBroker license?

Only users with License verified badge are allowed to post on this forum.

So before posting, make sure you verify your license as explained here.

Great. Read the QuickAFL facts and I understand now.
Used the SetBarsRequired(sbrAll, sbrAll) directive and it works fine.
Will ensure my loops behave accordingly.
Thank you Tomasz..

1 Like

Yes, I am a fully licensed user. I will follow the link to "verify your license"

Generally, it is better to adjust code so it does NOT require all bars. For start, formulas should not use future data, so you should consider using

SetBarsRequired( sbrAll, 0 ); // DON'T request future data

If your code does not work without seeing future data, it means it looks into the future. Codes like that may produce fantastic (but unrealistic) results and misguide you into thinking you found holy grail.

Also QuickAFL is for a reason. By turning it off you are decreasing performance significantly, potentially by (several) orders of magnitude.

I will follow your recommendations and modify the logic accordingly. I will post the results when I am done.

@camo I thought it might be of help to point out that

TDsPoints = IIf((tH > Ref(tH, -1) AND tH > Ref(tH, 1)), 1, 0);

can be written as

TDsPoints = tH > Ref(tH, -1) AND tH > Ref(tH, 1);

Try a few samples and debug this code to confirm but I am pretty sure you don't require the Immediate If (IIf) as both methods will return a True(1) or False(0) appropriately.

I look forward to your work as personally I always found it difficult to read DeMark's writing (but the Jason Perl book was a bit more explanatory). I think in recent years DeMark may have created a web site that clarifies what he published back in the 1990's while expanding his arsenal of proprietary indicators.

1 Like

Thank you @portfoliobuilder, this is good....
The quicker I learn, the better....

1 Like

Actually it is much much simpler.

You can remove the entire switch statements and replace them by these few lines.

pointLevel = 3;

tL = Min(Ref(C,-1), L);
tH = Max(Ref(C,-1), H);

ll = LLV(tL,pointLevel);
hh = HHV(tH,pointLevel);

TDdPoints = tL < Ref(ll,-1) AND tL < Ref(ll,pointLevel);
TDsPoints = tH > Ref(hh,-1) AND tH > Ref(hh,pointLevel);

Two lines for TD points vs. "quadrillions" of switch lines.


Also these ones

tL = IIf(Ref(C,-1) < L, Ref(C,-1), L);
tH = IIf(Ref(C, -1) > H, Ref(C, -1), H);

are just Min Max.

tL = Min(Ref(C,-1), L);
tH = Max(Ref(C,-1), H);

BTW, this one

appears to be typo.

Did you mean AND tL < Ref(tL, -2) for case 3 of first switch?

2 Likes

Yes it is a typo. I did intend to compare with true Lows (tL).
By the way, I have no words to express how appreciative I am for your assistance.
I am doing the best I can to search and learn, and your help is a God send.

Thank you @fxshrat and @portfoliobuilder. Of course Tomasz for his superb watchful eyes on all of us.
I am working on redoing this indicator of low entry risk identification and will post the final results if any are interested.
I am also working on a full programming of Tom Demarks TD Trendlines with Qualifiers and Cancellations with price objectives.
I will post it also once I tune it with the advice you are giving me.
Thanks again.

After a full set of days of AFL learning and programming, I have completed and debugged the Tom DeMark "TD Trendlines" indicator.
It implements the qualification of trendline breaks for both supply and demand.
It calculates 3 different price objectives for both long and short trades.
I have added some risk management calculations for me to start paper trading using IB plugin and real time quotes.

I am sure many sections could be much improved. If any of you have interest in Tom DeMark's methodologies you are welcome to use it.
@portfoliobuilder mentioned some interest in TD so here goes.
It includes lots of documentation as to the TD rules.

_SECTION_BEGIN ("TD Trendlines-true TD");

GraphXSpace = 20;
//SetChartOptions(0,chartShowArrows|chartShowDates);

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

Plot(C, "", IIf(O>=C, colorRed, colorGreen), styleBar, Null, Null, 0, 0, 3);

pointLevel =              Param("Pivot Point Level.................", 1, 1,10,1,0);
pointAddCondition = ParamToggle("Additional TD Point Condition.....", "No|Yes", 0);
showPoints =        ParamToggle("Show TD Points....................","No|Yes", 1);
showDblPoints =     ParamToggle("Show Double TD Points.............", "No|Yes", 0);
C1cancel =          ParamToggle("Use break Cancellation C1.........", "No|Yes", 1);
C2cancel =          ParamToggle("Use break Cancellation C2.........", "No|Yes", 1);
C3cancel =          ParamToggle("Use break Cancellation C3.........", "No|Yes", 0);
minPercent =              Param("Minimum Percent gain to enter.....", 3, 1, 10, .1, .1);
lPriceObjRed =            Param("Long Price Objective % reduction..", 0, 0, 25, 1,1);
sPriceObjRed =            Param("Short Price Objective % reduction.", 0, 0, 25, 1,1);
//previousSTrendline =          Param("Use Previous Supply Trendline.....", 0, 0, 2, 1, 1);
//previousDTrendline =          Param("Use Previous Demand Trendline.......", 0, 0, 2, 1, 1);
showDisqualified =  ParamToggle("Show Disqualified Breaks..........", "No|Yes", 1);
showQualified =     ParamToggle("Show Qualified Breaks.............", "No|Yes", 1);
capital = 				Param  ("Working Capital...................", 2000, 1000, 5000, 100, 100);
riskPercent = 			Param  ("Percentage of capital to risk.....", .01, .01, .02, .001, .001);

SetBarsRequired(sbrAll, 0);

//****************************************variables declaration*****************************************
firstSupply = secondSupply = Null;
twoSupplyPointsFound = twoDemandPointsFound = 0;
firstDemand = secondDemand = Null;
supplyLine = demandLine = Null;
loopLimit = Null;
TDsPoints = Null;
sLineCancelled = dLineCancelled = Null;
sBreakCancelled = dBreakCancelled = Null;
//sLineQualified = Null;
//dLineQualified = Null;
shapePosAdj = 8;
textPosAdj = 18;
sStartPos  = 23;
dStartPos  = -32;
prev1SPos  = sStartPos;
currSPos   = sStartPos;
currDPos   = dStartPos;
prev1DPos  = dStartPos;
sLineROA   = dLineROA = Null;

sendSAlert = sendDAlert = False;
disqualifiedSBreak = disqualifiedDBreak = False;
sx0 = sx1 = sy0 = sy1 = dx0 = dx1 = dy0 = dy1 = Null;
supplyBreakOutBar = demandBreakOutBar = Null;

slevel = dLevel = False;
sBreak = dBreak = Null;
sQ1 = sQ2 = sQ3 = dQ1 = dQ2 = dQ3 = sC1 = sC2 = sC3 = dC1 = dC2 = dC3 = Null;
lastBar = Barcount - 1;

supplyPriceObj1Value = Null;
supplyPriceObj2Value = Null;

demandPriceObj1Value = Null;
demandPriceObj2Value = Null;

//*************************************Functions Declarations******************************************

//****************************************** TD Price Projectors **********************************************
//TD Price Projector 1, 
//  the least precise and the easiest to calculate, is as follows: 
//  when price advances above a declining TD Line, 
//  usually price continues to advance to at least a price level 
//  equivalent to the distance tance between the lowest price value beneath the TD Line 
//  and the TD Line value directly above it, 
//  added to the TD Line value on the day of the breakout to the upside
//  What may sound complex when described in words is very simple when viewed on the chart (see Figure 1.16).
//
//Thomas R. DeMark. The New Science of Technical Analysis (Kindle Locations 244-247). Kindle Edition. 

function lowestObjective (obj1, obj2, obj3)
{
	if (obj1 < obj2 AND NOT AlmostEqual(obj1, obj2, 5))
	{
		lowestObj = obj1;
	} else if (obj2 < obj3 AND NOT AlmostEqual(obj2, obj3, 5))
	{
		lowestObj = obj2;
	} else{
		lowestObj = obj3;
	}
	return lowestObj;
}
function highestObjective (obj1, obj2, obj3)
{
	if (obj1 > obj2 AND NOT AlmostEqual(obj1, obj2, 5))
	{
		highestObj = obj1;
	} else if (obj2 > obj3 AND NOT AlmostEqual(obj2, obj3, 5))
	{
		highestObj = obj2;
	} else{
		highestObj = obj3;
	}
	return highestObj;
}

function unitsToBuy ( entryPrice, risk)
{
	amountToRisk = capital * riskPercent;
	units = floor(amountToRisk / risk);
	capitalUnits = floor(capital / 5 / entryPrice);
	if (units < capitalUnits)
	{
		maxUnits = units;
	} else 	maxUnits = capitalUnits;
	return maxUnits;
}

function breakSPriceObj3 (supplyline, trueLow, barID)
{
	obj = supplyline[barID-1] - truelow[barID-1];
	nextBarLineValue = supplyline[barID] - (supplyline[barID-1] - supplyline[barID]);
	priceObj = supplyline[barID] + (obj * (1 - (lPriceObjRed/100)));
	return priceObj;
}

function breakDPriceObj3 (demandline, trueHigh, barID)
{
	obj = trueHigh[barID-1] - demandline[barID-1];
	nextBarLineValue = demandline[barID] + (demandline[barID] - demandLine[barID-1]);
	priceObj = demandline[barID] - (obj * (1 - (sPriceObjRed/100)));
	return priceObj;
}

function LineCrossBar(dLine, sLine)
{
	notFound = 1;
	crossBar = Null;
	TDCrossBar = Cross(dLine, sLine);
	for (i=BarCount - 1; i > 0 AND notFound; i--)
	{
		if (TDCrossBar[i] == 1)
		{
			crossBar = i;
			notFound = 0;
			break;
		}
	}
	shortLoop = IIf(IsNull(crossBar), BarCount - 1, crossBar);
	return shortLoop;
}

function trendLineROA(tdLine, firstPoint, secondPoint)  
{
	//determine number of bars between points
	//substract linevalue between second and first point
	//divide change by number of bars
	// return ROC
	// negative values for supply line
	// positive values for demand line
	
	lineROA =  (tdLine[secondPoint] - tdLine[firstPoint] ) / (firstPoint - secondPoint);
	return lineROA;
}

//****************************************BUILD Arrays******************************************

//******************************* True Lows and True Highs**************************************

// a true High is the high of the current day or the previous day's Close if higher
// a true Low is the Low of the current day or the previous day's Close if lower

tL = Min(Ref(C,-1), L);
tH = Max(Ref(C,-1), H);

//********************* TD Points arrays (supply and demand) - level as per Parameter ***********

//ll = LLV(tL,pointLevel);		// if tL == L  no TD point is defined
//hh = HHV(tH,pointLevel);		// if tH == H  no TD point is defined
// when I become fully familiar in recognizing the Qualifies and Cancellations
// I will reinstate the true Highs(tH) and true Lows (tL)

ll = LLV(L,pointLevel);
hh = HHV(H,pointLevel);

// Consider adding an additional qualifier to identify TD Points:
//   2. Not only must the highs the day before and the day after be less than the highest high-the high in between
//   but the pivot high must also be greater than the close two days before the high.
//
// Thomas R. DeMark. The New Science of Technical Analysis (Kindle Locations 200-201). Kindle Edition. Chapter 1 Figure 1.7
//
//      pivot High > Ref(C, -2) with Parameter to turn On/Off
//      pivot Low > Ref(C, -2)

if (pointAddCondition AND pointLevel == 1) // see paramToggle
{
	TDdPoints = L < Ref(ll,-1) AND L < Ref(ll,pointLevel) AND L < Ref(C, -2);
	TDsPoints = H > Ref(hh,-1) AND H > Ref(hh,pointLevel) AND H > Ref(C, -2);
} else
{
	TDdPoints = L < Ref(ll,-1) AND L < Ref(ll,pointLevel);
	TDsPoints = H > Ref(hh,-1) AND H > Ref(hh,pointLevel);
}
	

//*********************************** TD Double TD Points ******************************************
/*
If a level 1 TD Point low is defined, 
and subsequently a second level 1 TD Point low is formed at a higher price level, 
then this secondary test of the original low generally confirms that the price trend should be up. 
On the other hand, if a level 1 TD Point high is defined, 
and subsequently a second level 1 TD Point high is formed at a lower price level, 
then this secondary test of the original high usually confirms that the trend should be down.

Research indicates that subsequent to the formation of the second TD Point low, 
once the high of the second TD Point low day is exceeded upside, 
then the trend should be up. 
Conversely, subsequent to the formation of the second TD Point high, 
once the low of the second TD Point high day is exceeded downside, 
then the trend should be down.

page 230-231
Thomas R. DeMark. New Market Timing Techniques: Innovative Studies in Market Rhythm & Price Exhaustion (p. 230). Kindle Edition. 
*/
//******************************** will use True Highs and True Lows to identify TD Double TD Points *************************************

firstSpoint = firstDPoint = True;
subsequentSPoint = subsequentDPoint = True;
TDdTDsPoints = TDdTDdPoints = Null;

for (i =  0; i < BarCount-1; i++)
{
	if (TDsPoints[i]) 
	{
		if (firstSPoint)
		{
			firstSPoint = False;
			previousSTDPoint = i;
		}
		else if (tH[i] < tH[previousSTDpoint])
		{
			firstSPoint = False;
			TDdTDsPoints[i] = True;
			TDdTDsPoints[previousSTDPoint] = True;
			previousSTDPoint = i;
		} else
		{
			TDdTDsPoints[i] = False;
			previousSTDPoint = i;
			//firstSPoint = True;
			subsequentSPoint = True;
		}
	} 
}

for (i = 0; i < BarCount-1; i++)
{
	if (TDdPoints[i])
	{
		if (firstDPoint)
		{
			firstDPoint = False;
			previousDTDPoint = i;
		}else if(tL[i] > tL[previousDTDPoint])
		{
			firstDPoint = False;
			TDdTDdPoints[i] = True;
			TDdTDsdPoints[previousDTDPoint] = True;
			previousDTDPoint = i;
		} else
		{
			TDdTDdPoints[i] = False;
			previousDTDPoint = i;
			//firstSPoint = True;
			subsequentDPoint = True;
		}
	}
}

//*********************************** Show all TD Points ***************************************
if (showPoints) 
{
	PlotShapes(IIf(TDsPoints, shapeSmallCircle, shapeNone),colorRed, 0, H, shapePosAdj, 0);
	PlotShapes(IIf(TDdPoints, shapesmallCircle, shapeNone), colorGreen, 0, L, -shapePosAdj, 0);
}
if (showDblPoints)
{
	PlotShapes(IIf(TDdTDsPoints, shapeHollowCircle, shapeNone),colorRed, 0, H, shapePosAdj, 0);
	PlotShapes(IIf(TDdTDdPoints, shapeHollowCircle, shapeNone), colorGreen, 0, L, -shapePosAdj, 0);
}

//***************************** Supply and Demand Lines ****************************************

// For now only the last supply and demand lines are drawn as per level parameter
// TD Trendlines are drawn from Right to Left as per TD Demark
// Loop from right to left

for (i = BarCount-1; twoSupplyPointsFound < 2 AND i != 0; i--)
{
	if (twoSupplyPointsFound == 0 AND TDsPoints[i])
	{
		firstSupply = i;
		sx0 = firstSupply;
		sy0 = H[firstSupply];
		twoSupplyPointsFound++;
	} else if (twoSupplyPointsFound ==  1 AND TDsPoints[i] AND H[i] > H[firstSupply])
	{
		secondSupply = i;
		sx1 = secondSupply;
		sy1 = H[secondSupply];
		twoSupplyPointsFound++;
	}
}

for (i = BarCount-1; twoDemandPointsFound < 2 AND i != 0; i--)
{	
	if (twoDemandPointsFound == 0 AND TDdPoints[i])
	{
		firstDemand = i;
		dx0 = firstDemand;
		dy0 = L[firstDemand];
		twoDemandPointsFound++;
	} else if (twoDemandPointsFound == 1 AND TDdPoints[i] AND (L[i] < L[firstDemand]))
	{
		secondDemand = i;
		dx1 = secondDemand;
		dy1 = L[secondDemand];
		twoDemandPointsFound++;
	} 
}

foundTwoSPoints = NOT IsNull(secondSupply) AND NOT IsNull(firstSupply);
foundTwoDpoints = (NOT IsNull(secondDemand)) AND (NOT IsNull(firstDemand));

if (foundTwoSPoints)
{
	supplyLine = LineArray(sx1, sy1, sx0, sy0, 1, 0);
	sLineROA = trendLineROA(supplyLine, firstSupply, secondSupply);
} else
{
	printf("No second supply point found for level %g-No supply trendline\n", pointLevel);
}

if (foundTwoDpoints)
{
	demandLine = LineArray(dx1, dy1, dx0, dy0, 1, 0);
	dLineROA = trendLineROA(demandLine, firstDemand, secondDemand);
} else
{
	printf("No second demand point found for level %g- No Demand Line\n", pointLevel);
}

// sQ2 *********************
// if the opening price is above the TD Line, 
// then the dynamics of the marketplace have more than likely shifted so dramatically in favor of buyers 
// and the upside since the previous day's close that the breakout is legitimized. 
// However; two additional conditions, not included in my previous book, must also be present to confirm Qualifier 2 
//		1.- First of all, the opening price must not only be above the TD Supply Line 
//			but also above the previous day's close since it is possible, if the TD Supply Line is very steep, 
//			that price can open above the line but below the previous day's close. 
// 		2.- Second, price must follow through upside by at least one or two price ticks above the opening price level 
//			since the opening could be a last gasp of demand in a price vacuum caused by the specialists or market makers, 
//			and this exhaustion would not present a buying opportunity; rather, it would coincide with a price peak.
//
// Thomas R. DeMark. New Market Timing Techniques: Innovative Studies in Market Rhythm & Price Exhaustion (pp. 167-168). Kindle Edition. 
//
// sQ2 is valid if:
//		O > trendline AND
//		O > ref(C, -1)
//		Follow through on Open by a few ticks to qualify (H > O)
//		- supply trendline if qualified
//		intraday entry is justified
//		determine price objective and draw price objective line
//
// if price opens below supply trendline sQ1 needs to be validated
// sQ1 is valid if:
// 		The close on the trading day before an upside breakout must be a down close
//
// or sQ3 is valid if:
// 		Even if the close the previous trading day is up 
//		and the current day's opening price is below the TD Supply Line 
//		or fails to follow through after the open, 
//		if the current day's high is able to surpass a measure of the previous day's demand 
//		and then exceed the TD Supply Line, this demand should be sufficient to justify an intraday upside breakout, 
//		which will be confirmed for a conventional chartist by a closing price above the TD Supply Line. 
//		Calculate the previous day's expression of demand or buying 
//		by subtracting the difference between the previous day's closing price and that same day's true low 
//		(that same day's low or the previous trading day's close, whichever is less). 
//		Then add that value to the previous day's close to identify the level at which the buying pressure expressed that trading day 
//		will be replicated the current trading day. 
//		If this value is beneath the TD Supply Line and subsequent to price exceeding this measurement of demand 
//		and then exceeding the TD Supply Line, there is sufficient buying to justify intraday entry on the upside breakout. 
//		However, if the TD Supply Line breakout upside occurs prior to the measure of demand 
//		exceeding the previous day's level, Qualifier 3 is not fulfilled.
//
// Thomas R. DeMark. New Market Timing Techniques: Innovative Studies in Market Rhythm & Price Exhaustion (p. 168). Kindle Edition. 
//
// 4. A potential new Qualifier that I am currently researching for inclusion to the Qualifier set 
//    relates the true price range the day before a breakout to the breakout level. 
//    Specifically, if that price range is doubled 
//    and the breakout level added to the previous trading day's close for an upside breakout 
//    (subtracted from the previous trading day's close for a downside breakout) 
//    exceeds these price levels, then the breakouts are disqualified. 
//    Otherwise they are qualified. 
//    Note that this is preliminary work, however.
//
// Thomas R. DeMark. New Market Timing Techniques: Innovative Studies in Market Rhythm & Price Exhaustion (p. 168). Kindle Edition. 

// build arrays for qualification of bars within the supply and demand lines 
// for Q1 and Q3 and C1, C2, C3 and C4

lowestSLowValue = 99999;
lowestSCloseValue = 99999;

if (foundTwoSPoints)		// if a supply line exists
{
	for (i = BarCount-1; i > secondSupply; i--) 	// find Lowest Low and lowest Close for upside price projections
	{
		if (L[i-1] < L[i] AND L[i-1] < lowestSLowValue AND L[i-1] < supplyLine[i-1])
		{
			lowestSLowBar = i - 1;
			lowestSLowValue = L[i - 1];
			supplyPriceObj1Value = supplyLine[i - 1] - L[i - 1];
		} else 
		{
			lowestSLowBar = i;
			lowestSLowValue = L[i];
			supplyPriceObj1Value = supplyLine[i] - L[i];
		}
		
		if (C[i-1] < C[i] AND C[i-1] < lowestSCloseValue AND C[i-1] < supplyLine[i-1])
		{
			lowestSCloseBar = i-1;
			lowestSCloseValue = C[i-1];
			supplyPriceObj2Value = supplyLine[i-1] - L[i-1];
		}else
		{
			lowestSCloseBar = i;
			lowestSCloseValue = C[i];
			supplyPriceObj2Value = supplyLine[i] - L[i];
		}
	}

	for (i = BarCount - 1; i > firstSupply + pointLevel; i--)
	{
		sBreak[i] = IIf(tH[i] > supplyLine[i] AND NOT AlmostEqual(tH[i], supplyLine[i], 4), True, False);	// build supply break array
		
		sQ2[i] = O[i] > supplyLine[i] AND H[i] > O[i];		// does bar i qualify for a sQ2 qualified break?
		sendSAlert = IIf(sQ2[i] AND i == lastBar, True, False);
		
		sQ1[i-1] = C[i-1] < C[i-2];							// now check for supply line Q1
				
		sDiff = C[i-1] - tL[i-1];							// now check for supply line Q3
		prevSDemandLevel = C[i-1] + sDiff;					// yesterdays demand:
		sQ3[i-1] = prevSDemandLevel < supplyLine[i];		// if previousDemandLevel < today's supplyLine
		
		// Now does today's bar cancel yesterday's possible supply line Break
		sC1[i] = iif (C1cancel AND O[i] < supplyLine[i], True, False);
		sC2[i] = IIf (C2Cancel AND O[i] < C[i-1] AND C[i] < supplyLine[i-1], True, False);
		sC3[i] = IIf (C3Cancel AND H[i] < tH[i -1], True, False );
		// sC4 = see New Market timing Techniques pp 168	
	}
}
highestDHighValue = -99999;
highestDCloseValue = -99999;

if (foundTwoDPoints)   // if a demand line exists
{
	for (i = BarCount - 1; i >= secondDemand; i--)		// find highest High for downside price projections
	{
		if (H[i-1] > H[i] AND H[i-1] > highestDHighValue AND H[i-1] > demandLine[i-1])
		{
			highestDHighBar = i-1;
			highestDHighValue = L[i-1];
			demandPriceObj1Value = H[i-1] - demandLine[i-1];
		} else
		{
			highestDHighBar = i;
			highestDHighValue = L[i];
			demandPriceObj1Value = H[i] - demandLine[i];
		}
		
		if (C[i-1] > C[i] AND C[i-1] > highestDCloseValue AND C[i-1] > demandLine[i-1])
		{
			highestDCloseBar = i-1;
			highestDCloseValue = C[i-1];
			demandPriceObj2Value = H[i-1] - demandLine[i-1];
		}
	}
	
	for (i = BarCount - 1; i > firstDemand + pointLevel; i--)		// identify Qualifier and Cancellation bars
	{			
	// build demand break array
		dBreak[i] = IIf(tL[i] < demandLine[i] AND NOT AlmostEqual(tL[i], demandLine[i], 5), True, False);
		
		dQ2[i] = O[i] < demandLine[i] AND NOT AlmostEqual(O[i], demandLine[i], 5) AND L[i] < O[i];								// initialize dQ2 as array of nulls
		sendDAlert = IIf(dQ2[i] AND i == lastBar, True, False);
			
		dQ1[i-1] = (C[i-1] > C[i-2]);						// now check for demand line break Q1
		
		dDiff = tH[i-1] - C[i-1] ;						// now check for demand line break Q3
		prevDdemandLevel = C[i-1] + dDiff;				// determine yesterdays demand level
		dQ3[i-1] = prevDdemandLevel > demandLine[i];		// if previous Demand Level > today's demand line
		
		// now does today's bar cancel yesterdays possible demand line Break?
		dC1[i] = IIf (C1cancel AND O[i] > demandLine[i], True, False);
		dC2[i] = IIf (C2cancel AND O[i] > C[i-1] AND C[i] > demandLine[i-1], True, False);
		dC3[i] = IIf(C3Cancel AND L[i] < tL[i -1], True, False );
		//dC4 = see New Market timing Techniques pp 168
	}
}

// Identify valid breaks according to parameter selections for Cancellation

qualifiedSBreak = Ref(sBreak, 0) AND (Ref(sQ3, -1) OR Ref(sQ1, -1) OR Ref(sQ2, 0));
qualifiedDBreak = Ref(dBreak, 0) AND (Ref(dQ3, -1) OR Ref(dQ1, -1) OR Ref(dQ2, 0));
disqualifiedDBreak[0] = False;
disqualifiedSBreak[0] = False;

if (C1Cancel AND NOT C2Cancel AND NOT C3Cancel)
{
	disqualifiedSBreak = Ref(sBreak,0) AND (Ref(sQ3, -1) OR Ref(sQ1, -1) OR Ref(sQ2, 0))
						AND (Ref(sC1, 1));
} else if (C1Cancel AND C2Cancel AND NOT C3Cancel)
{
	disqualifiedSBreak = Ref(sBreak,0) AND (Ref(sQ3, -1) OR Ref(sQ1, -1) OR Ref(sQ2, 0))
					AND (Ref(sC1, 1) OR Ref(sC2, 1));				
} else if (C1Cancel AND C2Cancel AND C3Cancel)
{
	disqualifiedSBreak = Ref(sBreak, 0) AND (Ref(sQ3, -1) OR Ref(sQ1, -1) OR Ref(sQ2, 0))
					AND (Ref(sC1, 1) OR Ref(sC2, 1) OR Ref(sC3, 1));
} else if (NOT C1Cancel AND C2Cancel AND NOT C3Cancel)
{
	disqualifiedSBreak = Ref(sBreak, 0) AND (Ref(sQ3, -1) OR Ref(sQ1, -1) OR Ref(sQ2, 0))
					AND Ref(sC2, 1);
} else if (NOT C1Cancel AND C2Cancel AND C3Cancel)
{
	disqualifiedSBreak = Ref(sBreak, 0) AND (Ref(sQ3, -1) OR Ref(sQ1, -1) OR Ref(sQ2, 0))
					AND (Ref(sC2, 1) OR Ref(sC3, 1));
} else if (NOT C1Cancel AND NOT C2Cancel AND C3Cancel)
{
	disqualifiedSBreak = Ref(sBreak, 0) AND (Ref(sQ3, -1) OR Ref(sQ1, -1) OR Ref(sQ2, 0))
					AND Ref(sC3, 1);
}

if (C1Cancel AND NOT C2Cancel AND NOT C3Cancel)
{
	disqualifiedDBreak = Ref(dBreak,0) AND (Ref(dQ3, -1) OR Ref(dQ1, -1) OR Ref(dQ2, 0))
						AND (Ref(dC1, 1));
} else if (C1Cancel AND C2Cancel AND NOT C3Cancel)
{
	disqualifiedDBreak = Ref(dBreak,0) AND (Ref(dQ3, -1) OR Ref(dQ1, -1) OR Ref(dQ2, 0))
					AND (Ref(dC1, 1) OR Ref(dC2, 1));				
} else if (C1Cancel AND C2Cancel AND C3Cancel)
{
	disqualifiedDBreak = Ref(dBreak,0) AND (Ref(dQ3, -1) OR Ref(dQ1, -1) OR Ref(dQ2, 0))
					AND (Ref(dC1, 1) OR Ref(dC2, 1) OR Ref(dC3, 1));
} else if (NOT C1Cancel AND C2Cancel AND NOT C3Cancel)
{
	disqualifiedDBreak = Ref(dBreak,0) AND (Ref(dQ3, -1) OR Ref(dQ1, -1) OR Ref(dQ2, 0))
					AND Ref(dC2, 1);
} else if (NOT C1Cancel AND C2Cancel AND C3Cancel)
{
	disqualifiedDBreak = Ref(dBreak,0) AND (Ref(dQ3, -1) OR Ref(dQ1, -1) OR Ref(dQ2, 0))
					AND (Ref(dC2, 1) OR Ref(dC3, 1));
} else if (NOT C1Cancel AND NOT C2Cancel AND C3Cancel)
{
	disqualifiedDBreak = Ref(dBreak,0) AND (Ref(dQ3, -1) OR Ref(dQ1, -1) OR Ref(dQ2, 0))
					AND Ref(dC3, 1);
}
				
//******************************************* Plotting Section analysis has been done *******************

posAdjust = 2;			// used to properly align various Qualifiers and Cancellations
sLineType = 1;			// Qualified Trendlines with be lines and Disqualified Trendlines will be dotted lines
spobjDrawn = False;		// Supply price objective drawn
maxsPrice = Null;		// maximum entry price to expect minimum percentage gain or risk/reward ratio
Buy = False;
buyPrice = Null;

for (i = firstSupply + pointLevel + 1; i <= BarCount-1; i++)
{
	if (qualifiedSBreak[i])
	{
			if(sQ1[i-1])
			{
				PlotText("Q1", i-1.00, H[i-1], colorYellow, colorBlack, prev1SPos);
				prev1SPos += textPosAdj;
			}
			if(sQ2[i])
			{
				PlotText("Q2", i, H[i], colorYellow, colorBlack, currSPos);
				currSPos += textPosAdj;
			}
			if(sQ3[i-1])
			{
				PlotText("Q3", i-1.00, H[i-1], colorYellow, colorBlack, prev1SPos);
				prev1SPos += textPosAdj;
			}
			
/* ***********************************************************************************************************************
		
 TBD - If previous break is disqualified today, cancel price objectives and recalculate them if today's break is qualified
 
**************************************************************************************************************************/			
			if(disqualifiedSBreak[i-1])
			{
				if(sC1[i])
				{
					PlotText("C1", i, H[i], colorBlack, colorYellow, currSPos);
					currSPos -= textPosAdj;
				}
				if(sC2[i])
				{
					PlotText("C2", i, H[i], colorBlack, colorYellow, currSPos);
					currSPos -= textPosAdj;
				}
				if(sC3[i])
				{
					PlotText("C3", i, H[i], colorBlack, colorYellow, currSPos);
					currSPos -= textPosAdj;
				}
			}
			
			if (NOT spobjDrawn)
			{
				PlotText("B", i+.02, H[i], colorDarkGreen, colorWhite, 2);
				currSpos += textPosAdj;
				
			//Draw Price Objective1 lines
				ddt = Ref(DateTime(),i-1);
				strdt = DateTimeToStr(ddt[i-1], 1);
				brkPriceObj1 = supplyLine[i] + (supplyPriceObj1Value * ( 1.00 - (lPriceObjRed/100)));
				brkPriceObj2 = supplyLine[i] + (supplyPriceObj2Value * ( 1.00 - (lPriceObjRed/100)));
				brkPriceObj3 = breakSPriceObj3 (supplyLine, tL, i-1);		
				
				printf("(" + strdt +") intraday supply break, price obj 1:    $%.3f \n", brkPriceObj1);
				printf("(" + strdt +") intraday supply break, price obj 2:    $%.3f \n", brkPriceObj2);
				printf("(" + strdt +") intraday supply break, price obj 3:    $%.3f \n", brkPriceObj3);
				
				// When and how to enter trade as per my mgmt rules	
				safestObj = lowestObjective (brkPriceObj1, brkPriceObj2, brkPriceObj3);
				entryPrice = supplyLine[i];
				reward = safestObj - entryPrice;
				riskAmt = supplyLine[i] - demandLine[firstDemand];
				units2Buy = unitsToBuy(entryPrice, riskAmt);
				riskReward = reward / riskAmt;
				//Buy = riskReward > 1.25;
				buyPrice = entryPrice;
				SellPrice = safestObj;
				printf("Units to buy: %.0f  with Entry at $%.2f for a risk reward of %.2f\n", units2Buy, entryPrice, riskReward);
				
				GfxSetCoordsMode(1);
				GfxSetOverlayMode(0);
				GfxSelectPen(colorGreen, 1, 0);
								
				GfxMoveTo(i+0.1, brkPriceObj1);
				GfxLineTo(BarCount + 1, brkPriceObj1);
				pobjText = "a: " + NumToStr(brkPriceObj1, 1.3, True, False);	
				PlotTextSetFont(pobjText, "Times New Roman", 10, BarCount + 1.5, brkPriceObj1 * 1.000 , colorBlack, coloryellow, 0);
				
				if (NOT AlmostEqual(brkPriceObj2, brkPriceObj1,  5))
				{
					GfxMoveTo(i+0.1, brkPriceObj2);
					GfxLineTo(BarCount + 1, brkPriceObj2);
					pobjText = "b: " + NumToStr(brkPriceObj2, 1.3, True, False);	
					PlotTextSetFont(pobjText, "Times New Roman", 10, BarCount + 1.5, brkPriceObj2 * 1.000 , colorBlack, coloryellow, 0);
					posAdjust += posAdjust;
				}
				
				if (NOT AlmostEqual(brkPriceObj3, brkPriceObj2, 5))
				{
					GfxMoveTo(i+0.1, brkPriceObj3);
					GfxLineTo(BarCount + 1, brkPriceObj3);
					pobjText = "c: " + NumToStr(brkPriceObj3, 1.3, True, False);	
					PlotTextSetFont(pobjText, "Times New Roman", 8, BarCount + 1.5, brkPriceObj3 * 1.000 , colorBlack, colorYellow, 0);
				}
				spobjDrawn = True;		
			}
	} 
	currSPos = sStartPos;
	prev1SPos = currSPos;
}

dLineType = 1;
dpobjDrawn = False;
maxdPrice = Null;
dLineType = 1;
		
for (i = firstDemand + pointLevel + 1; i <= BarCount-1; i++)
{
	if (qualifiedDBreak[i])
	{
			if(dQ1[i-1])
			{
				PlotText("Q1", i-1.00, L[i-1], colorYellow, colorBlack, prev1DPos);
				prev1DPos -= textPosAdj;
			}
			if(dQ2[i])
			{
				PlotText("Q2", i, L[i], colorYellow, colorBlack, currDPos);
				currDPos -= textPosAdj;
			}
			if(dQ3[i-1])
			{
				PlotText("Q3", i-1.00, L[i-1], colorYellow, colorBlack, prev1DPos);
				prev1DPos -= textPosAdj;
			}
			
			if(disqualifiedDBreak[i-1])
			{
				if(dC1[i])
				{
					PlotText("C1", i, L[i], colorBlack, colorYellow, currDPos);
					currDPos -= textPosAdj;
				}
				if(dC2[i])
				{
					PlotText("C2", i, L[i], colorBlack, colorYellow, currDPos);
					currDPos -= textPosAdj;
				}
				if(dC3[i])
				{
					PlotText("C3", i, L[i], colorBlack, colorYellow, currDPos);
					currDPos -= textPosAdj;
				}
			}
			
			if (NOT dpobjDrawn)
			{

				PlotText("B", i+.02, L[i], colorDarkGreen, colorWhite, -15);
				currDpos -= textPosAdj;
				
			//plot price objectives
				ddt = Ref(DateTime(),i-1);
				strdt = DateTimeToStr(ddt[i-1], 1);
				brkPriceObj1 = demandLine[i] - (demandPriceObj1Value * ( 1 - (sPriceObjRed/100)));
				brkPriceObj2 = demandLine[i] - (demandPriceObj2Value * ( 1 - (sPriceObjRed/100)));
				brkPriceObj3 = breakDPriceObj3 (demandLine, tH, i-1);
				
				printf("(" + strdt +") intraday demand break, price obj 1:   $%.3f \n", brkPriceObj1);
				printf("(" + strdt +") intraday demand break, price obj 2:   $%.3f \n", brkPriceObj2);
				printf("(" + strdt +") intraday demand break, price obj 3:   $%.3f \n", brkPriceObj3);
				
			// When and how to enter trade as per my mgmt rules	
				safestObj = highestObjective (brkPriceObj1, brkPriceObj2, brkPriceObj3);
				entryPrice = demandLine[i];
				reward =  entryPrice - safestObj;
				riskAmt = demandLine[firstSupply] - demandLine[i];
				units2Buy = unitsToBuy(entryPrice, riskAmt);
				riskReward = reward / riskAmt;
				//Buy = riskReward > 1.25;
				buyPrice = entryPrice;
				sellPrice = safestObj;
				printf("Units to buy: %.0f  with Entry at $%.2f for a risk reward of %.2f\n", units2Buy, entryPrice, riskReward);
				

				GfxSetCoordsMode(1);
				GfxSetOverlayMode(0);
				GfxSelectPen(colorRed, 1, 0);
				
				GfxMoveTo(i+0.1, brkPriceObj1);
				GfxLineTo(BarCount + 1, brkPriceObj1);
				pobjText = "a: " + NumToStr(brkPriceObj1, 1.3, True, False);	
				PlotTextSetFont(pobjText, "Times New Roman", 8, BarCount + 1.5, brkPriceObj1 * 1.00 , colorBlack, coloryellow, 0);
				
				if (NOT AlmostEqual(brkPriceObj2, brkPriceObj1, 5))
				{
					GfxMoveTo(i+0.1, brkPriceObj2);
					GfxLineTo(BarCount + 1, brkPriceObj2);
					pobjText = "b: " + NumToStr(brkPriceObj2, 1.3, True, False);	
					PlotTextSetFont(pobjText, "Times New Roman", 10, BarCount + 1.5, brkPriceObj2 * 1.00 , colorBlack, coloryellow, 0);
				}
				if (NOT AlmostEqual(brkPriceObj3, brkPriceObj2, 5))
				{
					GfxMoveTo(i+0.1, brkPriceObj3);
					GfxLineTo(BarCount + 1, brkPriceObj3);
					pobjText = "c: " + NumToStr(brkPriceObj3, 1.3, True, False);	
					PlotTextSetFont(pobjText, "Times New Roman", 10, BarCount + 1.5, brkPriceObj3 * 1.00 , colorBlack, coloryellow, 0);
				}
				dpobjDrawn = True;	
			}
	}
	currDPos = dStartPos;
	prev1DPos = currDPos;
}

// send Alert
Buy = sendSAlert OR sendDAlert;  // a buy signal can also be derived from the disqualification (or cancellation) of a demand line break
Sell = False; // will implement later after I assess the precision of the Cancellation signals (sC1, sC2, sC3 and dC1, dC2, dC3)

AlertIf(Buy, "EMAIL", "Supply break Alert at " + NumToStr(buyPrice, 1.3, True)+" for: " +Name()+ "-"+FullName(), 1, 15, 1);
AlertIf(Sell, "EMAIL", "Yesterday's Supply break cancelled for: " + Name() + "-" + FullName(), 2, 15, 1); 

Plot (supplyLine, "Supply Line", colorRed, slineType, Null, Null, 0, 0, 2);
Plot (demandLine, "Demand Line", colorGreen, dLineType, Null, Null, 0, 0, 2);

_SECTION_END();
10 Likes

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.