//@version=6 // PRISM Forecast - draws a PRISM Forecasting 48-hour forecast on the chart from a pasted "forecast code". // Copy the code on www.prismforecasting.com (the TradingView button on the live chart, or tradingview.php), open this // indicator's settings and paste it into "Forecast code". The code is one line: // PRISM1|SYMBOL|anchor_unix|decimals|base|p_up_end|candles|S|R|model // candles = h:o/h/l/c/s/lo/hi per H1 candle (h = hours after the anchor; integer price offsets from base in 10^-decimals // units: expected open/high/low/close, central-scenario close, 80% range of the simulated closes). See the README. // Draws: the expected (median) close path (dashed), the 80% range of closes (filled band), the central-scenario path, // the AI briefing's key levels, labels for the expected end close, the 80% interval and the end direction ("no clear // edge" within 6 points of 50%, the website's rule), "forecast expired" when the code is older than 48 h. // Pine Script cannot fetch URLs: paste a fresh code when a new forecast is issued (every hour on the website). indicator("PRISM Forecast", "PRISM", overlay = true, max_lines_count = 50, max_labels_count = 50, max_polylines_count = 10) // ---------------------------------------------------------------------------------------------------------- inputs string code = input.text_area("", "Forecast code", tooltip = "Paste the code copied from www.prismforecasting.com (TradingView button on the chart). One forecast = one code; copy a new one after the next hourly issue.") bool showExp = input.bool(true, "Expected (median) close path", group = "Draw") bool showBand = input.bool(true, "80% range of closes (filled band)", group = "Draw") bool showScen = input.bool(true, "Central scenario path", group = "Draw") bool showLvls = input.bool(true, "Key levels (AI briefing)", group = "Draw") bool showLbls = input.bool(true, "Labels", group = "Draw") bool rebase = input.bool(true, "Rebase to this chart's open at the anchor bar", group = "Prices", tooltip = "With this on, every forecast price is scaled by (this chart's open at the anchor bar / the forecast's anchor open) so feed differences do not shift the drawing. Off = raw website prices.") color cExp = input.color(#6366f1, "Expected close", group = "Colours") color cBandFill= input.color(color.new(#6366f1, 86), "80% band fill", group = "Colours") color cBandEdge= input.color(color.new(#6366f1, 60), "80% band edge", group = "Colours") color cScen = input.color(#f59e0b, "Central scenario", group = "Colours") color cSup = input.color(#10b981, "Support", group = "Colours") color cRes = input.color(#ef4444, "Resistance", group = "Colours") color cLabelBg = input.color(color.new(#1e2537, 10), "Label background", group = "Colours") color cLabelTx = input.color(#ffffff, "Label text", group = "Colours") // ------------------------------------------------------------------------------------------- parsed state (once) var bool ok = false var string status = "Paste a PRISM forecast code into the indicator settings (Forecast code)." var string sym = "" var int anchorMs = 0 var float scale = 1.0 var float base = 0.0 var float pUp = na var string modelTag = "" var array H = array.new() var array EO = array.new() var array EH = array.new() var array EL = array.new() var array EC = array.new() var array SC = array.new() var array LO = array.new() var array HI = array.new() var array SUP = array.new() var array RES = array.new() // an offset field -> price (na for an empty field) toPrice(string s, float b, float sc) => float o = str.tonumber(s) na(o) ? na : (b + o) / sc if barstate.isfirst string c = str.replace_all(str.replace_all(str.replace_all(code, "\n", ""), "\r", ""), " ", "") array f = str.split(c, "|") if str.length(c) == 0 status := "Paste a PRISM forecast code into the indicator settings (Forecast code)." else if array.size(f) < 10 or array.get(f, 0) != "PRISM1" status := "This is not a PRISM1 forecast code - copy it again from www.prismforecasting.com." else float an = str.tonumber(array.get(f, 2)) float dc = str.tonumber(array.get(f, 3)) float bs = str.tonumber(array.get(f, 4)) if na(an) or na(dc) or na(bs) or bs <= 0 status := "The forecast code is damaged (anchor / decimals / base) - copy it again." else sym := array.get(f, 1) anchorMs := int(an) * 1000 scale := math.pow(10, dc) base := bs pUp := str.tonumber(array.get(f, 5)) modelTag := array.get(f, 9) array cs = str.split(array.get(f, 6), ";") for s in cs array hv = str.split(s, ":") if array.size(hv) == 2 float hh = str.tonumber(array.get(hv, 0)) array v = str.split(array.get(hv, 1), "/") if not na(hh) and array.size(v) == 7 array.push(H, int(hh)) array.push(EO, toPrice(array.get(v, 0), base, scale)) array.push(EH, toPrice(array.get(v, 1), base, scale)) array.push(EL, toPrice(array.get(v, 2), base, scale)) array.push(EC, toPrice(array.get(v, 3), base, scale)) array.push(SC, toPrice(array.get(v, 4), base, scale)) array.push(LO, toPrice(array.get(v, 5), base, scale)) array.push(HI, toPrice(array.get(v, 6), base, scale)) for s in str.split(array.get(f, 7), ",") float p = toPrice(s, base, scale) if not na(p) array.push(SUP, p) for s in str.split(array.get(f, 8), ",") float p = toPrice(s, base, scale) if not na(p) array.push(RES, p) if array.size(H) == 0 status := "The forecast code carries no candles - copy it again." else ok := true status := "" // -------------------------------------------------------------------------- the chart's price at the anchor (rebase) var float anchorOpenChart = na var bool anchorExact = false int tfSec = timeframe.in_seconds() int tfMs = tfSec * 1000 if ok and not anchorExact if time == anchorMs anchorOpenChart := open anchorExact := true else if time <= anchorMs and anchorMs < time + tfMs anchorOpenChart := open // ------------------------------------------------------------------------------------------------------- drawing var array LINES = array.new() var array