PuetzUWS [time, price] multiFractal mirrors, SPX 1872-2020
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Bill_Howell

//@version=5
indicator(title="PuetzUWS [time, price] multiFractal mirrors, SPX 1872-2020", shorttitle="PuetzUWS 1872-2020 fractal mirrors", overlay=true)



// 24************************24
// Table of Contents : 
// Quick notes 
// Descriptions of [constant, function] symbols used in this PineScript
// Key [setup, parameters to adjust]
// Setup - timeframe.period, n_bars, [min, max] of tracked main price series in chart
// Symbols' pricing semi-log {[trend, relStdDev], priceRsd[Min, Max, Spread]Idx} 
// PriceFractals [Fibonacci, Liebnitz, Puetz]
// 		Fibonnacci PriceFractals 
// 		Leibnitz PriceFractals (Gottfried Wilhelm Leibnitz), 19May2022 not coded yet (text pulled out)
// 		Puetz PriceFractals (Stephen J. Puetz), 19May2022 not coded yet (text pulled out)
// 		Plot PriceFractals
// TimeFractals  
// 		Puetz TimeFractals (Stephen J. Puetz) - used for ALL [Fibonacci, Liebnitz, Puetz] timeFractals
//				as I know of no common timeFractals for [Fibonacci, Liebnitz]  
// 			however, the levels are different for each
// 		plot Time Fractals (spikes or verticalLines) 
// 24************************24



// 24************************24
// Quick notes 

// For [intro, concept, reference, TradingView's Pine Script language, [challenge, problem, debug]s] of this script, webSearch my name and this script?  

// Make sure that the overlay output "PeutzUWS 1872-2020 fractal mirrors" is on a separate axis!  
//     right click on detrended SPX, select "pin to new right scale"
// Select "seetings for each axis (except TVC: TNX 10yT-bond rate) and select "logaritmic" 

// There is a great deal of [vestigial, anachronistic] code here, as I kept going back & forth to different
// approaches to find solutions.  Not entirely successfully...


// cannot do fractals with [hline, plot] - not accepted in local [function, for, if]
//		can use line.new [vertical, multiple of a base line, etc] 
// Don't use libraries!! it's too much of a mess...
// 04Jun2022 I was finally able to publish the library but it doesn't seem to import? no action 


// 24************************24
// Descriptions of [constant, function] symbols used in this PineScript

// priceFractal   |  timeFractal
// ---------------|---------------
// flagSpike      |  flagSpike			boolean -whether to output a [line, spike]
// lambdaPuetzUWS |  lambdaPuetzUWS		PuetzUWS (Universal Wave Series), period = cycle lengths (years)
// rsdPuetzUWS    |  timeLambda			PuetzUWS converted to relStdDev [time, price]
// midIdx         |  n/a					[PuetzUWS, rsdPuetzUWS] index, such that rsdPuetzUWS = 1.000
// p_cycle        |  t_cycle				current cycle length in [drawPriceFrac, drawTimeFrac]
// priceRsdMax?   |  t_now					.
// priceRsdMax?   |  timenow				.
// p_mod          |  t_mod					modulus (leftover) iUWS p_cycles from basLevel to rsdLevel
// priceRsdCum    |  t_spike				.
// n_spikes       |  n_spikes				number of iUWS [line, spike]s to show on chart
// n/a            |  t_lengthUnix		chart's time duration (t_lengthYear) converted to Unix (milliseconds)
// n/a				|	timenow				Unix current time UTC  (milliseconds since 01Jan1970)
//	n_bars			|	?can't get			starting [bar, time]	- sloppy guess
//	y_offset			|	y_offset				price offset, typically from a [line, spike] on chart, for labels



// 24************************24
// Basic [constant, function]s used in rest of library

// lambdaPuetzUWS - used for timeFractals, all of which are positive!
lambdaPuetzUWS =  array.from(0.0001072980, 0.0003218942, 0.0009656826, 0.002897047, 0.008691143, 0.02607343, 0.07822029, 0.2346608, 0.7039826, 2.111947, 6.335843,  19.00753,  57.02259, 171.0677, 513.2033)  

// rsdPuetzUWS - used for priceFractals, which are both [posit, negat]ive!
//		rsdPuetzUWS =  lambdaPuetzUWS / 0.2346608 (mid-range value)
// full sequence reversal 
// this shorter rsdPuetzUWS sequence - will NOT accomodate cypto and other extremely high growth! nor negatives

rsdPuetzUWS =  array.from(-9., -3., -1., -0.333333, -0.111111, -0.037037, -0.0123457, -0.00411523, -0.00137174, -0.000457247, 0.000, 0.000457247, 0.00137174, 0.00411523, 0.0123457, 0.037037, 0.111111, 0.333333, 1., 3., 9.)

// 21 elements, (0 - 20) midIdx == 10
lastRsdIdx = array.size(rsdPuetzUWS) - 1
midIdx = math.floor(array.size(rsdPuetzUWS) / 2)

// 09Jun2022 not used yet :
string  fracTyp = "Fibonacci"   // had to rip out to revert to old code, still want in future
float   t_lagYears = 0.05      // shifts all time spikes 

// Find the highest and lowest values for the entire dataset
// 20Mar2021 Howell - adapt this for viewable data
// 11Jun2022 old approach very inefficient, don't use these normally!

biggest(series,size) =>
	max =  0.0
	for j = 0 to size
		if series[j] > max 
			max := series[j]
	max

smalest(series,size) =>
	min =  100000.  
	for j = 0 to size
		if series[j] < min 
			min := series[j]
	min



// 24************************24
// Time - timeframe.period, n_bars, [min, max] of tracked main price series in chart (once stable, generalised)
// 03Jun2022 Much of this should go into a library to use across my PineScript code (once stable, generalised)
//		but I couldn't get a library to work
// User must turn OFF : SP500 axis menu -> Labels -> Indicators and financials name labels (no checkmark)

// time conversions  
var float t_year_to_millisecs  = 365.25*24*60*60*1000
var float year_to_minutes      = 365.25*24*60 
var float year_to_hours        = 365.25*24 
var float year_to_days         = 365.25 
var float year_to_weeks        = 52.1786 
var float year_to_months       = 12.0 

// timeIdx = index to ary_t_length, the active chart timeSpan 
// visual  = "1D"			 "5D"			 "1M"			"3M"		"6M"	 "1Y"		"5Y"	"All"  "All, 01Feb1871"  	 	
// actual  =  1    		 1      		 30			120		D		  W		30? or 20?    20?
//qnial> 	 (1/365.25)  (5/365.25)  (1/12)		(3/12)	(1/2)  (1.0)  (5.0)	(20)   (20) 
//  			 .00273785	 0.0136893   0.0833333	0.25 		0.5 	 1. 		5.		 20.   150.

// set to true to go back to 01Feb1871
flag_1871 = true 

timeIdx      =  0			// index to UWS constants

if			(timeframe.period == "1") 	
	timeIdx :=  0                    
else if	(timeframe.period == "5") 	
	timeIdx :=  1                    
else if	(timeframe.period == "30")  
	timeIdx :=  2                    
else if	(timeframe.period == "60") 
	timeIdx :=  3                    
else if	(timeframe.period == "120")
	timeIdx :=  4                    
else if	(timeframe.period == "D")	
	timeIdx :=  5                    
else if	(timeframe.period == "W")	
	timeIdx :=  6                    
else if	(timeframe.period == "M")	and  flag_1871 
	timeIdx :=  8
else if	(timeframe.period == "M")	
	timeIdx :=  7

// does "1" n_bars (1 hour timeframe.period, 1 day t_length) change over course of day?


// t_lengthYear =  duration of graph timescale for each timeperiod
// 1Jun2022 this did NOT work when declared as t_lenghtYear = 0.0, then 
//			t_lengthYear := array.get(ary_t_length, timeIdx)  Whyy!!!? very frustrating!!!

ary_t_length = array.from(0.00273785, 0.0136893, 0.0833333, 0.25, 0.5, 1.0, 5.0, 20.0, 150.0) 

// declaring these as float, or "var float" won't work???!!!???
t_lengthYear = array.get(ary_t_length, timeIdx)						// duration of graph timescale (years)
t_lengthUnix = math.floor(t_lengthYear * t_year_to_millisecs)	// duration of graph timescale (milliseconds)

year_fraction = time / t_year_to_millisecs + 1970

// strange shift 01Mar1922 to 01Aug1929
t_unix01Jan1970_to_unix01Jul1929(t_unix01Jan1970) => 
	math.floor(t_unix01Jan1970 + (1929.5833 - 7.25)*t_year_to_millisecs)

// ary_n_bar -> can cause scrunching of outputs!!!
//		27May2022 PineScript provides no bug-free means of getting n_bars.  
//		see '0_PineScript notes.txt' : 
//			I did it by trial & error for each time period and even then...
// std TradingView timeScales	( 1D      5D     1M    3M     6M     1Y    5Y   All  "All, 01Feb1871")
// last_bar_index			 		(24924, 21257, 28294, 20055, 22804, 4951, 1000, 230, ????)  these vary a bit

ary_n_bars = array.new<int>(8, 0)
ary_n_bars := array.from(       1300,  1300,   1100, 1500, 1500,   240,  240, 250, 1500) // 15Jun2022 

n_bars  = 2500			// number of datapoints on time axis
n_bars := array.get(ary_n_bars, timeIdx)

bar_2_3rds  =  last_bar_index - math.round(n_bars*2/3)
bar_1_half  =  last_bar_index - math.round(n_bars  /2)
bar_3_8ths  =  last_bar_index - math.round(n_bars*3/8)
bar_1_qrtr  =  last_bar_index - math.round(n_bars  /4)
bar_1_8ths  =  last_bar_index - math.round(n_bars  /8)


// 24************************24
// Labels
// 03Jun2022 Much of this should go into a library to use across my PineScript code (once stable, generalised)
//		but I couldn't get a library to work

simple_label(int bar_index, float price, string txt) => 
    label.new(bar_index, price, txt, textcolor = color.black, color = na, style=label.style_label_left, size=size.large)

barLast_label(float price, string txt) => 
    label.new(bar_index, price, txt, textcolor = color.black, color = na, style=label.style_label_left)

// priceOffsets are : + = upward; - = downward
barFirst_label(float barFractionFrmRight, float serPrice, float priceOffset, string txt) => 
    label.new(last_bar_index-math.round(n_bars*barFractionFrmRight), serPrice + priceOffset, txt, textcolor = color.black, color = na, style=label.style_label_left)

// 14Jun2022 doesn't work - need a series to et price level?
// 04Jun2022 Used for timeSpikes, can't get priceOffset to work, doesn't seem work with label.new...
time_label(int timer, float serPrice, float priceOffset, string txt) => 
	label.new(timer, serPrice + priceOffset, txt, xloc = xloc.bar_time, textcolor = color.black, color = na, style=label.style_label_left)

// didn't work - dumVar not typed?
//time_label(int timer, float serPrice, float priceOffset, string txt) => 
//	dummyVar =  priceOffset
//	label.new(timer, serPrice + priceOffset, txt, xloc = xloc.bar_time, textcolor = color.black, color = na, style=label.style_label_left)


// 03Jun2022 I should use "table" functions - maybe later
mat_write(string matTitle, matrix<float> matSymbol, int n_bars, int barRightShift, float serPrice, float txtLineHeight) =>  
	rows     = matrix.rows(matSymbol)
	cols     = matrix.columns(matSymbol)
	rowStr	= ""
	num		= 10.0
	y_offset	= 0.0
	y_offset := -6*txtLineHeight 
	// barFirst_label(float barFractionFrmRight, float serPrice, float priceOffset, string txt) => 
	barFirst_label(0.75, serPrice, y_offset, matTitle)
	for iRow = 0 to (rows - 1)
		rowStr  := ""
		for iCol = 0 to (cols - 1)
			num := matrix.get(matSymbol, iRow, iCol)
			rowStr :=  rowStr + str.tostring(num, '#.000') + " " 
		y_offset :=  float(-(iRow + 7)*txtLineHeight)
		barFirst_label(0.75, serPrice, y_offset, rowStr)



// 24************************24
// price[Min, Max] over full length of t_timelength, txtLineHeight
// Do not put in an if statement yet - needed by draw_spikes
// Note : the y_axis may not be a price, it could be [volume, other], but the "price" term used here
// 23May2022 do NOT declare with 'var'!!! :
// 11Jun2022 old approach very inefficient, not necessary : 

priceMin	=  smalest(close, n_bars) 
priceMax	=  biggest(close, n_bars) 

// 12Jun2022 this approach did NOT work - it was an attempt to improve efficiency [memory, computation]s
//float priceMin	=  1.0E6 
//float priceMax	=  0 
//
//if close  <  priceMin
//	priceMin :=  close
//
//if close  >  priceMax
//	priceMax :=  close


// 24************************24
// 24************************24
// Symbols' pricing semi-log {[trend, relStdDev], priceRsd[Min, Max, Spread]Idx, etc} 
// priceFractals - are hard-bound to the SP500USD 1926-2020 semi-log trend line and 
//		its standard deviation.  (fib = number of relStdDev) 
//		So they are tied to the SPXUSD over the last 83+ years, but are NOT related to current market specifics.

// Wilhelm Abel ?1937?, David Fischer 1996, I ve used the following split : 
//		price equilibrium (stable prices)  1871-1926
//		price revolution  1926-2020

// calculate trendline for [1926.25-2020.???, 1872-1926.25] too lzy to get 2020 exact endDate of regression fit 
calc_SP500Trend(y_fraction) =>  
	expnt = 0.0
	if  y_fraction  >=  1926.25  
		expnt := 0.792392+0.0289587*(y_fraction - 1926.25)  
	else 
		expnt := 0.784617+1.40925E-04  *(y_fraction - 1871.08)	
	SP500base = math.pow(10,expnt)

lin0000  = calc_SP500Trend(year_fraction)

//lin0000_[start, end] : don't need, its just lin0000 at barstate.islast (horizontal lines)


// The "relative standard deviation" (relStdDev or rsd) of the 83y trend is from the data 1926-2020
// calculated in LibreOffice Calc spreadsheet  =STDEV(SP500_detrend_1926_2020), i.e on semi-log detrend basis
// see "$d_webRawe"'economics, markets/SP500/multi-fractal/SP500 1872-2020 TradingView, 1928-2020 yahoo 
// WARNING!!!  Redo this yourself!!!  I made so many changes of a period of two years, weird things could have happened.
// keep coming back like zombies.  examples : 
// 	fib0236Hi =  (1 +  0.236*relStdDev/2)  should be relStdDev (should be no "/2")
//		I don't trust this 

relStdDev  =  0.4308 

price_to_rsd(price) =>
	(price / lin0000 - 1) / relStdDev

rsd_to_price(rsd) =>
	lin0000 * (rsd * relStdDev + 1)  

// rsd = relative standard deviation series, with respect to 1926-2020 SP500 semilog [trend, relStdDev]
priceRsd  = price_to_rsd(close)		// 25May2022 probably WRONG!! - consider semi-log 
plot(priceRsd, color=color.aqua, linewidth=2)


// priceRsd[Min, Max] over full length of t_timelength
float priceRsdMin	=   5.0 
float priceRsdMax	=  -5.0
float txtLineHeight = 0.0

// 11Jun2022 old approach very inefficient, not necessary : 
priceRsdMin  :=  smalest(priceRsd, n_bars)
priceRsdMax  :=  biggest(priceRsd, n_bars)

// 11Jun2022 new approach, trying to get simple floats to work (does not work!)
//if priceRsd  <  priceRsdMin
//	priceRsdMin :=  priceRsd
//
//if priceRsd  >  priceRsdMax
//	priceRsdMax :=  priceRsd


priceRsdMin_to_lambdaIdx() =>
	idxMin  =  0
	for j = 0 to lastRsdIdx
		if  array.get(rsdPuetzUWS, j) < priceRsdMin
			idxMin := j
	idxMin

priceRsdMax_to_lambdaIdx() =>
	idxMax  =  0
	for j = lastRsdIdx to 0
		if  array.get(rsdPuetzUWS, j) > priceRsdMax
			idxMax := j
	idxMax


// priceRsd[Min, Max, Bas] to indexs 
float priceRsdBas   =  0.0
int priceRsdMinIdx  =  0  
int priceRsdMaxIdx  =  0  
int priceRsdBasIdx  =  0
int priceRsdLesIdx  =  0
int priceRsdBelowIdx      = midIdx
int priceRsdAboveIdx      = midIdx
int priceRsdBelowStopIdx  = midIdx
int priceRsdAboveStopIdx  = midIdx

if  barstate.islast  
	txtLineHeight  := (priceRsdMax - priceRsdMin)/10
	//
	priceRsdMinIdx	:=  priceRsdMin_to_lambdaIdx()
	priceRsdMaxIdx	:=  priceRsdMax_to_lambdaIdx()
	//
	// set iUWS ranges to provide common fractal levels [below, above] rsd = 1.000  
	if      priceRsdMinIdx <= midIdx  and  priceRsdMaxIdx <= midIdx    
		priceRsdBelowIdx        := priceRsdMinIdx
		priceRsdBelowStopIdx    := priceRsdBelowIdx + 2  
		if priceRsdBelowStopIdx > midIdx
			priceRsdBelowStopIdx := midIdx
	else if priceRsdMinIdx >= midIdx  and  priceRsdMaxIdx >= midIdx                        
		priceRsdAboveIdx        := priceRsdMaxIdx
		priceRsdAboveStopIdx    := priceRsdAboveIdx - 2	 
		if priceRsdAboveStopIdx < midIdx
			priceRsdAboveStopIdx := midIdx
	else 
		// adjust +1 because off midIdx
		if  math.abs(priceRsdMinIdx - midIdx) > math.abs(priceRsdMaxIdx - midIdx)
			priceRsdBasIdx :=  math.abs(priceRsdMinIdx - midIdx) - 1
		else
			priceRsdBasIdx :=  math.abs(priceRsdMaxIdx - midIdx) - 1
		//
		if priceRsdBasIdx  <  2
			priceRsdLesIdx :=  midIdx
		else
			priceRsdLesIdx :=  priceRsdBasIdx - 2  
		//
		priceRsdBelowIdx      :=  midIdx - priceRsdBasIdx
		priceRsdAboveIdx      :=  midIdx + priceRsdBasIdx   
		priceRsdBelowStopIdx  :=  midIdx - priceRsdLesIdx            
		priceRsdAboveStopIdx  :=  midIdx + priceRsdLesIdx
	


// Bug check - output variables on chart

simple_label_prices() => 
	float priceLabel        = 0.0 
	// bar_[2_3rds, 1_half, 1_qrtr] are defined above in section "Time - timeframe.period, n_bars, [min, max]"
	//  
	priceLabel := priceRsdMax - 2*txtLineHeight
	simple_label(bar_2_3rds, priceLabel, "priceMin = " + str.tostring(priceMin))
	simple_label(bar_1_half, priceLabel, "priceMax = " + str.tostring(priceMax))
	simple_label(bar_1_qrtr, priceLabel, "midIdx   = " + str.tostring(midIdx))
	//  
	priceLabel := priceRsdMax - 3*txtLineHeight
	simple_label(bar_2_3rds, priceLabel, "priceRsdMin = " + str.tostring(priceRsdMin, '#.000'))
	simple_label(bar_1_half, priceLabel, "priceRsdMax = " + str.tostring(priceRsdMax, '#.000'))
	simple_label(bar_1_qrtr, priceLabel, "lastRsdIdx      = " + str.tostring(lastRsdIdx))
	//  
	priceLabel := priceRsdMax - 4*txtLineHeight
	simple_label(bar_2_3rds, priceLabel, "priceRsdMinIdx = " + str.tostring(priceRsdMinIdx))
	simple_label(bar_1_half, priceLabel, "priceRsdMaxIdx = " + str.tostring(priceRsdMaxIdx))
	simple_label(bar_1_qrtr, priceLabel, "priceRsdBasIdx = " + str.tostring(priceRsdBasIdx))
	//  
	priceLabel := priceRsdMax - 5*txtLineHeight
	simple_label(bar_2_3rds, priceLabel, "priceRsdBelowIdx     = " + str.tostring(priceRsdBelowIdx))
	simple_label(bar_1_half, priceLabel, "priceRsdBelowStopIdx = " + str.tostring(priceRsdBelowStopIdx))
	//  
	priceLabel := priceRsdMax - 6*txtLineHeight
	simple_label(bar_2_3rds, priceLabel, "priceRsdAboveStopIdx = " + str.tostring(priceRsdAboveStopIdx))
	simple_label(bar_1_half, priceLabel, "priceRsdAboveIdx     = " + str.tostring(priceRsdAboveIdx))
	//
	simple_label(bar_1_qrtr, priceRsdMin, "n_bars   = " + str.tostring(n_bars))


//if barstate.islast
//	simple_label_prices()

//if barstate.islast
//	priceLabel = priceRsdMax - 1*txtLineHeight
//	simple_label(bar_2_3rds, priceLabel, "timeframe.period = " + timeframe.period)



// 24************************24
// 24************************24
// [Puetz, Fibonacci] priceFractals - are hard-bound to the SP500USD 1926-2020 semi-log trend line and 
//		its standard deviation.  (fib = number of relStdDev) 
//		So they are tied to the SPXUSD over the last 83+ years, but are NOT related to current market specifics.
// [lambdaPuetzUWS, rsdPuetzUWS] defined in setup section above
// note that the "plot" function cannot be a local : ie in [function, for, if, etc]
// for price, key fractal measure is rsdPrice, preferably with 1.0 in set rsdPuetzUWS?
// wanted to have options on type of priceFractals : Puetz framework, Fibonacci lowest level?


// drawPriceFrac() - for now only nests 2 levels below topLevel
// 08Jun2022 need to do [above, below] lin0000 (fractal = 1.000) - currently just above

drawPriceFrac()  => 
	bool  flagSpike = true  
	int   iStop     = 0
	int   widthr    = 1
	float y_offset          = 0.0
	float priceRsdBelow     = 0.0
	float priceRsdAbove     = 0.0
	float priceRsdBelowStop = 0.0
	float priceRsdAboveStop = 0.0
	float priceRsdNow       = 0.0
	float priceRsdStep      = 0.0
	float priceLabel        = 0.0 
	//
	// do levels below priceRsd = 1.000, if they are present 
	if priceRsdMin < 0.000
		iStop  := 0
		widthr := 0  
		priceRsdBelow     := array.get(rsdPuetzUWS, priceRsdBelowIdx)
		priceRsdBelowStop := array.get(rsdPuetzUWS, priceRsdBelowStopIdx)
		//priceLabel := priceRsdMax - 7*txtLineHeight
		//simple_label(bar_2_3rds, priceLabel, "priceRsdBelowStop = " + str.tostring(priceRsdBelowStop))
		//simple_label(bar_1_half, priceLabel, "priceRsdBelow     = " + str.tostring(priceRsdBelow))
		for iRSD = priceRsdBelowStopIdx to priceRsdBelowIdx 
			widthr := widthr + 1  
			priceRsdStep := array.get(rsdPuetzUWS, iRSD)
			priceRsdNow  := 0.0
			while (priceRsdMin <= priceRsdNow)  and  (iStop < 300)
				if priceRsdMax >= priceRsdNow 
					line.new(last_bar_index - n_bars, priceRsdNow, last_bar_index, priceRsdNow, color=color.black, width=widthr) 
					barLast_label(priceRsdNow, "       " + str.tostring(rsd_to_price(priceRsdNow), '#'))
				priceRsdNow := priceRsdNow + priceRsdStep
				iStop  := iStop + 1
	// 
	// do levels above priceRsd = 1.000, if they are present  
	if priceRsdMax >= 0.000
		iStop  := 0
		widthr := 0  
		priceRsdAbove     := array.get(rsdPuetzUWS, priceRsdAboveIdx)
		priceRsdAboveStop := array.get(rsdPuetzUWS, priceRsdAboveStopIdx)
		//priceLabel := priceRsdMax - 8*txtLineHeight
		//simple_label(bar_2_3rds, priceLabel, "priceRsdAboveStop = " + str.tostring(priceRsdAboveStop))
		//simple_label(bar_1_half, priceLabel, "priceRsdAbove     = " + str.tostring(priceRsdAbove))
		for iRSD = priceRsdAboveStopIdx to priceRsdAboveIdx 
			widthr := widthr + 1  
			priceRsdNow  := 0.0
			priceRsdStep := array.get(rsdPuetzUWS, iRSD)
			while (priceRsdNow <= priceRsdMax)  and  (iStop < 300)
				if priceRsdMin <= priceRsdNow  
					line.new(last_bar_index - n_bars, priceRsdNow, last_bar_index, priceRsdNow, color=color.black, width=widthr)
					barLast_label(priceRsdNow, "       " + str.tostring(rsd_to_price(priceRsdNow), '#'))
				priceRsdNow := priceRsdNow + priceRsdStep
				iStop  := iStop + 1


if barstate.islast
	drawPriceFrac()

//					line.new(last_bar_index - n_bars, priceRsdNow, last_bar_index, priceRsdNow, color=color.black, width=iRSD + 1)


// 24************************24
// 24************************24
// TimeFractals  

//+-----+
// Puetz TimeFractals (Stephen J. Puetz) - used for ALL [Fibonacci, Liebnitz, Puetz] timeFractals
//		as I know of no common timeFractals for [Fibonacci, Liebnitz]  
// However, the levels are different for each
// 12May2022 initial  
// SP500 multi-fractal : 1926-2020 semi-log trend; Puetz "Universal Wave Series" temporal nested cycles
// UWS wavelengths (cycle times in years) that I will use for the 1926-2020 SP500 semi-log trend are (rounded to 6 digits), but these NEED CORRECTION for trading hours etc (a project for the future - not likely for me). As TradingView typically plots trading days only, there won't be a good time-fit, especially for lower timescales.  More sophisticated calculations could fix this, but that's for another day if ever. TradingView has "cyclic lines" to easily show temporal cycles on charts.  
// Fourier series analysis - might be a good starting spectral analysis to pick out relevant periods. 
// again, missing multiple n (HUWS)  
// While the ratios of UWS sequential periods are fixed to a factor of 3, the "anchor time" and perhaps more importantly the phase angles, depend on the application.  

// set UWS series parameters (not yet HUWS)
// 16Mar2015 Howell adapted from "TradingView auto fib extension" standard PineScript library
// 25May2022 oops still had erroneous "/2" :
// 26May2022 these are NOT nested, as with the Puetz timeFractals below.  
//		I couldn't get [line.new, nested [for, if] expressions] to work (see a previous section above)
// NOTE : as per the reference in section "" above, Puetz has recently used : 
//        P(0,0) is a base cycle with a period of 2.82894367327307 solar years.
// In comparison, above I have UWS[09] = 2.111947 years.  But I do not have a complete updated table of USW [lambda, phi] to go by, so I simply used 2011 lambda rather than the 2014 numbers.
// However, since the initial list of lamda in Puetz's 2011 book, the assigned values have been updated as many new time series have qualified for the Puetz UWS.  Note that phi angles are specific to each application of each time series, and must be optimized for the problem at hand.  12May2022 THIS NEEDS TO BE UPDATED!!
// Also not that the Half-UWS harmonics bring the UWS-HUWS cmbination much closer to a Fourier series! :
//      n is one of eight period-halving harmonics where n ∈ {0, 1, 2, 3, 4, 5, 6, 7}
// Rather than show all of the above cycles on a graph, I will reduce the cycles according to the timescale of the chart  

// lamda (cycle periods)
// years      		months	   	days          hours  
// 1.072980E-04                                  0.940574 
// 3.218942E-04                    0.117572      2.82173 
// 9.656826E-04                   0.352716      8.46518 
// 2.897047E-03                   1.05815      25.3956 
// 8.691143E-03   0.104294        3.17444      76.1866 
// 2.607343E-02   0.312881        9.52332     228.56  
// 7.822029E-02   0.938643       28.57        
// 2.346608E-01   2.81593       85.7099      
// 7.039826E-01   8.44779      257.13       
// 2.111947E+00   25.3434      771.389      
// 6.335843E+00   76.0301
// 1.900753E+01   228.09
// 5.702259E+01  
// 1.710677E+02  
// 5.132033E+02  


// "anchor time" NYET -not used!!  - just use the pure UWS times??  But how to adjust?
// I will simply take the somewhat arbitrary date of the 1926 start of the SP500 series that I used :  1926.25.  Fudging and excuses will follow later. Note that all UWS cycles align with the largest of the series, and hence with one another, so the choice of the start time is "universal" in a sense (but coud be wrong, obviously).

// [lambdaPuetzUWS, rsdPuetzUWS] defined in setup section above

// find the lambdaPuetzUWS index of the largest ary_t_length fits into the chart timescale
t_lengthMaxFitIdx() =>
	max = 0
	for j = 0 to 14
		if  array.get(lambdaPuetzUWS, j)   <  t_lengthYear
			max := j
	max

// timeLambdaYearL - active lambdaPuetzUWS for the current timescale [D, 5D, W, M, ...]
timeLambdaYearL =  array.new_float(5, 0.0)  
timeLambdaUnixL =  array.new_int(5, 0)  
int  timeLamdaMaxIdx  =  0  

init_timeLambdaL()  =>  
	lambdaYear =  0.0
	for j = 0 to 4  
		lambdaYear  :=  array.get(lambdaPuetzUWS, timeLamdaMaxIdx - 2 + j) 
		array.set(timeLambdaYearL, j, lambdaYear)  
		array.set(timeLambdaUnixL, j, math.round(lambdaYear * t_year_to_millisecs))  


// Jun2022 timePhi - not used yet.I am actually reluctant to going astray with optimization, 
//		when I don't understand the results, and I'm fighting to better understand Pine Script.
// phi = time [lag, phase angle], not currently implemented
// these are simply fit to each of the data series
// 13May2022 simple set phase angles to zero for initial coding.
// UWS_phi =  array.from(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) 

//timePhi  =  array.new_float(5, 0.0)
//init_timePhi()  =>  				          
//	for j = 0 to 4  
//		array.set(timePhi, j, array.get(UWS_phi, timeLamdaMaxIdx - 2))    


// 14Jun2022 if statement didn't work
//if  barstate.isfirst  
//	timeLamdaMaxIdx	:=  t_lengthMaxFitIdx() 
//	init_timeLambdaL()
//	//init_timePhi()


timeLamdaMaxIdx	:=  t_lengthMaxFitIdx() 
init_timeLambdaL()
//init_timePhi()


// write debug information to chart : 
//debugTimeFrac()  => 
//	priceLabel := priceRsdMax - 3*txtLineHeight
//	simple_label(bar_2_3rds, priceLabel, "timeIdx = "          + str.tostring(timeIdx))
//	simple_label(bar_1_half, priceLabel, "timeLamdaMaxIdx = "  + str.tostring(timeLamdaMaxIdx))
//	priceLabel := priceRsdMax - 4*txtLineHeight
//	simple_label(bar_2_3rds, priceLabel, "t_lengthYear = "     + str.tostring(t_lengthYear))
//	simple_label(bar_1_half, priceLabel, "t_lengthUnix = "     + str.tostring(t_lengthUnix))
//	priceLabel := priceRsdMax - 5*txtLineHeight
//	simple_label(bar_2_3rds, priceLabel, "t_cycle = "          + str.tostring(t_cycle))
//	simple_label(bar_1_half, priceLabel, "t_mod   = "          + str.tostring(t_mod))
// find last, closest] spike in t_lengthUnix



//+--+
// drawTimeFrac [spikes, label]s (spikes = verticalLines) 
// 12May2022 initial, 19May2022 generalized for [Fibonacci, Liebnitz, Puetz]   
// 19May2022 Half-UWS pike is NOT provided yet 
// inputs tab - cannot find anywhere for : 
//		fracTyp = input.string(defval="Fibonacci", title="fracTyp", options= ["Fibonacci", "Liebnitz", "Puetz"])
// skip daily chart, as SP500 [t_lengthUnix, n_bars] seem messed up
// (maybe not available by minute or whatever)

// plot the UWS cycle times (not yet the HUWS)

// from Setup (reminder) : 
// float t_year_to_millisecs  =  1000*60*60*24*365.25

float t_shiftYears = 0.05


drawTimeFrac()  => 
	bool  flagSpike   = true  
	int   t_cycle     = 0
	int   time_now    = 0
	int   t_mod       = 0
	int   t_spike     = 0
	int   n_spikes    = 0  
	float y_offBase   = 0.0
	float y_offset    = 0.0
	float priceLabel  = 0.0 
	for iUWS = 0 to 4
		flagSpike  :=  true
		t_cycle  :=  array.get(timeLambdaUnixL, iUWS)
		if flag_1871
			time_now :=  t_unix01Jan1970_to_unix01Jul1929(timenow) 
		else
			time_now :=  timenow  
		t_mod  :=  math.floor(((time_now / t_cycle) % 1) * t_cycle)
		//if  iUWS == 2
		//	debugTimeFrac()
		if      t_mod  <  t_lengthUnix 
			t_spike  :=  timenow  - t_mod
			n_spikes :=  math.floor(((t_lengthUnix - t_mod) /  t_cycle))  
		else if t_mod  == 0.0  
			t_spike  :=  timenow
			n_spikes :=  math.floor(((t_lengthUnix - t_mod) /  t_cycle))  + 1  
		else if t_mod  >  t_lengthUnix 
			flagSpike  :=  false  
		if flagSpike 
			for i_spike  =  0  to (n_spikes - 1)
				line.new(t_spike, priceRsdMin, t_spike, priceRsdMax, xloc = xloc.bar_time, color=color.black, width=iUWS + 1)
				y_offset :=  (y_offBase - 1 - iUWS)*txtLineHeight/2
				time_label(t_spike, priceRsdMax, y_offset, "   " + str.tostring(iUWS))
				t_spike :=  t_spike - t_cycle


if barstate.islast
	drawTimeFrac()


// remi - defined earlier
//bar_2_3rds  =  last_bar_index - math.round(n_bars*2/3)
//bar_1_half  =  last_bar_index - math.round(n_bars  /2)
//bar_3_8ths  =  last_bar_index - math.round(n_bars*3/8)
//bar_1_qrtr  =  last_bar_index - math.round(n_bars  /4)
//bar_1_8ths  =  last_bar_index - math.round(n_bars  /8)

//		simple_label(bar_2_3rds, priceLabel, "timeframe.period = " + timeframe.period)
//				y_offset :=  (y_offBase - 1 - i_spike)*txtLineHeight


// Create a legend showing periods for fractal depths 

if barstate.islast 
	periodLabel = "error"
	period      =  0.0
	y_offBase   = -3.0
	y_offset    =  0.0
	//
	y_offset :=  y_offBase*txtLineHeight 
	// barFirst_label(float barFractionFrmRight, float serPrice, float priceOffset, string txt) => 
	barFirst_label(0.85, priceRsdMax, y_offset, "Puetz timeFractal [depth, period]s")
	for  iUWS = 0 to 4
		period :=  array.get(timeLambdaYearL, iUWS)
		if			period < 60/year_to_minutes
			period := period * year_to_minutes 
			periodLabel  :=  "minutes"
		else if  period < 24/year_to_hours
			period := period * year_to_hours 
			periodLabel  :=  "hours    "
		else if  period < 7/year_to_days 
			period := period * year_to_days 
			periodLabel  :=  "days     "
		else if  period < 4.1/year_to_weeks 
			period := period * year_to_weeks
			periodLabel  :=  "weeks   "
		else if  period < 1
			period := period * year_to_months 
			periodLabel  :=  "months "
		else 
			periodLabel  :=  "years     "
		// timeBarOffsets are backwards from a specified bar_index
		// priceOffsets are : + = upward; - = downward
		y_offset :=  (y_offBase - 1 - iUWS)*txtLineHeight
		// 27May2022 cannot use parenthesis in labels?
		barFirst_label(0.85, priceRsdMax, y_offset, str.tostring(iUWS) + ".  " + str.tostring(period, '#.000') + " " + periodLabel)






// endcode
