// This source code is subject to the terms of the Mozilla Public License 2.0 // Gebouwd voor Leon — buy side / sell side, vastgezet op de key open. // // DE REGEL, ZO SIMPEL ALS HIJ IS // // Als de key open begint, neem de DICHTSTBIJZIJNDE pivot boven en de // dichtstbijzijnde pivot onder. Zet ze vast. Klaar. // // Twee voorwaarden daarbij, allebei omdat een pivot voor Leon een // liquidity level is: // // * ALLEEN UIT DE LOPENDE HANDELSDAG (vanaf 18:00 NY). Een high van // eergisteren is geen liquiditeit meer; die is allang opgeruimd. // * ALLEEN ONGEVEEGD. Is de prijs er al doorheen geweest, dan is de // liquiditeit gepakt en telt het niveau niet meer mee. // // ── WAT HIERVOOR MIS GING, zodat het niet terugkomt ───────────────────────── // // v1/v2 zochten ELKE BAR opnieuw rond de huidige prijs, dus de lijnen // schoven mee. De meting pakt één paar en houdt dat vast. // v3 koos niet de dichtstbijzijnde maar liep expres naar buiten tot een // minimale spanwijdte gehaald was — precies de niveaus die Leon ziet // werden overgeslagen. Dat was optimaliseren voor een getal dat de // MÉTING netjes maakte, niet voor wat hij handelt. // v3 trok bovendien pivots uit de hele chartgeschiedenis in plaats van // uit de lopende dag, en filterde niet op geveegd. Vandaar de // "random" niveaus. // // v4 doet wat er gevraagd is: dichtstbijzijnde, lopende dag, ongeveegd. // // ── WAT DE METING ZEGT (NQ 2018-2026, ~Projects/pivot-kwaliteit/) ─────────── // // 1. Een pivot is GEEN muur. Over ~45 definities reageert de prijs nergens // vaker op een pivot dan op een willekeurige lijn op dezelfde afstand // (eigen placebo per cel). Behandel deze lijnen als DOELWITTEN. // // 2. De SPANWIJDTE tussen buy en sell side bepaalt of de vraag "welke kant // werd gepakt" überhaupt een antwoord heeft. Staat hij te krap, dan raakt // één bar ze allebei. Daarom staat de span in het paneel en kleurt hij // rood onder de waarschuwingsgrens — als MELDING, niet als filter. Je // weet dan vooraf dat het zo'n dag is. // // 3. 10:00 is structureel lastiger dan 03:00 en 07:00: rond de NY-open staat // de nachtstructuur verder weg en loopt het gelijkspel op. // //@version=6 indicator("Key Open Levels", "KO Levels", overlay = true, max_lines_count = 500, max_labels_count = 500) const string VERSIE = "v4" const string GR_P = "Pivots" const string GR_K = "Key opens" const string GR_W = "Weergave" const int MAX_SES = 4 // zoveel key-open paren in beeld // ─── Pivots ────────────────────────────────────────────────────────────────── pivTf = input.timeframe("15", "Pivot-timeframe", group = GR_P, tooltip = "Op welk timeframe de highs en lows gezocht worden. 15 minuten geeft de zwaaien die je op de 1-minuut chart als duidelijk herkent.") pivN = input.int(2, "Sterkte (bars opzij)", minval = 1, maxval = 10, group = GR_P, tooltip = "Hoger = alleen grotere zwaaien. Sterkte 1 pikt ook tussenstations halverwege een beweging op, en die markeren dan NET onder de top die je ziet. Een pivot is pas bekend na `sterkte` bars: 15m sterkte 2 = 45 minuten.") alleenOngeveegd = input.bool(true, "Alleen ongeveegde niveaus", group = GR_P, tooltip = "Is de prijs al door het niveau heen geweest, dan is die liquiditeit gepakt en telt hij niet meer mee.") alleenVandaag = input.bool(true, "Alleen de lopende handelsdag", group = GR_P, tooltip = "Vanaf 18:00 New York. Uit staat = de hele chartgeschiedenis, en dan kun je niveaus van dagen terug krijgen.") // ─── Key opens ─────────────────────────────────────────────────────────────── ko1 = input.string("03:00", "Key open 1", group = GR_K) ko2 = input.string("07:00", "Key open 2", group = GR_K) ko3 = input.string("10:00", "Key open 3", group = GR_K) tz = input.string("America/New_York", "Tijdzone", group = GR_K) // ─── Weergave ──────────────────────────────────────────────────────────────── buyKleur = input.color(#26a69a, "Buy side (boven)", group = GR_W) selKleur = input.color(#ef5350, "Sell side (onder)", group = GR_W) oudKleur = input.color(color.new(#787b86, 45), "Geveegd", group = GR_W) dikte = input.int(2, "Lijndikte actief", minval = 1, maxval = 4, group = GR_W) vervaag = input.int(70, "Vervaging oude sessies", minval = 0, maxval = 95, group = GR_W) spanWaarsch = input.float(15.0, "Waarschuw onder span (punten)", minval = 0.0, step = 0.5, group = GR_W, tooltip = "Puur een melding in het paneel. Onder deze afstand is de kans groot dat één bar beide kanten raakt — dan heeft 'welke kant werd gepakt' geen antwoord.") toonKOlijn = input.bool(true, "Streep op de key open", group = GR_W) toonTab = input.bool(true, "Infopaneel", group = GR_W) mono = input.bool(true, "Monospace lettertype", group = GR_W) f_font() => mono ? font.family_monospace : font.family_default f_pxTxt(float p) => str.tostring(p, format.mintick) // ─── Tijd ──────────────────────────────────────────────────────────────────── // Splitsen op de dubbele punt, zodat "7:00" het net zo goed doet als "07:00". f_isTijd(string hhmm) => array dl = str.split(str.replace_all(hhmm, " ", ""), ":") bool ok = dl.size() == 2 float h = ok ? str.tonumber(dl.get(0)) : na float m = ok ? str.tonumber(dl.get(1)) : na na(h) or na(m) ? false : hour(time, tz) == int(h) and minute(time, tz) == int(m) isKO = f_isTijd(ko1) or f_isTijd(ko2) or f_isTijd(ko3) nieuweKO = isKO and not isKO[1] // Nieuwe handelsdag om 18:00 NY. Zo geschreven dat hij ook over maand- en // jaargrenzen klopt — een datum +1 doen gaat daar juist fout. nieuweDag = hour(time, tz) == 18 and hour(time, tz)[1] != 18 // ─── Pivots verzamelen ─────────────────────────────────────────────────────── // lookahead_off plus de ingebouwde vertraging van ta.pivothigh: geen // toekomstkennis. De waarde komt pas binnen als de HTF-bar gesloten is. f_piv() => [ta.pivothigh(high, pivN, pivN), ta.pivotlow(low, pivN, pivN)] [nwH, nwL] = request.security(syminfo.tickerid, pivTf, f_piv(), lookahead = barmerge.lookahead_off) var array hiPx = array.new() var array hiOp = array.new() // true = nog niet geveegd var array loPx = array.new() var array loOp = array.new() if alleenVandaag and nieuweDag hiPx.clear() hiOp.clear() loPx.clear() loOp.clear() if not na(nwH) and (na(nwH[1]) or nwH != nwH[1]) hiPx.unshift(nwH) hiOp.unshift(true) if not na(nwL) and (na(nwL[1]) or nwL != nwL[1]) loPx.unshift(nwL) loOp.unshift(true) // Vegen bijhouden. Een high die de prijs al geraakt heeft is geen liquiditeit // meer, ook niet als de prijs daarna weer onder dat niveau zakt. if hiOp.size() > 0 for i = 0 to hiOp.size() - 1 if hiOp.get(i) and high >= hiPx.get(i) hiOp.set(i, false) if loOp.size() > 0 for i = 0 to loOp.size() - 1 if loOp.get(i) and low <= loPx.get(i) loOp.set(i, false) // ─── Het paar kiezen: DICHTSTBIJZIJNDE, verder niets ───────────────────────── f_dichtstbij(array px, array op, float ref, bool boven) => float best = na if px.size() > 0 for i = 0 to px.size() - 1 float v = px.get(i) if (not alleenOngeveegd or op.get(i)) and (boven ? v > ref : v < ref) if na(best) or (boven ? v < best : v > best) best := v best // ─── Sessies: één vastgezet paar per key open ──────────────────────────────── var array sUp = array.new() var array sDn = array.new() var array sUpSw = array.new() var array sDnSw = array.new() var array sAct = array.new() var array sLnU = array.new() var array sLnD = array.new() var array