Files
atm-curs-dl/gen/strategii/selectia_sectoare.py
Claude Agent f1033a916c selectia: diagramele refacute dupa sablon (textul urmeaza)
Agentul a atins pragul de context dupa diagrame; capitolul 04_selectia
e inca in formatul vechi si se reia separat.

Ce s-a schimbat: ansamblul nu mai arata ULTA mergand la TP2 — ULTA pica
chiar filtrul de selectie (RVOL 0.61 si 5.01 % de suport), deci nu putea
fi exemplul-vitrina; acum arata doar ce citeste selectia, cu granita
"aici se termina selectia" marcata. Funnel-ul trece de la 8 trepte la 6,
aliniat cu pasii nivelului 2, si fiecare treapta spune de ce. Cadranele
primesc mini-histograme RS, ca sa se recunoasca dintr-un desen, nu
dintr-o propozitie. Trimiterile fisier:linie sunt scoase de pe desene.

Noi: univers (pragurile de pret si volum), sectoare (dominantul din cele
11), proximitate (banda de 3 % peste suportul monthly, cu observatia ca
nu e pragul de 1-2 % din Buy Doji — masoara altceva).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-20 12:43:12 +00:00

154 lines
6.6 KiB
Python

#!/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'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" font-weight="{weight}" '
f'fill="{fill}" text-anchor="{anchor}">{html.escape(s, quote=False)}</text>')
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'<rect x="{X_NUME - 8:.1f}" y="{Y0 - 20:.1f}" width="{X1 - X_NUME + 60:.1f}" '
f'height="{ROW:.1f}" fill="{BLUE_BG}" stroke="{BLUE}" stroke-width="1.6"/>')
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'<rect x="{X0:.1f}" y="{y - 12:.1f}" width="{w:.1f}" height="16" '
f'fill="{col}" fill-opacity="{0.85 if dominant else 0.35}"/>')
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'<path d="M {X1 + 46:.1f} {Y_ARR:.1f} L {X1 + 76:.1f} {Y_ARR - 26:.1f}" '
f'stroke="{BLUE}" stroke-width="2" fill="none"/>')
add(X1 + 80, Y_ARR - 30, "aici te uiți", 13, "bold", BLUE, "end")
Y_NOTE = Y0 + len(SECTOARE) * ROW + 26
p.append(f'<rect x="40" y="{Y_NOTE:.1f}" width="620" height="96" fill="#fdf0e0" '
f'stroke="{ORANGE}" stroke-width="1.6" rx="8"/>')
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'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" width="{W}" '
f'height="{H}" font-family="system-ui, sans-serif" role="img" '
f'aria-label="Cele 11 sectoare S&P sortate dupa cate semnale a dat scanerul '
f'in fiecare, cu sectorul dominant evidentiat">')
body = [head, f'<rect x="0" y="0" width="{W}" height="{H}" fill="#ffffff"/>'] + p
body += [txt(*t) for t in TEXTS]
body.append("</svg>")
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")