One TradingView indicator for the SPX chart. It draws the day’s gamma levels, the expected-move band, and the running high and low, with a Level Key that names every line.
Paste the Suite the day’s prompt returns over the saved script and save. The indicator on the chart redraws in place. If you changed settings by hand, reset them to their defaults once (Chapter 12, Section 7).
Charting on Webull? The three WebullScript indicators are printed in full in Appendix B, ready to paste into Webull’s script editor.
//@version=5 indicator("0DTE Playbook - GEX Suite", overlay=true, max_lines_count=60, max_labels_count=60) // ===================== SECTIONS (show/hide) ===================== showGex = input.bool(true, "GEX Levels", group="Sections") showEM = input.bool(true, "Expected Move", group="Sections") showHL = input.bool(true, "Intraday HOD/LOD", group="Sections") // ===================== LEVEL KEY (corner table) ===================== showTable = input.bool(true, "Show Level Key (table)", group="Level Key") tablePos = input.string("Top Right", "Key position", options=["Top Right", "Bottom Right", "Top Left", "Bottom Left"], group="Level Key") // ===================== ON-CHART LABELS ===================== labelStyle = input.string("Off", "On-chart labels", options=["Full names", "Codes", "Off"], group="On-chart Labels", tooltip="Names/codes printed on the lines themselves, at each line's true price; close levels can overlap. Default Off - the corner key handles identification.") // ===================== GEX LEVELS (enter the day's levels; 0 = not drawn) ===================== gt = input.float(0, "GF - Gamma Flip", group="GEX: Gamma Flip & Pin") pn = input.float(0, "PN - Pin", group="GEX: Gamma Flip & Pin") rt = input.float(0, "RT - Resistance Top", group="GEX: Resistance Zone") rb = input.float(0, "RB - Resistance Bottom", group="GEX: Resistance Zone") st = input.float(0, "ST - Support Top", group="GEX: Support Zone") sb = input.float(0, "SB - Support Bottom", group="GEX: Support Zone") dt = input.float(0, "DT - Danger Top", group="GEX: Danger Zone") db = input.float(0, "DB - Danger Bottom", group="GEX: Danger Zone") cw = input.float(0, "CW - Call Wall", group="GEX: Walls") pw = input.float(0, "PW - Put Wall", group="GEX: Walls") // ===================== EXPECTED MOVE (set once at the open) ===================== emu = input.float(0, "EM+ (upper)", group="Expected Move") emd = input.float(0, "EM- (lower)", group="Expected Move") // ===================== INTRADAY HOD/LOD (auto-tracked) ===================== var float hod = na var float lod = na if ta.change(time("D")) != 0 hod := high lod := low else hod := math.max(hod, high) lod := math.min(lod, low) // ===================== DRAWING ===================== var line[] lineArr = array.new_line() var label[] lblArr = array.new_label() var table tbl = na var float[] lvlPrice = array.new_float() var string[] lvlCode = array.new_string() var string[] lvlName = array.new_string() var color[] lvlColor = array.new_color() // Level Key column alignment, set once here and used by every key cell. keyTextAlign = text.align_left keyPriceAlign = text.align_right // Draw the line at its true price; stash level data for the label pass and the key. // A level set to 0 (or not yet available) is skipped: no line, no key row. addLevel(float price, string code, string name, color col, bool dashed, int lwidth) => if price > 0 stl = dashed ? line.style_dashed : line.style_solid array.push(lineArr, line.new(bar_index - 1, price, bar_index, price, xloc=xloc.bar_index, extend=extend.both, color=col, style=stl, width=lwidth)) array.push(lvlPrice, price) array.push(lvlCode, code) array.push(lvlName, name) array.push(lvlColor, col) if barstate.islast // clear previous drawings / collections / key for ln in lineArr line.delete(ln) array.clear(lineArr) for lb in lblArr label.delete(lb) array.clear(lblArr) if not na(tbl) table.delete(tbl) array.clear(lvlPrice) array.clear(lvlCode) array.clear(lvlName) array.clear(lvlColor) if showGex addLevel(gt, "GF", "Gamma Flip", color.white, false, 3) addLevel(pn, "PN", "Pin", color.yellow, false, 2) addLevel(rt, "RT", "Resistance Top", color.red, false, 1) addLevel(rb, "RB", "Resistance Bottom", color.red, false, 1) addLevel(st, "ST", "Support Top", color.green, false, 1) addLevel(sb, "SB", "Support Bottom", color.green, false, 1) addLevel(dt, "DT", "Danger Top", color.orange, false, 1) addLevel(db, "DB", "Danger Bottom", color.orange, false, 1) addLevel(cw, "CW", "Call Wall", color.rgb(214, 108, 108), true, 1) addLevel(pw, "PW", "Put Wall", color.lime, true, 1) if showEM addLevel(emu, "EM+", "Expected Move Upper", color.teal, true, 1) addLevel(emd, "EM-", "Expected Move Lower", color.teal, true, 1) if showHL addLevel(hod, "HOD", "High of Day", color.fuchsia, true, 1) addLevel(lod, "LOD", "Low of Day", color.fuchsia, true, 1) // ---- ON-CHART LABELS (optional): one chip per line, at the line's true price ---- if labelStyle != "Off" and array.size(lvlPrice) > 0 for i = 0 to array.size(lvlPrice) - 1 txt = labelStyle == "Full names" ? array.get(lvlName, i) : array.get(lvlCode, i) array.push(lblArr, label.new(bar_index + 3, array.get(lvlPrice, i), txt, style=label.style_label_left, textcolor=array.get(lvlColor, i), color=color.new(color.black, 10), size=size.small)) // ---- LEVEL KEY (optional): clean corner table, sorted high -> low ---- if showTable and array.size(lvlPrice) > 0 pos = tablePos == "Top Right" ? position.top_right : tablePos == "Bottom Right" ? position.bottom_right : tablePos == "Top Left" ? position.top_left : position.bottom_left tbl := table.new(pos, 3, array.size(lvlPrice) + 1, bgcolor=color.new(color.black, 15), frame_color=color.new(color.gray, 40), frame_width=1, border_color=color.new(color.gray, 60), border_width=1) table.cell(tbl, 0, 0, "", text_size=size.small) table.cell(tbl, 1, 0, "Level", text_color=color.gray, text_size=size.small, text_halign=keyTextAlign) table.cell(tbl, 2, 0, "Price", text_color=color.gray, text_size=size.small, text_halign=keyPriceAlign) idxT = array.sort_indices(lvlPrice, order.descending) for k = 0 to array.size(idxT) - 1 i = array.get(idxT, k) c = array.get(lvlColor, i) table.cell(tbl, 0, k + 1, array.get(lvlCode, i), text_color=c, text_size=size.small, text_halign=keyTextAlign) table.cell(tbl, 1, k + 1, array.get(lvlName, i), text_color=c, text_size=size.small, text_halign=keyTextAlign) table.cell(tbl, 2, k + 1, str.tostring(array.get(lvlPrice, i), "#.##"), text_color=c, text_size=size.small, text_halign=keyPriceAlign)
A level set to 0 is not drawn and drops out of the key. The flip and the pin are drawn heavier than the rest, the flip heaviest; the walls, the expected-move bounds, and the day’s high and low are dashed.
| Code | Level | On the chart | Section | Source |
|---|---|---|---|---|
| GF | Gamma Flip | GEX Levels | Entered | |
| PN | Pin | GEX Levels | Entered | |
| RT · RB | Resistance Top · Bottom | GEX Levels | Entered | |
| ST · SB | Support Top · Bottom | GEX Levels | Entered | |
| DT · DB | Danger Top · Bottom | GEX Levels | Entered | |
| CW | Call Wall | GEX Levels | Entered | |
| PW | Put Wall | GEX Levels | Entered | |
| EM+ · EM− | Expected Move Upper · Lower | Expected Move | Entered at the open | |
| HOD · LOD | High of Day · Low of Day | Intraday HOD/LOD | Computed |