From e58763d26d6ba7dc1d564a570546ca5e73c48a29 Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Sun, 20 Sep 2026 19:08:04 +0000 Subject: [PATCH] 04_selectia: ultimele doua diagrame (rvol, trinity) Pasul 6 si cele 3 stele, singurele doua marcaje ramase LIPSA. Ambele conceptuale, dupa tiparul lui selectia_scor / selectia_cadrane. Singurul prag desenat la RVOL e 100 %, cat da sursa; trinity ilustreaza doar sensul din acest capitol (RS + Power + volum in cadranul 1). Assert-urile masoara in pixeli raportul bara/medie (2.00 si 0.61), deci o schimbare de scara sau de serie face eticheta sa pice, nu sa minta. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ --- gen/strategii/selectia_rvol.py | 315 ++++++++++++++++++++++++ gen/strategii/selectia_rvol.spec.txt | 27 ++ gen/strategii/selectia_rvol.svg | 63 +++++ gen/strategii/selectia_trinity.py | 314 +++++++++++++++++++++++ gen/strategii/selectia_trinity.spec.txt | 24 ++ gen/strategii/selectia_trinity.svg | 120 +++++++++ summaries/17_STRATEGII/04_selectia.html | 187 +++++++++++++- summaries/17_STRATEGII/04_selectia.md | 185 ++++++++++++++ 8 files changed, 1234 insertions(+), 1 deletion(-) create mode 100644 gen/strategii/selectia_rvol.py create mode 100644 gen/strategii/selectia_rvol.spec.txt create mode 100644 gen/strategii/selectia_rvol.svg create mode 100644 gen/strategii/selectia_trinity.py create mode 100644 gen/strategii/selectia_trinity.spec.txt create mode 100644 gen/strategii/selectia_trinity.svg diff --git a/gen/strategii/selectia_rvol.py b/gen/strategii/selectia_rvol.py new file mode 100644 index 0000000..92ee850 --- /dev/null +++ b/gen/strategii/selectia_rvol.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python3 +"""Relative volume (RVOL) — bara zilei față de media ultimelor 10 bare. + +Diagramă conceptuală (NU grafic de preț): pentru +`summaries/17_STRATEGII/04_selectia.md`, Nivel 2, Pasul 6. Citește +specificația `gen/strategii/selectia_rvol.spec.txt` și scrie +`gen/strategii/selectia_rvol.svg`. + +Nu folosește `svg_candles` / `pchart`: SVG-ul e construit ca string, doar +stdlib. Fără `` — toate coordonatele sunt absolute. + +Ce arată: + - două panouri cu ACELAȘI istoric de 10 bare de volum și aceeași medie; + - linia mediei = 100 %, punctată, etichetată pe ambele panouri; + - bara „azi": stânga de 2x media (RVOL 200 %, cazul HIG), dreapta 0.61x + media (RVOL 0.61, cazul ULTA); + - banda de jos: sub 100 % e minus, nu veto (cele trei rezultate reale ale + scanerului de Buy Doji: 0.86, 1.03, 0.61); + - nota: media e pe 10 bare, nu pe 50 ca în setările implicite. + +Singurul prag din material e „peste 100 %"; nu se desenează alt prag. + +Rulare: .venv/bin/python gen/strategii/selectia_rvol.py +""" +import html +import re +import xml.etree.ElementTree as ET +from pathlib import Path + +W, H = 700, 764 + +BLACK = "#000000" +GRAY = "#777777" +RED = "#c00" +GREEN = "#188a3e" +BLUE = "#1a5fb4" +ORANGE = "#b06000" + +GREEN_BG = "#eef7f0" +RED_BG = "#fdeeee" +NOTE_BG = "#fdf0e0" +GRAY_BG = "#f7f7f7" + +# ------------------------------------------------------- date (unități de volum) +# Istoricul e ales ca media lui să fie exact 100 -> „media = 100 %". +ISTORIC = [82, 95, 88, 91, 97, 140, 103, 110, 120, 74] +MEDIA = sum(ISTORIC) / len(ISTORIC) # = 100.0 +AZI_HIG = 2.00 * MEDIA # RVOL 200 % (cazul HIG) +AZI_ULTA = 0.61 * MEDIA # RVOL 0.61 (cazul ULTA) + +# ------------------------------------------------------- geometrie panouri +PAN_Y0, PAN_Y1 = 128.0, 438.0 # cadrul panoului +BASE = 430.0 # linia de 0 a volumului +UNIT = 0.84 # px per unitate de volum +PAN_W = 284.0 +PAN_AX, PAN_BX = 40.0, 376.0 # marginea stângă a fiecărui panou + +TEXTS = [] # (x, y, s, size, weight, fill, anchor) +RECTS = [] # (x, y, w, h, tag) + + +def add(x, y, s, size=13, weight="normal", fill=BLACK, anchor="middle"): + TEXTS.append((float(x), float(y), s, size, weight, fill, anchor)) + return s + + +def est_w(s, size, weight): + factor = 0.62 if weight == "bold" else 0.56 + return len(s) * size * factor + + +def txt(x, y, s, size=13, weight="normal", fill=BLACK, anchor="middle"): + return (f'' + f'{html.escape(s, quote=False)}') + + +def rect(x, y, w, h, tag, fill="#ffffff", stroke=GRAY, sw=1.5, dash=None, + opacity=None): + RECTS.append((float(x), float(y), float(w), float(h), tag)) + d = f' stroke-dasharray="{dash}"' if dash else "" + o = f' fill-opacity="{opacity}"' if opacity is not None else "" + return (f'') + + +def line(x1, y1, x2, y2, stroke=GRAY, width=1.0, dash=None): + d = f' stroke-dasharray="{dash}"' if dash else "" + return (f'') + + +def arrow(x1, y1, x2, y2, stroke=BLACK, width=1.6): + """Săgeată verticală simplă (vârf triunghiular la capătul (x2, y2)).""" + dy = 1 if y2 > y1 else -1 + return [line(x1, y1, x2, y2 - dy * 7, stroke, width), + f''] + + +# ---------------------------------------------------------------- panou +BARS = {} # tag panou -> listă de (x_centru, y_top, înălțime, valoare) + + +def panou(tag, px, azi, culoare_azi, eticheta_azi, subeticheta_azi, captura): + """Desenează un panou: 10 bare de istoric + linia mediei + bara „azi".""" + out = [rect(px, PAN_Y0, PAN_W, PAN_Y1 - PAN_Y0, f"pan_{tag}", + fill="#ffffff", stroke=GRAY, sw=1.2)] + vals = ISTORIC + [azi] + slot = PAN_W / len(vals) + bw = slot * 0.62 + bars = [] + for i, v in enumerate(vals): + cx = px + slot * (i + 0.5) + h = v * UNIT + top = BASE - h + ultima = (i == len(vals) - 1) + col = culoare_azi if ultima else "#9aa3ad" + out.append(f'') + bars.append((cx, top, h, v)) + BARS[tag] = bars + + # linia de bază (volum 0) și linia mediei + out.append(line(px, BASE, px + PAN_W, BASE, BLACK, 1.2)) + y_med = BASE - MEDIA * UNIT + out.append(line(px, y_med, px + PAN_W, y_med, ORANGE, 2.0, "5 3")) + # eticheta stă în stânga, unde barele sunt sub linie, ca să nu cadă peste ele + add(px + 6, y_med - 6, "media = 100 %", 13, "bold", ORANGE, "start") + + # eticheta barei „azi" + săgeata către vârful ei + cx, top, _h, _v = bars[-1] + add(px + PAN_W / 2, PAN_Y0 + 24, eticheta_azi, 15, "bold", culoare_azi) + add(px + PAN_W / 2, PAN_Y0 + 44, subeticheta_azi, 13, "normal", BLACK) + out += arrow(cx, PAN_Y0 + 54, cx, top - 4, culoare_azi, 1.8) + + # axa de jos: ce sunt barele + add(px + (PAN_W - slot) / 2 - slot * 0.3, BASE + 20, + "ultimele 10 bare de volum", 13, "normal", GRAY) + add(cx, BASE + 20, "azi", 13, "bold", culoare_azi) + + # captura: de ce contează + add(px + PAN_W / 2, BASE + 48, captura[0], 13, "bold", culoare_azi) + add(px + PAN_W / 2, BASE + 68, captura[1], 13, "normal", BLACK) + return out + + +# =============================================================== CONȚINUT +p = [] + +add(W / 2, 34, "Relative volume (RVOL): volumul zilei față de media pe 10 bare", + 17, "bold") +add(W / 2, 58, "RVOL = volumul de azi ÷ media ultimelor 10 bare de volum", + 14, "bold", GRAY) +add(W / 2, 80, + "Măsoară dacă azi chiar participă lumea, nu doar tu. Singurul prag din curs: peste 100 %.", + 13, "normal", GRAY) + +add(PAN_AX + PAN_W / 2, 110, "Cazul HIG — peste prag", 15, "bold", GREEN) +add(PAN_BX + PAN_W / 2, 110, "Cazul ULTA — sub prag", 15, "bold", RED) + +p += panou("hig", PAN_AX, AZI_HIG, GREEN, + "azi = 2 × media → RVOL 200 %", + "cel mai mare din cele 16 rezultate", + ("peste 100 % = confirmare", + "se tranzacționează dublu față de normal")) +p += panou("ulta", PAN_BX, AZI_ULTA, RED, + "azi = 0,61 × media → RVOL 0.61", + "sub jumătate față de o zi normală", + ("sub 100 % = minus, NU veto", + "îl notezi, dar analiza continuă")) + +# ---- banda „minus, nu veto" +BAND_Y = 526.0 +p.append(rect(40, BAND_Y, 620, 112, "band_veto", fill=GRAY_BG, stroke=GRAY, sw=1.2)) +add(60, BAND_Y + 26, "Sub 100 % e un minus, nu un veto", 15, "bold", BLACK, "start") +add(60, BAND_Y + 50, + "Pe scanerul real de Buy Doji, cele trei rezultate aveau RVOL 0.86, 1.03 și 0.61.", + 13, "normal", BLACK, "start") +add(60, BAND_Y + 70, + "Un prag dur de genul „RVOL ≥ 1.2” ar fi dat zero rezultate în ziua aia.", + 13, "normal", BLACK, "start") +add(60, BAND_Y + 94, + "De aceea RVOL e o coloană pe care o citești, nu un filtru care taie automat.", + 13, "bold", BLACK, "start") + +# ---- nota: media pe 10, nu pe 50 +NOTE_Y = 654.0 +p.append(rect(40, NOTE_Y, 620, 90, "note_10", fill=NOTE_BG, stroke=ORANGE, sw=1.4)) +add(60, NOTE_Y + 26, + "Media e pe 10 bare, nu pe 50 (implicit în platforme).", + 13, "bold", BLACK, "start") +add(60, NOTE_Y + 48, + "Cifra cursului e mai sensibilă la ziua curentă. Dacă vezi altă valoare în", + 13, "normal", GRAY, "start") +add(60, NOTE_Y + 68, + "altă platformă, nu e greșeală de calcul, e altă lungime de medie.", + 13, "normal", GRAY, "start") + +# =============================================================== EMITERE +head = (f'') +body = [head, f''] +body += p +for x, y, s, size, weight, fill, anchor in TEXTS: + body.append(txt(x, y, s, size, weight, fill, anchor)) +body.append("") +svg = "\n".join(body) + "\n" + +# =============================================================== VERIFICARE +# 1. geometria desenată chiar spune ce spun etichetele: raportul dintre +# înălțimea barei „azi" și înălțimea liniei mediei (măsurate în px, de la +# linia de 0) trebuie să fie 2.00, respectiv 0.61. +h_media_px = BASE - (BASE - MEDIA * UNIT) +for tag, rvol_cerut in (("hig", 2.00), ("ulta", 0.61)): + h_azi_px = BARS[tag][-1][2] + assert abs(h_azi_px / h_media_px - rvol_cerut) < 0.005, \ + f"{tag}: bara desenată dă RVOL {h_azi_px / h_media_px:.3f}, nu {rvol_cerut}" + +# 2. istoricul e chiar de 10 bare și media lui e chiar linia trasată +for tag in ("hig", "ulta"): + istoric = [b[3] for b in BARS[tag][:-1]] + assert len(istoric) == 10, f"{tag}: {len(istoric)} bare de istoric, nu 10" + assert abs(sum(istoric) / 10 - MEDIA) < 1e-9, f"{tag}: media desenată nu e media barelor" +# istoricul e identic în ambele panouri (același simbol de referință vizual) +assert [b[3] for b in BARS["hig"][:-1]] == [b[3] for b in BARS["ulta"][:-1]] + +# 3. bara „azi" din HIG e chiar cea mai înaltă, iar cea din ULTA e sub medie +assert BARS["hig"][-1][2] == max(b[2] for b in BARS["hig"]), "bara HIG nu e cea mai înaltă" +assert BARS["ulta"][-1][2] < h_media_px, "bara ULTA nu e sub linia mediei" +# și barele nu ies din cadrul panoului +for tag in ("hig", "ulta"): + for cx, top, h, v in BARS[tag]: + assert top >= PAN_Y0, f"{tag}: bară peste marginea de sus a panoului" + assert abs(top + h - BASE) < 1e-6, f"{tag}: bară care nu pleacă de la linia de 0" + +# 3b. eticheta mediei nu cade peste nicio bară (e pusă în stânga, unde barele +# sunt sub linia mediei) +_y_med = BASE - MEDIA * UNIT +for tag in ("hig", "ulta"): + px = PAN_AX if tag == "hig" else PAN_BX + lat = est_w("media = 100 %", 13, "bold") + for cx, top, h, v in BARS[tag]: + if cx - 9 < px + 6 + lat and cx + 9 > px + 6: + assert top > _y_med - 6 + 3, \ + f"{tag}: eticheta mediei cade peste o bară (valoare {v})" + +# 4. nicio cifră care nu e desenată sau care nu vine din material +PERMISE = {"2", "200", "100", "10", "50", "0.61", "0,61", "0.86", "1.03", "1.2", "16"} +for _x, _y, s, *_ in TEXTS: + for n in re.findall(r"\d+(?:[.,]\d+)?", s): + assert n in PERMISE, f"cifră fără acoperire în material: {n!r} în {s!r}" +# pragul inventat „ideal > 200 %" nu are voie să apară +assert "ideal" not in svg.lower(), "prag inventat de tip ideal peste N la suta" + +# 5. textele obligatorii +for req in ("Relative volume (RVOL): volumul zilei față de media pe 10 bare", + "RVOL = volumul de azi ÷ media ultimelor 10 bare de volum", + "Singurul prag din curs: peste 100 %", + "media = 100 %", + "azi = 2 × media → RVOL 200 %", + "azi = 0,61 × media → RVOL 0.61", + "peste 100 % = confirmare", + "sub 100 % = minus, NU veto", + "Sub 100 % e un minus, nu un veto", + "RVOL 0.86, 1.03 și 0.61", + "zero rezultate în ziua aia", + "nu un filtru care taie automat", + "Media e pe 10 bare, nu pe 50"): + assert req in svg, f"lipsește din desen: {req!r}" +# eticheta mediei apare o dată pe fiecare panou +assert svg.count("media = 100 %") == 2, "linia mediei neetichetată pe ambele panouri" + +# 6. fiecare etichetă: mărime >= 13, î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} ({size})" + w = est_w(s, size, weight) + if anchor == "end": + left = x - w + elif anchor == "start": + left = x + else: + left = x - w / 2.0 + right = left + w + top, bottom = y - size * 0.78, y + size * 0.22 + assert -2 <= left and right <= W + 2, f"text în afara pânzei orizontal: {s!r}" + assert 0 <= top and bottom <= H, f"text în afara pânzei vertical: {s!r}" + boxes.append((left, right, top, bottom, 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}") + +# 7. stil + XML valid, fără transformări (audit_diagrams dă fals-pozitive pe ele) +assert "filter=" not in svg and "gradient" not in svg, "fără umbre/gradienți" +assert "transform=" not in svg, "fără : coordonate absolute" +assert 'font-family="system-ui, sans-serif"' in svg, "lipsește fontul cerut" +assert svg.lstrip().startswith("" +assert ET.fromstring(svg).tag.endswith("svg") + +OUT = Path(__file__).with_suffix(".svg") +OUT.write_text(svg, encoding="utf-8") +print(f"scris {OUT} ({len(svg)} bytes, {len(TEXTS)} etichete, " + f"2 panouri x 11 bare, medie {MEDIA:.0f}, " + f"RVOL desenat {BARS['hig'][-1][2] / h_media_px:.2f} / " + f"{BARS['ulta'][-1][2] / h_media_px:.2f}, {W}x{H}), verificare OK") diff --git a/gen/strategii/selectia_rvol.spec.txt b/gen/strategii/selectia_rvol.spec.txt new file mode 100644 index 0000000..02d0cab --- /dev/null +++ b/gen/strategii/selectia_rvol.spec.txt @@ -0,0 +1,27 @@ +TIP: CONCEPT (bare de volum + linia mediei, nu candele) + +UNDE APARE: strategii/04_selectia.md, Nivel 2, Pasul 6 „Relative volume peste 100 %". + +CE ARATA: cum se citeste RVOL — volumul zilei impartit la media ultimelor 10 bare +de volum — in doua cazuri reale din material: HIG la 200 % si ULTA la 0.61. + +COMPOZITIE: +- doua panouri de volum, identice ca istoric (10 bare) si ca linie a mediei; +- linia mediei = 100 %, punctata, etichetata pe ambele panouri; +- bara „azi": in panoul stang de 2x media (RVOL 200 %), in cel drept 0.61 x media. + +CIFRE (toate din capitol / material, niciuna inventata): +- HIG: RVOL 200 %, cel mai mare din cele 16 rezultate ale scanerului din ziua aceea. +- ULTA: RVOL 0.61, sub jumatate fata de normal. +- scanerul real de Buy Doji: cele trei rezultate aveau 0.86, 1.03 si 0.61. +- media e pe 10 bare; indicatorul standard din platforme o calculeaza pe 50. +- pragul din sursa e doar „peste 100 %". NU se pune alt prag (nu exista + „ideal > 200 %" in material). + +MESAJ: +- peste 100 % = confirmare (participa lumea, nu doar tu); +- sub 100 % = minus, NU veto: analiza a continuat la 0.99, 0.96, 0.94, 0.88, 0.74; +- un prag dur „RVOL >= 1.2" ar fi dat zero rezultate in ziua scanerului de Buy Doji, + de aceea RVOL e coloana pe care o citesti, nu filtru care taie automat. + +TITLU: "Relative volume (RVOL): volumul zilei față de media pe 10 bare" diff --git a/gen/strategii/selectia_rvol.svg b/gen/strategii/selectia_rvol.svg new file mode 100644 index 0000000..6bb172a --- /dev/null +++ b/gen/strategii/selectia_rvol.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Relative volume (RVOL): volumul zilei față de media pe 10 bare +RVOL = volumul de azi ÷ media ultimelor 10 bare de volum +Măsoară dacă azi chiar participă lumea, nu doar tu. Singurul prag din curs: peste 100 %. +Cazul HIG — peste prag +Cazul ULTA — sub prag +media = 100 % +azi = 2 × media → RVOL 200 % +cel mai mare din cele 16 rezultate +ultimele 10 bare de volum +azi +peste 100 % = confirmare +se tranzacționează dublu față de normal +media = 100 % +azi = 0,61 × media → RVOL 0.61 +sub jumătate față de o zi normală +ultimele 10 bare de volum +azi +sub 100 % = minus, NU veto +îl notezi, dar analiza continuă +Sub 100 % e un minus, nu un veto +Pe scanerul real de Buy Doji, cele trei rezultate aveau RVOL 0.86, 1.03 și 0.61. +Un prag dur de genul „RVOL ≥ 1.2” ar fi dat zero rezultate în ziua aia. +De aceea RVOL e o coloană pe care o citești, nu un filtru care taie automat. +Media e pe 10 bare, nu pe 50 (implicit în platforme). +Cifra cursului e mai sensibilă la ziua curentă. Dacă vezi altă valoare în +altă platformă, nu e greșeală de calcul, e altă lungime de medie. + diff --git a/gen/strategii/selectia_trinity.py b/gen/strategii/selectia_trinity.py new file mode 100644 index 0000000..5a5ea95 --- /dev/null +++ b/gen/strategii/selectia_trinity.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +"""Cele 3 stele (Trinity) — RS, Power și volum, toate în cadranul 1. + +Diagramă conceptuală (NU grafic de preț): pentru +`summaries/17_STRATEGII/04_selectia.md`, Nivel 3, „Cele 3 stele (Trinity)". +Citește specificația `gen/strategii/selectia_trinity.spec.txt` și scrie +`gen/strategii/selectia_trinity.svg`. + +Nu folosește `svg_candles` / `pchart`: SVG-ul e construit ca string, doar +stdlib. Fără `` — toate coordonatele sunt absolute. + +Ce arată: + - stânga: un simbol cu RS, Power și volum toate în cadranul 1 -> 3 stele; + - dreapta: un simbol cu doar două din trei în cadranul 1 -> nu e trinity; + - fiecare indicator e o mini-histogramă cu linia de 0 și media mobilă + peste ea; cadranul 1 = ultima bară peste 0 ȘI peste MA (definiția de la + pasul 5 al capitolului); + - nota că Power e proprietar, fără formulă publicată; + - nota că „trinity" are în material și alte sensuri — aici e numai ăsta. + +Desenul e pur calitativ: nicio valoare de RS / Power / volum pe etichete. + +Rulare: .venv/bin/python gen/strategii/selectia_trinity.py +""" +import html +import re +import xml.etree.ElementTree as ET +from pathlib import Path + +W, H = 700, 868 + +BLACK = "#000000" +GRAY = "#777777" +RED = "#c00" +GREEN = "#188a3e" +ORANGE = "#b06000" + +GREEN_BG = "#eef7f0" +RED_BG = "#fdeeee" +NOTE_BG = "#fdf0e0" +GRAY_BG = "#f7f7f7" + +# ------------------------------------------------------- serii (calitative) +# Cadranul 1 = ultima bară peste 0 ȘI peste media ei mobilă. +IN_Q1 = [-2, -1, 0, 1, 2, 2, 3, 4, 5, 6] # urcă: ultima e peste 0 și peste MA +IN_Q3 = [6, 6, 5, 5, 4, 4, 3, 3, 2, 2] # coboară: peste 0, dar SUB MA + + +def ma4(vals): + """Media mobilă pe 4, ca la indicatorii cursului (histogramă + MA peste ea).""" + return [sum(vals[max(0, i - 3):i + 1]) / len(vals[max(0, i - 3):i + 1]) + for i in range(len(vals))] + + +def in_cadranul_1(vals): + """Ultima bară: peste 0 ȘI peste media ei mobilă.""" + return vals[-1] > 0 and vals[-1] > ma4(vals)[-1] + + +# ------------------------------------------------------- geometrie +COL_W = 300.0 +COL_AX, COL_BX = 32.0, 368.0 +COL_Y0 = 110.0 +ROW_H = 118.0 # un indicator pe rând +ROW_Y0 = COL_Y0 + 60.0 # primul rând, sub titlul coloanei +COL_H = 60.0 + 3 * ROW_H + 62.0 + +TEXTS = [] # (x, y, s, size, weight, fill, anchor) +RECTS = [] # (x, y, w, h, tag) +STARS = {} # tag coloană -> câte stele s-au desenat + + +def add(x, y, s, size=13, weight="normal", fill=BLACK, anchor="middle"): + TEXTS.append((float(x), float(y), s, size, weight, fill, anchor)) + return s + + +def est_w(s, size, weight): + factor = 0.62 if weight == "bold" else 0.56 + return len(s) * size * factor + + +def txt(x, y, s, size=13, weight="normal", fill=BLACK, anchor="middle"): + return (f'' + f'{html.escape(s, quote=False)}') + + +def rect(x, y, w, h, tag, fill="#ffffff", stroke=GRAY, sw=1.5, dash=None): + RECTS.append((float(x), float(y), float(w), float(h), tag)) + d = f' stroke-dasharray="{dash}"' if dash else "" + return (f'') + + +def line(x1, y1, x2, y2, stroke=GRAY, width=1.0, dash=None): + d = f' stroke-dasharray="{dash}"' if dash else "" + return (f'') + + +def stea(cx, cy, r, fill, stroke): + """Steluță cu 5 colțuri, desenată ca poligon (fără font de emoji).""" + import math + pts = [] + for k in range(10): + ang = -math.pi / 2 + k * math.pi / 5 + rad = r if k % 2 == 0 else r * 0.42 + pts.append(f"{cx + rad * math.cos(ang):.1f},{cy + rad * math.sin(ang):.1f}") + return (f'') + + +def histograma(x0, y0, w, h, vals, culoare): + """Mini-histogramă: barele, linia de 0 și media mobilă punctată peste ea.""" + yz = y0 + h / 2.0 + unit = (h / 2.0 - 4) / max(abs(v) for v in vals) + bw = w / (len(vals) * 1.6) + xs = [x0 + w * (i + 0.5) / len(vals) for i in range(len(vals))] + out = [f'', + line(x0, yz, x0 + w, yz, BLACK, 1.0)] + for i, v in enumerate(vals): + top = yz - v * unit if v > 0 else yz + out.append(f'') + pts = " ".join(f"{xs[i]:.1f},{yz - m * unit:.1f}" for i, m in enumerate(ma4(vals))) + out.append(f'') + return out, xs[-1], (yz - vals[-1] * unit if vals[-1] > 0 else yz) + + +def coloana(tag, cx0, titlu, subtitlu, serii, culoare_cadru, fundal): + """O coloană: trei indicatori, fiecare cu histograma lui și verdictul lui.""" + out = [rect(cx0, COL_Y0, COL_W, COL_H, f"col_{tag}", + fill=fundal, stroke=culoare_cadru, sw=1.8)] + add(cx0 + COL_W / 2, COL_Y0 + 26, titlu, 16, "bold", culoare_cadru) + add(cx0 + COL_W / 2, COL_Y0 + 46, subtitlu, 13, "normal", BLACK) + + stele = 0 + for i, (nume, vals) in enumerate(serii): + y = ROW_Y0 + i * ROW_H + q1 = in_cadranul_1(vals) + col = GREEN if q1 else RED + add(cx0 + 16, y + 16, nume, 14, "bold", BLACK, "start") + hist, _x_ult, _y_ult = histograma(cx0 + 16, y + 26, COL_W - 90, 56, vals, col) + out += hist + if q1: + stele += 1 + out.append(stea(cx0 + COL_W - 34, y + 54, 15, "#e0a800", "#8a6d00")) + add(cx0 + 16, y + 100, "cadranul 1: peste 0 și peste MA", + 13, "normal", GREEN, "start") + else: + out.append(line(cx0 + COL_W - 46, y + 42, cx0 + COL_W - 22, y + 66, RED, 2.4)) + out.append(line(cx0 + COL_W - 22, y + 42, cx0 + COL_W - 46, y + 66, RED, 2.4)) + add(cx0 + 16, y + 100, "cadranul 3: peste 0, dar sub MA", + 13, "normal", RED, "start") + STARS[tag] = stele + return out + + +# =============================================================== CONȚINUT +p = [] + +add(W / 2, 32, "Cele 3 stele (Trinity): RS, Power și volum, toate în cadranul 1", + 17, "bold") +add(W / 2, 56, + "Sortare finală, când după cei șase pași ți-au rămas prea multe simboluri.", + 13, "normal", GRAY) +add(W / 2, 78, + "Cadranul 1 = ultima bară a indicatorului e peste 0 ȘI peste media lui mobilă (linia punctată).", + 13, "normal", GRAY) + +SERII_A = [("Relative strength", IN_Q1), ("Power", IN_Q1), ("Volum", IN_Q1)] +SERII_B = [("Relative strength", IN_Q1), ("Power", IN_Q3), ("Volum", IN_Q1)] + +p += coloana("a", COL_AX, "Simbol A — 3 stele", + "toate trei arată în aceeași direcție", SERII_A, GREEN, GREEN_BG) +p += coloana("b", COL_BX, "Simbol B — 2 din 3", + "Power rămâne în urmă: nu e trinity", SERII_B, RED, RED_BG) + +# verdictul fiecărei coloane, sub cele trei rânduri +VY = ROW_Y0 + 3 * ROW_H + 24 +add(COL_AX + COL_W / 2, VY, "3 stele → îl iei primul la analiză", 14, "bold", GREEN) +add(COL_BX + COL_W / 2, VY, "2 stele → nu e „3 stele”", 14, "bold", RED) + +# ---- ce înseamnă aici, și ce NU înseamnă +BAND_Y = COL_Y0 + COL_H + 22 +p.append(rect(32, BAND_Y, 636, 98, "band_sens", fill=GRAY_BG, stroke=GRAY, sw=1.2)) +add(50, BAND_Y + 26, + "În capitolul ăsta „3 stele” / „trinity” înseamnă exact un lucru:", + 14, "bold", BLACK, "start") +add(50, BAND_Y + 48, + "relative strength, Power și volumul, toate trei în cadranul 1 pe același simbol.", + 13, "normal", BLACK, "start") +add(50, BAND_Y + 74, + "Cuvântul apare în material și cu alte sensuri (la MDSRT: trei indicatori în divergență", + 13, "normal", GRAY, "start") +add(50, BAND_Y + 92, + "simultană). Acelea sunt altceva și nu se aplică aici.", + 13, "normal", GRAY, "start") + +# ---- nota Power +NOTE_Y = BAND_Y + 114 +p.append(rect(32, NOTE_Y, 636, 92, "note_power", fill=NOTE_BG, stroke=ORANGE, sw=1.4)) +add(50, NOTE_Y + 26, + "Power e proprietar: măsoară tăria trendului, dar formula nu e publicată.", + 13, "bold", BLACK, "start") +add(50, NOTE_Y + 48, + "Citești cadranul din indicator, nu îl poți reconstrui singur.", + 13, "normal", BLACK, "start") +add(50, NOTE_Y + 72, + "De aceea sortarea după 3 stele e o rafinare, nu un pas obligatoriu.", + 13, "normal", GRAY, "start") + +# =============================================================== EMITERE +head = (f'') +body = [head, f''] +body += p +for x, y, s, size, weight, fill, anchor in TEXTS: + body.append(txt(x, y, s, size, weight, fill, anchor)) +body.append("") +svg = "\n".join(body) + "\n" + +# =============================================================== VERIFICARE +# 1. seriile desenate chiar satisfac definiția cadranului 1 (nu e tautologic: +# verifică ultima bară față de 0 și față de media mobilă calculată din serie) +assert in_cadranul_1(IN_Q1), "seria „cadranul 1” nu e în cadranul 1" +assert IN_Q3[-1] > 0 and IN_Q3[-1] < ma4(IN_Q3)[-1], \ + "seria „cadranul 3” trebuie să fie peste 0, dar sub MA" + +# 2. numărul de stele desenate = numărul de indicatori în cadranul 1 +assert STARS["a"] == sum(in_cadranul_1(v) for _, v in SERII_A) == 3, \ + f"coloana A: {STARS['a']} stele, dar seriile dau altceva" +assert STARS["b"] == sum(in_cadranul_1(v) for _, v in SERII_B) == 2, \ + f"coloana B: {STARS['b']} stele, dar seriile dau altceva" +assert svg.count("= 13, î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} ({size})" + w = est_w(s, size, weight) + if anchor == "end": + left = x - w + elif anchor == "start": + left = x + else: + left = x - w / 2.0 + right = left + w + top, bottom = y - size * 0.78, y + size * 0.22 + assert -2 <= left and right <= W + 2, f"text în afara pânzei orizontal: {s!r}" + assert 0 <= top and bottom <= H, f"text în afara pânzei vertical: {s!r}" + boxes.append((left, right, top, bottom, 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}") + +# 8. stil + XML valid, fără transformări +assert "filter=" not in svg and "gradient" not in svg, "fără umbre/gradienți" +assert "transform=" not in svg, "fără : coordonate absolute" +assert 'font-family="system-ui, sans-serif"' in svg, "lipsește fontul cerut" +assert svg.lstrip().startswith("" +assert ET.fromstring(svg).tag.endswith("svg") + +OUT = Path(__file__).with_suffix(".svg") +OUT.write_text(svg, encoding="utf-8") +print(f"scris {OUT} ({len(svg)} bytes, {len(TEXTS)} etichete, " + f"stele: A={STARS['a']}, B={STARS['b']}, {W}x{H}), verificare OK") diff --git a/gen/strategii/selectia_trinity.spec.txt b/gen/strategii/selectia_trinity.spec.txt new file mode 100644 index 0000000..dfec248 --- /dev/null +++ b/gen/strategii/selectia_trinity.spec.txt @@ -0,0 +1,24 @@ +TIP: CONCEPT (trei mini-histograme cu linia de 0 si media mobila, nu candele) + +UNDE APARE: strategii/04_selectia.md, Nivel 3, „Cele 3 stele (Trinity)". + +CE ARATA: sensul pe care „trinity / 3 stele" il are IN CAPITOLUL ASTA si numai el: +relative strength, Power si volumul, toate trei in cadranul 1 pe acelasi simbol. + +COMPOZITIE: +- coloana stanga: un simbol cu toate trei in cadranul 1 -> 3 stele; +- coloana dreapta: un simbol cu doar doua din trei in cadranul 1 -> nu e trinity; +- fiecare indicator e o mini-histograma cu linia de 0 si media mobila peste ea; + cadranul 1 = ultima bara peste 0 SI peste MA (definitie din pasul 5). + +NOTE OBLIGATORII: +- Power e proprietar, fara formula publicata in material: citesti cadranul, + nu il poti reconstrui. De aceea sortarea e rafinare, nu pas obligatoriu. +- „trinity" are in material si alte sensuri (la MDSRT = trei indicatori in + divergenta; intr-o sesiune de intrebari = 3 indicatori + bull reversal + + suport = 5 stele). Diagrama asta ilustreaza DOAR sensul din capitolul de + selectie. + +FARA CIFRE: desenul e pur calitativ, nu contine valori de RS / Power / volum. + +TITLU: "Cele 3 stele (Trinity): RS, Power și volum, toate în cadranul 1" diff --git a/gen/strategii/selectia_trinity.svg b/gen/strategii/selectia_trinity.svg new file mode 100644 index 0000000..e4deef3 --- /dev/null +++ b/gen/strategii/selectia_trinity.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Cele 3 stele (Trinity): RS, Power și volum, toate în cadranul 1 +Sortare finală, când după cei șase pași ți-au rămas prea multe simboluri. +Cadranul 1 = ultima bară a indicatorului e peste 0 ȘI peste media lui mobilă (linia punctată). +Simbol A — 3 stele +toate trei arată în aceeași direcție +Relative strength +cadranul 1: peste 0 și peste MA +Power +cadranul 1: peste 0 și peste MA +Volum +cadranul 1: peste 0 și peste MA +Simbol B — 2 din 3 +Power rămâne în urmă: nu e trinity +Relative strength +cadranul 1: peste 0 și peste MA +Power +cadranul 3: peste 0, dar sub MA +Volum +cadranul 1: peste 0 și peste MA +3 stele → îl iei primul la analiză +2 stele → nu e „3 stele” +În capitolul ăsta „3 stele” / „trinity” înseamnă exact un lucru: +relative strength, Power și volumul, toate trei în cadranul 1 pe același simbol. +Cuvântul apare în material și cu alte sensuri (la MDSRT: trei indicatori în divergență +simultană). Acelea sunt altceva și nu se aplică aici. +Power e proprietar: măsoară tăria trendului, dar formula nu e publicată. +Citești cadranul din indicator, nu îl poți reconstrui singur. +De aceea sortarea după 3 stele e o rafinare, nu un pas obligatoriu. + diff --git a/summaries/17_STRATEGII/04_selectia.html b/summaries/17_STRATEGII/04_selectia.html index 65e2ae2..2a1dda7 100644 --- a/summaries/17_STRATEGII/04_selectia.html +++ b/summaries/17_STRATEGII/04_selectia.html @@ -896,6 +896,70 @@ să reacționeze la ziua curentă. Dacă îl compari cu RVOL-ul din setările implicite ale altei platforme, vei vedea alte cifre — nu e o greșeală de calcul.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Relative volume (RVOL): volumul zilei față de media pe 10 bare +RVOL = volumul de azi ÷ media ultimelor 10 bare de volum +Măsoară dacă azi chiar participă lumea, nu doar tu. Singurul prag din curs: peste 100 %. +Cazul HIG — peste prag +Cazul ULTA — sub prag +media = 100 % +azi = 2 × media → RVOL 200 % +cel mai mare din cele 16 rezultate +ultimele 10 bare de volum +azi +peste 100 % = confirmare +se tranzacționează dublu față de normal +media = 100 % +azi = 0,61 × media → RVOL 0.61 +sub jumătate față de o zi normală +ultimele 10 bare de volum +azi +sub 100 % = minus, NU veto +îl notezi, dar analiza continuă +Sub 100 % e un minus, nu un veto +Pe scanerul real de Buy Doji, cele trei rezultate aveau RVOL 0.86, 1.03 și 0.61. +Un prag dur de genul „RVOL ≥ 1.2” ar fi dat zero rezultate în ziua aia. +De aceea RVOL e o coloană pe care o citești, nu un filtru care taie automat. +Media e pe 10 bare, nu pe 50 (implicit în platforme). +Cifra cursului e mai sensibilă la ziua curentă. Dacă vezi altă valoare în +altă platformă, nu e greșeală de calcul, e altă lungime de medie. +

+

Sub 100 % e un minus, nu un veto. În material analiza a continuat la 0.99, 0.96, 0.94, 0.88 și 0.74. Iar pe scanerul real de Buy Doji, cele trei @@ -925,6 +989,127 @@ simbol. Asta e ce numește cursul „3 stele" sau „trinity" în capitolul de selecție — și numai asta. Când toate trei arată în aceeași direcție, simbolul are și forță relativă, și trend susținut, și participare.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Cele 3 stele (Trinity): RS, Power și volum, toate în cadranul 1 +Sortare finală, când după cei șase pași ți-au rămas prea multe simboluri. +Cadranul 1 = ultima bară a indicatorului e peste 0 ȘI peste media lui mobilă (linia punctată). +Simbol A — 3 stele +toate trei arată în aceeași direcție +Relative strength +cadranul 1: peste 0 și peste MA +Power +cadranul 1: peste 0 și peste MA +Volum +cadranul 1: peste 0 și peste MA +Simbol B — 2 din 3 +Power rămâne în urmă: nu e trinity +Relative strength +cadranul 1: peste 0 și peste MA +Power +cadranul 3: peste 0, dar sub MA +Volum +cadranul 1: peste 0 și peste MA +3 stele → îl iei primul la analiză +2 stele → nu e „3 stele” +În capitolul ăsta „3 stele” / „trinity” înseamnă exact un lucru: +relative strength, Power și volumul, toate trei în cadranul 1 pe același simbol. +Cuvântul apare în material și cu alte sensuri (la MDSRT: trei indicatori în divergență +simultană). Acelea sunt altceva și nu se aplică aici. +Power e proprietar: măsoară tăria trendului, dar formula nu e publicată. +Citești cadranul din indicator, nu îl poți reconstrui singur. +De aceea sortarea după 3 stele e o rafinare, nu un pas obligatoriu. +

+

Power e proprietar și nu are formulă publicată în material: știi ce măsoară (tăria trendului), citești cadranul, dar nu îl poți reconstrui singur. De @@ -1341,6 +1526,6 @@ selecției).

  • [ ] Dacă au rămas prea mulți: sortez după 3 stele (RS + Power + volum în cadranul 1).
  • [ ] Între doi candidați: trend, apoi volum, apoi fundamental.
  • [ ] Am notat simbolul pe listă fără să intru. Trigger-ul se caută separat, pe grafic.
  • - + diff --git a/summaries/17_STRATEGII/04_selectia.md b/summaries/17_STRATEGII/04_selectia.md index edbf2c0..2bc3675 100644 --- a/summaries/17_STRATEGII/04_selectia.md +++ b/summaries/17_STRATEGII/04_selectia.md @@ -919,6 +919,70 @@ implicite ale altei platforme, vei vedea alte cifre — nu e o greșeală de calcul. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Relative volume (RVOL): volumul zilei față de media pe 10 bare +RVOL = volumul de azi ÷ media ultimelor 10 bare de volum +Măsoară dacă azi chiar participă lumea, nu doar tu. Singurul prag din curs: peste 100 %. +Cazul HIG — peste prag +Cazul ULTA — sub prag +media = 100 % +azi = 2 × media → RVOL 200 % +cel mai mare din cele 16 rezultate +ultimele 10 bare de volum +azi +peste 100 % = confirmare +se tranzacționează dublu față de normal +media = 100 % +azi = 0,61 × media → RVOL 0.61 +sub jumătate față de o zi normală +ultimele 10 bare de volum +azi +sub 100 % = minus, NU veto +îl notezi, dar analiza continuă +Sub 100 % e un minus, nu un veto +Pe scanerul real de Buy Doji, cele trei rezultate aveau RVOL 0.86, 1.03 și 0.61. +Un prag dur de genul „RVOL ≥ 1.2” ar fi dat zero rezultate în ziua aia. +De aceea RVOL e o coloană pe care o citești, nu un filtru care taie automat. +Media e pe 10 bare, nu pe 50 (implicit în platforme). +Cifra cursului e mai sensibilă la ziua curentă. Dacă vezi altă valoare în +altă platformă, nu e greșeală de calcul, e altă lungime de medie. + + **Sub 100 % e un minus, nu un veto.** În material analiza a continuat la 0.99, 0.96, 0.94, 0.88 și 0.74. Iar pe scanerul real de Buy Doji, cele trei @@ -958,6 +1022,127 @@ selecție — și numai asta. Când toate trei arată în aceeași direcție, simbolul are și forță relativă, și trend susținut, și participare. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Cele 3 stele (Trinity): RS, Power și volum, toate în cadranul 1 +Sortare finală, când după cei șase pași ți-au rămas prea multe simboluri. +Cadranul 1 = ultima bară a indicatorului e peste 0 ȘI peste media lui mobilă (linia punctată). +Simbol A — 3 stele +toate trei arată în aceeași direcție +Relative strength +cadranul 1: peste 0 și peste MA +Power +cadranul 1: peste 0 și peste MA +Volum +cadranul 1: peste 0 și peste MA +Simbol B — 2 din 3 +Power rămâne în urmă: nu e trinity +Relative strength +cadranul 1: peste 0 și peste MA +Power +cadranul 3: peste 0, dar sub MA +Volum +cadranul 1: peste 0 și peste MA +3 stele → îl iei primul la analiză +2 stele → nu e „3 stele” +În capitolul ăsta „3 stele” / „trinity” înseamnă exact un lucru: +relative strength, Power și volumul, toate trei în cadranul 1 pe același simbol. +Cuvântul apare în material și cu alte sensuri (la MDSRT: trei indicatori în divergență +simultană). Acelea sunt altceva și nu se aplică aici. +Power e proprietar: măsoară tăria trendului, dar formula nu e publicată. +Citești cadranul din indicator, nu îl poți reconstrui singur. +De aceea sortarea după 3 stele e o rafinare, nu un pas obligatoriu. + + Power e proprietar și nu are formulă publicată în material: știi ce măsoară (tăria trendului), citești cadranul, dar nu îl poți reconstrui singur. De