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
167 lines
7.3 KiB
Python
167 lines
7.3 KiB
Python
"""Diagrama de ansamblu MDSRT long (strategii, marcajul `ansamblu`).
|
|
|
|
„SPY — 5 min: al doilea picior mai jos la 589.80, sub suportul 590.00 →
|
|
divergență bull (M) preț-vs-volum → candela-trigger Buy Hammer/Doji →
|
|
intrare 590.20 → SL 589.40 (risc 0.80) → TP1 591.00 (1:1) → poziția rămasă
|
|
pe trail, ieșire la close 592.40 (≈ 2.75R)."
|
|
|
|
Toate close-urile, wick-urile speciale și nivelurile vin din
|
|
`gen/strategii/mdsrt_ansamblu.spec.txt`. Structura M→A→P, suportul 590.00,
|
|
minimul 589.80 și riscul 0.80 sunt din exemplul cursului
|
|
(`05_MODUL_4/02_strategia_5_mdsrt.md:159-177, 246, 308-311`). Intrare/SL/TP
|
|
sunt exemplul construit al autorului sumarului, marcat explicit ca NU citat
|
|
din curs (`...:166-177, 439-445`); transcrierea se întrerupe înainte de
|
|
planul numeric. Volumele sunt relative, alese de generator (materialul nu dă
|
|
volum pentru acest exemplu).
|
|
|
|
Rulează:
|
|
python3 gen/strategii/mdsrt_ansamblu.py
|
|
Scrie lângă el `mdsrt_ansamblu.svg`.
|
|
"""
|
|
import re
|
|
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 / "mdsrt_ansamblu.svg"
|
|
|
|
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
|
|
|
# ---------------------------------------------------------------- seria (30 bare)
|
|
CLOSES = [
|
|
596.00, 595.20, 595.60, 594.80, 594.10, 593.40, 593.70, 592.90, # 0-7
|
|
592.20, 591.60, 591.90, 591.30, 590.80, 590.30, 590.55, 590.10, # 8-15
|
|
589.85, 590.05, # 16-17
|
|
589.80, # 18 = al doilea picior mai jos + candela-trigger (hammer)
|
|
590.20, # 19 = intrare (close = entry; sparge high-ul trigger-ului)
|
|
590.60, 590.90, 591.00, # 20-22 (22 = TP1 1:1)
|
|
591.30, 591.10, 591.45, 591.70, 591.95, 592.15, # 23-28
|
|
592.40, # 29 = ieșire pe trail (close)
|
|
]
|
|
assert len(CLOSES) == 30, len(CLOSES)
|
|
|
|
# fitile (sus, jos): (0.25, 0.30) peste tot, cu excepțiile din spec.
|
|
WICKS = [(0.25, 0.30)] * 30
|
|
WICKS[18] = (0.10, 0.40) # hammer: high 590.15, low 589.40 (= SL)
|
|
WICKS[19] = (0.10, 0.05) # intrare: high 590.30, low 589.75
|
|
# bara 21 coborâtă sub TP1 (high 590.95 < 591.00), ca TP1 să fie atins exact
|
|
# prima dată la bara 22 (altfel assert-ul 5 ar pica pe „primul high").
|
|
WICKS[21] = (0.05, 0.30)
|
|
|
|
# volum relativ (ales de generator): 16-19 crescător, 18-19 peste medie,
|
|
# vol[18] > vol[10] (higher low pe volum la lower low pe preț).
|
|
VOLS = [
|
|
48, 46, 44, 45, 43, 41, 40, 42, 44, 46, # 0-9
|
|
52, 38, 40, 42, 39, 44, # 10-15
|
|
50, 58, 72, 85, # 16-19 absorbție la minim
|
|
70, 64, 58, 60, 55, 52, 50, 48, 46, 44, # 20-29
|
|
]
|
|
assert len(VOLS) == 30, len(VOLS)
|
|
|
|
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=596.60)
|
|
|
|
# ---------------------------------------------------------------- assert-urile din spec
|
|
entry = cds[19].c
|
|
sl = cds[18].l
|
|
tp1 = cds[22].c
|
|
|
|
# 1. minimul și SL-ul pe bara 18
|
|
assert cds[18].c == 589.80, cds[18].c
|
|
assert cds[18].l == 589.40, cds[18].l
|
|
# 2. al doilea picior rupe suportul/pivotul 590.00
|
|
assert cds[18].l < 590.00, cds[18].l
|
|
# 3. intrarea = close-ul barei 19 și sparge high-ul trigger-ului
|
|
assert cds[19].c == 590.20, cds[19].c
|
|
assert cds[19].c == entry
|
|
assert cds[19].c > cds[18].h, (cds[19].c, cds[18].h)
|
|
# 4. TP1 exact 1:1
|
|
assert abs((entry - sl) - (tp1 - entry)) < 1e-9, (entry - sl, tp1 - entry)
|
|
assert abs((entry - sl) - 0.80) < 1e-9
|
|
# 5. TP1 atins la bara 22 (și nu mai devreme)
|
|
assert cds[22].c == 591.00, cds[22].c
|
|
assert cds[22].c == tp1
|
|
assert max(c.h for c in cds[20:22]) < tp1, [c.h for c in cds[20:22]]
|
|
# 6. ieșirea pe trail e pe profit
|
|
assert cds[29].c == 592.40, cds[29].c
|
|
assert cds[29].c > entry, (cds[29].c, entry)
|
|
# 7. hammer: corp mic, coadă lungă în jos
|
|
body = abs(cds[18].o - cds[18].c)
|
|
rng = cds[18].h - cds[18].l
|
|
tail = min(cds[18].o, cds[18].c) - cds[18].l
|
|
assert body == 0.25, body
|
|
assert body <= 0.5 * rng, (body, rng)
|
|
assert tail > body, (tail, body)
|
|
# 8. downtrend vizibil
|
|
assert cds[13].c < cds[0].c - 5, (cds[13].c, cds[0].c)
|
|
# 9. divergență pe volum: lower low pe preț, higher low pe volum
|
|
assert cds[18].l < cds[10].l, (cds[18].l, cds[10].l)
|
|
assert VOLS[18] > VOLS[10], (VOLS[18], VOLS[10])
|
|
# barele 16-19 au volum crescător; 18-19 peste media seriei
|
|
assert VOLS[16] < VOLS[17] < VOLS[18] < VOLS[19], VOLS[16:20]
|
|
avg_vol = sum(VOLS) / len(VOLS)
|
|
assert VOLS[18] > avg_vol and VOLS[19] > avg_vol, (VOLS[18], VOLS[19], avg_vol)
|
|
# 10. fiecare open == close-ul barei anterioare
|
|
for a, b in zip(cds, cds[1:]):
|
|
assert a.c == b.o, (a.c, b.o)
|
|
|
|
r_mult = (cds[29].c - entry) / (entry - sl)
|
|
assert abs(r_mult - 2.75) < 1e-9, r_mult
|
|
|
|
# ---------------------------------------------------------------- niveluri, marks
|
|
levels = [
|
|
Level(590.00, "suport / pivot 590.00", BLUE, label_at="left"),
|
|
Level(sl, f"SL {sl:.2f} — risc 0.80", RED, label_at="left"),
|
|
# stânga: altfel eticheta din dreapta se calcă pe textul „SMA-20" al mediei
|
|
Level(tp1, f"TP1 {tp1:.2f} = 1:1", GREEN, label_at="left"),
|
|
]
|
|
|
|
marks = [
|
|
Mark(18, f"minim 589.80 — divergență (M)", RED, "below"),
|
|
# ridicat deasupra vârfurilor candelelor de urcare (altfel textul cade peste
|
|
# fitilele barelor 20-22) — bara 19 e totuși exact sub el
|
|
Mark(19, f"intrare {entry:.2f}", GREEN, "above", dy=-46),
|
|
Mark(29, f"ieșire trail {cds[29].c:.2f}", GREEN, "above"),
|
|
]
|
|
|
|
# titlul e injectat pe două linii: textul din spec (82 caractere) nu încape
|
|
# pe un singur rând în 640 px fără să fie tăiat la margine
|
|
TITLE = ["SPY — 5 min (exemplu construit pe scara lecției;",
|
|
"cifrele planului nu vin din curs)"]
|
|
|
|
ch = Chart(
|
|
candles=cds, levels=levels, marks=marks,
|
|
mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")],
|
|
scale=48.0, gap=15, body_w=9, left=44, width=640, top=64, vol_h=70,
|
|
vol_label="volum",
|
|
title="", caption="",
|
|
)
|
|
svg = ch.render()
|
|
title_svg = "\n ".join(
|
|
f'<text x="24" y="{26 + 18 * i}" fill="#333" style="font-weight:bold">{t}</text>'
|
|
for i, t in enumerate(TITLE))
|
|
i = svg.index(">") + 1
|
|
svg = svg[:i] + "\n " + title_svg + svg[i:]
|
|
|
|
# ---------------------------------------------------------------- legenda (onestitate)
|
|
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
|
|
W, H0 = int(m.group(1)), float(m.group(2))
|
|
legend = [
|
|
"Legendă: entry/SL/TP/trail sunt exemplul construit al autorului sumarului,",
|
|
"nu citate din curs (transcrierea se întrerupe înainte de planul numeric).",
|
|
]
|
|
legend_svg = [f'<text x="24" y="{H0 + 16 + 17 * i:.1f}" fill="#555">{t}</text>'
|
|
for i, t in enumerate(legend)]
|
|
new_h = H0 + 16 + 17 * (len(legend) - 1) + 14
|
|
svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")',
|
|
lambda mm: mm.group(1) + f"{new_h:.0f}" + mm.group(2), svg, count=1)
|
|
svg = svg.replace("\n</svg>", "\n " + "\n ".join(legend_svg) + "\n</svg>")
|
|
|
|
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
|
assert f"TP1 {tp1:.2f}" in svg and f"intrare {entry:.2f}" in svg
|
|
OUT.write_text(svg)
|
|
print(f"scris {OUT.name}: {len(cds)} candele, suport 590.00, minim {cds[18].l:.2f}, "
|
|
f"intrare {entry:.2f}, SL {sl:.2f} (risc 0.80), TP1 {tp1:.2f} (1:1), "
|
|
f"ieșire {cds[29].c:.2f} = {r_mult:.2f}R, vol[18]={VOLS[18]} > vol[10]={VOLS[10]}")
|