"""După gap down — primul MBT-10 te scoate, al doilea merge (exemplu construit). Citește `gen/strategii/hammer_mbt10_mbt_gapdown.spec.txt` (marcajul ``, Partea B §2.3 Gotchas). Serie continuă de 33 candele daily, construită din close-urile date în specificație, cu open-ul fiecărei bare = close-ul precedentei (STYLE.md), cu două excepții impuse de exemplu: - bara 13 (ultima dinainte de gap): ca să iasă ȘI ``low[13] = 70.60`` (nivelul de închidere a gap-ului, cerut de assert 1 și 6) ȘI ``high[13] = 71.00`` (structura seriei, „de la ~71"), bara trebuie să fie un doji ``o = c = 70.60``. De aceea am ridicat ``close[12]`` de la 69.80 (cât dă lista) la 70.60: altfel ``min(open,close)=69.80`` și low-ul ar ieși 69.80, nu 70.60. Wu=0.40 => high 71.00, wd=0.00 => low 70.60. (Linia „Mecioni" cere wu=0.20 => high 70.80; am ales 71.00, cifra din structura seriei și din textul „~71".) - bara 14 (gap down): ``open[14] = 66.40``, NU close[13]=70.60 — singura bară cu open diferit de close-ul precedent. O construim manual: o=c=66.40, wu=0.30 => high 66.70, wd=0.30 => low 66.10. Wick-urile speciale: bara 16 wd=0.20 (low 64.60), bara 18 wd=0.20 (low 63.20), bara 21 wd=0.20 (low 62.00); restul (0.30, 0.40). Rulează: python3 gen/strategii/hammer_mbt10_mbt_gapdown.py Scrie: gen/strategii/hammer_mbt10_mbt_gapdown.svg """ import re import sys from pathlib import Path sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Candle, Chart, Level, Mark, from_closes # noqa: E402 OUT = Path(__file__).resolve().parent / "hammer_mbt10_mbt_gapdown.svg" GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000" # --- seria: 33 de close-uri, exact ca în specificație ------------------------ # Singura abatere: closes[12] 69.80 -> 70.60, ca bara 13 să poată avea # low 70.60 (vezi docstring-ul). closes = [ 60.00, 60.80, 61.60, 62.50, 63.30, 64.20, 65.00, 65.80, 66.60, 67.40, # 0-9 68.20, 69.00, 70.60, 70.60, # 10-13 66.40, # 14 = bara de GAP DOWN (open impus 66.40, NU close[13]) 65.50, # 15 64.80, # 16 = MBT-10 #1, închidere => intrare #1 64.20, # 17 63.40, # 18 = low 63.20 => stop #1 62.90, # 19 62.50, # 20 62.20, # 21 = MBT-10 #2, închidere => intrare #2 62.80, 63.60, 64.50, 65.50, 66.50, 67.50, 68.50, 69.50, # 22-29 70.60, # 30 = gap închis (TP) 70.90, # 31 70.50, # 32 ] assert len(closes) == 33, len(closes) WICKS = [(0.30, 0.40)] * 33 # restul barelor WICKS[13] = (0.40, 0.00) # high 71.00, low 70.60 WICKS[14] = (0.30, 0.30) # high 66.70, low 66.10 (bara de gap) WICKS[16] = (0.30, 0.20) # low 64.60 WICKS[18] = (0.30, 0.20) # low 63.20 WICKS[21] = (0.30, 0.20) # low 62.00 # Volume relative, alese de generator (materialul nu dă volum aici): # în scădere pe piciorul coborârii după gap (14 -> 21), apoi recuperare. vols = [ 48, 46, 44, 45, 43, 42, 44, 41, 40, 42, # 0-9 43, 41, 46, 58, # 10-13 90, # 14 = gap down 76, 66, 57, 50, 45, 42, # 15-20 40, # 21 = MBT-10 #2 (minim) 46, 52, 58, 63, 68, 73, 78, 82, 86, 88, 80, # 22-32 ] assert len(vols) == 33, len(vols) coborare = vols[14:22] assert all(coborare[i] >= coborare[i + 1] for i in range(len(coborare) - 1)), \ f"volumul nu scade pe piciorul coborârii: {coborare}" assert vols[-1] > vols[21], "volumul nu recuperează după bara 21" cds = from_closes(closes, wicks=WICKS, vols=vols, start=60.00) # bara de gap: open = 66.40, nu close[13] = 70.60 cds[14] = Candle(66.40, 66.70, 66.10, 66.40, vol=vols[14], color=RED) # --- assert-urile din specificație ----------------------------------------- # 1. gap down real assert abs(cds[14].o - 66.40) < 1e-9, cds[14].o assert abs(cds[13].l - 70.60) < 1e-9, f"low[13]={cds[13].l} != 70.60" assert abs(cds[13].c - 70.60) < 1e-9, cds[13].c assert cds[14].o < cds[13].l and cds[14].h < cds[13].l, "gapul nu e real" assert abs(cds[14].o - cds[13].c) > 1e-9, "open[14] nu e excepția de la serie" # 2. #1 intrare1, sl1 = 64.80, 63.20 assert abs(cds[16].c - intrare1) < 1e-9, f"close[16]={cds[16].c} != 64.80" assert abs(sl1 - 63.20) < 1e-9 assert min(c.l for c in cds[16:19]) <= sl1, "stopul #1 nu e atins" assert cds[16].c > sl1, "intrarea #1 e sub stopul ei" # 3. #2 intrare2, sl2 = 62.20, 60.40 assert abs(cds[21].c - intrare2) < 1e-9, f"close[21]={cds[21].c} != 62.20" assert abs(sl2 - 60.40) < 1e-9 assert not any(c.l <= sl2 for c in cds[22:]), "un low atinge SL2 după bara 21" # 4. riscuri risc1 = intrare1 - sl1 # 1.60 risc2 = intrare2 - sl2 # 1.80 assert abs(risc1 - 1.60) < 1e-9 and abs(risc2 - 1.80) < 1e-9, (risc1, risc2) # 5. TP = închiderea gap-ului TP = 70.60 reward2 = TP - intrare2 # 8.40 assert abs(reward2 - 8.40) < 1e-9, reward2 assert round(reward2 / risc2, 1) == 4.7, round(reward2 / risc2, 1) # 6. low[13] e nivelul de închidere a gap-ului și TP-ul e atins în urcare assert abs(cds[13].l - TP) < 1e-9, "low[13] != 70.60 (nivelul gap-ului)" assert max(c.h for c in cds[22:33]) >= TP, "TP 70.60 nu e atins după #2" primul_tp = next(i for i in range(22, 33) if cds[i].h >= TP) # 7. același risc în bani: mărimea #2 = mărimea #1 × (risc1 / risc2) marime1 = 1.0 marime2 = marime1 * (risc1 / risc2) assert abs(marime2 - 1.60 / 1.80) < 1e-9, marime2 print(f"gap {cds[13].l:.2f} -> {cds[14].o:.2f} " f"#1 intrare {intrare1:.2f} SL {sl1:.2f} (risc {risc1:.2f}) " f"#2 intrare {intrare2:.2f} SL {sl2:.2f} (risc {risc2:.2f}) " f"TP {TP:.2f} = {reward2 / risc2:.1f}R " f"atins la bara {primul_tp} (high {cds[primul_tp].h:.2f}) " f"mărime #2 = {marime2:.3f} × mărime #1") # --- desenul ---------------------------------------------------------------- ch = Chart( candles=cds, levels=[ # toate etichetele stau în dreapta (x > ultima candelă), fără suprapuneri Level(intrare1, "#1 intrare 64.80", GRAY, label_at="right"), Level(sl1, "#1 SL 63.20", GRAY, label_at="right"), Level(intrare2, "#2 intrare 62.20", GRAY, label_at="right"), Level(sl2, "#2 SL 60.40", GRAY, label_at="right"), Level(TP, "TP 70.60 = 4.7R (gap închis)", BLUE, label_at="right"), ], marks=[ # decalate pe verticală cât să nu atingă candelele din coborâre Mark(14, "gap down", RED, "above", dy=-22), Mark(16, "MBT-10 #1", GRAY, "above", dy=-25), Mark(18, "stop #1 atins", RED, "below", dy=30), Mark(21, "MBT-10 #2", GREEN, "below", dy=25), ], scale=28.0, gap=15, body_w=9, left=44, width=820, vol_h=70, vol_label="volum", title="După gap down — primul MBT-10 te scoate, al doilea merge (exemplu construit)", caption="", ) svg = ch.render() assert "", "".join( f'{t}' for i, t in enumerate(linii)) + "") assert "\n\n" not in svg, "linie goală în SVG după legendă" OUT.write_text(svg) print(f"scris {OUT.relative_to('/workspace/atm-curs-dl')}")