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
195 lines
8.1 KiB
Python
195 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Pasul 1 — universul și lichiditatea: cele două praguri, desenate ca o zonă.
|
|
|
|
Diagramă conceptuală (NU grafic de preț), pentru
|
|
`summaries/17_STRATEGII/04_selectia.md`, pasul 1. Pe orizontală prețul de
|
|
închidere, pe verticală volumul mediu zilnic. Pragurile cursului (close peste
|
|
30-40 $, volum mediu de cel puțin 500.000 de acțiuni, ideal peste un milion)
|
|
taie planul în patru; dreptunghiul verde e singurul colț din care ai voie să
|
|
iei simboluri.
|
|
|
|
Cele trei simboluri sunt anonime (A, B, C) și cu cifre construite de mine:
|
|
cursul dă pragurile, nu exemple numerice. Le pun ca să se vadă că un simbol
|
|
poate pica pe oricare din cele două praguri, separat.
|
|
|
|
Nu folosește `svg_candles`: SVG-ul e construit ca string, doar stdlib.
|
|
|
|
Rulare: python3 gen/strategii/selectia_univers.py
|
|
"""
|
|
import html
|
|
from pathlib import Path
|
|
|
|
W, H = 700, 604
|
|
|
|
BLACK, GRAY, RED, GREEN, BLUE, ORANGE = "#000000", "#777777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
|
GREEN_BG = "#eef7f0"
|
|
|
|
# ---------------------------------------------------------------- praguri (cursul)
|
|
PRET_MIN, PRET_IDEAL = 30.0, 40.0 # close peste 30-40 $
|
|
VOL_MIN, VOL_IDEAL = 0.5, 1.0 # milioane de acțiuni pe zi
|
|
|
|
# ---------------------------------------------------------------- simboluri (construite)
|
|
# (nume, preț, volum mediu în milioane, verdict)
|
|
SIMB = [
|
|
("A", 58.0, 2.4, "trece"),
|
|
("B", 24.0, 3.1, "cade pe preț"),
|
|
("C", 72.0, 0.18, "cade pe volum"),
|
|
]
|
|
|
|
# ---------------------------------------------------------------- geometrie
|
|
X0, X1 = 110.0, 630.0
|
|
Y0, Y1 = 130.0, 420.0
|
|
PMAX, VMAX = 100.0, 3.5
|
|
|
|
|
|
def px(p):
|
|
return X0 + p / PMAX * (X1 - X0)
|
|
|
|
|
|
def py(v):
|
|
return Y1 - v / VMAX * (Y1 - Y0)
|
|
|
|
|
|
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>')
|
|
|
|
|
|
def line(x1, y1, x2, y2, stroke=GRAY, w=1.0, dash=None):
|
|
d = f' stroke-dasharray="{dash}"' if dash else ""
|
|
return (f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" '
|
|
f'stroke="{stroke}" stroke-width="{w}"{d}/>')
|
|
|
|
|
|
p = []
|
|
|
|
add(W / 2, 34, "Pasul 1 — cine intră în universul de scanare", 20, "bold")
|
|
add(W / 2, 58, "pe orizontală prețul de închidere, pe verticală volumul mediu zilnic",
|
|
13, "normal", GRAY)
|
|
add(W / 2, 78, "de ce pragurile: pe un simbol subțire, ordinul tău mută singur prețul",
|
|
13, "normal", GRAY)
|
|
|
|
# zona verde: singurul colț bun
|
|
p.append(f'<rect x="{px(PRET_MIN):.1f}" y="{Y0:.1f}" width="{X1 - px(PRET_MIN):.1f}" '
|
|
f'height="{py(VOL_MIN) - Y0:.1f}" fill="{GREEN_BG}" stroke="{GREEN}" stroke-width="1.8"/>')
|
|
add(px(PRET_MIN) + 10, Y0 + 22, "zona din care iei simboluri", 14, "bold", GREEN, "start")
|
|
|
|
# cadrul și axele
|
|
p.append(line(X0, Y0, X0, Y1, GRAY, 1.2))
|
|
p.append(line(X0, Y1, X1, Y1, GRAY, 1.2))
|
|
|
|
# pragurile
|
|
p.append(line(px(PRET_MIN), Y0, px(PRET_MIN), Y1, GREEN, 1.8))
|
|
p.append(line(px(PRET_IDEAL), Y0, px(PRET_IDEAL), Y1, GREEN, 1.2, "5 3"))
|
|
p.append(line(X0, py(VOL_MIN), X1, py(VOL_MIN), GREEN, 1.8))
|
|
p.append(line(X0, py(VOL_IDEAL), X1, py(VOL_IDEAL), GREEN, 1.2, "5 3"))
|
|
|
|
add(px(PRET_MIN), Y1 + 38, "30 $", 13, "bold", GREEN)
|
|
add(px(PRET_IDEAL), Y1 + 56, "40 $ (ideal)", 13, "normal", GREEN)
|
|
add(X0 - 10, py(VOL_MIN) + 4, "500.000", 13, "bold", GREEN, "end")
|
|
add(X0 - 10, py(VOL_IDEAL) + 4, "1 milion", 13, "normal", GREEN, "end")
|
|
|
|
# repere de axă
|
|
for v in (0, 1, 2, 3):
|
|
p.append(line(X0 - 5, py(v), X0, py(v), GRAY, 1.0))
|
|
add(X0 - 62, py(0) + 4, "0", 13, "normal", GRAY, "end")
|
|
add(X0 - 62, py(2) + 4, "2 mil.", 13, "normal", GRAY, "end")
|
|
add(X0 - 62, py(3) + 4, "3 mil.", 13, "normal", GRAY, "end")
|
|
for pr in (0, 50, 100):
|
|
p.append(line(px(pr), Y1, px(pr), Y1 + 5, GRAY, 1.0))
|
|
add(px(pr), Y1 + 20, f"{pr} $", 13, "normal", GRAY)
|
|
|
|
add(X1, Y1 + 56, "prețul de închidere", 13, "normal", GRAY, "end")
|
|
add(X0 - 48, Y0 - 12, "volum mediu zilnic", 13, "normal", GRAY, "start")
|
|
|
|
# cele trei simboluri
|
|
for nume, pret, vol, verdict in SIMB:
|
|
x, y = px(pret), py(vol)
|
|
col = GREEN if verdict == "trece" else RED
|
|
p.append(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="7" fill="{col}"/>')
|
|
add(x, y - 14, f"Simbol {nume}", 13, "bold", col)
|
|
add(x, y + 26, f"{pret:.0f} $ · {vol:.2f} mil.", 13, "normal", BLACK)
|
|
add(x, y + 44, verdict, 13, "bold", col)
|
|
|
|
# caseta cu de ce
|
|
NOTE_Y = 504.0
|
|
p.append(f'<rect x="40" y="{NOTE_Y}" width="620" height="76" fill="#fdf0e0" '
|
|
f'stroke="{ORANGE}" stroke-width="1.6" rx="8"/>')
|
|
add(60, NOTE_Y + 26, "De ce contează amândouă, nu doar una", 15, "bold", ORANGE, "start")
|
|
add(60, NOTE_Y + 48,
|
|
"Simbolul B se tranzacționează mult, dar e prea ieftin; simbolul C e scump, dar",
|
|
13, "normal", BLACK, "start")
|
|
add(60, NOTE_Y + 66,
|
|
"aproape nimeni nu-l cumpără: acolo ordinul tău e cel care mișcă prețul.",
|
|
13, "normal", BLACK, "start")
|
|
|
|
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="Pragurile de pret si de volum ale universului de scanare, '
|
|
f'cu trei simboluri dintre care doar unul cade in zona buna">')
|
|
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
|
|
# 1. verdictul fiecărui simbol iese din cifrele lui, nu e scris de mână
|
|
for nume, pret, vol, verdict in SIMB:
|
|
trece = pret >= PRET_MIN and vol >= VOL_MIN
|
|
assert trece == (verdict == "trece"), f"Simbol {nume}: verdictul nu iese din cifre"
|
|
if verdict == "cade pe preț":
|
|
assert pret < PRET_MIN and vol >= VOL_MIN, f"Simbol {nume} nu cade doar pe preț"
|
|
if verdict == "cade pe volum":
|
|
assert vol < VOL_MIN and pret >= PRET_MIN, f"Simbol {nume} nu cade doar pe volum"
|
|
assert sum(1 for *_, v in SIMB if v == "trece") == 1, "aștept exact un simbol care trece"
|
|
|
|
# 2. zona verde e exact colțul „peste ambele praguri"
|
|
assert px(PRET_MIN) > X0 and py(VOL_MIN) < Y1, "zona verde nu e un colț al planului"
|
|
for nume, pret, vol, verdict in SIMB:
|
|
inauntru = px(PRET_MIN) <= px(pret) <= X1 and Y0 <= py(vol) <= py(VOL_MIN)
|
|
assert inauntru == (verdict == "trece"), f"Simbol {nume}: punctul nu e desenat în zona lui"
|
|
|
|
# 3. toate punctele încap în cadru
|
|
for nume, pret, vol, _ in SIMB:
|
|
assert X0 < px(pret) < X1, f"Simbol {nume}: prețul iese din cadru"
|
|
assert Y0 < py(vol) < Y1, f"Simbol {nume}: volumul iese din cadru"
|
|
|
|
# 4. liniile de prag nu cad pe marginea cadrului
|
|
for x in (px(PRET_MIN), px(PRET_IDEAL)):
|
|
assert x - X0 > 20 and X1 - x > 20, "linie de prag lipită de marginea cadrului"
|
|
for y in (py(VOL_MIN), py(VOL_IDEAL)):
|
|
assert y - Y0 > 20 and Y1 - y > 20, "linie de prag lipită de marginea cadrului"
|
|
|
|
# 5. etichete în pânză și 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}")
|
|
|
|
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(SIMB)} simboluri, {W}x{H}), verificare OK")
|