svg_candles: cautarea nivelurilor, scoasa in functie comuna; 11 generatoare reparate
Unsprezece generatoare nu mai rulau deloc la HEAD: cautau <line x1="24", iar svg_candles scrie x1="24.0" de cand are Level.i0. Un generator mort inseamna ca diagrama nu mai poate fi regenerata, si nimeni n-ar fi aflat pana cand ar fi incercat cineva, peste luni. Cele 11 copii ale aceluiasi regex au disparut: y_niveluri() si offset_render() se agata acum de semnatura liniei de Level (stroke-width + dasharray), nu de x-ul ei. Caz nou in selftest, cu Level(i0=) si panou de volum - combinatia la care varianta veche fie crapa, fie prindea tacut linia de zero a volumului. 41 de SVG-uri regenerate, toate identice cu cele comise dupa normalizarea x1=24 -> x1=24.0, deci niciun desen n-a sarit. Verificarea asta a prins o regresie reala: la buy_doji_transe substitutia lasase scaderea veche pe randul urmator, offsetul se scadea de doua ori si desenul urca 92 px. Inca doua generatoare moarte din aceeasi familie, gasite pe drum (intrarea_cazul_lui, intrarea_doua_variante): desenau peste grafic citind ch._y() fara offset. Nu crapau zgomotos - produceau suprapuneri la 22 px. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
This commit is contained in:
@@ -409,6 +409,35 @@ class Chart:
|
||||
return "\n ".join(out).replace("\n </svg>", "\n</svg>")
|
||||
|
||||
|
||||
# --------------------------------------------------------- citit din randare
|
||||
# Un generator care desenează peste graficul randat are nevoie de deplasarea
|
||||
# pe care `render()` a aplicat-o (face loc titlului și săgeților, apoi
|
||||
# restaurează `top`), și o citește din prima linie de Level.
|
||||
#
|
||||
# Nu o căuta cu `x1="24"`. Formatarea e `:.1f` de când Level are `i0` ("24.0"),
|
||||
# iar cu `i0` linia nici nu mai pornește din margine. Unsprezece generatoare
|
||||
# aveau fiecare copia lui `re.search(r'<line x1="24" ...')`: cele fără panou de
|
||||
# volum au început să crape, iar cele cu volum au prins TĂCUT linia de zero a
|
||||
# volumului și au desenat cu offsetul ei. De aia căutarea stă aici, într-un
|
||||
# singur loc, și se agață de semnătura liniei de Level, nu de x-ul ei.
|
||||
_LINIE_NIVEL = re.compile(
|
||||
r'<line x1="(?:-?[\d.]+)" y1="(-?[\d.]+)"[^/]*?'
|
||||
r'stroke-width="1\.5" stroke-dasharray=')
|
||||
|
||||
|
||||
def y_niveluri(svg: str) -> list[float]:
|
||||
"""Y-urile liniilor de Level dintr-un SVG randat, în ordinea desenării."""
|
||||
return [float(m.group(1)) for m in _LINIE_NIVEL.finditer(svg)]
|
||||
|
||||
|
||||
def offset_render(svg: str, ch: "Chart") -> float:
|
||||
"""Câți px a coborât `render()` desenul față de ce întoarce acum `ch._y()`."""
|
||||
ys = y_niveluri(svg)
|
||||
assert ys, "SVG fără linii de Level: nu pot citi offsetul randării"
|
||||
assert ch.levels, "Chart fără levels: nu am față de ce măsura offsetul"
|
||||
return ys[0] - ch._y(ch.levels[0].price)
|
||||
|
||||
|
||||
def walk(start: float, moves: list[tuple[float, float, float]],
|
||||
vols: list[float] | None = None) -> list[Candle]:
|
||||
"""Construiește o serie continuă de candele dintr-un preț de start.
|
||||
@@ -526,6 +555,21 @@ def selftest():
|
||||
"zona trebuie desenată înaintea candelelor"
|
||||
assert svg3.count("<polygon") == 3 and "pe close</text>" in svg3
|
||||
|
||||
# offset_render se agață de linia de Level, nu de x-ul ei: și cu `i0`
|
||||
# (linia nu mai pornește din margine), și cu panou de volum (care are și el
|
||||
# o linie de la x=24, și pe care varianta veche o prindea în loc)
|
||||
cdv = [Candle(c.o, c.h, c.l, c.c, vol=100) for c in ser]
|
||||
ch4 = Chart(candles=cdv, scale=40, gap=14, body_w=9, width=200, vol_h=40,
|
||||
levels=[Level(ser[0].l, "nivel", i0=1), Level(ser[0].h, "sus")],
|
||||
title="T")
|
||||
svg4 = ch4.render()
|
||||
ys = y_niveluri(svg4)
|
||||
assert len(ys) == 2, f"am găsit {len(ys)} linii de Level, aștept 2"
|
||||
assert abs(offset_render(svg4, ch4) - 22) < 1e-6, \
|
||||
f"offsetul titlului nu e cel așteptat: {offset_render(svg4, ch4)}"
|
||||
assert all(abs(y - ch4._y(lv.price) - offset_render(svg4, ch4)) < 1e-6
|
||||
for y, lv in zip(ys, ch4.levels)), "y-urile nu sunt ale nivelurilor"
|
||||
|
||||
print("selftest ok: 2R la exact 2R, serie fără goluri, volum proporțional, zone+săgeți")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user