Files
atm-curs-dl/gen/selectia/pe_venit.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

123 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Diagrama forward P/E x venit pentru 11_selectia_simbolului.md (marcaj pe_venit).
Matrice 2x2: coloane = forward P/E (mic / mare), randuri = venit (creste / scade).
Fiecare cifra si eticheta vine din textul din jurul marcajului sau din citatele
lui (vezi caption-ul din josul SVG-ului). Nu e grafic de pret, deci nu foloseste
svg_candles / pchart.
Ruleaza:
.venv/bin/python gen/selectia/pe_venit.py
Scrie gen/selectia/pe_venit.svg (fara dependinte, SVG construit ca string).
"""
import html
import re
from pathlib import Path
W, H = 700, 620
X0, X1 = 120, 660
Y0, Y1 = 110, 520
COL_MID = (X0 + X1) / 2.0
ROW_MID = (Y0 + Y1) / 2.0
C1 = (X0 + COL_MID) / 2.0
C2 = (COL_MID + X1) / 2.0
R1 = (Y0 + ROW_MID) / 2.0
R2 = (ROW_MID + Y1) / 2.0
GRAY, BLUE = "#777", "#1a5fb4"
BLACK = "#000"
TINT = "#eaf1fb"
texts = []
def text(x, y, s, size=14, weight="normal", fill=BLACK, anchor="middle", transform=""):
texts.append((x, y, s, size, weight, fill, anchor, transform))
def cell_outline(x, y, w, h, accent=False):
fill = TINT if accent else "#fff"
stroke = BLUE if accent else "#999"
return (
f'<rect x="{x:.0f}" y="{y:.0f}" width="{w:.0f}" height="{h:.0f}" '
f'fill="{fill}" stroke="{stroke}" stroke-width="{2 if accent else 1}"/>'
)
parts = [
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" width="{W}" height="{H}" '
f'font-family="system-ui, -apple-system, Segoe UI, Roboto, sans-serif">',
f'<rect x="0" y="0" width="{W}" height="{H}" fill="#fff"/>',
]
# ---------------------------------------------------------------- titluri
text(W / 2, 34, "Forward P/E × venit: candidat sau capcană", 20, "bold")
text(W / 2, 58, "Forward P/E mic contează doar dacă veniturile cresc.", 13, "normal", GRAY)
# ---------------------------------------------------------------- capete de coloana / rand
text(C1, 96, "Forward P/E mic", 15, "bold")
text(C2, 96, "Forward P/E mare", 15, "bold")
text(110, R1 + 5, "venit crește", 14, "bold", BLACK, "end")
text(110, R2 + 5, "venit scade", 14, "bold", BLACK, "end")
text(30, (Y0 + Y1) / 2.0, "venit", 14, "bold", BLACK, "middle",
f"rotate(-90 30 {(Y0 + Y1) / 2.0:.0f})")
# ---------------------------------------------------------------- celule
parts.append(cell_outline(X0, Y0, COL_MID - X0, ROW_MID - Y0, accent=True)) # candidat
parts.append(cell_outline(COL_MID, Y0, X1 - COL_MID, ROW_MID - Y0)) # bun dar scump
parts.append(cell_outline(X0, ROW_MID, COL_MID - X0, Y1 - ROW_MID)) # probleme
parts.append(cell_outline(COL_MID, ROW_MID, X1 - COL_MID, Y1 - ROW_MID)) # scump + scadere
# celula A: P/E mic + venit crește
text(C1, 192, "Candidat", 17, "bold", BLUE)
text(C1, 220, "TWLO", 15, "bold")
text(C1, 244, "forward P/E ~23", 14)
text(C1, 264, "venit în creștere", 14)
# celula B: P/E mare + venit crește
text(C2, 192, "Bun, dar scump", 17, "bold")
text(C2, 226, "scor fundamental bun,", 14)
text(C2, 246, "dar „nu aș cumpăra aici sus”", 14)
# celula C: P/E mic + venit scade
text(C1, 398, "Probleme, nu chilipir", 17, "bold")
text(C1, 426, "CRM", 15, "bold")
text(C1, 450, "forward P/E ~15", 14)
text(C1, 470, "veniturile sunt în scădere", 14)
# celula D: P/E mare + venit scade
text(C2, 400, "Scump + venit în scădere", 16, "bold")
text(C2, 432, "business care se micșorează", 14)
text(C2, 452, "scepticism", 14)
# ---------------------------------------------------------------- surse
text(110, 540, "Sursă: TWLO forward P/E ~23, venit în creștere", 13, "normal", GRAY, "start")
text(110, 558, "și CRM forward P/E ~15, venituri în scădere", 13, "normal", GRAY, "start")
text(110, 576, "(125_18022026.txt:20-35). P/E mare = scump,", 13, "normal", GRAY, "start")
text(110, 594, "„nu aș cumpăra aici sus” (44_22062026.txt:167).", 13, "normal", GRAY, "start")
# ---------------------------------------------------------------- emitere
for x, y, s, size, weight, fill, anchor, transform in texts:
t = f' transform="{transform}"' if transform else ""
parts.append(
f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" font-weight="{weight}" '
f'fill="{fill}" text-anchor="{anchor}"{t}>{html.escape(s, quote=True)}</text>'
)
parts.append("</svg>")
svg = "\n".join(parts) + "\n"
# ---------------------------------------------------------------- verificare in-script
assert 'filter=' not in svg and 'gradient' not in svg, "fara umbre/gradienti"
seen = set()
for m in re.finditer(r'<text x="([\d.]+)" y="([\d.]+)"[^>]*font-size="(\d+)"', svg):
x, y, size = float(m.group(1)), float(m.group(2)), int(m.group(3))
assert 0 <= x <= W and 0 <= y <= H, f"eticheta in afara panzei: {x},{y}"
assert size >= 13, f"font sub 13: {size}"
key = (round(x), round(y))
assert key not in seen, f"doua texte pe aceeasi pozitie: {key}"
seen.add(key)
assert "~23" in svg and "~15" in svg, "cifrele sursa lipsesc"
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})")