// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © Bill_Howell // "$d_web"'CompLangs/PineScript/PuetzUWS IntlStkIdxs multi-fractals.txt' // view this file in a text editor, with [constant width font, tab = 3 spaces], no line-wrap //@version=5 indicator(title="Intl Stock Indexes detrended, PuetzUWS [time, price] multiFractals", shorttitle="IntlStkIdxs detrended multi-fractals", overlay=true) // Howell's version: 18Apr2023 revamp to plot Fibs directly on normal chart // 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) - 1st 2 levels are similar to Fibonacci // Fibonacci [0.000 0.146 0.236 0.382 0.500+ 0.618 0.764+ 1.000] // Puetz mix [0.000 0.133 0.222 0.333 0.500* 0.667 0.778 1.000] // [+Fibonacci, *Puetz UWS] do NOT have, but traders like these ratios // Plot PriceFractals // SP500 semiLog trendline 1926-2022 : 10 power (-56.7736 + (0.029831 * year)) // relStdDev_plus1 = 1 + 0.0937853 // yearFrac 2000 2005 2010 2015 2020 2022 2023 2025 2030 2040 2050 (01Jan of year) // SP500Pric 773 1090 1537 2167 2020 3505 3754 4307 6071 12,068 23,985 // // 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 // barstate.islast [nowest, ender] time on chart // barstate.isfirst [oldest, start] time on chart // 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 // bar 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(barsFrmRight, float serPrice, float priceOffset, string txt) => label.new(barsFrmRight, serPrice + priceOffset, txt, textcolor = color.black, color = na, style=label.style_label_left) // 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) => dummyVar = priceOffset label.new(timer, serPrice + priceOffset, txt, xloc = xloc.bar_time, textcolor = color.black, color = na, style=label.style_label_left) // 24************************24 // Basic [PuetzUWS fractals, [bigg, smal]est functions] used in rest of library // lambdaPuetzUWS - used for timeFractals, all of which are positive! lambdaPuetzUWS = array.from(0.000178326, 0.000534979, 0.00160494, 0.00481481, 0.0144444, 0.0433333, 0.13, 0.39, 1.17, 3.51, 10.53, 31.59, 94.77, 284.31, 852.93, 2558.79, 7676.37) count_lambdaPuetzUWS = array.size(lambdaPuetzUWS) // 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, 14Apr2023 not used yet : string fracTyp = "Fibonacci" // had to rip out to revert to old code, still want in future // Find the highest and lowest values for the entire dataset // 20Mar2021 Howell - adapt this for viewable data (gives WRONG answers!) // 11Jun2022 old approach very inefficient, don't use these normally! // 14Apr2023 PineScript sucks at this type of whole-series stats // 17Aug2023 this will cause trouble if series[Max, Min] don't include priceRange // https://stackoverflow.com/questions/60904563/how-to-change-global-variable-from-function-in-pine-script // answered Dec 5, 2020 at 10:28, Adam Wallner idxMax = 0 idxMin = 1 priceSPXMaxMin = array.new_float(2) // each time-of-use, rest these!! check that the range is OK for data! array.set(priceSPXMaxMin, idxMax, 0.0) array.set(priceSPXMaxMin, idxMin, 10000.) biggest(series,size) => max = array.get(priceSPXMaxMin, idxMax) for j = 0 to size if series[j] > max max := series[j] array.set(priceSPXMaxMin, idxMax, max) max smalest(series,size) => min = array.get(priceSPXMaxMin, idxMin) for j = 0 to size if series[j] < min min := series[j] array.set(priceSPXMaxMin, idxMin, min) min // big = biggest(close,20) // sml = smalest(close,20) if barstate.islast bigat = array.get(priceSPXMaxMin, idxMin) smlat = array.get(priceSPXMaxMin, idxMin) // barFirst_label(barsFrmRight, float serPrice, float priceOffset, string txt) barFirst_label(40, bigat, 10, "bigat : SPX semi-log priceTrend 1871-2022") barFirst_label(40, 2500, 10, "bigat : SPX semi-log priceTrend 1871-2022") // plot(bigat, color=color.navy, title="SPX semi-log priceTrend 1871-2022", linewidth=1) // plot(smlat, color=color.navy, title="SPX semi-log priceTrend 1871-2022", linewidth=1) // 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 tNow_to_yrDate = timenow / t_year_to_millisecs + 1970 tUnix_to_yrDate(timer) => timer / t_year_to_millisecs + 1970 yrDate_to_UnixTime(yrFrac) => (yrFrac - 1970) * t_year_to_millisecs // qnial> 1929 + (10 / 12) + (24 / 365) // 1929.9 tUnix_from_24Oct1929(tUnix) => math.floor(tUnix - (1929.9 * t_year_to_millisecs)) // 16Aug2023 this assumes that all charts end at timenow, won't work well < "1M" var float yrFracMax = tNow_to_yrDate var float yrFracMin = 0. yearsB4now_to_yrFrac(yearsB4now) => yrFracMax - yearsB4now + 1970 // "anchor time" NYET -not used!! - just use the pure UWS times?? But how to adjust? // set to true to go back to 01Feb1871 flag_1871 = true timeIdx = 0 // index to UWS constants var int n_bars = 0 if (timeframe.period == "1") timeIdx := 0 n_bars := 600 yrFracMin := yearsB4now_to_yrFrac(1 / year_to_days) else if (timeframe.period == "5") timeIdx := 1 n_bars := 350 yrFracMin := yearsB4now_to_yrFrac(1 / year_to_weeks) else if (timeframe.period == "30") timeIdx := 2 n_bars := 400 yrFracMin := yearsB4now_to_yrFrac(1 / year_to_months) else if (timeframe.period == "60") timeIdx := 3 n_bars := 600 yrFracMin := yearsB4now_to_yrFrac(3 / year_to_months) else if (timeframe.period == "120") timeIdx := 4 n_bars := 1500 yrFracMin := yearsB4now_to_yrFrac(6 / year_to_months) else if (timeframe.period == "D") timeIdx := 5 n_bars := 240 yrFracMin := yearsB4now_to_yrFrac(6 / year_to_months) else if (timeframe.period == "W") timeIdx := 6 n_bars := 240 yrFracMin := yearsB4now_to_yrFrac(2023 - 1871) else if (timeframe.period == "M") and flag_1871 timeIdx := 8 n_bars := 250 yrFracMin := yearsB4now_to_yrFrac(5) else if (timeframe.period == "M") timeIdx := 7 n_bars := 1500 yrFracMin := yearsB4now_to_yrFrac(1 / year_to_days) // 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!!! // timeIdx = index to ary_t_length, the active chart timeSpan // ary_t_length = array.from(0.00273785, 0.0136893, 0.0833333, 0.25, 0.5, 1.0, 5.0, 20.0, 150.0) // visual = "1D" "5D" "1M" "3M" "6M" "1Y" "5Y" "All" "01Feb1871" // actual = 1 1 30 120 D W M? 20? // 0.00273785 0.0136893 0.0833333 0.25 0.5 1. 5. 20. 150. // qnial> (1/365.25) (5/365.25) (1/12) (3/12) (1/2) 1.0 5.0 20 20 ary_t_length = array.from(0.00273785, 0.0136893, 0.0833333, 0.25, 0.5, 1.0, 5.0, 20.0, 150.0) // duration of graph timescale (years) // declaring these as float, or "var float" won't work???!!!??? t_lengthYear = array.get(ary_t_length, timeIdx) t_lengthUnix = math.floor(t_lengthYear * t_year_to_millisecs) // 24************************24 // 16Aug2023 take previous price stuff out - frigging crappy PineScript headache // just plot a SP500 price trend line on chart! SP500_semiLog_relStdDev_plus1 = 1 + 0.0937853 // semi-log trend only : price = request.security("TVC:SPX", timeframe.period, close) const = -56.7736 slope = 0.029831 SPX_SLtrend_1871_2022 = math.pow(10, const + (slope * tUnix_to_yrDate(time))) midSPX_MaxMin_1871_2022 = (array.get(priceSPXMaxMin, idxMax) + array.get(priceSPXMaxMin, idxMin)) / 2 midSPX_SLtrend_1871_2022 = math.pow(midSPX_MaxMin_1871_2022, 2) / SPX_SLtrend_1871_2022 if barstate.islast barFirst_label(50, midSPX_SLtrend_1871_2022, 0, "midSPX_SLtrend_1871_2022") // var label1 = label.new(yLoc=midSPX_SLtrend_1871_2022, text="midSPX_SLtrend_1871_2022", style=label.style_circle) // label.set_xloc(label1, time, xloc.bar_time) // label.set_text(label1, text=tostring(diff)) // label.set_y(label1, highest(10)[1] + tr[1]) // plot(midSPX_SLtrend_1871_2022, color=color.navy, title="SPX semi-log priceTrend 1871-2022", linewidth=4) // 24************************24s // 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 float t_lagYears = 0.05 // shifts all time spikes // 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 count_lambdaPuetzUWS 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) timeLamdaMaxIdx = t_lengthMaxFitIdx() 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)) init_timeLambdaL() //init_timePhi() //+--+ // drawTimeFrac [spikes, label]s (spikes = verticalLines) // 12May2022 initial, 19May2022 generalized for [Fibonacci, Liebnitz, Puetz] // 19May2022 Half-UWS spike 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) // 16Aug2023 for charts, txtLineHeight depends on window size etc // assuming half-height HDMI window, just use : float y_offset = .0 drawTimeFrac() => bool flagSpike = true int t_cycle = 0 int time_now = 0 int t_mod = 0 int t_spike = 0 int n_spikes = 0 priceMax = array.get(priceSPXMaxMin, idxMax) priceMin = array.get(priceSPXMaxMin, idxMin) // for iUWS = 0 to 4 flagSpike := true t_cycle := array.get(timeLambdaUnixL, iUWS) time_now := tUnix_from_24Oct1929(timenow) t_mod := math.floor(((time_now / t_cycle) % 1) * t_cycle) 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, priceMin, t_spike, priceMax, xloc = xloc.bar_time, color=color.black, width=iUWS + 1) time_label(t_spike, priceMax, y_offset, " " + str.tostring(iUWS)) t_spike := t_spike - t_cycle // if barstate.islast // drawTimeFrac() // float priceLabel = 0.0 // 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 // just copy from 'strings.ndf' Puetz_UWS IS OP periodBase // endcode