//@version=6 indicator("Cycle Raster", shorttitle="Raster", overlay=true, max_boxes_count=500, max_lines_count=500, max_labels_count=500) // ═══════════════════════════════════════════════════════════════════════════ // The cycle grid as a background, drawn for the WHOLE day — including the // cycles that have not happened yet. // // WHY IT HAS TO BE BOXES. bgcolor() can only paint a bar that exists, which is // why a shaded grid appears to grow bar by bar and never shows what is coming. // Boxes anchored on xloc.bar_time take a timestamp rather than a bar, and Pine // will happily draw one past the last bar. That is the whole trick, and it is // the only way to see the next cycle arrive before price does. // // For bar replay this matters more than it does live: you can see where the // boundary falls while you are stepping through, instead of discovering it one // bar late. // // ── THE GRID ─────────────────────────────────────────────────────────────── // Identical to CyclePOC and to his own SMT file, verified minute by minute // over a full day (1,440 minutes x 4 grids, 0 differences). New York time, // DST automatic. Anchor 02:30, end 15:59. // // 10M 10 min 81/day // 30M 30 min 27/day // 90M 90 min 9/day LO1 02:30-03:59 LO2 04:00-05:29 LO3 05:30-06:59 // AM1 07:00-08:29 AM2 08:30-09:59 AM3 10:00-11:29 // PM1 11:30-12:59 PM2 13:00-14:29 PM3 14:30-15:59 // Asia 18:00-02:29 . London 02:30-06:59 . NY AM 07:00-11:29 . NY PM 11:30-15:59 // 16:00-17:59 has no session and no cycle, by design — it is left unshaded. // // Everything is redrawn on the last bar rather than accumulated, so the grid // is always right and never has to be repaired. That also makes it correct in // replay, where "the last bar" moves backwards. // ═══════════════════════════════════════════════════════════════════════════ gC = "Grid" mode = input.string("90M", "Cycle", options=["Sessions", "90M", "30M", "10M"], group=gC) daysAhead = input.int(1, "Days drawn", minval=1, maxval=3, group=gC, tooltip="1 = today only, which already includes every cycle still to come. 2 or 3 also draws the days after it — useful when you are stepping through a replay and want the next session already on screen. More days means more boxes; the 10M grid uses 81 of the 500 available per day.") showPast = input.bool(true, "Shade cycles that have already happened", group=gC, tooltip="Off leaves the past clean and shades only what is still to come, which is the whole point of the thing.") gS = "Look" shadeA = input.color(color.new(color.gray, 93), "Shading", inline="s", group=gS) shadeB = input.color(color.new(color.gray, 97), "alternating", inline="s", group=gS) futureA = input.color(color.new(color.blue, 93), "Future", inline="f", group=gS) futureB = input.color(color.new(color.blue, 97), "alternating", inline="f", group=gS) showEdge = input.bool(true, "Boundary lines", group=gS) edgeCol = input.color(color.new(color.gray, 55), " └ colour", group=gS) showName = input.bool(true, "Cycle names", group=gS) nameCol = input.color(color.new(color.gray, 35), " └ colour", group=gS) nameSzIn = input.string("tiny", " └ size", options=["tiny","small","normal"], group=gS) nameSz = nameSzIn == "tiny" ? size.tiny : nameSzIn == "small" ? size.small : size.normal NY = "America/New_York" // A background box needs a top and a bottom, and there is no "full height" in // Pine. Half again above and below the current price is far outside any // sensible view on an index future, so the box reads as a background. f_top() => close * 1.5 f_bot() => close * 0.5 var array bx = array.new_box() var array ln = array.new_line() var array