Files
atm-curs-dl/gen/strategii/mp2_exemplu.py
Claude Agent 602419c93a etichete: doua defecte vechi la mdsrt, trei la mp2, si verdictul pe restul
La mdsrt_aplus eticheta spunea "primul low 94.60" pentru o bara al carei low
era 94.50. Reparatia a mutat low-ul, nu eticheta: regula capitolului numara
piciorul dupa low, iar specul spune tot 94.60 - doar umbra de 0.10 o
contrazicea. Nicio cifra tiparita nu se schimba, iar nivelul isi ia acum
eticheta din date, cu assert, ca sa nu mai poata minti.

Cele trei de la mp2 aveau toate aceeasi cauza fizica: text colorat peste un
corp de candela de aceeasi culoare. Acolo caracterele nu se incaleca, dispar -
"intrare 577.00" se citea "intrare 577. 0", "crossing" se citea "crossi g".

VERDICT pe restul de ~94: nu merita un lot. Din 20 de suprapuneri vazute
efectiv pe ecran, doar 3 schimbau ce citesti, si toate trei din cauza aia.
Celelalte 17 - text peste fitil, peste linie de nivel, peste chenar, peste
coada de sageata - raman lizibile, fiindca fundalul e o linie subtire, nu o
suprafata plina. Filtrul util nu e tiparul geometric, ci: eticheta cu cifra,
peste un <rect> de aceeasi culoare.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-10-02 06:00:45 +00:00

154 lines
7.1 KiB
Python

"""Diagrama din `gen/strategii/mp2_exemplu.spec.txt` (TIP: PRET).
Exemplul complet din curs: SPY long la bottom-ul de aprilie 2020 — double
bottom + Inverted Head & Shoulders la suport quarterly, trigger MP² long
(pivotul mic închide peste cel mare), prima candelă verde după umărul drept.
Intrare 577.00, SL 554.40 (1R = 22.60), TP1 599.60, ținta 4R era 667.00, iar
mișcarea reală s-a oprit la 646.00 (~3R) într-o lună.
Toate cifrele vin din `summaries/09_QA/01_2026-08-25.md:384-394,485-494`
(liniile citate în spec). Serie continuă de 38 de candele daily
(`from_closes`), panou de volum, SMA-20, niveluri reale și marcaje ancorate.
"""
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
sys.path.insert(0, "/workspace/atm-curs-dl/gen")
from eticheta_libera import eticheta_libera # noqa: E402
OUT = Path(__file__).resolve().parent / "mp2_exemplu.svg"
# ── cifrele din material (09_QA/01_2026-08-25.md:384-394,485-494) ───────────
ENTRY = 577.00 # candela de intrare (prima verde după umărul drept)
SL = 554.40 # sub umărul drept, chiar peste suportul quarterly
TP1 = 599.60 # 577.00 + 22.60 (1R)
TARGET_4R = 667.00 # ținta declarată; 4R exact = 667.40
REAL = 646.00 # mișcarea reală (~3R, +12% efectiv)
QUARTERLY = 554.00 # suport quarterly
SHOULDER_L = 566.0 # umăr stâng
HEAD = 555.0 # head (cel mai jos)
SHOULDER_R = 568.0 # umăr drept
NECKLINE = 578.00
# ── structura seriei (spec): ~14 coborâre → ~10 bază → intrare → ~12 urcare ─
closes = [
# 0-13: ~14 candele de coborâre de la ~600 spre umărul stâng 566
600.0, 597.0, 594.0, 596.5, 591.0, 587.5, 590.0,
584.0, 579.5, 581.5, 576.0, 571.0, 567.0,
566.0, # 13 umăr stâng
# 14-24: baza — double bottom + Inverted Head & Shoulders
570.5, 578.0, 572.0, 567.0, 560.0,
555.0, # 19 head (cel mai jos)
561.5, 568.0, 578.0, 573.0,
568.0, # 24 umăr drept
# 25: candela de intrare, prima verde după umărul drept
577.00,
# 26-37: ~12 candele de urcare spre 646.00 (marcajul „real")
581.0, 585.5, 589.0, 584.0, 590.5, 596.0, 602.0,
609.0, 616.0, 624.0, 635.0, 646.0,
]
assert len(closes) == 38, len(closes)
ENTRY_I = 25
HEAD_I = 19
SHL_I = 13
SHR_I = 24
wicks = [(0.4, 0.4)] * len(closes)
wicks[SHL_I] = (0.4, 0.5) # low umăr stâng 565.50
wicks[HEAD_I] = (0.4, 0.6) # low head = 555.0 - 0.6 = 554.40 (suportul atins)
wicks[SHR_I] = (0.4, 0.5)
wicks[15] = (0.4, 0.4) # vârf 578.0 = neckline
wicks[22] = (0.4, 0.4) # vârf 578.0 = neckline
wicks[ENTRY_I] = (0.7, 0.4) # intrare verde, wick mic
wicks[37] = (0.0, 0.3) # high = 646.00 exact (mișcarea reală se oprește aici)
vols = [
# coborâre
48, 46, 50, 44, 52, 49, 45, 53, 55, 47, 50, 54, 58, 62,
# bază (head-ul are volum mare — capitulare)
60, 72, 65, 58, 66, 88, 70, 64, 74, 60, 56,
# intrare
92,
# urcare
78, 72, 68, 60, 66, 74, 82, 88, 80, 84, 90, 95,
]
assert len(vols) == len(closes)
cds = from_closes(closes, wicks=wicks, vols=vols, start=602.0)
# ── assert-urile din specificație ───────────────────────────────────────────
entry = cds[ENTRY_I].c
assert abs(entry - 577.00) < 0.05, entry
assert abs(SL - 554.40) < 1e-9, SL
assert SL > QUARTERLY, (SL, QUARTERLY) # peste suportul quarterly
assert abs((ENTRY - SL) - 22.60) < 0.05, ENTRY - SL # 1R
assert abs(TP1 - (ENTRY + 22.60)) < 0.05, TP1 # TP1 = intrare + 1R
target_4r = ENTRY + 4 * (ENTRY - SL)
assert abs(target_4r - 667.40) < 0.05, target_4r # 4R exact
assert abs(target_4r - TARGET_4R) < 0.5, target_4r # ≈ 667.00, rotunjit în curs
assert abs((REAL - ENTRY) / (ENTRY - SL) - 3.05) < 0.02, (REAL - ENTRY) / (ENTRY - SL)
assert abs((REAL - ENTRY) / (ENTRY - SL) - 4.0) > 0.5, "mișcarea reală NU e 4R"
# structura care susține afirmațiile din text
assert abs(cds[SHL_I].c - SHOULDER_L) < 0.05, cds[SHL_I].c
assert abs(cds[HEAD_I].c - HEAD) < 0.05, cds[HEAD_I].c
assert abs(cds[SHR_I].c - SHOULDER_R) < 0.05, cds[SHR_I].c
assert HEAD < SHOULDER_L and HEAD < SHOULDER_R, (HEAD, SHOULDER_L, SHOULDER_R)
assert cds[SHL_I].c == min(c.c for c in cds[SHL_I - 1:SHL_I + 2]), "umărul stâng nu e minim local"
assert cds[HEAD_I].c == min(c.c for c in cds[HEAD_I - 1:HEAD_I + 2]), "head-ul nu e minim local"
assert cds[SHR_I].c == min(c.c for c in cds[SHR_I - 1:SHR_I + 2]), "umărul drept nu e minim local"
min_pre = min(c.l for c in cds[:ENTRY_I])
assert abs(min_pre - 554.0) < 1.0, min_pre # suportul quarterly atins
assert abs(min_pre - SL) < 0.05, (min_pre, SL) # SL chiar la minimul head-ului
assert cds[ENTRY_I].c > cds[ENTRY_I].o, "candela de intrare trebuie verde"
assert cds[ENTRY_I - 1].c < cds[ENTRY_I - 1].o, "umărul drept e roșu"
assert abs(cds[37].c - REAL) < 0.05, cds[37].c
assert all(c.vol is not None for c in cds), "lipsește panoul de volum"
assert len(cds) == 38
# ── desenul ─────────────────────────────────────────────────────────────────
levels = [
Level(TARGET_4R, "ținta 4R era 667.00 (+16%)", "#555"),
Level(REAL, "646.00 — +12% efectiv (~3R)", "#188a3e", label_at="left"),
Level(TP1, "TP1 599.60", "#1a5fb4"),
Level(NECKLINE, "neckline 578.00", "#1a5fb4"),
Level(SL, "SL 554.40", "#c00"),
Level(QUARTERLY, "suport quarterly 554.00", "#777", label_at="left"),
]
marks = [
Mark(SHL_I, "umăr stâng 566", "#1a1a1a", "below"),
Mark(HEAD_I, "head 555", "#1a1a1a", "below"),
Mark(SHR_I, "umăr drept 568", "#1a1a1a", "below"),
# dy/dx: la dy=-10 corpul verde de peste două candele mânca primul
# zecimal — pe ecran scria „intrare 577. 0" (02.10.2026)
Mark(ENTRY_I, "intrare 577.00", "#188a3e", "above", dy=-32, dx=-10),
]
ch = Chart(
candles=cds,
levels=levels,
marks=marks,
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
scale=3.8, width=720, gap=15, body_w=9, left=44, vol_h=70,
vol_label="volum", title="SPY — daily",
caption=("Intrare 577.00 = prima verde după umărul drept (MP² long). "
"1R = 22.60; 646.00 ~3R, într-o lună."),
)
svg = ch.render()
assert svg.count("\n\n") == 0, "linie goală în SVG — rupe markdown-ul"
# eticheta intrării poartă cifra: să nu mai ajungă peste un corp de candelă
eticheta_libera(svg, "exemplu", {"intrare 577.00"}, rects=True)
OUT.write_text(svg)
print(f"serie {len(cds)} candele | intrare {entry:.2f} @ {ENTRY_I} | "
f"SL {SL:.2f} | 1R {ENTRY - SL:.2f} | TP1 {TP1:.2f} | "
f"4R {target_4r:.2f} (ținta declarată {TARGET_4R:.2f}) | "
f"real {REAL:.2f} = {(REAL - ENTRY) / (ENTRY - SL):.2f}R | "
f"min pre-intrare {min_pre:.2f}")
print(f"scris {OUT}")