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
159 lines
6.4 KiB
Python
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")
|