Files
atm-curs-dl/gen/strategii/mdsrt_trinity.py
Claude Agent c08c857274 atmi: diagramele refacute, cinci noi, si fisa de surse
Noi: pivoti (PPQ/SQ calculati din trimestrul precedent, cu formula pe
desen), perechi_tf (weekly -> daily ca descompunere reala, cu caseta,
ghidaje si PPQ pe ambele panouri), trigger (BIM apoi verde, fata de
verde inainte de BIM — cutitul in cadere), risc (ce inseamna "fara
stop": 24 de bare sub apa, -20 % pe pozitie), reinvestire (ciclul
banilor pe trei cicluri).

aplus devine corectie: diagrama arata un prag de corectie, nu un setup
A+, si numele vechi trimitea la altceva.

Trei bug-uri reale: exemplul era etichetat weekly cu 20 de bare, dar
textul spunea "un an mai tarziu" — trecut pe monthly cu assert pe numarul
de luni; ansamblul avea o trimitere fisier:linie desenata in SVG; si
benzile PPQ/SQ erau inversate ca sens, cu SQ fix pe intrare.

PPQ 95.80 si SQ 90.80 sunt completare proprie — cursul spune explicit ca
nu da valorile; derivate cu formula standard, cu assert.

Textul capitolului e neatins — agentul a atins pragul de context.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-20 12:46:55 +00:00

196 lines
8.3 KiB
Python

#!/usr/bin/env python3
"""MDSRT — minimul obligatoriu (M complet) vs. varianta Trinity.
Diagramă conceptuală (nu grafic de preț): pentru
`summaries/strategii/07_mdsrt.md`, Nivel 2, §2.1 „Ce dă edge", după tabel
(marcajul `<!-- DIAGRAMA: trinity -->`). Citește specificația
`gen/strategii/mdsrt_trinity.spec.txt` și scrie
`gen/strategii/mdsrt_trinity.svg`.
NU folosește `svg_candles`: SVG-ul e construit ca string, fără dependințe.
Conținutul, cu sursa fiecărei afirmații:
- Power obligatoriu; dacă nu e în divergență, setup-ul nu se califică:
`05_MODUL_4/02_strategia_5_mdsrt.md:59-61, 432-434`.
- Minim 2 indicatori: Power + (Volume SAU Relative Strength):
`05_MODUL_4/02_strategia_5_mdsrt.md:85-88`.
- Trinity = toți trei în divergență simultan, „setup de 5 stele":
`05_MODUL_4/02_strategia_5_mdsrt.md:6-8, 34-42, 87-88`.
- Sensul din selecția simbolului (aliniere în cadranul 1; trei divergențe
simultane NU sunt Trinity): `08_ANALIZA_ZILNICA/11_selectia_simbolului.md:612-627`.
Rulează: .venv/bin/python gen/strategii/mdsrt_trinity.py
"""
import html
from pathlib import Path
W, H = 700, 550
BLACK = "#000000"
GRAY = "#777777"
RED = "#c00"
GREEN = "#188a3e"
BLUE = "#1a5fb4"
texts = [] # (x, y, text, size, weight, fill, w_est)
boxes = [] # dict-uri: doar casetele, pentru numărătoarea din assert-uri
def add(x, y, s, size=13, weight="normal", fill=BLACK):
factor = 0.62 if weight == "bold" else 0.56
w = len(s) * size * factor
texts.append((x, y, s, size, weight, fill, w))
return (x, y, s, size, weight, fill, w)
def box(x, y, w, h, *, fill="#ffffff", stroke=GRAY, sw=1.5, dash=None, row=None):
b = {"x": x, "y": y, "w": w, "h": h, "row": row, "lines": []}
boxes.append(b)
return b
def box_line(b, dx, dy, s, size=14, weight="normal", fill=BLACK):
cx = b["x"] + b["w"] / 2.0 + dx if dx is not None else b["x"] + b["w"] / 2.0
add(cx, b["y"] + dy, s, size, weight, fill)
b["lines"].append(s)
parts = []
def rect(b, fill, stroke, sw=1.5, dash=None):
d = f' stroke-dasharray="{dash}"' if dash else ""
return (f'<rect x="{b["x"]}" y="{b["y"]}" width="{b["w"]}" '
f'height="{b["h"]}" fill="{fill}" stroke="{stroke}" '
f'stroke-width="{sw}"{d}/>')
# --------------------------------------------------------------- titlu
add(W / 2, 34, "MDSRT — minimul obligatoriu vs. Trinity", 20, "bold")
add(W / 2, 58,
"Minim Power + unul din ceilalți e obligatoriu; toți trei în divergență = Trinity.",
13, "normal", GRAY)
# ------------------------------------------------- rând 1 — minim obligatoriu
add(W / 2, 100, "Minimul obligatoriu (M complet)", 16, "bold")
B1 = box(40, 112, 250, 56, row=1)
B2 = box(310, 112, 350, 56, row=1)
parts.append(rect(B1, GREEN, GREEN))
parts.append(rect(B2, "#ffffff", GREEN, sw=2, dash="5 3"))
box_line(B1, None, 31, "Power (obligatoriu)", 15, "bold", "#ffffff")
box_line(B2, None, 22, "Volume SAU Relative Strength", 14, "normal", GREEN)
box_line(B2, None, 44, "— oricare una", 13, "normal", GREEN)
add(W / 2, 196, "Power lipsește → setup-ul NU se califică", 14, "bold", RED)
# ------------------------------------------------- rând 2 — Trinity (opțional)
add(W / 2, 250, "Trinity (opțional, semnal mai puternic)", 16, "bold")
TITLES = ["Power (obligatoriu)", "Volume", "Relative Strength"]
B3 = box(30, 262, 200, 76, row=2)
B4 = box(250, 262, 200, 76, row=2)
B5 = box(470, 262, 200, 76, row=2)
for b in (B3, B4, B5):
parts.append(rect(b, GREEN, GREEN))
for b, t in zip((B3, B4, B5), TITLES):
box_line(b, None, 34, t, 14, "bold", "#ffffff")
box_line(b, None, 56, "divergență", 14, "bold", "#ffffff")
add(W / 2, 360, "toți trei în divergență simultan, pe aceeași zonă de preț",
14, "normal", BLACK)
add(W / 2, 386, "comparat în curs cu un «setup de 5 stele»", 14, "bold", GREEN)
# --------------------------------------------------------------- subsol
parts.append(f'<line x1="40" y1="420" x2="660" y2="420" stroke="{GRAY}" '
f'stroke-width="1"/>')
add(W / 2, 448,
"La MDSRT, „Trinity” se folosește local cu sensul „toți trei în divergență”.",
13, "normal", GRAY)
add(W / 2, 468,
"Lecția de selecție dă aceluiași cuvânt alt sens:", 13, "normal", GRAY)
add(W / 2, 486,
"aliniere în cadranul 1, nu divergență (contradicția 2.4).",
13, "normal", GRAY)
# trimiterile la material stau în fișa de surse a capitolului, nu pe desen
# --------------------------------------------------------------- emitere
head = (f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
f'width="{W}" height="{H}" font-family="system-ui, sans-serif" role="img" '
f'aria-label="MDSRT: minimul obligatoriu este Power (obligatoriu) plus '
f'Volume sau Relative Strength; varianta Trinity are toți trei '
f'indicatori în divergență simultan, pe aceeași zonă de preț">')
parts.insert(0, f'<rect x="0" y="0" width="{W}" height="{H}" fill="#ffffff"/>')
assert ".md:" not in "".join(parts), "trimiteri fișier:linie pe desen"
parts.insert(0, head)
body = []
for x, y, s, size, weight, fill, _ in texts:
body.append(
f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" '
f'font-weight="{weight}" fill="{fill}" text-anchor="middle">'
f'{html.escape(s, quote=False)}</text>')
svg = "\n".join(parts + body + ["</svg>"]) + "\n"
# =============================================================== VERIFICARE
# 1. Rândul 1 conține exact 2 casete (Power + „Volume SAU Relative Strength"),
# nu 3.
row1 = [b for b in boxes if b["row"] == 1]
assert len(row1) == 2, f"rândul 1 are {len(row1)} casete, trebuie exact 2"
# 2. Rândul 2 conține exact 3 casete (Power, Volume, Relative Strength).
row2 = [b for b in boxes if b["row"] == 2]
assert len(row2) == 3, f"rândul 2 are {len(row2)} casete, trebuie exact 3"
assert [b["lines"][0] for b in row2] == TITLES, "titlurile din rândul 2 diferă"
# 3. Textul „Power (obligatoriu)" apare în ambele rânduri.
r1_labels = [s for b in row1 for s in b["lines"]]
r2_labels = [s for b in row2 for s in b["lines"]]
assert "Power (obligatoriu)" in r1_labels, "lipsește din rândul 1"
assert "Power (obligatoriu)" in r2_labels, "lipsește din rândul 2"
assert svg.count(">Power (obligatoriu)</text>") == 2, \
"„Power (obligatoriu)” nu apare exact de două ori"
# 4. Eticheta „Power lipsește → setup-ul NU se califică" e lângă rândul 1.
assert "Power lipsește → setup-ul NU se califică" in svg, "lipsește eticheta rând 1"
# 5. Nota de subsol despre cele două sensuri ale lui „Trinity" e prezentă.
assert "se folosește local cu sensul" in svg and "alt sens" in svg \
and "cadranul 1, nu divergență" in svg and "contradicția 2.4" in svg, \
"lipsește nota despre cele două sensuri ale lui Trinity"
# stil: fără umbre/gradienti, text >= 13px, totul în panză, fără suprapuneri
assert "filter=" not in svg and "gradient" not in svg, "fără umbre/gradienti"
assert 'font-family="system-ui, sans-serif"' in svg, "lipsește fontul cerut"
rects_collide = []
for x, y, s, size, weight, fill, w in texts:
assert size >= 13, f"font sub 13px: {s!r} ({size})"
left, right = x - w / 2.0, x + w / 2.0
top, bottom = y - size * 0.78, y + size * 0.22
assert 0 <= left and right <= W, f"etichetă în afara panzei (orizontal): {s!r}"
assert 0 <= top and bottom <= H, f"etichetă în afara panzei (vertical): {s!r}"
rects_collide.append((left, right, top, bottom, s))
for i in range(len(rects_collide)):
for j in range(i + 1, len(rects_collide)):
l1, r1, t1, b1, s1 = rects_collide[i]
l2, r2, t2, b2, s2 = rects_collide[j]
if r1 > l2 and r2 > l1 and b1 > t2 and b2 > t1:
raise AssertionError(f"etichete suprapuse: {s1!r} / {s2!r}")
for required in ("Minimul obligatoriu (M complet)",
"Volume SAU Relative Strength",
"oricare una",
"Trinity (opțional, semnal mai puternic)",
"divergență",
"pe aceeași zonă de preț",
"setup de 5 stele"):
assert required in svg, f"lipsește din desen: {required!r}"
OUT = Path(__file__).with_suffix(".svg")
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT} ({len(svg)} bytes, {len(texts)} etichete, "
f"{len(boxes)} casete, {W}x{H}), verificare OK")