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
This commit is contained in:
194
gen/strategii/selectia_univers.py
Normal file
194
gen/strategii/selectia_univers.py
Normal file
@@ -0,0 +1,194 @@
|
||||
#!/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")
|
||||
Reference in New Issue
Block a user