"""Diagramele pentru summaries/08_ANALIZA_ZILNICA/04_pivoti_si_proiectii.md. Toate prețurile sunt construite (fără ticker). Rulează: .venv/bin/python gen/az_pivoti.py Înlocuiește în .md fiecare bloc ... cu output-ul generatorului. Ca să schimbi o cifră, o schimbi aici și rulezi din nou. Pivoții sunt „în trepte": fiecare perioadă (lună / trimestru) are nivelurile ei, calculate din H/L/C ale perioadei anterioare. `PChart` desenează aceste segmente cu aceeași scară ca a candelelor (`_y`, `_cx` din svg_candles) — nu există coordonate scrise de mână. """ import re import sys from dataclasses import dataclass, field from pathlib import Path sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402 MD = Path("/workspace/atm-curs-dl/summaries/08_ANALIZA_ZILNICA/04_pivoti_si_proiectii.md") GRAY, RED, GREEN, BLUE, ORANGE, PURPLE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#6a3d9a" STYLE = dict(gap=15, body_w=9, left=44, width=830) svgs = {} from pchart import PChart, Seg, spread # noqa: E402 def pivots(bars): """floor pivots din H/L/C ale perioadei încheiate""" H, L, C = max(c.h for c in bars), min(c.l for c in bars), bars[-1].c P = (H + L + C) / 3 return dict(H=H, L=L, C=C, P=P, R1=2 * P - L, S1=2 * P - H, R2=P + (H - L), S2=P - (H - L)) def px_ratio(ch, a, b, c): """raportul (c-b)/(b-a) măsurat în pixeli, după render""" return (ch._y(b) - ch._y(c)) / (ch._y(a) - ch._y(b)) def f2(x): return f"{x:.2f}" # ================================================================ 1. calcul + proiecție bull (daily, pivot monthly) A0, B0, N = 0, 21, 42 PA = 48.00 # pivotul lunii A (calculat din luna dinaintea ei, nu e pe desen) a_cl = [47.0, 46.8, 47.4, 47.1, 48.0, 48.5, 48.2, 49.1, 49.7, 49.3, 50.2, 50.8, 50.4, 51.3, 51.8, 51.5, 51.9, 51.3, 51.7, 51.1, 51.4] b_cl = [51.0, 50.6, 50.4, 51.1, 51.8, 52.5, 52.2, 53.0, 53.5, 53.0, 52.7, 53.3, 54.0, 54.6, 54.3, 55.0, 55.5, 54.9, 54.5, 54.9, 55.1] w = [(0.3, 0.3)] * 21 + [(0.3, 0.3), (0.2, 0.3), (0.3, 0.35), (0.3, 0.3)] + [(0.3, 0.3)] * 17 cds = from_closes(a_cl + b_cl, wicks=w, start=47.3) pv = pivots(cds[A0:B0]) b = cds[B0:] assert pv["P"] > PA, "proiecția trebuie să fie bull: pivotul lunii B peste pivotul lunii A" assert max(c.h for c in b) >= pv["R2"], "în proiecția bull exemplul trebuie să atingă R2" hit_r1 = B0 + next(i for i, c in enumerate(b) if c.h >= pv["R1"]) hit_r2 = B0 + next(i for i, c in enumerate(b) if c.h >= pv["R2"]) low_b = min(c.l for c in b) i_low_b = B0 + min(range(len(b)), key=lambda i: b[i].l) assert abs(low_b - pv["P"]) <= 0.15 and low_b > pv["S1"], "pullback-ul lunii B trebuie să se oprească la pivot, peste S1" P1 = pv assert hit_r1 - B0 + 1 == 9 and hit_r2 - B0 + 1 == 17, "textul: R1 în ziua 9, R2 în ziua 17" print(f"D1 luna A H {f2(pv['H'])} L {f2(pv['L'])} C {f2(pv['C'])} -> P {f2(pv['P'])} R1 {f2(pv['R1'])} " f"R2 {f2(pv['R2'])} S1 {f2(pv['S1'])}; low B {f2(low_b)} la {i_low_b}; R1 la {hit_r1}, R2 la {hit_r2}") ch = PChart(candles=cds, segs=[Seg(A0, B0 - 1, PA, f"pivot A {PA:.2f}", ORANGE, "lm"), Seg(B0, N - 1, pv["R2"], f"R2 {f2(pv['R2'])}", BLUE), Seg(B0, N - 1, pv["R1"], f"R1 {f2(pv['R1'])}", BLUE), Seg(B0, N - 1, pv["P"], f"pivot B {f2(pv['P'])}", ORANGE), Seg(B0, N - 1, pv["S1"], f"S1 {f2(pv['S1'])}", RED)], seps=[B0], marks=[Mark(i_low_b, "ține pivotul", ORANGE, "below", dy=4), Mark(hit_r1, "R1", BLUE, "above"), Mark(hit_r2, "R2", BLUE, "above")], scale=20, title="Exemplu construit (fără ticker) — daily, pivot monthly", caption=f"Pivotul B = (H {f2(pv['H'])} + L {f2(pv['L'])} + C {f2(pv['C'])}) / 3 = {f2(pv['P'])}, peste pivotul A {PA:.2f}. Presupunem o proiecție bull, așa cum o dă platforma.", **STYLE) svgs["bull"] = ch.render() # ================================================================ 2. proiecție bear + reject sub pivot (short) PA2 = 52.00 a_cl = [53.0, 53.2, 52.6, 52.9, 52.0, 51.5, 51.8, 50.9, 50.3, 50.7, 49.8, 49.2, 49.6, 48.7, 48.2, 48.5, 48.1, 48.7, 48.3, 48.9, 48.6] b_cl = [49.0, 49.4, 49.7, 49.0, 48.8, 48.3, 48.6, 47.9, 47.3, 47.6, 46.9, 46.3, 46.7, 46.0, 45.5, 45.8, 45.0, 44.6, 44.9, 44.5, 44.8] w = [(0.3, 0.3)] * 21 + [(0.3, 0.3), (0.3, 0.3), (0.6, 0.2), (0.2, 0.3)] + [(0.3, 0.3)] * 17 cds = from_closes(a_cl + b_cl, wicks=w, start=52.7) pv = pivots(cds[A0:B0]) b = cds[B0:] RJ, TR = B0 + 2, B0 + 3 assert pv["P"] < PA2, "proiecția trebuie să fie bear" assert cds[RJ].h > pv["P"] and cds[RJ].c < pv["P"], "bara de reject: codiță peste pivot, închidere sub" assert all(c.h < pv["P"] for c in cds[B0:RJ]), "înainte de reject prețul e sub pivot" assert cds[TR].c < cds[RJ].l, "trigger-ul short: închidere sub low-ul barei de reject" entry = cds[TR].c sl = round(cds[RJ].h + 0.10, 2) risk = sl - entry tp = round(entry - 2 * risk, 2) hit = next(i for i in range(TR + 1, N) if cds[i].l <= tp) assert min(c.l for c in b) <= pv["S2"], "în proiecția bear exemplul atinge S2" assert max(c.h for c in cds[TR + 1:]) < pv["P"], "după reject prețul nu mai revine peste pivot" hit_s1 = next(i for i in range(B0, N) if cds[i].l <= pv["S1"]) hit_s2 = next(i for i in range(B0, N) if cds[i].l <= pv["S2"]) assert hit - B0 + 1 == 12, "textul: TP în a 12-a zi" print(f"D2 P {f2(pv['P'])} R1 {f2(pv['R1'])} S1 {f2(pv['S1'])} S2 {f2(pv['S2'])}; reject high {f2(cds[RJ].h)} " f"close {f2(cds[RJ].c)}; entry {f2(entry)} SL {f2(sl)} risc {f2(risk)} TP {f2(tp)} atins la {hit}; S1 {hit_s1} S2 {hit_s2}") ch = PChart(candles=cds, segs=[Seg(A0, B0 - 1, PA2, f"pivot A {PA2:.2f}", ORANGE, "lm"), Seg(B0, N - 1, pv["R1"], f"R1 {f2(pv['R1'])}", BLUE), Seg(B0, N - 1, pv["P"], f"pivot B {f2(pv['P'])}", ORANGE), Seg(B0, N - 1, pv["S1"], f"S1 {f2(pv['S1'])}", RED), Seg(B0, N - 1, pv["S2"], f"S2 {f2(pv['S2'])}", RED)], seps=[B0], levels=[Level(sl, f"SL {f2(sl)}", RED, label_at="left"), Level(entry, f"intrare short {f2(entry)}", GREEN, label_at="left"), Level(tp, f"TP {f2(tp)} = 2R", BLUE, label_at="left")], marks=[Mark(RJ, "reject", RED, "above", dy=-4)], scale=20, title="Exemplu construit (fără ticker) — daily, pivot monthly", caption=f"Pivotul B {f2(pv['P'])}, sub pivotul A {PA2:.2f}. Presupunem o proiecție bear, așa cum o dă platforma. Rejectul sub pivot dă short-ul.", **STYLE) svg_ = ch.render() assert abs(1 / px_ratio(ch, tp, entry, sl) - 2.0) < 0.01, "2R nu e desenat la 2x risc" svgs["bear"] = svg_ BEAR = dict(pv=pv, entry=entry, sl=sl, tp=tp, risk=risk, rj_h=cds[RJ].h, rj_c=cds[RJ].c, PA=PA2) # ================================================================ 3-4. pivoți divergenți = trading range PAd, R1Ad, S1Ad = 50.00, 54.00, 46.00 # nivelurile lunii A (din luna dinaintea ei) a_cl = [51.6, 52.3, 52.9, 53.2, 52.6, 52.0, 51.3, 50.6, 49.9, 49.2, 48.7, 48.4, 48.9, 49.6, 50.3, 50.9, 51.4, 50.8, 50.2, 50.6, 50.2] aw = [(0.3, 0.3)] * 21 # 3. NU: luna B rămâne între S1 și R1 b_nu = [50.8, 51.5, 52.2, 52.6, 52.1, 51.4, 50.6, 49.8, 49.0, 48.5, 48.9, 49.7, 50.5, 51.3, 52.0, 52.5, 52.0, 51.2, 50.5, 49.8, 50.3] cds = from_closes(a_cl + b_nu, wicks=aw + [(0.3, 0.3)] * 21, start=51.0) pv = pivots(cds[A0:B0]) b = cds[B0:] assert pv["P"] > PAd and pv["S1"] > S1Ad and pv["R1"] < R1Ad, "pivoții nu sunt divergenți" assert max(c.h for c in b) < pv["R1"] and min(c.l for c in b) > pv["S1"], "luna B trebuie să rămână în range" top_b = B0 + max(range(21), key=lambda i: b[i].h) bot_b = B0 + min(range(21), key=lambda i: b[i].l) DIV = pv print(f"D3 A-levels P {PAd} R1 {R1Ad} S1 {S1Ad}; B: P {f2(pv['P'])} R1 {f2(pv['R1'])} S1 {f2(pv['S1'])} R2 {f2(pv['R2'])}; " f"B high {f2(max(c.h for c in b))} low {f2(min(c.l for c in b))}") ch = PChart(candles=cds, segs=[Seg(A0, B0 - 1, R1Ad, f"R1 A {R1Ad:.2f}", BLUE, "lm"), Seg(A0, B0 - 1, PAd, f"pivot A {PAd:.2f}", ORANGE, "lm"), Seg(A0, B0 - 1, S1Ad, f"S1 A {S1Ad:.2f}", RED, "lm"), Seg(B0, N - 1, pv["R1"], f"R1 B {f2(pv['R1'])} (mai jos)", BLUE), Seg(B0, N - 1, pv["P"], f"pivot B {f2(pv['P'])} (mai sus)", ORANGE), Seg(B0, N - 1, pv["S1"], f"S1 B {f2(pv['S1'])} (mai sus)", RED)], seps=[B0], marks=[Mark(top_b, "nu atinge R1", GRAY, "above", dy=-12), Mark(bot_b, "nu atinge S1", GRAY, "below", dy=14)], scale=24, title="Exemplu construit (fără ticker) — daily, pivot monthly", caption="Suport și pivot mai sus, rezistență mai jos: luna B e un trading range.", **STYLE) svgs["divergenti_nu"] = ch.render() # 4. DA: luna B atinge R1 și continuă b_da = [50.8, 51.4, 52.0, 52.6, 53.4, 53.9, 53.6, 53.4, 53.9, 54.3, 54.9, 54.6, 55.2, 55.7, 55.4, 56.0, 56.5, 56.2, 56.6, 56.3, 56.8] bw = [(0.3, 0.3)] * 7 + [(0.2, 0.3), (0.3, 0.2)] + [(0.3, 0.3)] * 12 cds = from_closes(a_cl + b_da, wicks=aw + bw, start=51.0) pv = pivots(cds[A0:B0]) BO, RT, CF = B0 + 4, B0 + 7, B0 + 8 assert abs(pv["P"] - DIV["P"]) < 1e-9, "luna A trebuie să fie aceeași ca la diagrama NU" assert all(c.c <= pv["R1"] for c in cds[B0:BO]) and cds[BO].c > pv["R1"], "bara 25 e prima închidere peste R1 B" assert abs(cds[RT].l - pv["R1"]) <= 0.12 and cds[RT].c > pv["R1"], "retestul trebuie să atingă R1 B și să închidă peste" assert cds[CF].c > cds[RT].h, "candela din dreapta confirmă retestul" entry = cds[CF].c sl = round(cds[RT].l - 0.20, 2) risk = entry - sl tp = round(pv["R2"], 2) R = (tp - entry) / risk hit = next(i for i in range(CF + 1, N) if cds[i].h >= tp) hit_r1a = next(i for i in range(CF, N) if cds[i].h >= R1Ad) assert R >= 2, R print(f"D4 breakout {f2(cds[BO].c)} retest low {f2(cds[RT].l)} entry {f2(entry)} SL {f2(sl)} risc {f2(risk)} " f"TP R2 {f2(tp)} = {R:.2f}R atins la {hit}; R1 A atins la {hit_r1a}") DIVT = dict(entry=entry, sl=sl, tp=tp, R=R, bo=cds[BO].c, rt=cds[RT].l, risk=risk) ch = PChart(candles=cds, segs=[Seg(A0, B0 - 1, R1Ad, f"R1 A {R1Ad:.2f}", BLUE, "lm"), Seg(A0, B0 - 1, PAd, f"pivot A {PAd:.2f}", ORANGE, "lm"), Seg(A0, B0 - 1, S1Ad, f"S1 A {S1Ad:.2f}", RED, "lm"), Seg(B0, N - 1, pv["R1"], f"R1 B {f2(pv['R1'])}", BLUE), Seg(B0, N - 1, pv["P"], f"pivot B {f2(pv['P'])}", ORANGE), Seg(B0, N - 1, pv["S1"], f"S1 B {f2(pv['S1'])}", RED)], seps=[B0], levels=[Level(sl, f"SL {f2(sl)}", RED, label_at="left"), Level(entry, f"intrare {f2(entry)}", GREEN, label_at="left"), Level(tp, f"TP = R2 B {f2(tp)} = {R:.1f}R", BLUE, label_at="left")], marks=[Mark(BO, "close peste R1 B", GREEN, "above", dy=-18), Mark(RT, "retest", ORANGE, "below", dy=4)], scale=24, title="Exemplu construit (fără ticker) — daily, pivot monthly", caption=f"Aceeași lună A. De data asta luna B atinge R1 ({f2(pv['R1'])}) și merge spre R2.", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, sl, entry, tp) - R) < 0.01 svgs["divergenti_da"] = svg_ # ================================================================ 5-6. proiecție „inside" = compresie PAi, R1Ai, S1Ai = 50.00, 55.00, 45.00 a_cl = [50.6, 51.1, 51.5, 51.0, 50.4, 49.9, 49.6, 50.1, 50.7, 51.2, 51.4, 50.9, 50.3, 49.8, 50.2, 50.8, 51.2, 50.7, 50.3, 50.6, 50.9] aw = [(0.25, 0.25)] * 21 av = [52, 48, 50, 46, 44, 47, 45, 42, 44, 43, 40, 42, 41, 38, 40, 39, 37, 38, 36, 37, 35] # 5. NU: luna B stă în compresie b_nu = [50.5, 51.0, 51.5, 51.2, 50.6, 50.2, 50.5, 50.9, 51.3, 51.0, 50.6, 50.4, 50.7, 51.1, 50.9, 50.6, 50.5, 50.8, 51.0, 50.8, 50.7] bv = [34, 33, 35, 31, 30, 32, 29, 28, 30, 27, 26, 27, 25, 26, 24, 25, 23, 24, 22, 23, 22] cds = from_closes(a_cl + b_nu, wicks=aw + [(0.2, 0.2)] * 21, vols=av + bv, start=50.2) pv = pivots(cds[A0:B0]) b = cds[B0:] wa, wb = R1Ai - S1Ai, pv["R1"] - pv["S1"] assert abs(wb - (pv["H"] - pv["L"])) < 1e-9, "R1 - S1 = H - L al lunii anterioare" assert wb / wa < 0.35, "proiecția B nu e inside față de A" assert max(c.h for c in b) <= pv["R1"] and min(c.l for c in b) >= pv["S1"], "luna B trebuie să rămână între S1 și R1" INS = pv print(f"D5 A: H {f2(pv['H'])} L {f2(pv['L'])} C {f2(pv['C'])} -> P {f2(pv['P'])} R1 {f2(pv['R1'])} S1 {f2(pv['S1'])}; " f"lățime A {wa:.2f} B {wb:.2f} ({wb / wa:.0%})") ch = PChart(candles=cds, segs=[Seg(A0, B0 - 1, R1Ai, f"R1 A {R1Ai:.2f}", BLUE, "lm"), Seg(A0, B0 - 1, PAi, f"pivot A {PAi:.2f}", ORANGE, "lm"), Seg(A0, B0 - 1, S1Ai, f"S1 A {S1Ai:.2f}", RED, "lm"), Seg(B0, N - 1, pv["R1"], f"R1 B {f2(pv['R1'])}", BLUE), Seg(B0, N - 1, pv["S1"], f"S1 B {f2(pv['S1'])}", RED)], seps=[B0], marks=[Mark(10, "luna A: range îngust", GRAY, "above", dy=-24), Mark(B0 + 10, "luna B: compresie", GRAY, "below", dy=30)], scale=22, vol_h=70, title="Exemplu construit (fără ticker) — daily, pivot monthly", caption=f"R1 − S1 = H − L al lunii trecute: {wb:.2f} față de {wa:.2f}. Proiecție inside, volum în scădere.", **STYLE) svgs["inside_nu"] = ch.render() # 6. DA: din compresie, breakout cu volum până la rezistența lunii trecute b_da = [50.5, 51.0, 51.5, 51.1, 50.6, 50.2, 50.7, 51.3, 51.4, 51.3, 51.6, 52.3, 53.0, 53.5, 53.2, 53.9, 54.4, 54.1, 54.7, 55.2, 55.0] bw = [(0.2, 0.2)] * 7 + [(0.15, 0.15)] * 4 + [(0.1, 0.2)] + [(0.3, 0.3)] * 9 bv = [34, 33, 35, 31, 30, 32, 29, 28, 27, 26, 30, 92, 70, 64, 48, 60, 58, 44, 55, 57, 45] cds = from_closes(a_cl + b_da, wicks=aw + bw, vols=av + bv, start=50.2) pv = pivots(cds[A0:B0]) BK = B0 + 11 assert abs(pv["P"] - INS["P"]) < 1e-9 assert all(c.c <= pv["R1"] for c in cds[B0:BK]) and cds[BK].c > pv["R1"], "bara 32 e prima închidere peste R1 B" assert bv[11] >= 2.5 * sum(bv[1:11]) / 10, "breakout-ul trebuie să aibă volum clar peste media compresiei" entry = cds[BK].c hl = min(c.l for c in cds[BK - 3:BK]) sl = round(hl - 0.10, 2) risk = entry - sl tp = R1Ai R = (tp - entry) / risk hit = next(i for i in range(BK + 1, N) if cds[i].h >= tp) assert R >= 2, R assert min(c.l for c in cds[BK + 1:]) > sl assert hit - B0 + 1 == 19 and BK - B0 + 1 == 12, "textul: breakout ziua 12, TP ziua 19" assert all(abs(cds[i].l - 51.15) < 0.01 for i in range(BK - 3, BK)), "textul: ultimele trei low-uri la 51.15" print(f"D6 breakout close {f2(entry)} vol {bv[11]} vs medie {sum(bv[1:11]) / 10:.0f}; HL {f2(hl)} SL {f2(sl)} " f"risc {f2(risk)} TP {f2(tp)} = {R:.2f}R atins la {hit}") INST = dict(entry=entry, sl=sl, tp=tp, R=R, risk=risk, vol=bv[11], vavg=sum(bv[1:11]) / 10) ch = PChart(candles=cds, segs=[Seg(A0, B0 - 1, PAi, f"pivot A {PAi:.2f}", ORANGE, "lm"), Seg(A0, B0 - 1, S1Ai, f"S1 A {S1Ai:.2f}", RED, "lm"), Seg(B0, N - 1, pv["R1"], f"R1 B {f2(pv['R1'])}", PURPLE), Seg(B0, N - 1, pv["S1"], f"S1 B {f2(pv['S1'])}", RED)], seps=[B0], levels=[Level(sl, f"SL {f2(sl)}", RED, label_at="left"), Level(entry, f"intrare {f2(entry)}", GREEN, label_at="left"), Level(tp, f"TP = R1 lunii trecute {f2(tp)} = {R:.1f}R", BLUE, label_at="left")], marks=[Mark(BK, "breakout cu volum", GREEN, "above", dy=-30)], scale=22, vol_h=70, title="Exemplu construit (fără ticker) — daily, pivot monthly", caption="Aceeași compresie; volumul de 3x pe breakout o scoate până la rezistența lunii trecute.", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, sl, entry, tp) - R) < 0.01 svgs["inside_da"] = svg_ # ================================================================ 7. reject deasupra pivotului (suport) — long PIV = 50.00 cl7 = [55.8, 55.2, 55.5, 54.7, 54.1, 54.4, 53.6, 53.0, 53.2, 52.4, 51.8, 51.1, # 0-11 pullback spre pivot 50.5, 50.3, 50.7, 50.4, 50.6, # 12-16 rejecturi pe pivot 50.65, # 17 doji 51.6, # 18 buy doji 52.3, 52.0, 52.8, 53.5, 53.2, 54.0, 54.6, 54.3, 55.0, 55.6, 55.3, 56.0] # 19-30 w7 = [(0.3, 0.3)] * 12 + [(0.2, 0.7), (0.2, 0.6), (0.2, 0.8), (0.2, 0.5), (0.2, 0.6), (0.5, 0.5), (0.2, 0.2)] \ + [(0.3, 0.3)] * 12 cds = from_closes(cl7, wicks=w7, start=56.2) DJ, BD = 17, 18 rej = range(12, 17) assert all(cds[i].l < PIV and cds[i].c > PIV for i in rej), "fiecare reject: codiță sub pivot, închidere peste" assert abs(cds[DJ].o - cds[DJ].c) <= 0.1 and cds[BD].c > cds[DJ].h, "buy doji după doji" entry = cds[BD].c sl = round(min(cds[i].l for i in range(12, BD + 1)) - 0.10, 2) risk = entry - sl tp = round(entry + 2 * risk, 2) hit = next(i for i in range(BD + 1, len(cds)) if cds[i].h >= tp) assert min(c.l for c in cds[BD + 1:]) > PIV assert hit - BD == 12, "textul: TP după 12 săptămâni" print(f"D7 rejecturi lows {[f2(cds[i].l) for i in rej]}; entry {f2(entry)} SL {f2(sl)} risc {f2(risk)} TP {f2(tp)} atins la {hit}") REJL = dict(entry=entry, sl=sl, tp=tp, risk=risk, low=min(cds[i].l for i in rej)) ch = PChart(candles=cds, levels=[Level(PIV, f"pivot quarterly {PIV:.2f}", ORANGE, label_at="right"), Level(sl, f"SL {f2(sl)}", RED, label_at="left"), Level(entry, f"intrare {f2(entry)}", GREEN, label_at="left"), Level(tp, f"TP {f2(tp)} = 2R", BLUE, label_at="left")], marks=[Mark(4, "pullback", GRAY, "below", dy=8), Mark(14, "5 rejecturi", ORANGE, "above", dy=-16), Mark(BD, "buy doji", GREEN, "above", dy=-4), Mark(hit, "TP", BLUE, "above")], scale=22, pad_bot_px=14, title="Exemplu construit (fără ticker) — weekly, pivot quarterly", caption="Codițele coboară sub pivot, close-urile rămân deasupra: pivotul e apărat.", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, sl, entry, tp) - 2.0) < 0.01 svgs["reject_deasupra"] = svg_ # ================================================================ 8. reject sub pivot -> cumperi mai jos, în suportul quarterly PM, PQ = 52.00, 48.00 # pivot monthly, pivot quarterly cl8 = [49.4, 49.9, 50.6, 51.2, 51.7, # 0-4 urcare spre pivotul monthly 51.5, 51.8, 51.4, 51.6, # 5-8 rejecturi sub pivot 50.9, 50.3, 50.6, 49.8, 49.2, 49.5, 48.4, # 9-15 cădere 48.5, 48.55, # 16 hammer, 17 doji în pivotul quarterly 49.1, # 18 buy doji 49.6, 49.4, 50.2, 50.8, 50.5, 51.3, 51.9, 51.6, 52.3, 52.6, 52.4, 53.0] # 19-30 w8 = [(0.3, 0.3)] * 5 + [(0.6, 0.2), (0.5, 0.2), (0.7, 0.2), (0.5, 0.2)] + [(0.3, 0.3)] * 7 \ + [(0.1, 0.4), (0.35, 0.3), (0.2, 0.2)] + [(0.3, 0.3)] * 12 v8 = [40, 42, 45, 44, 41, 38, 36, 40, 35, 50, 55, 42, 58, 60, 44, 62, 70, 48, 66, 52, 40, 55, 58, 42, 54, 57, 40, 52, 50, 41, 49] cds = from_closes(cl8, wicks=w8, vols=v8, start=49.0) HM, DJ, BD = 16, 17, 18 assert all(cds[i].h > PM and cds[i].c < PM for i in range(5, 9)), "rejecturi: codiță peste pivotul monthly, close sub" assert abs(cds[HM].l - PQ) <= 0.1 and cds[HM].c > PQ, "hammer-ul atinge pivotul quarterly și închide peste" assert abs(cds[DJ].o - cds[DJ].c) <= 0.1 and cds[BD].c > cds[DJ].h entry = cds[BD].c sl = round(min(c.l for c in cds[HM:BD + 1]) - 0.20, 2) risk = entry - sl tp1 = PM R1m = (tp1 - entry) / risk tp2 = round(entry + 3 * risk, 2) hit1 = next(i for i in range(BD + 1, len(cds)) if cds[i].h >= tp1) hit2 = next(i for i in range(BD + 1, len(cds)) if cds[i].h >= tp2) assert R1m >= 2, R1m assert min(c.l for c in cds[BD + 1:]) > sl assert v8[HM] > max(v8[9:HM]), "textul: hammer cu cel mai mare volum de la cădere încoace" print(f"D8 rejecturi highs {[f2(cds[i].h) for i in range(5, 9)]}; hammer low {f2(cds[HM].l)}; entry {f2(entry)} SL {f2(sl)} " f"risc {f2(risk)} TP1 {f2(tp1)} = {R1m:.2f}R la {hit1}; TP2 {f2(tp2)} = 3R la {hit2}") REJS = dict(entry=entry, sl=sl, tp1=tp1, R1=R1m, tp2=tp2, risk=risk, hm=cds[HM].l) ch = PChart(candles=cds, levels=[Level(tp2, f"TP2 {f2(tp2)} = 3R", BLUE, label_at="left"), Level(PM, f"pivot monthly {PM:.2f} = TP1 ({R1m:.1f}R)", ORANGE, label_at="left"), Level(entry, f"intrare {f2(entry)}", GREEN, label_at="right"), Level(PQ, f"pivot quarterly {PQ:.2f}", PURPLE, label_at="right"), Level(sl, f"SL {f2(sl)}", RED, label_at="right")], marks=[Mark(6, "reject sub pivot: NU", RED, "above", dy=-16), Mark(HM, "hammer", PURPLE, "below", dy=0), Mark(BD, "buy doji", GREEN, "above", dy=-6)], scale=26, pad_bot_px=12, vol_h=70, title="Exemplu construit (fără ticker) — daily", caption="Nu cumperi rejectul de sub pivotul monthly. Cumperi mai jos, în pivotul quarterly.", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, sl, entry, tp2) - 3.0) < 0.01 svgs["reject_sub"] = svg_ # ================================================================ 9. respins de 5 ori sub pivot, apoi închidere deasupra cu doji PQ9 = 50.00 cl9 = [47.2, 46.6, 46.9, 46.2, 45.6, 45.9, 45.3, # 0-6 downtrend 45.8, 46.6, 47.4, 48.3, 49.1, # 7-11 revenire spre pivot 49.6, 49.3, 49.7, 49.4, 49.8, 49.5, # 12-17 rejecturi sub pivot (5 cu codiță peste) 50.35, 50.40, # 18 close peste, 19 doji peste 51.2, # 20 buy doji 51.0, 51.7, 52.3, 52.0, 52.8, 53.4, 53.1, 53.9, 54.4, 54.1, 54.8] # 21-31 w9 = [(0.3, 0.3)] * 12 + [(0.6, 0.2), (0.3, 0.2), (0.6, 0.2), (0.45, 0.2), (0.6, 0.2), (0.3, 0.2)] \ + [(0.2, 0.3), (0.35, 0.3), (0.2, 0.2)] + [(0.3, 0.3)] * 11 cds = from_closes(cl9, wicks=w9, start=47.6) rj = [i for i in range(12, 18) if cds[i].h > PQ9 and cds[i].c < PQ9] assert len(rj) == 5, rj assert all(c.c < PQ9 for c in cds[:18]), "până la bara 18 niciun close peste pivot" X, DJ, BD = 18, 19, 20 assert cds[X].c > PQ9 and cds[DJ].c > PQ9 and abs(cds[DJ].o - cds[DJ].c) <= 0.1 and cds[BD].c > cds[DJ].h entry = cds[BD].c sl = round(min(cds[DJ].l, PQ9) - 0.25, 2) # sub doji și sub pivot risk = entry - sl tp = round(entry + 2 * risk, 2) hit = next(i for i in range(BD + 1, len(cds)) if cds[i].h >= tp) assert min(c.l for c in cds[BD + 1:]) > PQ9 assert hit - BD == 8, "textul: TP după 8 săptămâni" print(f"D9 rejecturi la {rj}; close peste {f2(cds[X].c)}, doji {f2(cds[DJ].c)}; entry {f2(entry)} SL {f2(sl)} " f"risc {f2(risk)} TP {f2(tp)} atins la {hit}") BRK = dict(entry=entry, sl=sl, tp=tp, risk=risk) ch = PChart(candles=cds, levels=[Level(PQ9, f"pivot quarterly {PQ9:.2f}", ORANGE, label_at="left"), Level(sl, f"SL {f2(sl)}", RED, label_at="right"), Level(entry, f"intrare {f2(entry)}", GREEN, label_at="right"), Level(tp, f"TP {f2(tp)} = 2R", BLUE, label_at="left")], marks=[Mark(3, "downtrend", GRAY, "above", dy=-10), Mark(12, "5 rejecturi sub pivot", RED, "above", dy=-40), Mark(BD, "doji + buy doji", GREEN, "above", dy=-4)], scale=24, title="Exemplu construit (fără ticker) — weekly, pivot quarterly", caption="Cinci respingeri, apoi două close-uri peste pivot și buy doji: acum pivotul e suport.", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, sl, entry, tp) - 2.0) < 0.01 svgs["reject_apoi_peste"] = svg_ # ================================================================ 10-11. scărița pe pivoți (weekly, pivot quarterly) Q = 13 PQ1 = 44.00 q1 = [45.6, 45.2, 45.8, 46.5, 47.2, 48.0, 48.6, 48.2, 47.6, 47.9, 48.8, 49.4, 49.0] q2 = [48.7, 48.4, 48.9, 49.6, 50.3, 51.0, 51.5, 51.0, 50.4, 50.1, 50.6, 51.3, 51.1] q3_da = [50.8, 50.7, 51.1, 51.7, 52.3, 51.9, 52.6, 53.3, 53.0, 53.8, 54.4, 54.1, 54.9] q3_nu = [50.8, 50.7, 51.1, 51.4, 51.2, 50.6, 50.1, 49.6, 49.9, 49.2, 48.7, 49.0, 48.4] wq = [(0.3, 0.3)] * 39 wq[14] = (0.3, 0.3) # low-ul trimestrului 2 wq[22] = (0.3, 0.4) # higher low în trimestrul 2 wq[27] = (0.3, 0.3) # higher low în trimestrul 3 def stairs(q3): cds = from_closes(q1 + q2 + q3, wicks=wq, start=46.0) p2 = pivots(cds[0:Q]) p3 = pivots(cds[Q:2 * Q]) return cds, p2, p3 cds, p2, p3 = stairs(q3_da) lows = [min(range(0, Q), key=lambda i: cds[i].l), min(range(Q, 18), key=lambda i: cds[i].l), min(range(20, 2 * Q), key=lambda i: cds[i].l), min(range(2 * Q, 30), key=lambda i: cds[i].l)] hh = [max(range(0, Q), key=lambda i: cds[i].h), max(range(Q, 2 * Q), key=lambda i: cds[i].h)] assert PQ1 < p2["P"] < p3["P"], "pivoții quarterly nu urcă în trepte" assert all(cds[lows[k]].l < cds[lows[k + 1]].l for k in range(3)), "swing low-urile nu urcă" assert cds[lows[1]].l > p2["P"] and cds[lows[2]].l > p2["P"] and cds[lows[3]].l > p3["P"] - 0.05, "HL sub pivotul trimestrului" assert cds[lows[3]].l < p3["P"] + 0.5 brk = next(i for i in range(2 * Q, 39) if cds[i].c > cds[hh[1]].h) assert cds[hh[1]].h > cds[hh[0]].h print(f"D10 pivoti Q1 {PQ1} Q2 {f2(p2['P'])} Q3 {f2(p3['P'])}; lows {[f2(cds[i].l) for i in lows]} la {lows}; " f"high Q1 {f2(cds[hh[0]].h)} Q2 {f2(cds[hh[1]].h)}; breakout la {brk} close {f2(cds[brk].c)}") STAIR = dict(p2=p2, p3=p3, lows=[cds[i].l for i in lows], hq1=cds[hh[0]].h, hq2=cds[hh[1]].h, brk=cds[brk].c) ch = PChart(candles=cds, segs=[Seg(0, Q - 1, PQ1, f"pivot Q1 {PQ1:.2f}", ORANGE, "lm"), Seg(Q, 2 * Q - 1, p2["P"], f"pivot Q2 {f2(p2['P'])}", ORANGE, "left", 14), Seg(2 * Q, 38, p3["P"], f"pivot Q3 {f2(p3['P'])}", ORANGE)], seps=[Q, 2 * Q], marks=[Mark(lows[0], "low", RED, "above", dy=-2), Mark(lows[1], "HL", GREEN, "above", dy=-2), Mark(lows[2], "HL", GREEN, "above", dy=-2), Mark(lows[3], "HL", GREEN, "above", dy=-2), Mark(hh[1], "vârf Q2", BLUE, "above"), Mark(brk, "breakout", GREEN, "above", dy=-4)], mas=[MA(20, GRAY, dash="4 3", text="SMA-20")], scale=18, title="Exemplu construit (fără ticker) — weekly, pivot quarterly", caption="Treaptă cu treaptă: pivotul fiecărui trimestru mai sus, fiecare higher low deasupra lui.", **STYLE) svgs["scarita_da"] = ch.render() cds, p2n, p3n = stairs(q3_nu) assert abs(p3n["P"] - p3["P"]) < 1e-9, "Q1 și Q2 identice cu diagrama DA" assert max(c.h for c in cds[2 * Q:]) < STAIR["hq2"], "breakout-ul peste vârful Q2 nu vine" hl2 = STAIR["lows"][2] ll = next(i for i in range(2 * Q, 39) if cds[i].c < hl2) top3 = max(range(2 * Q, 39), key=lambda i: cds[i].h) assert cds[ll].c < p3n["P"] under = next(i for i in range(2 * Q, 39) if cds[i].c < p3n["P"]) print(f"D11 high Q3 {f2(max(c.h for c in cds[2 * Q:]))} < vârf Q2 {f2(STAIR['hq2'])}; close sub pivot Q3 la {under} " f"({f2(cds[under].c)}); close sub HL {f2(hl2)} la {ll} ({f2(cds[ll].c)})") STAIRN = dict(h3=max(c.h for c in cds[2 * Q:]), under=cds[under].c, ll=cds[ll].c, hl2=hl2) ch = PChart(candles=cds, segs=[Seg(0, Q - 1, PQ1, f"pivot Q1 {PQ1:.2f}", ORANGE, "lm"), Seg(Q, 2 * Q - 1, p2n["P"], f"pivot Q2 {f2(p2n['P'])}", ORANGE, "left", 14), Seg(2 * Q, 38, p3n["P"], f"pivot Q3 {f2(p3n['P'])}", ORANGE)], seps=[Q, 2 * Q], levels=[Level(STAIR["hq2"], f"vârf Q2 {f2(STAIR['hq2'])}", BLUE, label_at="left"), Level(hl2, f"HL din Q2 {f2(hl2)}", RED, label_at="left")], marks=[Mark(top3, "fără breakout", GRAY, "above", dy=-4), Mark(ll, "lower low", RED, "below", dy=4)], mas=[MA(20, GRAY, dash="4 3", text="SMA-20")], scale=18, title="Exemplu construit (fără ticker) — weekly, pivot quarterly", caption="Pivotul Q3 a urcat, dar prețul nu face vârf nou și închide sub HL-ul din Q2: scărița s-a rupt.", **STYLE) svgs["scarita_nu"] = ch.render() # ---------------------------------------------------------------- substituție în .md if MD.exists(): md = MD.read_text(encoding="utf-8") for name, svg in svgs.items(): pat = re.compile(rf".*?", re.S) assert pat.search(md), f"marcatorul {name} lipsește din .md" md = pat.sub(lambda _m: f"\n{svg}\n", md) MD.write_text(md, encoding="utf-8") print("scris", MD)