TradingView scripts cannot download data, so the PRISM Forecast indicator takes one pasted forecast code instead: copy it here or with the TradingView button on the live chart, paste it into the indicator, and the 48-hour forecast β the expected close path, the shaded 80% range of closes, the central scenario and the key levels of the AI briefing β is drawn on your chart from the forecast's anchor bar forward. Included with every plan that opens the charts, for the markets it opens.
On the live forecast page click TradingView in the chart header (or pick the market below): the forecast code of that market is copied β about 2,000 characters, the H1 forecast of the hour. Your website login gates the code, like the chart.
On TradingView open the same market, open the PRISM Forecast indicator's settings and paste the code into Forecast code.
The dashed expected path, the 80% band, the central scenario and the key levels appear from the anchor bar forward, with the end close, its 80% interval and the honest end-direction label. A new forecast is issued every hour β copy a new code whenever you want it; after 48 hours the indicator says forecast expired.
Create an account and watch the live forecast for 2 days β all 17 markets with the AI news briefing, ad-free β no card needed. After the demo your account continues on the Free plan: all 17 markets, supported by advertising.
A Pine Script v6 indicator β open source, nothing to buy on TradingView. It draws only what the pasted code carries; it never connects anywhere and it never trades.
//@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<int> H = array.new<int>()
var array<float> EO = array.new<float>()
var array<float> EH = array.new<float>()
var array<float> EL = array.new<float>()
var array<float> EC = array.new<float>()
var array<float> SC = array.new<float>()
var array<float> LO = array.new<float>()
var array<float> HI = array.new<float>()
var array<float> SUP = array.new<float>()
var array<float> RES = array.new<float>()
// 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<string> 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<string> cs = str.split(array.get(f, 6), ";")
for s in cs
array<string> hv = str.split(s, ":")
if array.size(hv) == 2
float hh = str.tonumber(array.get(hv, 0))
array<string> 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<line> LINES = array.new<line>()
var array<label> LABELS = array.new<label>()
var polyline pExp = na
var polyline pScen = na
var polyline pBand = na
var bool drawn = false
fmtPx(float p) =>
str.tostring(math.round_to_mintick(p), format.mintick)
if ok and barstate.islast and (not drawn or barstate.isnew)
drawn := true
// ---- clear the previous drawing
for l in LINES
line.delete(l)
array.clear(LINES)
for lb in LABELS
label.delete(lb)
array.clear(LABELS)
if not na(pExp)
polyline.delete(pExp)
if not na(pScen)
polyline.delete(pScen)
if not na(pBand)
polyline.delete(pBand)
pExp := na
pScen := na
pBand := na
// ---- the price factor (rebase) and the time step the chart can show
float basePx = base / scale
float k = rebase and not na(anchorOpenChart) and anchorOpenChart > 0 and basePx > 0 ? anchorOpenChart / basePx : 1.0
int stepH = tfSec > 3600 ? math.max(1, math.round(tfSec / 3600.0)) : 1 // H4: every 4th hour, D1: every 24th
int endMs = anchorMs + 48 * 3600000
bool expired = timenow > endMs
// objects may not lie more than ~500 bars right of the last bar: on M1 / M5 the 48 hours do not fit - say so
int maxFutureMs = last_bar_time + 480 * tfMs
bool tooFine = anchorMs + 47 * 3600000 > maxFutureMs
int n = array.size(H)
array<chart.point> ptsExp = array.new<chart.point>()
array<chart.point> ptsScen = array.new<chart.point>()
array<chart.point> ptsHi = array.new<chart.point>()
array<chart.point> ptsLo = array.new<chart.point>()
float lastC = na
float lastLo = na
float lastHi = na
int lastT = anchorMs
float minLo = na
float maxHi = na
for i = 0 to n - 1
int hh = array.get(H, i)
int t = anchorMs + hh * 3600000
bool keep = (hh % stepH == 0) or i == n - 1
if keep and t <= maxFutureMs
float c = array.get(EC, i) * k
float sc = array.get(SC, i) * k
float lo = array.get(LO, i) * k
float hi = array.get(HI, i) * k
if not na(c)
array.push(ptsExp, chart.point.from_time(t, c))
lastC := c
lastT := t
if not na(sc)
array.push(ptsScen, chart.point.from_time(t, sc))
if not na(lo) and not na(hi)
array.push(ptsHi, chart.point.from_time(t, hi))
array.push(ptsLo, chart.point.from_time(t, lo))
lastLo := lo
lastHi := hi
minLo := na(minLo) ? lo : math.min(minLo, lo)
maxHi := na(maxHi) ? hi : math.max(maxHi, hi)
if not expired
// the 80% band: one closed polygon - the upper edge left to right, the lower edge back
if showBand and array.size(ptsHi) >= 2
array<chart.point> ring = array.copy(ptsHi)
for j = array.size(ptsLo) - 1 to 0
array.push(ring, array.get(ptsLo, j))
pBand := polyline.new(ring, false, true, xloc.bar_time, cBandEdge, cBandFill, line.style_solid, 1)
if showScen and array.size(ptsScen) >= 2
pScen := polyline.new(ptsScen, false, false, xloc.bar_time, cScen, na, line.style_solid, 2)
if showExp and array.size(ptsExp) >= 2
pExp := polyline.new(ptsExp, false, false, xloc.bar_time, cExp, na, line.style_dashed, 2)
// the AI briefing's key levels across the horizon
if showLvls
for p in SUP
float y = p * k
array.push(LINES, line.new(anchorMs, y, endMs, y, xloc.bar_time, extend.none, cSup, line.style_dotted, 1))
if showLbls
array.push(LABELS, label.new(endMs, y, "S " + fmtPx(y), xloc.bar_time, yloc.price, color.new(cSup, 100), label.style_label_left, cSup, size.tiny))
for p in RES
float y = p * k
array.push(LINES, line.new(anchorMs, y, endMs, y, xloc.bar_time, extend.none, cRes, line.style_dotted, 1))
if showLbls
array.push(LABELS, label.new(endMs, y, "R " + fmtPx(y), xloc.bar_time, yloc.price, color.new(cRes, 100), label.style_label_left, cRes, size.tiny))
// ---- labels
if showLbls
string issued = str.format_time(anchorMs, "dd MMM HH:mm", "UTC") + " UTC"
string head = (modelTag == "" ? "PRISM" : modelTag) + " forecast " + sym + " - issued " + issued
if expired
head := "FORECAST EXPIRED - " + head + "\ncopy a fresh code on www.prismforecasting.com"
else
if not str.contains(str.upper(syminfo.ticker), str.upper(sym))
head += "\n(code is for " + sym + ", this chart is " + syminfo.ticker + ")"
if rebase
head += na(anchorOpenChart) ? "\nnot rebased: the anchor bar is not on this chart" : (anchorExact ? "" : "\nrebased on the bar containing the anchor (approximate)")
if tooFine
head += "\nthis timeframe cannot show 48 h ahead - switch to M15, H1 or H4"
else if tfSec > 3600
head += "\n" + timeframe.period + " chart: every " + str.tostring(stepH) + "th hour of the H1 forecast is drawn - best on H1"
float headY = na(maxHi) ? basePx * k : maxHi
array.push(LABELS, label.new(anchorMs, headY, head, xloc.bar_time, yloc.price, expired ? color.new(#b91c1c, 10) : cLabelBg, label.style_label_down, cLabelTx, size.small))
if not expired and not na(lastC)
float basePxK = basePx * k
float chg = basePxK > 0 ? (lastC / basePxK - 1) * 100 : na
float half = not na(lastLo) and not na(lastHi) ? (lastHi - lastLo) / 2 : na
bool flat = not na(half) and half > 0 and math.abs(lastC - basePxK) < 0.10 * half
string chgTxt = na(chg) ? "" : (flat ? "about flat " : (chg >= 0 ? "bullish " : "bearish ")) + (chg >= 0 ? "+" : "") + str.tostring(chg, "#.##") + "%"
string dirTxt = na(pUp) ? "" : (math.abs(pUp - 50) < 6 ? "no clear edge - " + str.tostring(pUp, "#") + "% of paths end up" : (pUp >= 50 ? "up " + str.tostring(pUp, "#") + "% of paths" : "down " + str.tostring(100 - pUp, "#") + "% of paths"))
string endTxt = "expected end close " + fmtPx(lastC) + (chgTxt == "" ? "" : " (" + chgTxt + ")")
if not na(lastLo) and not na(lastHi)
endTxt += "\n80% of closes: " + fmtPx(lastLo) + " - " + fmtPx(lastHi)
if dirTxt != ""
endTxt += "\nend direction: " + dirTxt
array.push(LABELS, label.new(lastT, lastC, endTxt, xloc.bar_time, yloc.price, cLabelBg, label.style_label_left, cLabelTx, size.small))
if not ok and barstate.islast
for lb in LABELS
label.delete(lb)
array.clear(LABELS)
array.push(LABELS, label.new(bar_index, close, "PRISM Forecast: " + status, xloc.bar_index, yloc.price, cLabelBg, label.style_label_left, cLabelTx, size.small))
The dashed line is the expected (median) close of every forecast hour; the solid line the central scenario β the most representative of the 256 simulated paths, the candles the website draws.
The shaded band is the 10thβ90th percentile of the simulated closes at every hour β the same cone as on the website and in MT5; the end label gives the 80% interval of the end close.
The end label reads βno clear edgeβ when the share of paths ending up is within 6 points of 50% and βabout flatβ when the expected move is smaller than a tenth of the 80% half-width β the website's rules, not a trading signal.
On plans with the AI briefing the code also carries the briefing's support and resistance levels, drawn dotted across the horizon.
Forecast hours are UTC candle boundaries, placed at the chart's bar times; the model's prices are rebased onto your chart's anchor bar.
The MT5 expert fetches the forecast itself every minute and adds the AI briefing on the chart β for the plans that include it.
Metals Β· FX majors & crosses Β· US indices Β· Energy Β· Crypto β forecast jointly by one engine.
All four views describe the same 48 hours and agree with each other exactly β the 15-minute candles aggregate perfectly into their hour, their 4-hour block, their day.
Every candle with full OHLC, the central scenario and its 80% range β a new forecast every hour, fixed at issue.
Install the PRISM app on your phone β full-screen live charts, your own icon, and live alerts from the AI assistant as phone notifications. Free, no store account needed.