Cea mai grava, si cea care a cerut refacerea seriei: exemplul central de MBT-10 era luat de pe 60 de minute si bagat in procedura de daily, cu barele numite "zile", desi sursa spune explicit de ce nu merge pe daily. Pe langa asta, suportul fusese schimbat din 88.00 (testat de trei ori, cu compresie, cum e in sursa) in 87.50 cu un double bottom si o divergenta de volum imprumutate din alt exemplu. Seria e refacuta pe exemplul materialului, cu assert pe cele trei atingeri ale lui 88.00 si pe compresia varfurilor. Adaosurile au fost scoase, nu doar marcate: cu ele in desen, suportul din sursa n-ar fi avut sens. Aritmetica a supravietuit neatinsa. Restul: stopul de 60.40 sta acum sub o structura reala; pivotul daily nu mai e o linie fixa peste tot graficul, ci un segment peste ziua intrarii; SL-ul de pe LABU respecta regula proprie de 5 centi; citatul de la A2 nu mai e dat ca al autorului; pragul de 1-2% isi spune originea (cursul da 0,5%, pe 60 min); o singura conventie de distanta, impartirea la nivel; swing low definit la fel in pas si in glosar. eticheta_libera a prins 7 suprapuneri reale. La bh_aplus a trebuit rulat pe fiecare panou separat: wrapper-ul le muta cu <g transform>, pe care unealta nu-l citeste - de notat pentru data viitoare. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
142 lines
7.4 KiB
Python
142 lines
7.4 KiB
Python
#!/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("</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}); teste {TESTE} pe {SUP_W}, R={R}")
|