#!/usr/bin/env python3 """Ce e un hammer și ce nu — patru candele cu același high 63.30 și low 62.10. Prima imagine a capitolului: cine o vede nu știe ce e un hammer, deci candela A primește săgeți pe corp și pe fiecare fitil, iar C și D sunt contraexemple cu aceeași înălțime. Zonă verde peste A și B, roșie peste C și D. Desenat direct, nu cu `Chart`: fiecare etichetă trebuie să stea lipită sub candela ei, pe 2-3 rânduri, plus săgeți cu text pe lateral. Rulare: python3 gen/strategii/hammer_mbt10_bh_forma.py """ from pathlib import Path OUT = Path(__file__).with_suffix(".svg") GREEN, RED, GREY, BLUE = "#188a3e", "#c00", "#555", "#1a5fb4" H, L = 63.30, 62.10 RNG = H - L # 1.20 PRAG_SUS = L + 2 / 3 * RNG # 62.90 — corpul trebuie să stea peste el # (nume, o, h, l, c, verdict, rânduri de etichetă sub candelă) CANDLES = [ ("A", 63.05, H, L, 63.30, "DA", ["DA: corp 0.25,", "fitil jos 0.95 = 3,8x corpul"]), ("B", 62.90, H, L, 63.30, "DA", ["DA, la limită: corp 0.40,", "fitil jos 0.80 = exact 2x"]), ("C", 62.60, H, L, 63.30, "NU", ["NU: corp 0.70,", "fitil jos 0.50 = 0,7x corpul"]), ("D", 62.25, H, L, 62.45, "NU", ["NU: corpul e jos, fitilul", "lung e sus — alt pattern"]), ] def masuri(o, h, l, c): return abs(c - o), min(o, c) - l, h - max(o, c) # corp, fitil jos, fitil sus # --- verificările ----------------------------------------------------------- M = {n: masuri(o, h, l, c) for n, o, h, l, c, *_ in CANDLES} for n, o, h, l, c, *_ in CANDLES: assert (h, l) == (H, L), (n, h, l) # 1 aceeași bară assert [tuple(round(v, 2) for v in M[n]) for n in "ABCD"] == [ # 2 cifrele din etichete (0.25, 0.95, 0.00), (0.40, 0.80, 0.00), (0.70, 0.50, 0.00), (0.20, 0.15, 0.85)], M for n in "AB": # 3 A și B trec ambele reguli corp, jos, _ = M[n] o, c = [(o, c) for nn, o, _, _, c, *_ in CANDLES if nn == n][0] assert jos >= 2 * corp - 1e-9, (n, jos, corp) assert min(o, c) >= PRAG_SUS - 1e-9, (n, min(o, c), PRAG_SUS) assert M["C"][1] < 2 * M["C"][0], "C ar trebui să pice pe regula fitilului" # 4 oD, cD = CANDLES[3][1], CANDLES[3][4] assert max(oD, cD) < PRAG_SUS, "D ar trebui să pice pe regula treimii de sus" # 5 assert round(M["A"][1] / M["A"][0], 1) == 3.8 and round(M["B"][1] / M["B"][0], 1) == 2.0 assert round(M["C"][1] / M["C"][0], 1) == 0.7 # --- geometrie -------------------------------------------------------------- SCALE, TOP, W = 260, 210, 1000 XS = [350, 490, 690, 860] BODY_W = 26 def y(p): return TOP + (H - p) * SCALE assert y(CANDLES[0][1]) > y(CANDLES[0][4]), "open A trebuie sub close A pe ecran" out = [] t = out.append def text(x, yy, s, color=GREY, anchor="start", bold=False, size=12): style = f' style="font-weight:bold;font-size:{size}px"' if bold else ( f' style="font-size:{size}px"' if size != 12 else "") t(f'{s}') def zone(x0, x1, y0, y1, label, color): t(f'') text(x0 + 6, y0 - 7, label, color, bold=True) def arrow(x1, y1, x2, y2, color, lines, anchor="start"): d = max(((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5, 1e-9) ux, uy = (x2 - x1) / d, (y2 - y1) / d bx, by = x2 - 8 * ux, y2 - 8 * uy t(f'') t(f'') dx = -6 if anchor == "end" else (0 if anchor == "middle" else 6) y0 = (y1 - 10 - 14 * (len(lines) - 1) if anchor == "middle" else y1 + 4 - 14 * (len(lines) - 1) / 2) for k, s in enumerate(lines): text(x1 + dx, y0 + 14 * k, s, color, anchor, bold=(k == 0)) text(24, 30, "Ce e un hammer și ce nu — aceeași bară, 63.30 sus și 62.10 jos", "#333", bold=True, size=14) label_y0 = y(L) + 28 z_bot = label_y0 + 16 * 2 - 4 zone(XS[0] - 58, XS[1] + 66, y(H) - 30, z_bot, "hammer", GREEN) zone(XS[2] - 60, XS[3] + 74, y(H) - 30, z_bot, "nu e hammer", RED) for p in (H, L): t(f'') text(24, y(H) - 6, "cât de sus a urcat prețul: 63.30") text(24, y(L) + 14, "cât de jos a coborât: 62.10") # pragul treimii de sus: linia pe care corpul trebuie s-o depășească t(f'') text(24, y(PRAG_SUS) - 4, f"treimea de sus începe la {PRAG_SUS:.2f}", BLUE) for (n, o, h, l, c, verdict, lines), x in zip(CANDLES, XS): color = GREEN if c >= o else RED vcol = GREEN if verdict == "DA" else RED t(f'') top, bot = y(max(o, c)), y(min(o, c)) t(f'') for k, s in enumerate(lines): text(x, label_y0 + 16 * k, s, vcol, "middle", bold=(k == 0)) xA = XS[0] arrow(xA - 60, y(63.10), xA - 15, y(63.17), BLUE, anchor="end", lines=["corpul: mic și sus în bară", "ziua s-a închis aproape de maxim —", "cumpărătorii au ținut până la final"]) arrow(xA - 60, y(62.55), xA - 3, y(62.60), BLUE, anchor="end", lines=["fitilul de jos, lung", "vânzătorii au dus prețul până la 62.10,", "dar cumpărătorii l-au adus înapoi sus"]) text(xA + 20, y(63.05) + 12, "open 63.05") text(xA + 20, y(63.30) - 4, "close = high 63.30") xC, xD = XS[2], XS[3] arrow(xC + 34, y(H) - 112, xC + 14, y(62.95), RED, lines=["corp prea gros", "fitilul de jos nu e nici cât corpul —", "nimeni n-a fost respins jos"]) arrow(xD, y(H) - 34, xD, y(63.24), RED, anchor="middle", lines=["fitilul lung e sus, nu jos", "aici cumpărătorii au fost respinși;", "e inverted hammer, alt pattern"]) cap_y = z_bot + 30 text(24, cap_y, "Hammer = fitilul de jos e cel puțin dublul corpului ȘI corpul stă în treimea " "de sus a barei (aici peste 62.90).") text(24, cap_y + 16, "Pragul „fitil ≥ 2x corpul\" e al cursului; „corpul în treimea de sus\" e " "traducerea mea a lui „corp mic sus, fitil de sus aproape inexistent\".", GREY) height = cap_y + 40 svg = (f'\n ' + "\n ".join(out) + "\n\n") assert "\n\n" not in svg OUT.write_text(svg, encoding="utf-8") print(f"scris {OUT.name} ({W}x{height:.0f})")