"""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'' ) parts = [ f'', f'', ] # ---------------------------------------------------------------- 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'{html.escape(s, quote=True)}' ) parts.append("") 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']*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})")