Documentul afirma ca formula "nu reiese din sesiuni" si ca bull/bear ar fi doar eticheta platformei. Gresit, in ambele sensuri. Cursul da doua niveluri, amandoua ale lui: - basic: pivotul perioadei fata de cel anterior (02_2...:206-211, 258-260); - advanced: filtrul PPA — pivotul fata de media ultimilor 3 pivoti, linia galbena din indicatorul lui (:261-288). Briefingul meu spunea ca varianta cu pivotul anterior e gresita. Nu e; e varianta de baza. Agentul m-a corectat cu citatul si are dreptate. Asta explica si ce documentul numea "contradictie a lui": pe 10.09 pivotul e peste cel anterior dar sub media celor trei, deci proiectie bear. La fel lunile bull cu toate nivelurile mai jos (22.07, 23.07), unde el insusi zice "culmea". Cifrele reale nu se aud in transcrieri, deci cazurile sunt doar compatibile cu formula, nu verificate pe numere — scris ca atare. PPA = pivot point average. PPAM/PPAW/PPAQ/PPAY, cu surse la linie; cea mai buna e 98_30032026:98, unde o defineste direct. Captionurile din gen/az_pivoti.py spun acum "varianta de baza (fara filtrul PPA)" — exemplele au doar doi pivoti, deci filtrul nu se poate arata pe ele. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
488 lines
29 KiB
Python
488 lines
29 KiB
Python
"""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 <!--SVG:nume--> ... <!--/SVG--> 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}: proiecție bull în varianta de bază (fără filtrul PPA).",
|
||
**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}: proiecție bear în varianta de bază. 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"<!--SVG:{name}-->.*?<!--/SVG-->", re.S)
|
||
assert pat.search(md), f"marcatorul {name} lipsește din .md"
|
||
md = pat.sub(lambda _m: f"<!--SVG:{name}-->\n{svg}\n<!--/SVG-->", md)
|
||
MD.write_text(md, encoding="utf-8")
|
||
print("scris", MD)
|