#!/usr/bin/env python3 """MBT-10, pasul „unde": ideea de bottom cu confluențe (exemplu construit). Seria de aici e SURSA UNICĂ a trade-ului MBT-10 din Nivelul 2: `hammer_mbt10_mbt_ppd`, `_mbt_stop` și `_mbt_iesire` importă `serie()` și pornesc din aceleași bare. Nivelurile 89.90 (intrare la close), 87.30 (SL) și 95.20 (ținta, vârful de unde a pornit coborârea) sunt cifrele exemplului construit din fișierul principal; suportul weekly 87.50 și pivotul daily 89.60 sunt puse de mine, ca acele cifre să aibă de unde ieși. Diagrama asta arată doar contextul: double bottom pe suportul weekly, cu volum mai mic pe al doilea picior. Nicio intrare desenată — intrarea vine la pasul următor. Rulare: python3 gen/strategii/hammer_mbt10_mbt_confluente.py """ import re import sys; sys.path.insert(0, "/workspace/atm-curs-dl") from pathlib import Path from svg_candles import Arrow, Chart, Level, Zone, from_closes OUT = Path(__file__).with_suffix(".svg") GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555" SUP_W, PPD = 87.50, 89.60 # suport weekly, pivotul daily INTRARE, SL, TP = 89.90, 87.30, 95.20 R = round(INTRARE - SL, 2) FUND1, FUND2 = 10, 15 # cele două picioare ale double bottom-ului VARF = 3 # ultimul maxim înainte de coborâre: high 95.20 = ținta CLOSE_I = 19 # bara care se închide peste pivotul daily CLOSES = [96.00, 95.00, 94.20, 95.00, 93.10, 92.00, 91.20, 90.10, 89.00, # 0-8 88.20, 87.70, 88.40, 89.10, # 9-12 88.60, 88.00, 87.65, 88.30, # 13-16 88.90, 89.40, 89.90, # 17-19 90.60, 91.40, 92.20, 91.80, 93.00, 94.10, 95.40] # 20-26 VOLS = [60, 58, 55, 50, 62, 66, 70, 74, 80, 86, 94, 62, 58, 55, 60, 66, 54, # piciorul 2: volum vizibil mai mic 58, 62, 96, 70, 66, 64, 58, 68, 72, 80] def serie(): w = [(0.20, 0.25)] * len(CLOSES) w[FUND1] = (0.20, 0.10) # low 87.60 w[FUND2] = (0.20, 0.10) # low 87.55 # barele imediat de după picioare pornesc din același preț: fără fitil lung # în jos, altfel ele ar face minimul, nu picioarele w[FUND1 + 1] = w[FUND2 + 1] = (0.20, 0.05) # bara 18 atinge pivotul intraday, dar se închide sub el: contraexemplul # de la pasul cu condiția de preț w[CLOSE_I - 1] = (0.30, 0.25) # bara de după vârf deschide chiar în el: fără fitil sus, ca vârful să # rămână singurul maxim la 95.20 w[VARF + 1] = (0.00, 0.25) cds = from_closes(CLOSES, wicks=w, vols=VOLS, start=96.60) assert len(cds) == len(VOLS) == 27 for i in range(1, len(cds)): assert abs(cds[i].o - cds[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]" l1, l2 = round(cds[FUND1].l, 2), round(cds[FUND2].l, 2) assert (l1, l2) == (87.60, 87.55), (l1, l2) assert abs(l1 - l2) <= 0.05 and l2 < l1, "double bottom: al doilea picior, puțin mai jos" assert round(min(c.l for c in cds), 2) == l2, "cel mai jos low nu e al piciorului 2" assert l2 > SUP_W > SL, "picioarele trebuie să stea peste suport, iar stopul sub el" assert VOLS[FUND2] < VOLS[FUND1], "fără divergență de volum pe piciorul 2" # de la al doilea picior încoace, nicio bară nu închide peste pivotul zilei # intrării — altfel trigger-ul ar fi venit mai devreme assert max(c.c for c in cds[FUND2:CLOSE_I]) < PPD < cds[CLOSE_I].c, \ "o bară dinaintea trigger-ului închide deja peste pivotul daily" assert round(cds[CLOSE_I].c, 2) == INTRARE assert cds[CLOSE_I - 1].h > PPD > cds[CLOSE_I - 1].c, \ "bara 18 trebuie să atingă pivotul intraday, dar să închidă sub el" assert min(c.l for c in cds[CLOSE_I:]) > SL, "stopul ar fi fost atins după intrare" prim_tp = next(i for i in range(CLOSE_I + 1, len(cds)) if cds[i].h >= TP) assert prim_tp == 26, prim_tp # ținta 95.20 nu e un număr ales: e high-ul ultimului maxim dinaintea coborârii assert round(cds[VARF].h, 2) == TP, cds[VARF].h assert max(c.h for c in cds[VARF + 1:CLOSE_I]) < TP, \ "un maxim de după vârf trece peste țintă: ținta n-ar mai fi prima structură" return cds if __name__ == "__main__": cds = serie() pct1 = round((cds[FUND1].l - SUP_W) / SUP_W * 100, 2) pct2 = round((cds[FUND2].l - SUP_W) / SUP_W * 100, 2) assert pct1 < 0.2 and pct2 < 0.2, (pct1, pct2) vis = cds[:CLOSE_I] # contextul, fără bara de trigger ch = Chart( candles=vis, levels=[Level(SUP_W, "suport weekly 87.50", BLUE, label_at="left")], zones=[Zone(FUND1, FUND2, "double bottom: 87.60 și 87.55", ORANGE)], arrows=[ Arrow(FUND1, "piciorul 1: low 87.60,\nla 0,11 % peste suport", where="below", color=GREY, dx=-150, dy=26), Arrow(FUND2, "piciorul 2: low 87.55, cu 5 cenți mai jos,\ndar cu volum mai mic decât " "la piciorul 1 —\nvânzarea a slăbit (divergență de volum)", where="below", color=RED, dx=-40, dy=64), ], scale=30.0, gap=22, body_w=12, left=54, width=700, vol_h=70, vol_label="volum", title="MBT-10, pasul „unde\" — double bottom pe suportul weekly (exemplu construit)", ) svg = ch.render() m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg) W, H = int(m.group(1)), int(m.group(2)) linii = [ ("Aici nu intri încă: diagrama arată doar ideea de bottom. MBT-10 e ultima piesă, și " "vine abia la close-ul unei bare.", "#333"), ("Cifrele 89.90 / 87.30 / 95.20 ale trade-ului sunt din exemplul construit al " "materialului;", GREY), ("suportul weekly 87.50, pivotul daily 89.60 și structura barelor sunt ale mele.", GREY), ] add = 16 * len(linii) + 10 svg = svg.replace(f'viewBox="0 0 {W} {H}"', f'viewBox="0 0 {W} {H + add}"') svg = svg.replace("", "".join( f'{t}' for i, (t, c) in enumerate(linii)) + "") assert "\n\n" not in svg, "linie goală în SVG" OUT.write_text(svg, encoding="utf-8") print(f"scris {OUT.name} ({W}x{H + add}); picioare {pct1}% / {pct2}% peste suport, R={R}")