"""Buy hammer — diagrama de ansamblu (daily, ticker ilustrativ XYZ). Citește specificația `gen/strategii/hammer_mbt10_bh_ansamblu.spec.txt`. Seria de 34 de candele e construită din lista de close-uri dată, cu open-ul fiecărei bare = close-ul precedentului; wick-urile sunt fixate ca nivelurile (hammer H=C 63.30 / L 62.10, intrare 63.35, SL 62.05, TP 65.95) să iasă exact din serie, nu doar din etichetă. """ import re import sys; sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Chart, Level, Mark, from_closes # --- seria: 34 de close-uri, exact ca în specificație ------------------------ closes = [ 72.00, 71.20, 71.50, 70.40, 69.60, 70.00, 68.90, 68.10, 68.40, 67.30, # 0-9 66.50, 66.80, 65.70, 64.90, 65.20, 64.10, 63.40, 63.70, 63.05, # 10-18 63.30, # 19 = hammer: o 63.05, c/h 63.30, l 62.10 (H = C, fitil sus 0) 63.35, # 20 = confirmare: close peste 63.30 => intrare 63.80, 64.20, 64.60, 64.40, 64.90, 65.20, 65.50, 65.80, 66.10, # 21-29 66.00, 65.70, 65.40, 65.60, # 30-33 ] assert len(closes) == 34 # wick-uri: (up, down) implicite (0.25, 0.30) + excepțiile din specificație wicks = [(0.25, 0.30)] * 34 wicks[19] = (0.00, 0.95) # hammer: high 63.30 (= close), low 62.10 # bara 20: wu=0.15 => high 63.50. Spec-ul dă wd=0.15 cu „low 63.20", dar # wd se măsoară de la min(o,c)=63.30, deci 0.15 dă 63.15; folosim tuplul # literal din specificație (0.15, 0.15), low-ul barei 20 nu e etichetat. wicks[20] = (0.15, 0.15) # volum relativ ales de generator (materialul nu dă volum pe exemplul construit): # normal pe downtrend, în creștere pe hammer (19) și pe confirmare (20), normal după vols = [50, 48, 47, 46, 45, 46, 44, 43, 45, 42, 41, 43, 40, 39, 41, 38, 37, 39, 55, 90, 95, 58, 55, 53, 50, 52, 54, 56, 60, 64, 58, 55, 52, 50] assert len(vols) == 34 media_vol = sum(vols) / len(vols) assert vols[19] > media_vol and vols[20] > media_vol, "volum peste medie pe hammer/confirmare" cds = from_closes(closes, wicks=wicks, vols=vols, start=72.00) hammer, conf = cds[19], cds[20] # confirmarea o punem ca notă sub grafic: un Mark deasupra barei 20 s-ar # suprapune cu fitilul lung al hammer-ului (bara 19) conf.note = "bara 20: confirmare — close 63.35 peste high-ul hammer-ului 63.30 (intrare)" intrare, sl, tp = 63.35, 62.05, 65.95 # --- cele 8 assert-uri cerute de specificație ------------------------------- # 1. hammer: H = C = 63.30, L = 62.10 assert abs(cds[19].c - 63.30) < 1e-9 and abs(cds[19].h - 63.30) < 1e-9, "H != C la hammer" assert abs(cds[19].l - 62.10) < 1e-9, f"low[19]={cds[19].l} != 62.10" # 2. open[19] = close[18]; corp 0.25; fitil jos 0.95 >= 2*0.25 assert abs(cds[19].o - cds[18].c) < 1e-9 and abs(cds[19].o - 63.05) < 1e-9, "open[19] != close[18]" corp = hammer.c - hammer.o fitil_jos = hammer.o - hammer.l assert abs(corp - 0.25) < 1e-9, f"corp={corp} != 0.25" assert abs(fitil_jos - 0.95) < 1e-9, f"fitil jos={fitil_jos} != 0.95" assert fitil_jos >= 2 * corp, "fitilul nu e cel puțin dublul corpului" # 3. confirmarea închide peste high-ul hammer-ului assert abs(conf.c - intrare) < 1e-9, f"close[20]={conf.c} != 63.35" assert conf.c > hammer.h and abs(hammer.h - 63.30) < 1e-9, "close[20] nu e peste high[19]" # 4. SL = low-ul hammer-ului - 0.05 assert abs(sl - (cds[19].l - 0.05)) < 1e-9, "SL != low[19] - 0.05" # 5. risc 1.30, TP 2.60 = 2R exact assert abs(abs(intrare - sl) - 1.30) < 1e-9, "risc != 1.30" assert abs(abs(tp - intrare) - 2.60) < 1e-9, "TP != 2R exact" # 6. primul high >= TP e bara 28 (65.80 + wick 0.25 = 66.05) primul_tp = next(i for i in range(21, len(cds)) if cds[i].h >= tp) assert primul_tp == 28, f"TP 65.95 atins întâi la bara {primul_tp}, nu 28" # 7. downtrend vizibil assert cds[18].c < cds[0].c - 8, "downtrend prea mic" # 8. ordinea nivelurilor assert sl < intrare < tp, "SL / intrare / TP nu sunt în ordine" print(f"hammer H/C {hammer.h:.2f} L {hammer.l:.2f} intrare {conf.c:.2f} " f"SL {sl:.2f} TP {tp:.2f} = {abs(tp-intrare)/abs(intrare-sl):.1f}R " f"TP atins la bara {primul_tp} (high {cds[primul_tp].h:.2f})") # --- desenul ---------------------------------------------------------------- ch = Chart( candles=cds, levels=[ Level(sl, "SL 62.05 — sub low-ul hammer-ului", "#c00", label_at="left"), Level(intrare, "intrare 63.35", "#188a3e"), Level(tp, "TP 65.95 = 2R", "#1a5fb4"), ], marks=[ Mark(19, "hammer 63.30 / 62.10", "#b06000", "below"), ], scale=32.0, gap=15, body_w=9, left=44, width=720, vol_h=70, vol_label="volum", title="Buy hammer — daily (ticker ilustrativ XYZ, din lecția de pattern)", caption="", # legenda e pusă manual, pe două rânduri, ca să nu iasă din pânză ) svg = ch.render() assert "", "".join( f'{t}' for i, t in enumerate(legenda)) + "") assert "\n\n" not in svg, "linie goală în SVG după legendă" with open("/workspace/atm-curs-dl/gen/strategii/hammer_mbt10_bh_ansamblu.svg", "w") as f: f.write(svg) print("scris gen/strategii/hammer_mbt10_bh_ansamblu.svg")