"""Buy Doji — unde pui stopul: 4 candele înapoi, sub structură. Citește `gen/strategii/buy_doji_stop.spec.txt`. Barele vin din `buy_doji_ansamblu.serie()` — e același trade ca în toate celelalte diagrame ale capitolului (doji 50.10/49.25, intrare 50.40, stop 49.20). Panoul de sus: cum numeri cele 4 candele, de la confirmare înapoi. Panoul de jos: același trade, cu un stop pus „la ochi" (49.70, sub candela de confirmare) față de stopul din regulă (49.20). Al doilea picior al fundului coboară la 49.50: mătură stopul la ochi, nu-l atinge pe cel din regulă. Barele de după intrare sunt CONSTRUITE. """ import re import sys from pathlib import Path sys.path.insert(0, "/workspace/atm-curs-dl") sys.path.insert(0, str(Path(__file__).parent)) from svg_candles import Candle, Chart, Level, Zone, Arrow # noqa: E402 from buy_doji_ansamblu import serie, DOJI, CONF, DOJI_L, ENTRY, SL, TP1, R # noqa: E402 OUT = Path(__file__).with_suffix(".svg") S = 6 # prima bară arătată din serie OCHI = 49.70 # stopul „la ochi", sub candela de confirmare GREEN, RED, ORANGE, BLUE, GREY = "#188a3e", "#c00", "#b06000", "#1a5fb4", "#555" W, VGAP = 720, 14 base = serie()[S:CONF + 1] # până la confirmare, inclusiv after = [Candle(*b) for b in [ (50.40, 50.50, 49.85, 49.95), # +1: corecție (49.95, 50.05, 49.50, 49.60), # +2: al doilea picior, low 49.50 — mătură 49.70 (49.60, 50.35, 49.55, 50.25), (50.25, 50.90, 50.15, 50.80), (50.80, 51.35, 50.70, 51.25), (51.25, 51.80, 51.15, 51.70), # +6: atinge ținta 51.60 ]] jos = base + after d, cf = DOJI - S, CONF - S # indici locali: doji, confirmare # --- aserțiuni ----------------------------------------------------------------- for i, c in enumerate(jos): assert c.l <= min(c.o, c.c) and c.h >= max(c.o, c.c), f"bara {i}: h/l nu cuprind o/c" if i: assert abs(c.o - jos[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]" # 1. cel mai jos low e al doji-ului, și cu 4 și cu 5 candele numărate assert min(c.l for c in jos[cf - 3:cf + 1]) == min(c.l for c in jos[cf - 4:cf + 1]) == DOJI_L assert jos[d].l == DOJI_L and round(DOJI_L - 0.05, 2) == SL # 2. al doilea picior: mătură stopul la ochi, nu-l atinge pe cel din regulă dip = [c.l for c in after] assert min(dip) == 49.50 and SL < 49.50 < OCHI, dip assert next(k for k, c in enumerate(after) if c.l <= OCHI) == 1, "stopul la ochi nu e lovit de al doilea picior" assert all(c.l > SL for c in after), "stopul din regulă ar fi fost lovit" # 3. al doilea picior e higher low față de low-ul doji-ului assert min(dip) > DOJI_L # 4. ținta e atinsă abia după corecție, la a 6-a bară de după intrare assert next(k for k, c in enumerate(after, 1) if c.h >= TP1) == 6 # 5. stopul la ochi e mai aproape de intrare decât cel din regulă, și peste low-ul doji-ului assert round(ENTRY - OCHI, 2) == 0.70 < R and DOJI_L < OCHI < jos[cf].l # sub candela de confirmare, peste low-ul doji-ului assert round(ENTRY - SL, 2) == R print(f"stop: low4 {DOJI_L:.2f} -> SL {SL:.2f}; la ochi {OCHI:.2f} lovit de low {min(dip):.2f}; " f"țintă {TP1:.2f} la a 6-a bară") common: dict = dict(scale=36.0, gap=20, body_w=10, left=48, width=W) # --- panoul de sus: numărătoarea celor 4 candele -------------------------------- sus = Chart( candles=base, levels=[Level(SL, "stopul: 49.20", RED, "4 3", "left")], zones=[Zone(cf - 3, cf, "numeri 4 candele, începând cu confirmarea", BLUE)], arrows=[ Arrow(d, "cel mai jos low din cele 4: 49.25\n(e al doji-ului)", where="below", price=DOJI_L, color=GREY, dx=250, dy=34), Arrow(cf - 1, "stopul: 49.20, cu 5 cenți mai jos\nsub el, ideea că vânzătorii\nau obosit e moartă", where="below", price=SL, color=RED, dx=250, dy=76), Arrow(cf, "confirmarea: close 50.40\nde aici numeri înapoi", where="above", color=GREEN, dx=250), ], title="Pasul 5 — stopul: numeri 4 candele înapoi de la confirmare", **common, ) # --- panoul de jos: stop la ochi vs stop din regulă ----------------------------- gata = Chart( candles=jos, levels=[Level(TP1, "ținta 1: 51.60", GREEN, "2 2", "left"), Level(ENTRY, "intrarea: 50.40", GREY, "4 3", "left"), Level(OCHI, "stop pus la ochi: 49.70", ORANGE, "4 3", "left"), Level(SL, "stopul din regulă: 49.20", RED, "4 3", "left")], # eticheta zonei o punem manual: Zone o ridică deasupra candelelor din # dreptul textului, adică departe de banda pe care o marchează zones=[Zone(cf + 1, cf + 2, "", RED, p_top=OCHI, p_bot=SL)], arrows=[ Arrow(cf + 2, "stopul pus la ochi (49.70) e măturat\nera prea aproape de intrare", where="below", price=OCHI, color=ORANGE, dx=230, dy=6), Arrow(cf + 2, "al doilea picior al fundului: 49.50\nfundurile fac des asta", where="below", price=49.50, color=BLUE, dx=230, dy=56), Arrow(cf + 1, "stopul din regulă (49.20) ține\nal doilea picior rămâne peste el", where="below", price=SL, color=RED, dx=230, dy=106), Arrow(len(jos) - 1, "prețul ajunge la țintă\ndar numai cu stopul corect\nmai erai în trade", where="above", price=TP1, color=GREEN, dx=150), ], title="Același trade: stop „la ochi” (sub candela de confirmare) vs stopul din regulă", caption="Barele de după intrare și stopul la ochi sunt construite, pentru contrast.", **common, ) def panou(ch, sus_de=0.0): ch.top += sus_de svg = ch.render() h = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1)) # render() mută `top` intern și îl restaurează; offsetul se citește din # prima linie de Level, altfel tot ce desenăm pe urmă iese mai sus cu ~22px off = float(re.search(r'") + 1:svg.rindex("")].strip(), h, off body_sus, h_sus, off_sus = panou(sus) # numerele 4 3 2 1 sub barele numărate, plus (5) — arată că iese la fel yb = sus._y(SL) + off_sus + 22 nums = [] for k, n in zip(range(cf - 3, cf + 1), "4321"): nums.append(f'{n}') nums.append(f'(5)') nums.append(f'' f'cu 4 sau cu 5 candele iese la fel: în scădere, low-ul doji-ului e oricum cel mai jos') h_sus = max(h_sus, yb + 32) body_jos, h_jos, off_jos = panou(gata, h_sus + VGAP) zx = gata._cx(cf + 3) + 10 banda = (f'pe aici trece al doilea picior') H = h_jos + 8 svg = "\n ".join([ f'', body_sus, *nums, f'', body_jos, banda, ]) + "\n" assert "\n\n" not in svg and "