Files
atm-curs-dl/gen/selectia/trinity.py
Claude Agent 3a6da7a916 cele 7 diagrame din 11_selectia
Sunt diagrame conceptuale - funnel, bara de scor, matrice, cele patru
cadrane, Trinity, sectoare - nu grafice de pret, deci nu trec prin
svg_candles / pchart, care raman obligatorii doar pentru candele si
entry/SL/TP. Agent nou, diagramist, cate unul per diagrama, fiecare cu
generatorul lui in gen/selectia/; substitutia in .md o face
gen/az_selectia.py, ca sa nu scrie sapte agenti in acelasi fisier.

Regula de continut a agentului: fiecare cifra si eticheta trebuie sa
existe in document sau in citate. Ce nu reiese din sursa e scris pe
desen ca atare - ordinea celor 7 sectoare, ce se intampla sub 100%
relative volume. Fara semafor rosu/verde, care nu e in sursa.

Verificat cu ochii pe doua diagrame randate in browser.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-18 16:52:01 +00:00

159 lines
6.4 KiB
Python

"""Trinity — 3 stele vs. trei divergențe simultane.
Diagramă conceptuală pentru `summaries/08_ANALIZA_ZILNICA/11_selectia_simbolului.md`
(marcaj `<!--SVG:trinity-->`), §3 „Trinity și power — stelele".
Două panouri:
1. Trinity complet — relative strength, power și volum, toate în cadranul 1,
aceeași direcție => „3 stele cu plus".
2. Trei divergențe simultane (volum, RS, power) => setup de bottom, dar fără
trigger încă: „doar pe listă".
Nu e grafic de preț, deci NU folosește `svg_candles` / `pchart`: SVG-ul e
construit ca string, fără dependințe. Rulează:
.venv/bin/python gen/selectia/trinity.py
Scrie `gen/selectia/trinity.svg`.
Fiecare cifră și etichetă de aici există în textul din jurul marcajului sau în
citatele lui:
- „3 stele cu plus" / „3 stele" / cadranul 1 / relativ strength, power, volum:
`75_05052026.txt:101-106` și `82_24042026.txt:334-335`;
- cele trei divergențe simultane și așteptarea trigger-ului:
`98_30032026.txt:395-397`;
- „3 stele cu roșu" pe indice = short: `75_05052026.txt:101-106` și tabelul
Trinity DA/NU.
Textul nu leagă DA de verde și nici divergențele de vreo culoare, așa că nu am
folosit verde/semafor: roșu apare doar acolo unde sursa spune explicit „roșu".
"""
import html
from pathlib import Path
W, H = 700, 640
BLACK = "#000000"
GRAY = "#777777"
RED = "#c00"
BLUE = "#1a5fb4"
texts = [] # (x, y, text, size, weight, fill, w_est)
def add(x, y, s, size=13, weight="normal", fill=BLACK):
factor = 0.62 if weight == "bold" else 0.56
texts.append((x, y, s, size, weight, fill, len(s) * size * factor))
return x, y, s, size, weight, fill, len(s) * size * factor
def box(x, y, w, h):
return (
f'<rect x="{x}" y="{y}" width="{w}" height="{h}" fill="#ffffff" '
f'stroke="{GRAY}" stroke-width="1.5"/>'
)
parts = [
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
f'width="{W}" height="{H}" role="img" '
f'aria-label="Trinity: relative strength, power și volum toate în cadranul 1 '
f'înseamnă 3 stele; trei divergențe simultane pe volum, relative strength și '
f'power sunt doar setup de bottom, fără trigger">',
f'<rect x="0" y="0" width="{W}" height="{H}" fill="#ffffff"/>',
]
# ---------------------------------------------------------------- titlu
add(W / 2, 32, "Trinity — 3 stele", 20, "bold")
add(W / 2, 56,
"Relative strength, power și volum aliniate în aceeași direcție, în cadranul 1;",
13, "normal", GRAY)
add(W / 2, 74, "„3 stele cu plus” pe simbol.", 13, "normal", GRAY)
# ---------------------------------------------------------------- panoul 1
P1_X, P1_Y, P1_W, P1_H = 40, 92, 620, 198
parts.append(box(P1_X, P1_Y, P1_W, P1_H))
add(W / 2, 118, "DA — Trinity complet: 3 stele cu plus", 16, "bold", BLUE)
COL_W, COL_H, COL_Y = 180, 96, 136
COL_X = [60, 260, 460]
CENTER = [x + COL_W / 2.0 for x in COL_X]
COL_TITLES = ["Relative strength", "Power", "Volum"]
for cx, title in zip(CENTER, COL_TITLES):
parts.append(box(cx - COL_W / 2.0, COL_Y, COL_W, COL_H))
add(cx, COL_Y + 26, title, 15, "bold")
add(cx, COL_Y + 56, "cadranul 1", 14, "bold", BLUE)
add(W / 2, 258, "toate trei: cadranul 1, aceeași direcție", 14, "bold")
add(W / 2, 280, "→ 3 stele cu plus", 15, "bold", BLUE)
# ---------------------------------------------------------------- panoul 2
P2_X, P2_Y, P2_W, P2_H = 40, 306, 620, 214
parts.append(box(P2_X, P2_Y, P2_W, P2_H))
add(W / 2, 332, "NU — trei divergențe simultane: nu sunt 3 stele", 16, "bold")
COL2_Y = 350
COL2_TITLES = ["Volum", "Relative strength", "Power"]
for cx, title in zip(CENTER, COL2_TITLES):
parts.append(box(cx - COL_W / 2.0, COL2_Y, COL_W, COL_H))
add(cx, COL2_Y + 26, title, 15, "bold")
add(cx, COL2_Y + 56, "divergență", 14, "bold", GRAY)
add(W / 2, 472,
"setup de bottom: prețul e la fund, dar încă nu s-a întors", 14)
add(W / 2, 496,
"aștepți trigger-ul — deocamdată doar pe listă", 14, "bold", GRAY)
# ---------------------------------------------------------------- note + sursă
add(W / 2, 548,
"Trinity — NU și: două din trei aliniate, a treia în roșu;", 13, "normal", GRAY)
add(W / 2, 566,
"„3 stele cu roșu” pe indice = short, nu cumperi contra.", 13, "normal", RED)
add(W / 2, 596,
"Sursa: 11_selectia_simbolului.md §3 — 75_05052026.txt:101-106;", 13,
"normal", GRAY)
add(W / 2, 614,
"82_24042026.txt:334-335; 98_30032026.txt:395-397.", 13, "normal", GRAY)
# ---------------------------------------------------------------- emitere
for x, y, s, size, weight, fill, _ in texts:
parts.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>'
)
parts.append("</svg>")
svg = "\n".join(parts) + "\n"
# ---------------------------------------------------------------- verificare
assert "filter=" not in svg and "gradient" not in svg, "fără umbre/gradienti"
boxes = []
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 pe orizontală: {s!r}"
assert 0 <= top and bottom <= H, f"etichetă în afara panzei pe verticală: {s!r}"
boxes.append((left, right, top, bottom, s))
for i in range(len(boxes)):
for j in range(i + 1, len(boxes)):
l1, r1, t1, b1, s1 = boxes[i]
l2, r2, t2, b2, s2 = boxes[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 ("3 stele", "cadranul 1", "Relative strength", "Power", "Volum",
"3 stele cu plus", "divergență", "trei divergențe simultane",
"setup de bottom", "trigger", "doar pe listă",
"3 stele cu roșu", "short"):
assert required in svg, f"lipsește din desen: {required!r}"
# fiecare din cele trei condiții apare o dată în panoul 1 și o dată în panoul 2
for word in ("Relative strength", "Power", "Volum"):
assert svg.count(f">{word}</text>") == 2, f"{word!r} nu apare de două ori"
OUT = Path(__file__).with_suffix(".svg")
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT} ({len(svg)} bytes, {len(texts)} etichete, {W}x{H}), verificare OK")