Sunt diagrame conceptuale - funnel, bara de scor, matrice, cele patru cadrane, Trinity, sectoare - nu grafice de pret, deci nu trec prin svg_candles / pchart, care raman obligatorii doar pentru candele si entry/SL/TP. Agent nou, diagramist, cate unul per diagrama, fiecare cu generatorul lui in gen/selectia/; substitutia in .md o face gen/az_selectia.py, ca sa nu scrie sapte agenti in acelasi fisier. Regula de continut a agentului: fiecare cifra si eticheta trebuie sa existe in document sau in citate. Ce nu reiese din sursa e scris pe desen ca atare - ordinea celor 7 sectoare, ce se intampla sub 100% relative volume. Fara semafor rosu/verde, care nu e in sursa. Verificat cu ochii pe doua diagrame randate in browser. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
188 lines
7.6 KiB
Python
188 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Harta celor patru cadrane relative strength, fata de benchmark.
|
|
|
|
Pentru `summaries/08_ANALIZA_ZILNICA/11_selectia_simbolului.md` (SVG:cadrane),
|
|
sectiunea 2.1. Nu e grafic de pret, deci nu se folosesc svg_candles / pchart:
|
|
SVG scris de mana, doar stdlib.
|
|
|
|
Ce arata: cadranele 1-4 asa cum le descrie documentul, grupate pe directie.
|
|
Randul de sus = conditia de intrare LONG (cadranele 1 si 2), randul de jos =
|
|
conditia de intrare SHORT (cadranele 3 si 4); sageti duble grupeaza perechile.
|
|
Fiecare cadran are numarul, sensul si directia.
|
|
|
|
Etichete si cifre, toate din text/glosar (fara axe inventate, fara procente):
|
|
- RS = cat de bine se misca simbolul fata de benchmark (ex. SPY) (.md:62)
|
|
- cadran 1 = supraperformanta, bate benchmark-ul, long (.md:63,310)
|
|
- cadran 2 = imbunatatire spre supraperformanta, zona de cumparat,
|
|
la long, de preferat (.md:64,311)
|
|
- cadran 3 = imbunatatire spre underperform, pentru short (.md:65,312)
|
|
- cadran 4 = underperform, pierde in fata benchmark-ului,
|
|
short / stai pe margine (.md:66,313)
|
|
- cadranul 4 nu se tranzactioneaza pe orb: astepti trecerea in 2 sau 1,
|
|
ori un MP4-1 / MBT-10 long ca semn de schimbare (.md:315-316)
|
|
- setup de bottom cu RS fara imbunatatire: sanse mai mici (.md:323-324)
|
|
|
|
Fara semafor rosu/verde (nu e in sursa); un singur accent albastru pe
|
|
chenare/sageti/numere, text negru pe fundal alb.
|
|
Ruleaza: .venv/bin/python gen/selectia/cadrane.py -> gen/selectia/cadrane.svg
|
|
"""
|
|
from pathlib import Path
|
|
from xml.sax.saxutils import escape
|
|
|
|
OUT = Path(__file__).with_suffix(".svg")
|
|
|
|
W, H = 700, 580
|
|
BLACK, GRAY, BLUE, WHITE = "#000000", "#777777", "#1a5fb4", "#ffffff"
|
|
|
|
COL1_X, COL_W, GAP = 40, 290, 40
|
|
COL2_X = COL1_X + COL_W + GAP # 370
|
|
COL1_R = COL1_X + COL_W # 330
|
|
COL2_R = COL2_X + COL_W # 660
|
|
|
|
CARD_H = 150
|
|
ROW1_Y, ROW2_Y = 86, 302
|
|
ROW1_B, ROW2_B = ROW1_Y + CARD_H, ROW2_Y + CARD_H # 236 / 452
|
|
BR1_Y, BR2_Y = ROW1_B + 14, ROW2_B + 14 # 250 / 466
|
|
|
|
# fiecare card: (x, y, linii [(dy, text, size, weight, fill)])
|
|
CARDS = [
|
|
(COL1_X, ROW1_Y, [
|
|
(34, "Cadran 1", 16, "700", BLACK),
|
|
(68, "supraperformanță", 14, "400", BLACK),
|
|
(96, "bate benchmark-ul", 13, "400", GRAY),
|
|
(124, "LONG", 13, "700", BLUE),
|
|
]),
|
|
(COL2_X, ROW1_Y, [
|
|
(34, "Cadran 2", 16, "700", BLACK),
|
|
(66, "îmbunătățire spre", 14, "400", BLACK),
|
|
(86, "supraperformanță", 14, "400", BLACK),
|
|
(110, "zona de cumpărat, la long", 13, "400", GRAY),
|
|
(132, "LONG", 13, "700", BLUE),
|
|
]),
|
|
(COL1_X, ROW2_Y, [
|
|
(34, "Cadran 3", 16, "700", BLACK),
|
|
(66, "îmbunătățire spre", 14, "400", BLACK),
|
|
(86, "underperform", 14, "400", BLACK),
|
|
(110, "pentru short", 13, "400", GRAY),
|
|
(132, "SHORT", 13, "700", BLUE),
|
|
]),
|
|
(COL2_X, ROW2_Y, [
|
|
(34, "Cadran 4", 16, "700", BLACK),
|
|
(68, "underperform", 14, "400", BLACK),
|
|
(96, "pierde în fața benchmark-ului", 13, "400", GRAY),
|
|
(124, "SHORT / stai pe margine", 13, "700", BLUE),
|
|
]),
|
|
]
|
|
|
|
TEXTS = [] # (x, y, text, size, weight, anchor) pentru verificare
|
|
|
|
|
|
def txt(x, y, s, size=13, weight="400", fill=BLACK, anchor="middle"):
|
|
TEXTS.append((x, y, s, size, weight, anchor))
|
|
return (
|
|
f'<text x="{x:.1f}" y="{y}" font-size="{size}" font-weight="{weight}" '
|
|
f'fill="{fill}" text-anchor="{anchor}">{escape(s)}</text>'
|
|
)
|
|
|
|
|
|
def line(x1, y1, x2, y2, stroke=BLUE, width=1.5, dash=None):
|
|
d = f' stroke-dasharray="{dash}"' if dash else ""
|
|
return (f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" '
|
|
f'stroke="{stroke}" stroke-width="{width}"{d}/>')
|
|
|
|
|
|
def arrow(x, y, direction, color=BLUE):
|
|
"""Varf de sageata orizontal: direction=1 spre dreapta, -1 spre stanga."""
|
|
d = f"M {x} {y} L {x - 9 * direction} {y - 5} L {x - 9 * direction} {y + 5} Z"
|
|
return f'<path d="{d}" fill="{color}"/>'
|
|
|
|
|
|
def card(x, y, lines):
|
|
out = [f'<rect x="{x}" y="{y}" width="{COL_W}" height="{CARD_H}" rx="10" '
|
|
f'fill="{WHITE}" stroke="{BLUE}" stroke-width="1.5"/>']
|
|
for dy, s, size, weight, fill in lines:
|
|
out.append(txt(x + COL_W / 2, y + dy, s, size, weight, fill))
|
|
return out
|
|
|
|
|
|
def bracket(y, label):
|
|
"""Bara dubla cu varfuri, grupeaza cele doua carduri de pe rand."""
|
|
x0, x1 = COL1_X + 16, COL2_R - 16
|
|
return [
|
|
line(x0, y, x1, y, BLUE, 1.5),
|
|
arrow(x1, y, 1),
|
|
arrow(x0, y, -1),
|
|
txt(W / 2, y + 22, label, 14, "700", BLUE),
|
|
]
|
|
|
|
|
|
def build():
|
|
p = [
|
|
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
|
|
f'width="{W}" height="{H}" role="img" '
|
|
f'aria-label="Cele patru cadrane relative strength fata de benchmark: '
|
|
f'cadranele 1 si 2 = conditie de intrare long, cadranele 3 si 4 = short">',
|
|
f'<g font-family="system-ui, sans-serif">',
|
|
f'<rect x="0" y="0" width="{W}" height="{H}" fill="{WHITE}"/>',
|
|
txt(W / 2, 34, "Relative strength — cele patru cadrane", 19, "700", BLACK),
|
|
txt(W / 2, 58, "RS = cât de bine se mișcă simbolul față de benchmark "
|
|
"(ex. SPY)", 13, "400", GRAY),
|
|
]
|
|
for x, y, lines in CARDS:
|
|
p.extend(card(x, y, lines))
|
|
p.extend(bracket(BR1_Y, "Condiție de intrare LONG — cadranele 1 și 2"))
|
|
p.extend(bracket(BR2_Y, "Condiție de intrare SHORT — cadranele 3 și 4"))
|
|
p.append(txt(W / 2, BR2_Y + 48,
|
|
"Cadranul 4 nu se tranzacționează pe orb, chiar cu scor bun:",
|
|
13, "400", GRAY))
|
|
p.append(txt(W / 2, BR2_Y + 68,
|
|
"aștepți trecerea în 2 sau 1, ori un MP4-1 / MBT-10 long ca semn "
|
|
"de schimbare.", 13, "400", GRAY))
|
|
p.append(txt(W / 2, BR2_Y + 90,
|
|
"Un setup de bottom cu RS fără îmbunătățire are șanse mai mici.",
|
|
13, "400", GRAY))
|
|
p.append('</g></svg>')
|
|
return "\n".join(p) + "\n"
|
|
|
|
|
|
# ---------------------------------------------------------------- verificare
|
|
def est_w(s, size, weight):
|
|
factor = 0.62 if weight == "700" else 0.56
|
|
return len(s) * size * factor
|
|
|
|
|
|
def verify():
|
|
for x, y, s, size, weight, anchor in TEXTS:
|
|
ew = est_w(s, size, weight)
|
|
left = x - ew if anchor == "end" else (x - ew / 2 if anchor == "middle" else x)
|
|
right = left + ew
|
|
assert -2 <= left and right <= W + 2, f"text in afara panzei: {s!r}"
|
|
assert 0 <= y <= H, f"text in afara inaltimii: {s!r} (y={y})"
|
|
|
|
for i in range(len(TEXTS)):
|
|
xi, yi, si, szi, wi, ai = TEXTS[i]
|
|
we = est_w(si, szi, wi)
|
|
li = xi - we if ai == "end" else (xi - we / 2 if ai == "middle" else xi)
|
|
ri = li + we
|
|
for j in range(i + 1, len(TEXTS)):
|
|
xj, yj, sj, szj, wj, aj = TEXTS[j]
|
|
wej = est_w(sj, szj, wj)
|
|
lj = xj - wej if aj == "end" else (xj - wej / 2 if aj == "middle" else xj)
|
|
rj = lj + wej
|
|
if abs(yi - yj) < 13 and li < rj - 4 and lj < ri - 4:
|
|
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
|
|
|
for x, y, lines in CARDS:
|
|
for dy, s, size, weight, fill in lines:
|
|
ew = est_w(s, size, weight)
|
|
assert ew <= COL_W - 24, f"eticheta nu incape in card: {s!r} ({ew:.0f})"
|
|
assert y + dy <= y + CARD_H - 8, f"eticheta iese din card: {s!r}"
|
|
assert "nan" not in OUT_TEXT[0]
|
|
return True
|
|
|
|
|
|
OUT_TEXT = [build()]
|
|
assert verify(), "verificare esuata"
|
|
OUT.write_text(OUT_TEXT[0], encoding="utf-8")
|
|
print(f"scris {OUT} ({W}x{H}), verificare OK: {len(TEXTS)} etichete")
|