#Sid6.7
#Point and Figure Chart Lower Study
#this is a lower study
declare lower;
#debug mode to expand all of the bar to see how the reversals are calculated and at what point
#1 = debug mode
#0 = regular mode
def debug = 0;
#box size: change this to determine the size of a block
def boxsize = atr()/2;
#rev size: change this to determine the reversal size. this is just blocksize * revsize = reversal total
def revsize = 3;
def revtotal = revsize * boxsize;
#output a label to show our boxsize and reversal size
AddLabel(1, "B:" + boxsize + " R:" + revtotal);
#variables, not sure all of these are even used?
def center = ((high - low) / 2);
def isred = open > close;
def lowvalue = if isred then close else open;
def highvalue = if isred then open else close;
def lastbar = HighestAll(if IsNaN(close) then 0 else BarNumber());
def cbar = BarNumber();
def lastbaros = -(lastbar) + cbar;
def islastbar = IsNaN(close[-1]) and !IsNan(close);
def isfirstbar = IsNaN(close[1]) and !IsNaN(close);
#plots a future blue line at this close to help see then distance of the current price
def isfuture = if IsNaN(close) then 1 else 0;
def futureprice = if isfuture and IsNaN(futureprice[1]) then close[1] else if isfuture then futureprice[1] else Double.NaN;
plot f = futureprice;
########
## do per candle direction lookup and store box value location
########
def trendchanged;
rec dir;
#the value of our box = box_price
def block_price = round(close/boxsize, 0) * boxsize;
#the start point of our current trend
def block_price_start = if isnan(trendchanged[1]) then block_price
else if trendchanged[1] then block_price
else block_price_start[1];
#the highest box size we acheived in this trend
def block_trend_highest = if isnan(trendchanged[1]) then block_price
else if trendchanged[1] then block_price
else if block_trend_highest[1] < block_price then block_price
else block_trend_highest[1];
#the lowest box size we acheived in this trend
def block_trend_lowest = if isnan(trendchanged[1]) then block_price
else if trendchanged[1] then block_price
else if block_trend_lowest[1] > block_price then block_price
else block_trend_lowest[1];
#the direction of our trendchanged (i got this from https://futures.io/thinkorswim/5129-point-figure-tos-thinkorswim-trading-platform.html)
dir = if islastbar then dir[1] * -1 else compoundValue(1, if dir[1] == 1 and block_price <= block_trend_highest-revtotal then -1 else if dir[1] != 1 and block_price >= block_trend_lowest+revtotal then 1 else dir[1],1);
#was our trendchanged? if so the next candle will update our trend block values
trendchanged = dir != dir[1];
#this isn't used but could be useful for debug
def pnf_bar_count = if isnan(pnf_bar_count[1]) then 0 else if trendchanged then pnf_bar_count[1] + 1 else pnf_bar_count[1];
#get the total from the last bar
def total = getvalue(pnf_bar_count,lastbaros);
#some magical stuff that starts from then left to right side candle reading
#which does some future lookups
#to condense the p and f chart into the bars we need
def barnum;
def nextbar = if barnumber() == 1 then 1 else if barnum[1] == 0 then lastbar else barnum[1]+1;
barnum = if nextbar >= lastbar then 0 else fold i = nextbar to lastbar with b = 0
while !isnan(getvalue(close,-i)) and b == 0
do if getvalue(trendchanged,-i) and b == 0 then i else b;
#debug plots
#if debug is enabled, we will mark where the trend changed, and the high/start/low points
AddChartBubble(trendchanged and debug, block_trend_lowest[1], dir[1]);
plot a1 = if trendchanged and debug then block_trend_highest[1] else double.nan;
a1.setdefaultColor(color.red);
a1.setpaintingStrategy(paintingStrategy.HORIZONTAL);
plot a2 = if trendchanged and debug then block_price_start[1] else double.nan;
a2.setdefaultColor(color.gray);
a2.setpaintingStrategy(paintingStrategy.HORIZONTAL);
plot a3 = if trendchanged and debug then block_trend_lowest[1] else double.nan;
a3.setdefaultColor(color.light_red);
a3.setpaintingStrategy(paintingStrategy.HORIZONTAL);
plot a4 = if debug then close else double.nan;
#after we do our magic above, we calculate how many plots we made, then we check the offset from the lastbar
#so we can move our chart from the leftside of the chart to the rightside of the chart
def charttotal = if barnum > 0 then (if isnan(charttotal[1]) then 0 else charttotal[1]) + 1 else charttotal[1];
#leftside and rightside here is technically, high and low, just for plotting our chart
def leftside = if debug == 1 then low else if barnum == 0 then double.nan else if getvalue(dir,-barnum) == 1 then getvalue(block_trend_lowest,-barnum) else getvalue(block_trend_highest,-barnum);
def rightside = if debug == 1 then high else if barnum == 0 then double.nan else if getvalue(dir,-barnum) == 1 then getvalue(block_trend_highest,-barnum) else getvalue(block_trend_lowest,-barnum);
#bar location we want to use, these are leftside bars, IE barnumber() == 1, 2, 3...to 'charttotal'
def barloc = if barnum == 0 and barnumber() + charttotal >= lastbar and barloc[1] == 0 then barnumber() else barloc[1];
#i got this from https://usethinkscript.com/threads/add-chart-as-a-lower-study.286/
#someone in then thinkscript trade chat lounge told me about the undocumented "addchart" method
DefineGlobalColor( "uptick", Color.UPTICK );
DefineGlobalColor( "downtick", Color.DOWNTICK );
#if we are debugging we will just output our high-low plots otherwise output pnf chart
#colors do not seem to work
def l = if debug then leftside else getvalue(leftside, barloc);
def r = if debug then rightside else getvalue(rightside, barloc);
AddChart( high = l,
close = l,
low = r,
open = r,
type = 2,
growColor = GlobalColor( "uptick" ),
fallColor = GlobalColor( "downtick" )
);