#!/usr/bin/env python3 """MBT-10, pasul „unde": ideea de bottom, pe exemplul materialului. 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. Exemplul e al materialului (`08_ANALIZA_ZILNICA/02_mbt10.md:440-455`) și e pe **60 de minute**, nu pe daily: suport weekly **88.00**, testat de **trei ori**, cu vârfuri tot mai mici între teste (compresie); intrare **89.90** la close-ul barei în MBT-10; **SL 87.30** sub suport; **țintă 95.20**, vârful de unde a pornit coborârea. Materialul spune și de ce nu pe daily: „o bară daily în suport weekly poate avea un low cu 2-3 % sub suport, deci stopul ar sta departe". Ale mele sunt doar forma exactă a barelor, volumul și pivotul daily 89.60 (pus ca să aibă ce verifica pasul B3). Nu există aici nici double bottom, nici divergență de volum: alea sunt din alt exemplu al materialului (gap up, cu picioarele 32.10 / 30.90) și nu se fuzionează cu ăsta. Rulare: python3 gen/strategii/hammer_mbt10_mbt_confluente.py """ import re import sys; sys.path.insert(0, "/workspace/atm-curs-dl") sys.path.insert(0, "/workspace/atm-curs-dl/gen") from pathlib import Path from eticheta_libera import eticheta_libera from svg_candles import Chart, Level, Mark, Zone, from_closes OUT = Path(__file__).with_suffix(".svg") GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555" SUP_W, PPD = 88.00, 89.60 # suport weekly (materialul), pivotul daily (al meu) INTRARE, SL, TP = 89.90, 87.30, 95.20 R = round(INTRARE - SL, 2) TESTE = (9, 12, 15) # cele trei atingeri ale suportului weekly VARFURI = (10, 13) # vârfurile dintre teste: tot mai mici (compresie) VARF = 3 # ultimul maxim înainte de coborâre: high 95.20 = ținta CLOSE_I = 19 # bara care se închide peste pivotul daily BARE_PE_ZI = 7 # 60 de minute, sesiune US: 7 bare pe zi ZI_INTRARE = (14, 20) # barele zilei în care vine trigger-ul CLOSES = [96.00, 95.00, 94.20, 95.00, 93.10, 92.00, 91.20, 90.10, 89.00, # 0-8 88.20, 89.00, 88.60, # 9-11 88.20, 89.00, 88.50, # 12-14 88.20, 88.60, 89.00, 89.40, 89.90, # 15-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, 62, 58, 84, 60, 56, 82, 58, 62, 66, 96, 70, 66, 64, 58, 68, 72, 80] def serie(): w = [(0.20, 0.25)] * len(CLOSES) for t in TESTE: w[t] = (0.20, 0.20) # low exact pe 88.00: suportul ține, nu cedează w[t + 1] = (0.20, 0.05) # bara de după pornește din 88.20: fără fitil lung în jos w[VARFURI[0]] = (0.50, 0.05) # high 89.50 w[VARFURI[1]] = (0.10, 0.05) # high 89.10 — vârf mai mic # 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}]" # suportul weekly e atins de exact trei ori și ține de fiecare dată atingeri = [i for i, c in enumerate(cds) if round(c.l, 2) <= SUP_W] assert atingeri == list(TESTE), atingeri for t in TESTE: assert round(cds[t].l, 2) == SUP_W, (t, cds[t].l) assert SUP_W > SL, "stopul trebuie să stea sub suport" # compresie: vârfurile dintre teste scad v1, v2 = (round(cds[i].h, 2) for i in VARFURI) assert v1 > v2, f"vârfurile dintre teste nu scad: {v1} > {v2}" assert (v1, v2) == (89.50, 89.10), (v1, v2) # de la primul test î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[TESTE[0]: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" # trigger-ul și contraexemplul lui stau în aceeași zi: pe 60 de minute # pivotul daily e un singur nivel pe toată ziua, dar se schimbă mâine assert ZI_INTRARE[0] <= CLOSE_I - 1 and CLOSE_I <= ZI_INTRARE[1] assert ZI_INTRARE[1] - ZI_INTRARE[0] + 1 == BARE_PE_ZI assert all(i // BARE_PE_ZI == ZI_INTRARE[0] // BARE_PE_ZI for i in range(*ZI_INTRARE)), "ziua intrării nu e un bloc de 7 bare" 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() vis = cds[:CLOSE_I] # contextul, fără bara de trigger ch = Chart( candles=vis, levels=[Level(SUP_W, "suport weekly 88.00", BLUE, label_at="left")], zones=[Zone(TESTE[0], TESTE[-1], "", ORANGE)], marks=[Mark(t, f"test {n}", ORANGE, "above", dy=-34) for n, t in enumerate(TESTE, 1)], scale=30.0, gap=22, body_w=12, left=54, width=700, vol_h=70, vol_label="volum", title="MBT-10, pasul „unde\" — suport weekly testat de trei ori, pe 60 de minute", ) svg = ch.render() eticheta_libera(svg, "mbt_confluente", {"test 1", "test 2", "test 3"}, rects=True) 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"), ("Suportul 88.00 ține de trei ori, iar între teste vârfurile scad — 89.50, apoi " "89.10: compresie.", "#333"), ("Exemplul e al materialului și e pe 60 de minute: suport 88.00, intrare 89.90, " "SL 87.30, țintă 95.20.", GREY), ("Nu e double bottom și nu e divergență de volum — alea sunt din alt exemplu al lui " "(gap up, picioarele 32.10 / 30.90).", GREY), ("Forma exactă a barelor și volumul sunt ale mele; la fel pivotul daily 89.60, pus ca " "să aibă ce verifica pasul B3.", 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}); teste {TESTE} pe {SUP_W}, R={R}")