Cate un agent grafician per diagrama, fiecare cu generatorul si SVG-ul lui. Graficele de pret trec prin svg_candles, cu assert-urile din specificatie scrise in generator — verifica structura, nu doar ca ruleaza: ca al doilea picior e mai jos, ca intrarea e close-ul barei de confirmare, ca 1:1 e exact. Verificare prin sondaj pe buy_doji_ansamblu, cu screenshot citit: serie continua de 32 de candele, panou de volum, MA(20), doji-ul si ieșirea pe trail etichetate, cifrele identice cu specificatia. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
148 lines
6.7 KiB
Python
148 lines
6.7 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
|
|
|
|
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"),
|
|
Mark(ENTRY_I, "intrare 577.00", "#188a3e", "above", dy=-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"
|
|
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}")
|