#!/usr/bin/env python3 """Buy Doji — NU: Sell Doji daily contra unui Buy Doji weekly. Citește `gen/strategii/buy_doji_directie.spec.txt` și scrie `gen/strategii/buy_doji_directie.svg`. Două panouri stivuite: sus weekly (10 bare, Buy Doji confirmat), jos daily (17 bare, uptrend, Sell Doji tehnic valid, short-ul lovește SL-ul). Coordonatele sunt absolute (fără ), ca audit_diagrams.py să vadă pozițiile reale ale textelor. Rulare: python3 gen/strategii/buy_doji_directie.py """ from pathlib import Path OUT = Path(__file__).with_suffix(".svg") W = 720 def trend(start, closes, wick=0.15): bars, o = [], start for c in closes: bars.append((o, round(max(o, c) + wick, 2), round(min(o, c) - wick, 2), c)) o = c return bars # --- seriile din spec: (open, high, low, close) ----------------------------- WK = trend(89.0, [88.0, 86.2, 84.9, 83.1, 81.6, 80.2, 78.9]) + [ (78.90, 79.80, 77.60, 78.80), # 7: doji (78.80, 81.60, 78.60, 81.30), # 8: confirmare => Buy Doji weekly (81.30, 82.70, 81.10, 82.40)] DY = trend(81.30, [81.7, 82.2, 82.0, 82.6, 83.1, 83.5, 83.3, 83.9, 84.4]) + [ (84.40, 84.80, 83.95, 84.35), # 9: doji (84.35, 84.45, 83.45, 83.60), # 10: confirmare => Sell Doji, intrare short (83.60, 83.70, 83.00, 83.20), (83.20, 84.00, 83.10, 83.90), (83.90, 84.70, 83.80, 84.60), (84.60, 85.25, 84.50, 85.10), # 14: SL lovit (85.10, 85.75, 85.00, 85.60), (85.60, 86.30, 85.50, 86.10)] def eq(a, b): return abs(a - b) < 0.005 # --- assert-urile din spec --------------------------------------------------- assert len(WK) == 10 and len(DY) == 17 for name, bars in (("weekly", WK), ("daily", DY)): for i, (o, h, l, c) in enumerate(bars): assert h >= max(o, c) and l <= min(o, c), f"{name}[{i}] h/l incoerent" if i: assert eq(o, bars[i - 1][3]), f"{name}[{i}] open != close precedent" for name, (o, h, l, c) in (("doji weekly", WK[7]), ("doji daily", DY[9])): assert abs(o - c) <= 0.10 * (h - l) + 1e-9, f"{name} nu e doji" assert WK[8][3] > WK[7][1], "weekly: fără confirmare" assert DY[10][3] < DY[9][2], "daily: fără confirmare Sell Doji" ENTRY = DY[10][3] SL = max(b[1] for b in DY[7:11]) RISK = round(SL - ENTRY, 2) TP1 = round(ENTRY - RISK, 2) assert eq(ENTRY, 83.60) and eq(SL, 84.80) and eq(RISK, 1.20) and eq(TP1, 82.40) LOW_AFTER = min(b[2] for b in DY[11:]) assert eq(LOW_AFTER, 83.00) and LOW_AFTER > TP1, "TP1 atins" HIT = next(i for i in range(11, 17) if DY[i][1] >= SL - 1e-9) assert HIT == 14, f"SL lovit la {HIT}" assert eq(DY[0][0], WK[8][3]), "daily nu începe la close-ul confirmării weekly" # --- desen ------------------------------------------------------------------- GREEN, RED, ORANGE, GRAY, INK = "#188a3e", "#c00", "#d06a00", "#777", "#333" BODY_W = 9 out = [] def text(x, y, s, fill=INK, anchor="start", bold=False, size=None): st = (["font-weight:bold"] if bold else []) + ([f"font-size:{size}px"] if size else []) style = f' style="{";".join(st)}"' if st else "" a = f' text-anchor="{anchor}"' if anchor != "start" else "" out.append(f'{s}') def line(x1, y1, x2, y2, color, width=1.5, dash=None, extra=""): d = f' stroke-dasharray="{dash}"' if dash else "" out.append(f'') def rect(x, y, w, h, fill, opacity, stroke="none"): out.append(f'') def candles(bars, x0, gap, yf): for i, (o, h, l, c) in enumerate(bars): cx = x0 + i * gap line(cx, yf(h), cx, yf(l), "#1a1a1a", 1.2) top = yf(max(o, c)) hh = max(yf(min(o, c)) - top, 1.6) out.append(f'') text(20, 24, "NU: Sell Doji daily contra unui Buy Doji weekly — exemplu construit", bold=True, size=14) # ---- panoul de sus: weekly ---------------------------------------------------- WX0, WGAP, WS, WT = 60, 16, 12.0, 66 wmax = max(b[1] for b in WK) yw = lambda p: WT + (wmax - p) * WS # noqa: E731 xw = lambda i: WX0 + i * WGAP # noqa: E731 text(20, 50, "weekly: direcția", fill="#555", bold=True) rect(xw(7) - 8, yw(WK[8][1]) - 4, WGAP + 16, yw(WK[7][2]) - yw(WK[8][1]) + 8, GREEN, 0.12, GREEN) candles(WK, WX0, WGAP, yw) text(xw(7), yw(WK[7][2]) + 16, "doji weekly", fill=ORANGE, anchor="middle", bold=True) text(xw(9) + 14, yw(WK[8][3]) + 4, "Buy Doji weekly: direcția = long", fill=GREEN, bold=True) # insigna de direcție rect(470, 66, 230, 58, GREEN, 0.10, GREEN) text(585, 90, "WEEKLY: LONG ↑", fill=GREEN, anchor="middle", bold=True, size=16) text(585, 110, "confirmare 81.30 > high doji 79.80", fill=GREEN, anchor="middle", size=11) w_bottom = yw(min(b[2] for b in WK)) # ---- panoul de jos: daily ----------------------------------------------------- DX0, DGAP, DS = 60, 18, 40.0 DT = w_bottom + 110 dmax = max(b[1] for b in DY) yd = lambda p: DT + (dmax - p) * DS # noqa: E731 xd = lambda i: DX0 + i * DGAP # noqa: E731 line(20, w_bottom + 34, W - 20, w_bottom + 34, "#ddd", 1) text(20, w_bottom + 56, "daily: săptămânile de după confirmare (open 81.30 = close-ul ei)", fill="#555", bold=True) LX1, LX2, COL = xd(9) - 10, 392, 400 y_sl, y_e, y_tp = yd(SL), yd(ENTRY), yd(TP1) assert abs((y_sl - y_e) - (y_e - y_tp)) < 1e-6, "SL-intrare != intrare-TP1 în px" rect(xd(9) - 8, yd(DY[9][1]) - 3, DGAP + 16, yd(DY[10][2]) - yd(DY[9][1]) + 6, ORANGE, 0.15, ORANGE) line(LX1, y_sl, LX2, y_sl, RED, 1.5, "6 3") line(xd(10) - 8, y_e, LX2, y_e, ORANGE, 1.5, "6 3") line(xd(10) - 8, y_tp, LX2, y_tp, GRAY, 1.5, "2 3") text(COL, y_sl + 4, "SL 84.80 = high 4 candele", fill=RED, size=11) text(COL, y_e + 4, "intrare short 83.60", fill=ORANGE, size=11) text(COL, y_tp + 4, "TP1 82.40 — neatins", fill=GRAY, size=11) candles(DY, DX0, DGAP, yd) # paranteza „short de 1-2 zile" peste barele 10-11 xa, xb, yb = xd(10) - 6, xd(11) + 6, y_sl - 12 out.append(f'') text(xb, yb - 6, "short de 1-2 zile: permis", fill=GRAY, anchor="end", bold=True) # SL lovit la bara 14 cx14 = xd(HIT) out.append(f'') y_lbl = DT - 22 line(cx14, y_sl - 8, cx14, y_lbl + 4, RED, 1.2) text(cx14 - 6, y_lbl, "SL lovit: poziția short contra weekly", fill=RED, anchor="middle", bold=True) # Sell Doji: etichetă sub panou, cu leader spre confirmare y_bot = yd(min(b[2] for b in DY)) y_sd = y_bot + 22 line(xd(10), yd(DY[10][2]) + 4, xd(10), y_sd - 12, ORANGE, 1.2) text(xd(10) - 20, y_sd, "Sell Doji daily: tehnic valid (close 83.60 < low doji 83.95)", fill=ORANGE, bold=True) # insigna de contrast rect(470, DT - 36, 230, 58, RED, 0.08, RED) text(585, DT - 12, "short daily = CONTRA ✗", fill=RED, anchor="middle", bold=True, size=16) text(585, DT + 8, "weekly zice long; daily urcă mai departe", fill=RED, anchor="middle", size=11) y0 = y_sd + 32 text(20, y0, "Short scurt înăuntrul structurii weekly: da. Poziție short contra ei: nu.", fill=INK, bold=True) text(20, y0 + 18, "Seria e construită: un singur caz ilustrează regula, nu o dovedește.", fill="#555") H = y0 + 30 svg = (f'\n ' + "\n ".join(out) + "\n") assert "\n\n" not in svg OUT.write_text(svg, encoding="utf-8") print(f"intrare {ENTRY:.2f} SL {SL:.2f} risc {RISK:.2f} TP1 {TP1:.2f} | min după {LOW_AFTER:.2f} | " f"SL lovit la bara {HIT}") print(f"scris {OUT} ({W}x{H:.0f})")