#!/usr/bin/env python3 """Buy hammer — locația: hammer la un nivel major vs. hammer în mijlocul range-ului. Două panouri alăturate cu ACEEAȘI serie de 12 bare, pe aceeași scară și aceeași axă de preț (candelele stau la aceleași y). Stânga: suport monthly 62.00, la 0,16 % sub low-ul hammer-ului. Dreapta: cel mai apropiat nivel de sub el e la 58.00, adică un gol de 7,07 %. Nivelurile se desenează de mână, cu coordonate absolute (liniile din Chart pornesc de la x=24 și ar trece peste panoul vecin). Rulare: python3 gen/strategii/hammer_mbt10_bh_locatie.py """ import re import sys; sys.path.insert(0, "/workspace/atm-curs-dl") from pathlib import Path from svg_candles import Arrow, Chart, Mark, from_closes OUT = Path(__file__).with_suffix(".svg") GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555" CLOSES = [65.40, 64.90, 64.40, 64.70, 64.00, 63.50, 63.05, 63.30, # 7 = hammer: o 63.05, h = c 63.30, l 62.10 63.35, # 8 = confirmare, close peste high-ul hammer-ului 63.80, 64.20, 64.60] wicks = [(0.15, 0.15)] * len(CLOSES) wicks[7] = (0.00, 0.95) cds = from_closes(CLOSES, wicks=wicks, start=65.90) ham = cds[7] SUP_DA, SUP_NU, REZ_NU = 62.00, 58.00, 67.00 # --- verificări ------------------------------------------------------------- for i in range(1, len(cds)): assert abs(cds[i].o - cds[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]" assert (round(ham.h, 2), round(ham.l, 2), round(ham.c, 2)) == (63.30, 62.10, 63.30) corp, jos = ham.c - ham.o, ham.o - ham.l assert jos >= 2 * corp, "bara 7 nu e hammer" assert cds[8].c > ham.h, "bara 8 nu confirmă" d_da = round((ham.l - SUP_DA) / SUP_DA * 100, 2) assert d_da == 0.16 and d_da <= 2, d_da d_nu = round((ham.l - SUP_NU) / SUP_NU * 100, 2) assert d_nu == 7.07 and d_nu > 2, d_nu d_rez = round((REZ_NU - ham.h) / ham.h * 100, 2) assert d_rez == 5.85 and d_rez > 2, d_rez assert not any(SUP_NU < p < REZ_NU for p in (SUP_NU, REZ_NU) if False) # --- layout ----------------------------------------------------------------- W, PW = 760, 350 PX = [24, 24 + PW + 28] SCALE, TOP = 34.0, 96 ARR = { PX[0]: Arrow(4, "suport monthly: nivel urmărit de mulți —\naici au cumpărători motiv să apară", price=SUP_DA, where="below", color=BLUE, dx=30, dy=22), PX[1]: Arrow(7, "aceeași formă, dar nimic sub ea:\nfitilul n-a fost oprit de un nivel,\nci s-a oprit singur", where="below", color=RED, dx=70, dy=30), } charts = [] for px in PX: ch = Chart(candles=cds, marks=[Mark(7, "hammer 63.30 / 62.10", ORANGE, "above", dy=-16)], arrows=[ARR[px]], scale=SCALE, gap=17, body_w=10, left=px + 34, width=px + PW, top=TOP) body = ch._render() assert f'viewBox="0 0 {px + PW} ' in body, f"panoul {px}: textul unei săgeți iese" assert (ch.top, ch.left) == (TOP, px + 34), "Chart a mutat graficul: panourile s-ar dezalinia" charts.append((ch, body.split(">", 1)[1].rsplit("", 1)[0].strip())) (ch_da, body_da), (ch_nu, body_nu) = charts assert ch_da.scale == ch_nu.scale and ch_da._pmax == ch_nu._pmax, "scări diferite" y = ch_da._y def level(px, price, text, color, below=False): yy = y(price) ty, anchor, tx = (yy + 14, "end", px + PW - 4) if below else (yy - 6, "end", px + PW - 4) return [f'', f'{text}'] def zone(px, p_top, p_bot, label, color, label_at_bottom=False): y0, y1 = y(p_top), y(p_bot) ly, anchor, lx = ((y1 - 12, "start", px + 8) if label_at_bottom else (y0 - 6, "end", px + PW - 4)) return [f'', f'{label}'] PCT_DA, PCT_NU = f"{d_da:.2f}".replace(".", ","), f"{d_nu:.2f}".replace(".", ",") y_bot = y(SUP_NU) parts = [ f'', 'Hammer la un nivel vs. hammer ' 'în mijlocul range-ului — daily, exemplu construit', f'DA — hammer pe suportul ' 'monthly', f'NU — hammer în mijlocul ' 'range-ului', f'', *zone(PX[0], ham.l, SUP_DA, f"low-ul hammer-ului: {ham.l:.2f}", GREEN), *zone(PX[1], ham.l, SUP_NU, f"low-ul hammer-ului: {ham.l:.2f}", RED), f'de la {ham.l:.2f} la {SUP_DA:.2f} = {PCT_DA} % — la nivel', f'' f'de la {ham.l:.2f} la {SUP_NU:.2f} = gol de {PCT_NU} % — hammer în aer', *level(PX[0], SUP_DA, "suport monthly 62.00", BLUE, below=True), *level(PX[1], REZ_NU, "rezistența range-ului 67.00", BLUE, below=True), *level(PX[1], SUP_NU, "baza range-ului 58.00", BLUE, below=True), body_da, body_nu, f'Completare proprie: materialul cere ' '„un nivel major (weekly / monthly / quarterly), nu mijlocul', f'range-ului", dar nu dă un prag numeric. ' f'Pragul de 1-2 % e împrumutat de la buy doji, ca să ai ce', f'măsura: aici {PCT_DA} % în stânga, ' f'{PCT_NU} % în dreapta.', "", ] svg = "\n ".join(parts).replace("\n ", "\n") assert "\n\n" not in svg, "linie goală în SVG" for m in re.finditer(r']*>(.*?)', body_da): assert float(m.group(1)) < PX[1] - 14, f"text din DA trece în NU: {m.group(2)}" OUT.write_text(svg, encoding="utf-8") print(f"scris {OUT.name} ({W}x{y_bot + 80:.0f}); DA {d_da}% NU {d_nu}% (rezistență {d_rez}%)")