Files
atm-curs-dl/gen/strategii/hammer_mbt10_mbt_confluente.py
Claude Agent a7785740cc 06_hammer_mbt10: capitol reconstruit dupa sablon
Doua proceduri complet separate, nu una amestecata, pentru ca se fac
altfel: buy hammer e pattern public, desenabil si exersabil retroactiv;
MBT-10 e cutie neagra din care se poate reface o singura conditie.
Nivelul 1 le pune fata in fata, ca cititorul sa stie din prima de ce nu
le poate trata la fel. Cate 5 pasi fiecare, oglinda pe short la hammer,
checklist dublu. Sursele in fise/06_hammer_mbt10_surse.md.

Zece diagrame noi: hammer-ul nu era definit vizual nicaieri, iar hanging
man era doar text — acum e aceeasi bara in doua contexte, cu assert ca e
identica. Cele 7 vechi si-au pierdut trimiterile fisier:linie din
legende; toate le aveau.

Doua serii partajate (bh_ansamblu si mbt_confluente expun serie()), deci
un trade nu poate avea doua variante. Oglinda pe short e trade-ul long
oglindit in jurul lui K=162.30, deci riscul ramane 1.30 in ambele.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-20 13:05:41 +00:00

123 lines
6.3 KiB
Python

#!/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("</svg>", "".join(
f'<text x="24" y="{H + 10 + 16 * i}" fill="{c}">{t}</text>'
for i, (t, c) in enumerate(linii)) + "</svg>")
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}")