#!/usr/bin/env python3 """Hartă de orientare: buy hammer (public) vs. MBT-10 (cutie neagră), același trigger de bottom. Pentru `strategii/06_hammer_mbt10.md`, marcajul ``. Nu e grafic de preț (spec: TIP CONCEPT, CHEIE harta) — SVG scris de mână, doar stdlib; nu se folosește svg_candles / pchart. Ce arată (toate citatele și trimiterile vin din spec, nu se inventează): - bara comună de sus: setup-urile de fond spun UNDE, trigger-ul de candelă spune CÂND (`02_mbt10.md:9-11`, `43-49`); - două cutii alăturate: BUY HAMMER (pattern standard, public) și MBT-10 (cutie neagră, indicator proprietar), fiecare cu regulile ei și cu trimiterile de rigoare; - săgeata de jos spre caseta finală: ce rămâne utilizabil pe BVB. Verdictele de BVB din cutii sunt judecata mea (secțiunea Transfer), marcate ca atare în legendă, exact cum cere specul. Rulare: python3 gen/strategii/hammer_mbt10_harta.py -> hammer_mbt10_harta.svg """ from pathlib import Path from xml.sax.saxutils import escape OUT = Path(__file__).with_suffix(".svg") W, H = 700, 610 BLACK, GRAY, BLUE, GREEN, RED, ORANGE, WHITE = ( "#000000", "#777777", "#1a5fb4", "#188a3e", "#c00", "#b06000", "#ffffff") BORDER = "#333333" # ---------------------------------------------------------------- geometrie BAR_X0, BAR_X1, BAR_Y, BAR_H = 40.0, 660.0, 82.0, 76.0 BOX_W = 302.0 BOX_LX, BOX_RX, BOX_Y = 40.0, 358.0, 190.0 BOX_H = 256.0 PAD = 16.0 ROW0 = BOX_Y + 56.0 # baseline-ul primei reguli ROW_H = 52.0 VERDICT_SEP = BOX_Y + 216.0 VERDICT_Y = VERDICT_SEP + 26.0 BUS_Y = 460.0 # linia orizontală de unire a celor două cutii TRI_TIP = 500.0 FINAL_X0, FINAL_Y, FINAL_W, FINAL_H = 110.0, 500.0, 480.0, 56.0 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'{escape(s)}' ) def line(x1, y1, x2, y2, stroke=GRAY, width=1.0, dash=None): d = f' stroke-dasharray="{dash}"' if dash else "" return (f'') def rect(x, y, w, h, fill=WHITE, stroke=GRAY, width=1.2): return (f'') def box(bx, title, title_fill, rows, verdict, verdict_fill, fill): """O cutie cu chenar: titlu, reguli (linii + trimitere), verdict.""" x = bx + PAD area = BOX_W - 2 * PAD out = [rect(bx, BOX_Y, BOX_W, BOX_H, fill=fill, stroke=title_fill, width=1.4)] out.append(txt(x, BOX_Y + 26, title, 15, "700", title_fill, "start")) out.append(line(bx + PAD, BOX_Y + 38, bx + BOX_W - PAD, BOX_Y + 38, title_fill, 1.0)) for i, (mains, ref) in enumerate(rows): top = ROW0 + i * ROW_H for k, m in enumerate(mains): out.append(txt(x, top + 14 + k * 17, m, 13, "400", BLACK, "start")) if ref is not None: out.append(txt(x, top + 48, ref, 13, "400", GRAY, "start")) out.append(line(bx + PAD, VERDICT_SEP, bx + BOX_W - PAD, VERDICT_SEP, verdict_fill, 1.2)) out.append(txt(x, VERDICT_Y, verdict, 13, "700", verdict_fill, "start")) return out, area def build(): p = [ f'', '', f'', txt(W / 2, 34, "Același trigger de bottom, două naturi", 19, "700", BLACK), txt(W / 2, 58, "buy hammer (public) vs. MBT-10 (cutie neagră)", 14, "400", GRAY), ] # ------- zona 1: bara comună p.append(rect(BAR_X0, BAR_Y, BAR_X1 - BAR_X0, BAR_H, fill="#f2f4f7", stroke=GRAY, width=1.2)) p.append(txt(BAR_X0 + PAD, BAR_Y + 26, "Trigger de bottom: unde + când", 15, "700", BLUE, "start")) p.append(txt(BAR_X0 + PAD, BAR_Y + 48, "Setup-urile de fond — suport, locul crimei, double bottom, buy " "doji,", 13, "400", BLACK, "start")) p.append(txt(BAR_X0 + PAD, BAR_Y + 66, "divergență de volum — spun UNDE. Trigger-ul de candelă spune " "CÂND.", 13, "400", BLACK, "start")) p.append(txt(BAR_X1 - PAD, BAR_Y + 26, "", 13, "400", GRAY, "end")) # ------- conector bară -> cutii (un cot, fără săgeți redundante) lx = BOX_LX + BOX_W / 2 rx = BOX_RX + BOX_W / 2 p.append(line(W / 2, BAR_Y + BAR_H, W / 2, 172.0, GRAY, 1.2)) p.append(line(lx, 172.0, rx, 172.0, GRAY, 1.2)) p.append(line(lx, 172.0, lx, BOX_Y, GRAY, 1.2)) p.append(line(rx, 172.0, rx, BOX_Y, GRAY, 1.2)) # ------- zona 2: cele două cutii left_rows = [ (['„formă de candelă + downtrend”'], ""), (['„trigger: close peste', 'high-ul hammer-ului”'], ""), (['„fără niciun indicator proprietar”'], None), ] right_rows = [ (['„formula nu e publică nicăieri”'], ""), (['„reconstituibil doar:', 'close peste pivotul daily”'], ""), (['„scanner + indicator proprietar”'], ""), ] left_svg, left_area = box( BOX_LX, "BUY HAMMER — pattern standard", BLUE, left_rows, "„pe BVB: se poate rula long-only”", GREEN, "#eef4fd") right_svg, right_area = box( BOX_RX, "MBT-10 — cutie neagră", BORDER, right_rows, "„pe BVB: nu, ca sistem”", RED, "#f5f5f5") p += left_svg + right_svg # ------- zona 3: ambele cutii -> caseta finală p.append(line(lx, BOX_Y + BOX_H, lx, BUS_Y, GRAY, 1.2)) p.append(line(rx, BOX_Y + BOX_H, rx, BUS_Y, GRAY, 1.2)) p.append(line(lx, BUS_Y, rx, BUS_Y, GRAY, 1.2)) p.append(line(W / 2, BUS_Y, W / 2, TRI_TIP - 11, GRAY, 1.4)) p.append(f'') p.append(rect(FINAL_X0, FINAL_Y, FINAL_W, FINAL_H, fill="#fdf0e0", stroke=ORANGE, width=1.6)) p.append(txt(W / 2, FINAL_Y + 24, "Ce rămâne pe BVB: hammer la un nivel major", 14, "700", BLACK)) p.append(txt(W / 2, FINAL_Y + 44, "+ close peste PPD", 14, "700", BLACK)) # ------- legendă p.append(txt(W / 2, 578, "Regulile = materialul cursului (cu trimiteri); verdictul de " "BVB = judecata mea", 13, "400", GRAY)) p.append(txt(W / 2, 596, "din secțiunea Transfer.", 13, "400", GRAY)) p.append('') return "\n".join(p) + "\n", left_area, right_area # ---------------------------------------------------------------- verificare def est_w(s, size, weight): factor = 0.62 if weight == "700" else 0.56 return len(s) * size * factor def box_of(x, y): for bx in (BOX_LX, BOX_RX): if bx <= x <= bx + BOX_W and BOX_Y <= y <= BOX_Y + BOX_H: return bx return None def verify(svg, left_area, right_area): # 1. geometrie: zonele nu se calcă assert BAR_Y + BAR_H < BOX_Y, "bara comună intră în cutii" assert BOX_Y + BOX_H < BUS_Y < TRI_TIP <= FINAL_Y, "săgeata nu are loc" assert FINAL_Y + FINAL_H < 578, "caseta finală intră în legendă" assert left_area == right_area == BOX_W - 2 * PAD # 2. toate textele în panză 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 în afara panzei: {s!r}" assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y})" # 3. texte care nu se suprapun 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}") # 4. textele din cutii încap în cutii for x, y, s, size, weight, anchor in TEXTS: b = box_of(x, y) if b is not None: ew = est_w(s, size, weight) assert x + ew <= b + BOX_W - PAD + 1, f"text iese din cutie: {s!r}" # 5. verdictul e sub linia lui de separare assert VERDICT_SEP < VERDICT_Y < BOX_Y + BOX_H # 6. conținutul obligatoriu din spec for cheie in ( "Trigger de bottom: unde + când", "Setup-urile de fond — suport, locul crimei, double bottom, buy doji,", "divergență de volum — spun UNDE. Trigger-ul de candelă spune CÂND.", "", "BUY HAMMER — pattern standard", "„formă de candelă + downtrend”", "", "„trigger: close peste", "high-ul hammer-ului”", "", "„fără niciun indicator proprietar”", "„pe BVB: se poate rula long-only”", "MBT-10 — cutie neagră", "„formula nu e publică nicăieri”", "", "„reconstituibil doar:", "close peste pivotul daily”", "", "„scanner + indicator proprietar”", "", "„pe BVB: nu, ca sistem”", "Ce rămâne pe BVB: hammer la un nivel major", "+ close peste PPD", "verdictul de BVB = judecata mea", "din secțiunea Transfer.", ): assert cheie in svg, f"lipsește din desen: {cheie!r}" # 7. fără axe de preț / candele / cifre inventate for interzis in ("candela", "SL ", "intrare ", "TP1", "TP2", "$", "USD"): assert interzis not in svg, f"nu ar trebui să apară preț: {interzis!r}" return True SVG, LEFT_AREA, RIGHT_AREA = build() assert verify(SVG, LEFT_AREA, RIGHT_AREA), "verificare eșuată" OUT.write_text(SVG, encoding="utf-8") print(f"scris {OUT} ({W}x{H}), verificare OK: {len(TEXTS)} etichete, " f"cutii {BOX_W:g}x{BOX_H:g} la {BOX_LX:g}/{BOX_RX:g}")