#!/usr/bin/env python3 """Pasul 3 — sectorul care domină: numeri semnalele pe sectoare. Diagramă conceptuală (NU grafic de preț), pentru `summaries/17_STRATEGII/04_selectia.md`, pasul 3. Cele 11 sectoare S&P, cu câte semnale a dat scanerul în fiecare într-o singură zi; sectorul cu cele mai multe e evidențiat, pentru că acolo intră banii. Ce vine din material: cele 11 sectoare S&P și ETF-urile lor, regula „uită-te ce predomină, în ce sectoare" și sortarea sectoarelor după cadranul 1 plus valoarea relative strength. Ce e al meu: **numărul de semnale din fiecare sector**. Cursul nu dă o zi cu cifre pe sectoare, așa că le-am construit; ele ilustrează operațiunea (numărarea), nu o zi reală de piață. Scrie asta și în desen, ca să nu fie luate drept date. Nu folosește `svg_candles`: SVG-ul e construit ca string, doar stdlib. Rulare: python3 gen/strategii/selectia_sectoare.py """ import html from pathlib import Path W, H = 700, 620 BLACK, GRAY, RED, GREEN, BLUE, ORANGE = "#000000", "#777777", "#c00", "#188a3e", "#1a5fb4", "#b06000" BLUE_BG = "#eef3fb" # (sector, ETF, semnale — numărul e construit de mine) SECTOARE = [ ("Sănătate", "XLV", 9), ("Energie", "XLE", 6), ("Financiar", "XLF", 5), ("Real estate", "XLRE", 4), ("Tehnologie", "XLK", 3), ("Industrial", "XLI", 3), ("Consum de bază", "XLP", 2), ("Consum discreționar", "XLY", 2), ("Utilități", "XLU", 1), ("Materiale", "XLB", 1), ("Comunicații", "XLC", 1), ] X_NUME = 40.0 X0, X1 = 250.0, 600.0 # zona barelor Y0, ROW = 132.0, 30.0 TEXTS = [] def add(x, y, s, size=13, weight="normal", fill=BLACK, anchor="middle"): TEXTS.append((x, y, s, size, weight, fill, anchor)) def est_w(s, size, weight): return len(s) * size * (0.62 if weight == "bold" else 0.56) def txt(x, y, s, size, weight, fill, anchor): return (f'{html.escape(s, quote=False)}') p = [] MAXS = max(s for *_, s in SECTOARE) assert SECTOARE[0][2] == MAXS, "prima linie trebuie să fie sectorul dominant" assert sum(1 for *_, s in SECTOARE if s == MAXS) == 1, "dominantul nu e unic" assert len(SECTOARE) == 11, "S&P are 11 sectoare" assert all(a[2] >= b[2] for a, b in zip(SECTOARE, SECTOARE[1:])), "lista nu e sortată descrescător" add(W / 2, 34, "Pasul 3 — din ce sector vin cele mai multe semnale", 20, "bold") add(W / 2, 58, "cele 11 sectoare S&P, cu semnalele scanerului dintr-o singură zi", 13, "normal", GRAY) add(W / 2, 78, "de ce: mai multe semnale în același sector = cineva mută bani acolo", 13, "normal", GRAY) add(X_NUME, 110, "sector (ETF)", 13, "bold", GRAY, "start") add(X0, 110, "semnale în ziua asta", 13, "bold", GRAY, "start") # dreptunghiul de evidențiere peste sectorul dominant p.append(f'') for i, (nume, etf, n) in enumerate(SECTOARE): y = Y0 + i * ROW dominant = n == MAXS col = BLUE if dominant else GRAY add(X_NUME, y, f"{nume} ({etf})", 13, "bold" if dominant else "normal", col, "start") w = n / MAXS * (X1 - X0) p.append(f'') add(X0 + w + 8, y, str(n), 13, "bold" if dominant else "normal", col, "start") # săgeata care spune de ce Y_ARR = Y0 - 6 p.append(f'') add(X1 + 80, Y_ARR - 30, "aici te uiți", 13, "bold", BLUE, "end") Y_NOTE = Y0 + len(SECTOARE) * ROW + 26 p.append(f'') add(60, Y_NOTE + 26, "Ce faci cu asta", 15, "bold", ORANGE, "start") add(60, Y_NOTE + 48, "Păstrezi simbolurile din sectorul dominant și le duci mai departe la pasul 4;", 13, "normal", BLACK, "start") add(60, Y_NOTE + 66, "restul le lași pentru azi. Sectoarele se mai sortează și după cadranul 1 plus", 13, "normal", BLACK, "start") add(60, Y_NOTE + 84, "valoarea relative strength — aceleași cadrane ca la pasul 5.", 13, "normal", BLACK, "start") add(W / 2, H - 14, "Numărul de semnale pe sector e construit de mine: cursul dă regula, nu o zi cu cifre.", 13, "normal", GRAY) head = (f'') body = [head, f''] + p body += [txt(*t) for t in TEXTS] body.append("") svg = "\n".join(body) + "\n" # =============================================================== VERIFICARE # lungimea barei e proporțională cu numărul din etichetă for i, (nume, etf, n) in enumerate(SECTOARE): w = n / MAXS * (X1 - X0) assert abs(w / (X1 - X0) - n / MAXS) < 1e-9, f"{nume}: bara nu e la scară" assert X0 + w <= X1 + 1e-9, f"{nume}: bara iese din zona ei" # etichete în pânză, fără suprapuneri boxes = [] for x, y, s, size, weight, fill, anchor in TEXTS: assert size >= 13, f"font sub 13px: {s!r}" w = est_w(s, size, weight) left = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x) assert -2 <= left and left + w <= W + 2, f"text în afara pânzei: {s!r}" assert 0 <= y - size * 0.78 and y + size * 0.22 <= H, f"text în afara pânzei: {s!r}" boxes.append((left, left + w, y - size * 0.78, y + size * 0.22, 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 + 2 and r2 > l1 + 2 and b1 > t2 + 2 and b2 > t1 + 2: raise AssertionError(f"etichete suprapuse: {s1!r} / {s2!r}") # numele de sector nu intră peste zona barelor for x, y, s, size, weight, fill, anchor in TEXTS: if anchor == "start" and x == X_NUME and y >= Y0: assert x + est_w(s, size, weight) < X0 - 6, f"numele iese peste bare: {s!r}" assert "\n\n" not in svg, "linie goală în SVG" OUT = Path(__file__).with_suffix(".svg") OUT.write_text(svg, encoding="utf-8") print(f"scris {OUT.name} ({len(svg)} bytes, {len(TEXTS)} etichete, " f"{len(SECTOARE)} sectoare, dominant {SECTOARE[0][0]} cu {MAXS}, {W}x{H}), verificare OK")