etichete: cele trei care mint, si doua bug-uri de infrastructura
Cele trei, toate verificate pe ecran dupa reparatie: "minimul din care a pornit miscarea 99.80" intra sub o candela si se citea "99.8" (m2d_directie); "2 saptamani" era acoperita de caseta de citat si se citea "2 saptaman" (atmi_orizont); cele doua stopuri de la buy_doji_stop stateau la 12 px cu liniile incrucisate peste etichete, iar diagrama e tocmai despre diferenta dintre ele - acum fiecare sta la capatul propriei linii. Doua bug-uri gasite pe drum, mai grave decat ce reparam: 1. render_html trecea SVG-urile prin markdown. In mp2_pivoti_excel cele doua asteriscuri din formulele Excel (=2*E2-C2) deveneau un <em> pus peste </text>, iar parserul browserului arunca restul SVG-ului: jumatate de tabel lipsea din pagina, fara niciun semn in .md sau in audit. Acum SVG-urile sunt scoase din calea lui markdown si puse inapoi dupa, cu assert ca nu ramane niciun marcaj. 2. eticheta_libera nu citea <g transform>, deci la diagramele cu panouri compara coordonate din sisteme diferite si rata suprapuneri reale. Acum aplatizeaza translate/scale pe <g>-uri imbricate. Prima diagrama pe care a prins-o dupa reparatie e chiar una deja comisa, din capitolul 10. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
This commit is contained in:
@@ -12,6 +12,10 @@ Verificarea e opt-in, pe etichete numite: pe un grafic de candele aproape
|
||||
orice text atinge ceva, iar o regulă globală ar bloca tot repo-ul.
|
||||
|
||||
eticheta_libera(svg, "wedge_desc", {"baza = 4.00"}, rects=True)
|
||||
|
||||
Coordonatele se aplatizează înainte de comparație: la diagramele cu panouri
|
||||
wrapper-ul e `<g transform="translate(0,h)">`, deci fără asta se comparau
|
||||
coordonate din sisteme diferite și suprapunerile reale treceau nevăzute.
|
||||
"""
|
||||
import re
|
||||
|
||||
@@ -19,15 +23,95 @@ CW = 6.2 # lățimea unui caracter bold de 11-12px
|
||||
WICK = "#1a1a1a" # fitilele: desenul le traversează oricum, prin construcție
|
||||
|
||||
|
||||
# --- aplatizarea transformărilor -------------------------------------------
|
||||
XA = ("x", "x1", "x2", "cx")
|
||||
YA = ("y", "y1", "y2", "cy")
|
||||
WA = ("width", "rx")
|
||||
HA = ("height", "ry")
|
||||
ID = (0.0, 0.0, 1.0, 1.0)
|
||||
_ATTR = re.compile(r'([a-zA-Z][\w-]*)="(-?[\d.]+)"')
|
||||
_TAG = re.compile(r'<(/?)([a-zA-Z]+)((?:"[^"]*"|[^>"])*)>')
|
||||
|
||||
|
||||
def _ctm(attrs, base):
|
||||
"""Transformarea cumulată: cea de pe `<g>` compusă peste cea moștenită."""
|
||||
tx, ty, sx, sy = base
|
||||
t = re.search(r'transform="([^"]*)"', attrs)
|
||||
for fn, args in re.findall(r"(\w+)\(([^)]*)\)", t.group(1) if t else ""):
|
||||
v = [float(a) for a in re.split(r"[,\s]+", args.strip()) if a]
|
||||
if fn == "translate" and v:
|
||||
tx, ty = tx + sx * v[0], ty + sy * (v[1] if len(v) > 1 else 0.0)
|
||||
elif fn == "scale" and v:
|
||||
sx, sy = sx * v[0], sy * (v[1] if len(v) > 1 else v[0])
|
||||
# rotate/matrix/skew: ignorate, ca înainte — nu apar în repo, iar
|
||||
# caseta unui text rotit n-ar fi oricum un dreptunghi drept.
|
||||
return tx, ty, sx, sy
|
||||
|
||||
|
||||
def _rescrie(attrs, ctm, tag):
|
||||
tx, ty, sx, sy = ctm
|
||||
|
||||
def unu(m):
|
||||
k, v = m.group(1), float(m.group(2))
|
||||
if k in XA:
|
||||
v = tx + sx * v
|
||||
elif k in YA:
|
||||
v = ty + sy * v
|
||||
elif k in WA:
|
||||
v = abs(sx) * v
|
||||
elif k in HA:
|
||||
v = abs(sy) * v
|
||||
else:
|
||||
return m.group(0)
|
||||
return f'{k}="{v:.4f}"'
|
||||
|
||||
out = _ATTR.sub(unu, attrs)
|
||||
out = re.sub(r'points="([^"]+)"', lambda m: 'points="%s"' % " ".join(
|
||||
"{:.4f},{:.4f}".format(tx + sx * float(p.split(",")[0]),
|
||||
ty + sy * float(p.split(",")[1]))
|
||||
for p in m.group(1).split()), out)
|
||||
if tag == "text" and sx != 1.0:
|
||||
out += f' data-sx="{sx:g}"' # lățimea casetei se scalează cu el
|
||||
return out
|
||||
|
||||
|
||||
def _aplatizeaza(svg):
|
||||
"""Rescrie coordonatele în sistemul rădăcinii, ținând cont de `<g transform>`.
|
||||
|
||||
`<g>`-urile pot fi imbricate; `<svg>`-urile imbricate rămân sisteme
|
||||
separate, ca până acum (scanarea se face oricum panou cu panou).
|
||||
"""
|
||||
if "transform=" not in svg:
|
||||
return svg
|
||||
taieturi, stiva = [], [ID]
|
||||
for m in _TAG.finditer(svg):
|
||||
inchis, tag, attrs = m.group(1), m.group(2).lower(), m.group(3)
|
||||
gol = attrs.rstrip().endswith("/")
|
||||
if tag == "g":
|
||||
if inchis:
|
||||
if len(stiva) > 1:
|
||||
stiva.pop()
|
||||
elif not gol:
|
||||
stiva.append(_ctm(attrs, stiva[-1]))
|
||||
continue
|
||||
if inchis or stiva[-1] == ID:
|
||||
continue
|
||||
taieturi.append((m.start(3), m.end(3), _rescrie(attrs, stiva[-1], tag)))
|
||||
for a, b, nou in reversed(taieturi):
|
||||
svg = svg[:a] + nou + svg[b:]
|
||||
return svg
|
||||
|
||||
|
||||
def _bbox(m, min_px):
|
||||
"""Caseta etichetei, lărgită cu marginea cerută."""
|
||||
tx, ty, attrs, s = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4)
|
||||
w = CW * len(s)
|
||||
k = float(re.search(r'data-sx="([\d.]+)"', attrs).group(1)) if "data-sx=" in attrs else 1.0
|
||||
w = CW * len(s) * k
|
||||
# fara text-anchor, SVG aliniaza la stanga (start), nu centrat. Presupunerea
|
||||
# ca e centrat dadea alarme false pe etichetele de Level cu label_at="left",
|
||||
# care se deseneaza fara anchor.
|
||||
x0 = tx - w if 'anchor="end"' in attrs else tx if 'anchor="middle"' not in attrs else tx - w / 2
|
||||
return s, (x0 - min_px, x0 + w + min_px, ty - 9 - min_px, ty + 3 + min_px)
|
||||
return s, (x0 - min_px, x0 + w + min_px, ty - 9 * k - min_px, ty + 3 * k + min_px)
|
||||
|
||||
|
||||
def _segmente(svg):
|
||||
@@ -60,6 +144,7 @@ def eticheta_libera(svg, name, etichete, min_px=5.0, rects=False, segmente=True)
|
||||
`segmente=False` pentru etichetele de nivel: alea stau lipite de linia lor
|
||||
prin construcție (y − 6), deci doar candelele sunt o suprapunere reală.
|
||||
"""
|
||||
svg = _aplatizeaza(svg)
|
||||
segs = list(_segmente(svg)) if segmente else []
|
||||
boxes = []
|
||||
if rects:
|
||||
@@ -99,6 +184,35 @@ def demo():
|
||||
else:
|
||||
raise AssertionError(f"'{et}' trebuia prinsa")
|
||||
eticheta_libera(svg, "demo", {"libera"}, rects=True)
|
||||
|
||||
# --- transformări: un panou mutat cu <g transform="translate(...)"> ------
|
||||
# 1. etichetă și linie în ACELAȘI <g> (imbricat), suprapuse: trebuie prinsă.
|
||||
# 2. etichetă și linie în <g>-uri DIFERITE, care s-ar suprapune în
|
||||
# coordonate brute (ambele pe y≈50) dar nu în cele finale.
|
||||
mutat = ('<svg><g transform="translate(10,0)"><g transform="translate(0,300)">'
|
||||
'<line x1="0" y1="50" x2="100" y2="50" stroke="#c00"/>'
|
||||
'<text x="10" y="55">in panoul mutat</text></g></g>'
|
||||
'<g transform="translate(0,0)"><text x="10" y="55">sus</text></g>'
|
||||
'<g transform="translate(0,200)">'
|
||||
'<line x1="0" y1="50" x2="100" y2="50" stroke="#c00"/></g></svg>')
|
||||
try:
|
||||
eticheta_libera(mutat, "demo", {"in panoul mutat"})
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("'in panoul mutat' trebuia prinsa (translate ignorat?)")
|
||||
eticheta_libera(mutat, "demo", {"sus"}) # 250px mai jos decat linia ei
|
||||
|
||||
# scale: o candela de 20x40 scalata x2 ajunge sub eticheta de la y=620
|
||||
scalat = ('<svg><g transform="scale(2)">'
|
||||
'<rect x="0" y="300" width="20" height="40" fill="#188a3e"/>'
|
||||
'<text x="5" y="310">pe candela scalata</text></g></svg>')
|
||||
try:
|
||||
eticheta_libera(scalat, "demo", {"pe candela scalata"}, rects=True)
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("'pe candela scalata' trebuia prinsa")
|
||||
print("eticheta_libera: ok")
|
||||
|
||||
|
||||
|
||||
@@ -20,9 +20,13 @@ Barele sunt pe o axă comună 0 → 1 an, ca diferența să fie vizibilă; bara
|
||||
Rulare: python3 gen/strategii/atmi_orizont.py -> gen/strategii/atmi_orizont.svg
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from eticheta_libera import eticheta_libera # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
W, H = 700, 670
|
||||
@@ -114,7 +118,9 @@ def build():
|
||||
p.append(line(AX_X0 + LEN_PROP, 290, AX_X0 + LEN_PROP, 360, ORANGE, 1.2, "4 3"))
|
||||
p.append(line(AX_X0 + LEN_PROP, BAR_PROP_Y + BAR_H / 2, CARD_X,
|
||||
BAR_PROP_Y + BAR_H / 2, ORANGE, 1.0, "3 3"))
|
||||
p.append(txt(AX_X0 + 30, 312, "2 săptămâni", 11, "700", ORANGE, "start"))
|
||||
# sub bară, nu lângă ea: la y=312 ultimele litere intrau sub marginea din
|
||||
# stânga a cardului (CARD_X=160) și se citea „2 săptămân"
|
||||
p.append(txt(AX_X0 + 12, 368, "2 săptămâni", 11, "700", ORANGE, "start"))
|
||||
|
||||
p.append(f'<rect x="{CARD_X}" y="{CARD_Y}" width="{CARD_W}" height="{CARD_H}" '
|
||||
f'rx="8" fill="{WHITE}" stroke="{ORANGE}" stroke-width="1.5"/>')
|
||||
@@ -221,6 +227,13 @@ def verify(svg):
|
||||
assert not re.search(r"\.(md|txt):\d", svg), \
|
||||
"eticheta SVG nu are voie să conțină trimitere fișier:linie"
|
||||
|
||||
# 6c. etichetele cu cifre nu stau sub niciun dreptunghi desenat (cardul de
|
||||
# citat a mâncat odată „i"-ul din „2 săptămâni"). Fundalul paginii se scoate:
|
||||
# e un dreptunghi cât toată pânza, altfel iese sub orice etichetă.
|
||||
eticheta_libera(re.sub(r'<rect x="0" y="0"[^/]*/>', "", svg), "atmi_orizont",
|
||||
{"2 săptămâni", "TP1 atins — aproximativ un an mai târziu"},
|
||||
rects=True, segmente=False)
|
||||
|
||||
# 7. fără cifre de preț inventate
|
||||
for interzis in ("$", "USD", "SL ", "intrare "):
|
||||
assert interzis not in svg, f"nu ar trebui să apară preț: {interzis!r}"
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<rect x="60.0" y="298.0" width="22.7" height="42" rx="6" fill="#fdf0e0" stroke="#b06000" stroke-width="1.5"/>
|
||||
<line x1="82.7" y1="290.0" x2="82.7" y2="360.0" stroke="#b06000" stroke-width="1.2" stroke-dasharray="4 3"/>
|
||||
<line x1="82.7" y1="319.0" x2="160.0" y2="319.0" stroke="#b06000" stroke-width="1.0" stroke-dasharray="3 3"/>
|
||||
<text x="90.0" y="312.0" font-size="11" font-weight="700" fill="#b06000" text-anchor="start">2 săptămâni</text>
|
||||
<text x="72.0" y="368.0" font-size="11" font-weight="700" fill="#b06000" text-anchor="start">2 săptămâni</text>
|
||||
<rect x="160.0" y="292.0" width="500.0" height="112.0" rx="8" fill="#ffffff" stroke="#b06000" stroke-width="1.5"/>
|
||||
<text x="410.0" y="316.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">„aștepți o corecție și intri pe ATMI la suport quarterly,</text>
|
||||
<text x="410.0" y="334.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">swing, 2 săptămâni în poziție”</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
@@ -17,6 +17,8 @@ from pathlib import Path
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
from svg_candles import Candle, Chart, Level, Zone, Arrow # noqa: E402
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from eticheta_libera import eticheta_libera # noqa: E402
|
||||
from buy_doji_ansamblu import serie, DOJI, CONF, DOJI_L, ENTRY, SL, TP1, R # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
@@ -84,7 +86,11 @@ gata = Chart(
|
||||
candles=jos,
|
||||
levels=[Level(TP1, "ținta 1: 51.60", GREEN, "2 2", "left"),
|
||||
Level(ENTRY, "intrarea: 50.40", GREY, "4 3", "left"),
|
||||
Level(OCHI, "stop pus la ochi: 49.70", ORANGE, "4 3", "left"),
|
||||
# linia „la ochi" pornește de la bara de intrare, iar eticheta ei
|
||||
# stă în dreapta: pe toată lățimea, în stânga, cele două stopuri
|
||||
# cădeau la 12 px unul sub altul și linia portocalie trecea prin
|
||||
# eticheta celui din regulă — nu puteai atribui liniile
|
||||
Level(OCHI, "stop pus la ochi: 49.70", ORANGE, "4 3", i0=cf),
|
||||
Level(SL, "stopul din regulă: 49.20", RED, "4 3", "left")],
|
||||
# eticheta zonei o punem manual: Zone o ridică deasupra candelelor din
|
||||
# dreptul textului, adică departe de banda pe care o marchează
|
||||
@@ -111,7 +117,11 @@ def panou(ch, sus_de=0.0):
|
||||
h = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1))
|
||||
# render() mută `top` intern și îl restaurează; offsetul se citește din
|
||||
# prima linie de Level, altfel tot ce desenăm pe urmă iese mai sus cu ~22px
|
||||
off = float(re.search(r'<line x1="24" y1="([\d.]+)"', svg).group(1)) - ch._y(ch.levels[0].price)
|
||||
# „24.0", nu „24": svg_candles formatează începutul liniei cu :.1f de când
|
||||
# are Level.i0 — generatorul crăpa aici, nu mai era rulabil
|
||||
m = re.search(r'<line x1="24(?:\.0)?" y1="([\d.]+)"', svg)
|
||||
assert m, "nu găsesc prima linie de Level: s-a schimbat formatul din svg_candles"
|
||||
off = float(m.group(1)) - ch._y(ch.levels[0].price)
|
||||
return svg[svg.index(">") + 1:svg.rindex("</svg>")].strip(), h, off
|
||||
|
||||
|
||||
@@ -131,7 +141,7 @@ nums.append(f'<text x="24" y="{y_sub:.1f}" fill="#555">'
|
||||
h_sus = max(h_sus, y_sub + 12)
|
||||
|
||||
body_jos, h_jos, off_jos = panou(gata, h_sus + VGAP)
|
||||
zx = gata._cx(cf + 3) + 10
|
||||
zx = gata._cx(cf + 4) + 10 # dupa corpul candelei 14, nu peste el
|
||||
banda = (f'<text x="{zx:.1f}" y="{gata._y(OCHI) + off_jos - 6:.1f}" fill="{RED}" '
|
||||
f'style="font-weight:bold">pe aici trece al doilea picior</text>')
|
||||
H = h_jos + 8
|
||||
@@ -145,5 +155,17 @@ svg = "\n ".join([
|
||||
assert "\n\n" not in svg and "<svg" in svg, "linie goală în SVG"
|
||||
for x, y in re.findall(r'<text x="([-\d.]+)" y="([-\d.]+)"', svg):
|
||||
assert 0 <= float(x) <= W and 0 <= float(y) <= H, f"text în afara pânzei la ({x}, {y})"
|
||||
# etichetele de nivel stau lipite de linia lor prin construcție, deci pentru
|
||||
# ele contează doar corpurile candelelor; pentru restul, și liniile
|
||||
eticheta_libera(svg, "buy_doji_stop", {"stop pus la ochi: 49.70",
|
||||
"stopul din regulă: 49.20",
|
||||
"intrarea: 50.40", "ținta 1: 51.60",
|
||||
"stopul: 49.20"}, rects=True, segmente=False,
|
||||
min_px=2.0) # marja mică: etichetele de nivel trec la ~8 px de
|
||||
# corpul candelei 7, iar CW supraestimează lățimea
|
||||
# eticheta benzii stă lipită deasupra liniei „la ochi", ca orice etichetă de
|
||||
# nivel: pentru ea contează doar candelele
|
||||
eticheta_libera(svg, "buy_doji_stop", {"pe aici trece al doilea picior"},
|
||||
rects=True, segmente=False)
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT} ({W}x{H:.0f})")
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Pasul 5 — stopul: numeri 4 candele înapoi de la confirmare</text>
|
||||
<rect x="178.0" y="289.2" width="80.0" height="88.0" fill="#1a5fb4" fill-opacity="0.08" stroke="#1a5fb4" stroke-width="1"/>
|
||||
<text x="178.0" y="284.2" fill="#1a5fb4" style="font-weight:bold">numeri 4 candele, începând cu confirmarea</text>
|
||||
<line x1="24" y1="371.0" x2="706" y2="371.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="365.0" fill="#c00">stopul: 49.20</text>
|
||||
<line x1="24.0" y1="371.0" x2="706" y2="371.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30.0" y="365.0" fill="#c00">stopul: 49.20</text>
|
||||
<line x1="48.0" y1="56.0" x2="48.0" y2="119.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="43.0" y="65.0" width="10" height="43.2" fill="#c00"/>
|
||||
<line x1="68.0" y1="99.2" x2="68.0" y2="158.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
@@ -48,14 +48,14 @@
|
||||
<line x1="24" y1="562" x2="706" y2="562" stroke="#ddd"/>
|
||||
<text x="24" y="617" fill="#333" style="font-weight:bold">Același trade: stop „la ochi” (sub candela de confirmare) vs stopul din regulă</text>
|
||||
<rect x="258.0" y="922.0" width="40.0" height="18.0" fill="#c00" fill-opacity="0.08" stroke="#c00" stroke-width="1"/>
|
||||
<line x1="24" y1="853.6" x2="706" y2="853.6" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 2"/>
|
||||
<text x="30" y="847.6" fill="#188a3e">ținta 1: 51.60</text>
|
||||
<line x1="24" y1="896.8" x2="706" y2="896.8" stroke="#555" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="890.8" fill="#555">intrarea: 50.40</text>
|
||||
<line x1="24" y1="922.0" x2="706" y2="922.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="916.0" fill="#b06000">stop pus la ochi: 49.70</text>
|
||||
<line x1="24" y1="940.0" x2="706" y2="940.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="934.0" fill="#c00">stopul din regulă: 49.20</text>
|
||||
<line x1="24.0" y1="853.6" x2="706" y2="853.6" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 2"/>
|
||||
<text x="30.0" y="847.6" fill="#188a3e">ținta 1: 51.60</text>
|
||||
<line x1="24.0" y1="896.8" x2="706" y2="896.8" stroke="#555" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30.0" y="890.8" fill="#555">intrarea: 50.40</text>
|
||||
<line x1="243.0" y1="922.0" x2="706" y2="922.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="916.0" fill="#b06000" text-anchor="end">stop pus la ochi: 49.70</text>
|
||||
<line x1="24.0" y1="940.0" x2="706" y2="940.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30.0" y="934.0" fill="#c00">stopul din regulă: 49.20</text>
|
||||
<line x1="48.0" y1="625.0" x2="48.0" y2="688.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="43.0" y="634.0" width="10" height="43.2" fill="#c00"/>
|
||||
<line x1="68.0" y1="668.2" x2="68.0" y2="727.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
@@ -108,5 +108,5 @@
|
||||
<text x="518.0" y="804.6" fill="#188a3e" text-anchor="middle" style="font-weight:bold">dar numai cu stopul corect</text>
|
||||
<text x="518.0" y="818.6" fill="#188a3e" text-anchor="middle" style="font-weight:bold">mai erai în trade</text>
|
||||
<text x="24" y="1143" fill="#555">Barele de după intrare și stopul la ochi sunt construite, pentru contrast.</text>
|
||||
<text x="318.0" y="916.0" fill="#c00" style="font-weight:bold">pe aici trece al doilea picior</text>
|
||||
<text x="338.0" y="916.0" fill="#c00" style="font-weight:bold">pe aici trece al doilea picior</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
@@ -20,9 +20,11 @@ sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from svg_candles import Arrow, Candle, Chart, Mark # noqa: E402
|
||||
import m2d_duo as duo # noqa: E402
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from eticheta_libera import eticheta_libera # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
W, SCALE, GAP, LEFT = 680, 46.0, 46, 74
|
||||
W, SCALE, GAP, LEFT = 680, 46.0, 46, 60 # LEFT 74->60: eticheta cyan din dreapta atingea corpul ultimei candele
|
||||
GREEN, RED, BLUE, CYAN, GREY = "#188a3e", "#c00", "#1a5fb4", "#0891b2", "#555"
|
||||
|
||||
# --- aceleași candele de 15 min ca în diagrama de ansamblu -----------------
|
||||
@@ -49,6 +51,9 @@ assert DA[:SIG + 2] == NU[:SIG + 2], "panourile nu pornesc din aceeași serie"
|
||||
SEMNAL = BASE[SIG][3] # 100.60 — același nivel ca în diagrama de ansamblu
|
||||
REF_LOW = BASE[SIG][2] # 99.80 — minimul din care a pornit mișcarea
|
||||
assert BASE[SIG][3] > BASE[SIG][0], "candela de semnal nu e verde"
|
||||
# pe un rând, eticheta ajungea cu „99.80" sub corpul candelei 4 (99.30-99.85)
|
||||
# și se citea „99.8"; pe două rânduri se termină înainte de candela 2
|
||||
REF_TXT = ["minimul din care a pornit", "mișcarea 99.80"]
|
||||
|
||||
# DA: nicio candelă de după semnal nu se închide sub minimul de referință
|
||||
assert all(b[3] > REF_LOW for b in DA[SIG + 1:]), "DA: o candelă închide sub referință"
|
||||
@@ -94,14 +99,12 @@ extra_da = (duo.titlu(TITLU_DA, color=GREEN)
|
||||
+ duo.zone(ch_da, SIG, 7, "zona în care ai voie long", GREEN, pad=20, lift=26)
|
||||
+ duo.level(ch_da, SEMNAL, ["close-ul candelei de semnal 100.60"], CYAN, W,
|
||||
below=False, at_left=False)
|
||||
+ duo.level(ch_da, REF_LOW, ["minimul din care a pornit mișcarea 99.80"],
|
||||
GREY, W, below=True, dash="2 3"))
|
||||
+ duo.level(ch_da, REF_LOW, REF_TXT, GREY, W, below=True, dash="2 3"))
|
||||
extra_nu = (duo.titlu(TITLU_NU, color=RED)
|
||||
+ duo.zone(ch_nu, 7, 8, "setup anulat", RED, pad=20)
|
||||
+ duo.level(ch_nu, SEMNAL, ["close-ul candelei de semnal 100.60"], CYAN, W,
|
||||
below=False, at_left=False)
|
||||
+ duo.level(ch_nu, REF_LOW, ["minimul din care a pornit mișcarea 99.80"],
|
||||
GREY, W, below=True, dash="2 3"))
|
||||
+ duo.level(ch_nu, REF_LOW, REF_TXT, GREY, W, below=True, dash="2 3"))
|
||||
|
||||
svg, H, n = duo.build(
|
||||
[("DA", body_da, h_da, extra_da), ("NU", body_nu, h_nu, extra_nu)],
|
||||
@@ -109,6 +112,10 @@ svg, H, n = duo.build(
|
||||
"Pe 15 min se citește DIRECȚIA, nu intrarea. Intrarea vine de pe 3 min (pasul 3)."],
|
||||
W)
|
||||
duo.fara_preturi_straine(svg, [SEMNAL, REF_LOW, NU[7][3]])
|
||||
# etichetele de nivel stau lipite de linia lor prin construcție, deci pentru ele
|
||||
# contează doar corpurile candelelor (`segmente=False`)
|
||||
eticheta_libera(svg, "m2d_directie", set(REF_TXT) | {
|
||||
"close-ul candelei de semnal 100.60"}, rects=True, segmente=False)
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT} ({W}x{H:.0f}), {n} etichete | semnal {SEMNAL:.2f}, referință {REF_LOW:.2f} | "
|
||||
f"DA: ultima închidere {DA[-1][3]:.2f} | NU: candela 7 închide {NU[7][3]:.2f}")
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
<svg viewBox="0 0 680 681" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<g transform="translate(0,0)"><text x="24" y="22" fill="#188a3e" style="font-weight:bold">DA — 15 min e încă în Buy când vine trigger-ul de pe 3 min</text><rect x="284.0" y="48.4" width="132.0" height="85.7" fill="#188a3e" fill-opacity="0.12" stroke="#188a3e" stroke-width="1"/><text x="290.0" y="41.4" fill="#188a3e" style="font-weight:bold">zona în care ai voie long</text><line x1="24" y1="89.3" x2="666" y2="89.3" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 3"/><text x="656" y="83.3" fill="#0891b2" text-anchor="end" style="font-weight:bold">close-ul candelei de semnal 100.60</text><line x1="24" y1="126.1" x2="666" y2="126.1" stroke="#555" stroke-width="1.5" stroke-dasharray="2 3"/><text x="30" y="147.1" fill="#555" text-anchor="start" style="font-weight:bold">minimul din care a pornit mișcarea 99.80</text>
|
||||
<line x1="74.0" y1="192.8" x2="74.0" y2="215.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="66.5" y="197.4" width="15" height="11.5" fill="#188a3e"/>
|
||||
<line x1="120.0" y1="176.7" x2="120.0" y2="202.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="112.5" y="181.3" width="15" height="16.1" fill="#188a3e"/>
|
||||
<line x1="166.0" y1="158.3" x2="166.0" y2="185.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="158.5" y="162.9" width="15" height="18.4" fill="#188a3e"/>
|
||||
<line x1="212.0" y1="142.2" x2="212.0" y2="167.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="149.1" width="15" height="13.8" fill="#188a3e"/>
|
||||
<line x1="258.0" y1="119.2" x2="258.0" y2="153.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="250.5" y="123.8" width="15" height="25.3" fill="#188a3e"/>
|
||||
<line x1="304.0" y1="82.4" x2="304.0" y2="126.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="296.5" y="89.3" width="15" height="34.5" fill="#188a3e"/>
|
||||
<line x1="350.0" y1="84.7" x2="350.0" y2="116.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="342.5" y="89.3" width="15" height="25.3" fill="#c00"/>
|
||||
<line x1="396.0" y1="96.2" x2="396.0" y2="121.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="388.5" y="98.5" width="15" height="16.1" fill="#188a3e"/>
|
||||
<line x1="442.0" y1="64.0" x2="442.0" y2="100.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="434.5" y="70.9" width="15" height="27.6" fill="#188a3e"/>
|
||||
<line x1="244.0" y1="247.9" x2="300.4" y2="137.2" stroke="#0891b2" stroke-width="1.8"/>
|
||||
<polygon points="304.0,130.1 303.9,139.0 296.8,135.4" fill="#0891b2"/>
|
||||
<text x="244.0" y="261.9" fill="#0891b2" text-anchor="middle" style="font-weight:bold">Buy Momentum pe 15 min:</text>
|
||||
<text x="244.0" y="275.9" fill="#0891b2" text-anchor="middle" style="font-weight:bold">de aici ai voie doar long</text>
|
||||
<line x1="320.0" y1="201.5" x2="347.2" y2="128.4" stroke="#555" stroke-width="1.8"/>
|
||||
<polygon points="350.0,120.9 351.0,129.8 343.5,127.0" fill="#555"/>
|
||||
<text x="320.0" y="215.5" fill="#555" text-anchor="middle" style="font-weight:bold">candelă roșie pe 15 min, dar close-ul</text>
|
||||
<text x="320.0" y="229.5" fill="#555" text-anchor="middle" style="font-weight:bold">rămâne peste 99.80: pullback, nu întoarcere</text>
|
||||
<line x1="372.0" y1="74.0" x2="389.6" y2="87.4" stroke="#188a3e" stroke-width="1.8"/>
|
||||
<polygon points="396.0,92.2 387.2,90.6 392.0,84.2" fill="#188a3e"/>
|
||||
<text x="372.0" y="69.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">aici cade trigger-ul de pe 3 min</text>
|
||||
<g transform="translate(0,0)"><text x="24" y="22" fill="#188a3e" style="font-weight:bold">DA — 15 min e încă în Buy când vine trigger-ul de pe 3 min</text><rect x="270.0" y="48.4" width="132.0" height="85.7" fill="#188a3e" fill-opacity="0.12" stroke="#188a3e" stroke-width="1"/><text x="276.0" y="41.4" fill="#188a3e" style="font-weight:bold">zona în care ai voie long</text><line x1="24" y1="89.3" x2="666" y2="89.3" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 3"/><text x="656" y="83.3" fill="#0891b2" text-anchor="end" style="font-weight:bold">close-ul candelei de semnal 100.60</text><line x1="24" y1="126.1" x2="666" y2="126.1" stroke="#555" stroke-width="1.5" stroke-dasharray="2 3"/><text x="30" y="147.1" fill="#555" text-anchor="start" style="font-weight:bold">minimul din care a pornit</text><text x="30" y="161.1" fill="#555" text-anchor="start" style="font-weight:bold">mișcarea 99.80</text>
|
||||
<line x1="60.0" y1="192.8" x2="60.0" y2="215.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="52.5" y="197.4" width="15" height="11.5" fill="#188a3e"/>
|
||||
<line x1="106.0" y1="176.7" x2="106.0" y2="202.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="98.5" y="181.3" width="15" height="16.1" fill="#188a3e"/>
|
||||
<line x1="152.0" y1="158.3" x2="152.0" y2="185.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="162.9" width="15" height="18.4" fill="#188a3e"/>
|
||||
<line x1="198.0" y1="142.2" x2="198.0" y2="167.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="190.5" y="149.1" width="15" height="13.8" fill="#188a3e"/>
|
||||
<line x1="244.0" y1="119.2" x2="244.0" y2="153.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="236.5" y="123.8" width="15" height="25.3" fill="#188a3e"/>
|
||||
<line x1="290.0" y1="82.4" x2="290.0" y2="126.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="282.5" y="89.3" width="15" height="34.5" fill="#188a3e"/>
|
||||
<line x1="336.0" y1="84.7" x2="336.0" y2="116.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="328.5" y="89.3" width="15" height="25.3" fill="#c00"/>
|
||||
<line x1="382.0" y1="96.2" x2="382.0" y2="121.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="374.5" y="98.5" width="15" height="16.1" fill="#188a3e"/>
|
||||
<line x1="428.0" y1="64.0" x2="428.0" y2="100.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="420.5" y="70.9" width="15" height="27.6" fill="#188a3e"/>
|
||||
<line x1="230.0" y1="247.9" x2="286.4" y2="137.2" stroke="#0891b2" stroke-width="1.8"/>
|
||||
<polygon points="290.0,130.1 289.9,139.0 282.8,135.4" fill="#0891b2"/>
|
||||
<text x="230.0" y="261.9" fill="#0891b2" text-anchor="middle" style="font-weight:bold">Buy Momentum pe 15 min:</text>
|
||||
<text x="230.0" y="275.9" fill="#0891b2" text-anchor="middle" style="font-weight:bold">de aici ai voie doar long</text>
|
||||
<line x1="306.0" y1="201.5" x2="333.2" y2="128.4" stroke="#555" stroke-width="1.8"/>
|
||||
<polygon points="336.0,120.9 337.0,129.8 329.5,127.0" fill="#555"/>
|
||||
<text x="306.0" y="215.5" fill="#555" text-anchor="middle" style="font-weight:bold">candelă roșie pe 15 min, dar close-ul</text>
|
||||
<text x="306.0" y="229.5" fill="#555" text-anchor="middle" style="font-weight:bold">rămâne peste 99.80: pullback, nu întoarcere</text>
|
||||
<line x1="358.0" y1="74.0" x2="375.6" y2="87.4" stroke="#188a3e" stroke-width="1.8"/>
|
||||
<polygon points="382.0,92.2 373.2,90.6 378.0,84.2" fill="#188a3e"/>
|
||||
<text x="358.0" y="69.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">aici cade trigger-ul de pe 3 min</text>
|
||||
</g>
|
||||
<g transform="translate(0,311)"><text x="24" y="22" fill="#c00" style="font-weight:bold">NU — 15 min s-a închis Sell: setup-ul long e anulat</text><rect x="376.0" y="85.9" width="86.0" height="85.0" fill="#c00" fill-opacity="0.12" stroke="#c00" stroke-width="1"/><text x="382.0" y="78.9" fill="#c00" style="font-weight:bold">setup anulat</text><line x1="24" y1="70.9" x2="666" y2="70.9" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 3"/><text x="656" y="64.9" fill="#0891b2" text-anchor="end" style="font-weight:bold">close-ul candelei de semnal 100.60</text><line x1="24" y1="107.7" x2="666" y2="107.7" stroke="#555" stroke-width="1.5" stroke-dasharray="2 3"/><text x="30" y="128.7" fill="#555" text-anchor="start" style="font-weight:bold">minimul din care a pornit mișcarea 99.80</text>
|
||||
<line x1="74.0" y1="174.4" x2="74.0" y2="197.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="66.5" y="179.0" width="15" height="11.5" fill="#188a3e"/>
|
||||
<line x1="120.0" y1="158.3" x2="120.0" y2="183.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="112.5" y="162.9" width="15" height="16.1" fill="#188a3e"/>
|
||||
<line x1="166.0" y1="139.9" x2="166.0" y2="167.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="158.5" y="144.5" width="15" height="18.4" fill="#188a3e"/>
|
||||
<line x1="212.0" y1="123.8" x2="212.0" y2="149.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="130.7" width="15" height="13.8" fill="#188a3e"/>
|
||||
<line x1="258.0" y1="100.8" x2="258.0" y2="135.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="250.5" y="105.4" width="15" height="25.3" fill="#188a3e"/>
|
||||
<line x1="304.0" y1="64.0" x2="304.0" y2="107.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="296.5" y="70.9" width="15" height="34.5" fill="#188a3e"/>
|
||||
<line x1="350.0" y1="66.3" x2="350.0" y2="98.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="342.5" y="70.9" width="15" height="25.3" fill="#c00"/>
|
||||
<line x1="396.0" y1="93.9" x2="396.0" y2="135.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="388.5" y="96.2" width="15" height="34.5" fill="#c00"/>
|
||||
<line x1="442.0" y1="126.1" x2="442.0" y2="162.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="434.5" y="130.7" width="15" height="25.3" fill="#c00"/>
|
||||
<line x1="244.0" y1="229.5" x2="300.4" y2="118.8" stroke="#0891b2" stroke-width="1.8"/>
|
||||
<polygon points="304.0,111.7 303.9,120.6 296.8,117.0" fill="#0891b2"/>
|
||||
<text x="244.0" y="243.5" fill="#0891b2" text-anchor="middle" style="font-weight:bold">Buy Momentum pe 15 min:</text>
|
||||
<text x="244.0" y="257.5" fill="#0891b2" text-anchor="middle" style="font-weight:bold">de aici ai voie doar long</text>
|
||||
<line x1="356.0" y1="190.9" x2="391.1" y2="145.6" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="396.0,139.3 394.3,148.1 387.9,143.2" fill="#c00"/>
|
||||
<text x="356.0" y="204.9" fill="#c00" text-anchor="middle" style="font-weight:bold">close 99.30, sub 99.80: direcția mare</text>
|
||||
<text x="356.0" y="218.9" fill="#c00" text-anchor="middle" style="font-weight:bold">s-a răsucit — nu mai intri long</text>
|
||||
<line x1="322.0" y1="274.9" x2="436.1" y2="172.3" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="442.0,166.9 438.7,175.2 433.4,169.3" fill="#c00"/>
|
||||
<text x="322.0" y="288.9" fill="#c00" text-anchor="middle" style="font-weight:bold">de aici cauți short, nu long</text>
|
||||
<g transform="translate(0,311)"><text x="24" y="22" fill="#c00" style="font-weight:bold">NU — 15 min s-a închis Sell: setup-ul long e anulat</text><rect x="362.0" y="85.9" width="86.0" height="85.0" fill="#c00" fill-opacity="0.12" stroke="#c00" stroke-width="1"/><text x="368.0" y="78.9" fill="#c00" style="font-weight:bold">setup anulat</text><line x1="24" y1="70.9" x2="666" y2="70.9" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 3"/><text x="656" y="64.9" fill="#0891b2" text-anchor="end" style="font-weight:bold">close-ul candelei de semnal 100.60</text><line x1="24" y1="107.7" x2="666" y2="107.7" stroke="#555" stroke-width="1.5" stroke-dasharray="2 3"/><text x="30" y="128.7" fill="#555" text-anchor="start" style="font-weight:bold">minimul din care a pornit</text><text x="30" y="142.7" fill="#555" text-anchor="start" style="font-weight:bold">mișcarea 99.80</text>
|
||||
<line x1="60.0" y1="174.4" x2="60.0" y2="197.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="52.5" y="179.0" width="15" height="11.5" fill="#188a3e"/>
|
||||
<line x1="106.0" y1="158.3" x2="106.0" y2="183.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="98.5" y="162.9" width="15" height="16.1" fill="#188a3e"/>
|
||||
<line x1="152.0" y1="139.9" x2="152.0" y2="167.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="144.5" width="15" height="18.4" fill="#188a3e"/>
|
||||
<line x1="198.0" y1="123.8" x2="198.0" y2="149.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="190.5" y="130.7" width="15" height="13.8" fill="#188a3e"/>
|
||||
<line x1="244.0" y1="100.8" x2="244.0" y2="135.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="236.5" y="105.4" width="15" height="25.3" fill="#188a3e"/>
|
||||
<line x1="290.0" y1="64.0" x2="290.0" y2="107.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="282.5" y="70.9" width="15" height="34.5" fill="#188a3e"/>
|
||||
<line x1="336.0" y1="66.3" x2="336.0" y2="98.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="328.5" y="70.9" width="15" height="25.3" fill="#c00"/>
|
||||
<line x1="382.0" y1="93.9" x2="382.0" y2="135.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="374.5" y="96.2" width="15" height="34.5" fill="#c00"/>
|
||||
<line x1="428.0" y1="126.1" x2="428.0" y2="162.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="420.5" y="130.7" width="15" height="25.3" fill="#c00"/>
|
||||
<line x1="230.0" y1="229.5" x2="286.4" y2="118.8" stroke="#0891b2" stroke-width="1.8"/>
|
||||
<polygon points="290.0,111.7 289.9,120.6 282.8,117.0" fill="#0891b2"/>
|
||||
<text x="230.0" y="243.5" fill="#0891b2" text-anchor="middle" style="font-weight:bold">Buy Momentum pe 15 min:</text>
|
||||
<text x="230.0" y="257.5" fill="#0891b2" text-anchor="middle" style="font-weight:bold">de aici ai voie doar long</text>
|
||||
<line x1="342.0" y1="190.9" x2="377.1" y2="145.6" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="382.0,139.3 380.3,148.1 373.9,143.2" fill="#c00"/>
|
||||
<text x="342.0" y="204.9" fill="#c00" text-anchor="middle" style="font-weight:bold">close 99.30, sub 99.80: direcția mare</text>
|
||||
<text x="342.0" y="218.9" fill="#c00" text-anchor="middle" style="font-weight:bold">s-a răsucit — nu mai intri long</text>
|
||||
<line x1="308.0" y1="274.9" x2="422.1" y2="172.3" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="428.0,166.9 424.7,175.2 419.4,169.3" fill="#c00"/>
|
||||
<text x="308.0" y="288.9" fill="#c00" text-anchor="middle" style="font-weight:bold">de aici cauți short, nu long</text>
|
||||
</g>
|
||||
<text x="24" y="641" fill="#555">Același grafic de 15 minute ca în diagrama de ansamblu; serie construită, fără ticker.</text>
|
||||
<text x="24" y="658" fill="#555">Pe 15 min se citește DIRECȚIA, nu intrarea. Intrarea vine de pe 3 min (pasul 3).</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 7.1 KiB |
Reference in New Issue
Block a user