"""Buy Doji — diagrama de ansamblu (daily, exemplu construit, fără ticker). Citește specificația `gen/strategii/buy_doji_ansamblu.spec.txt`. Seria de aici e SURSA UNICĂ a trade-ului din capitol: buy_doji_stop, buy_doji_transe și buy_doji_iesire importă `serie()` și pornesc din aceleași bare (doji 50.10/49.25, confirmare 50.40, SL 49.20, TP1 51.60), ca începătorul să urmărească un singur trade prin toată procedura. Barele 0-19: close-uri, open = close-ul precedentului. Barele 20-28 sunt date explicit: 21 și 27 deschid cu GAP (low peste high-ul precedentei), fiindcă regula de trail din curs se sprijină exact pe barele astea. """ import sys; sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Candle, Chart, Level, MA, Zone, Arrow, from_closes OUT = "/workspace/atm-curs-dl/gen/strategii/buy_doji_ansamblu.svg" DOJI, WAIT, CONF = 14, 15, 16 # indicii barelor-cheie DOJI_H, DOJI_L = 50.10, 49.25 ENTRY, SL, TP1 = 50.40, 49.20, 51.60 R = 1.20 def serie() -> list[Candle]: """Cele 32 de bare ale trade-ului, cu verificările de bază.""" closes = [ 60.20, 59.20, 59.60, 58.40, 57.30, 57.70, 56.50, 55.40, 55.80, 54.70, # 0-9 53.50, 52.30, 51.00, 49.65, # 10-13 downtrend 49.60, # 14 doji 49.90, # 15 +1 (nu confirmă) 50.40, # 16 confirmare => intrare 50.90, 51.20, 51.50, # 17-19 (19 atinge TP1) ] wicks = [(0.25, 0.30)] * len(closes) wicks[14] = (0.45, 0.35) # doji: high 50.10, low 49.25 wicks[15] = (0.15, 0.15) # high 50.05, low 49.45 wicks[16] = (0.15, 0.15) # high 50.55, low 49.75 cds = from_closes(closes, wicks=wicks, start=60.20) cds += [Candle(*b) for b in [ (51.50, 51.85, 51.40, 51.75), # 20 (52.00, 52.30, 51.95, 52.20), # 21 GAP: low 51.95 > high[20] 51.85 → trail 51.95 (52.20, 52.45, 52.05, 52.35), # 22 (52.35, 52.40, 52.00, 52.10), # 23 retragere, close peste trail: rămâi (52.10, 52.50, 52.05, 52.40), # 24 (52.40, 52.55, 52.25, 52.50), # 25 (52.50, 52.55, 52.35, 52.45), # 26 (52.70, 53.10, 52.60, 52.95), # 27 GAP: low 52.60 > high[26] 52.55 → trail 52.60 (52.95, 53.00, 52.20, 52.30), # 28 close 52.30 < 52.60: ieși ]] cds += from_closes([52.00, 51.80, 51.90], wick=0.25, start=52.30) # 29-31 assert len(cds) == 32 for i, c in enumerate(cds): 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 and i not in (21, 27): assert abs(c.o - cds[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]" d = cds[DOJI] assert (round(d.h, 2), round(d.l, 2)) == (DOJI_H, DOJI_L), (d.h, d.l) assert abs(d.o - d.c) <= 0.10 * (d.h - d.l), "bara 14 nu e doji" assert cds[WAIT].c <= DOJI_H < cds[CONF].c, "fereastra: 15 nu confirmă, 16 da" assert abs(cds[CONF].c - ENTRY) < 1e-9 assert abs(min(c.l for c in cds[CONF - 3:CONF + 1]) - DOJI_L) < 1e-9, "low-ul doji nu e cel mai jos din 4" assert round(DOJI_L - 0.05, 2) == SL and round(ENTRY - SL, 2) == R and round(ENTRY + R, 2) == TP1 return cds def trail(cds, start=CONF + 1): """Regula de trail: bara cu low > high-ul precedentei urcă linia la low-ul ei; ieși la primul close sub linie. Întoarce (treptele, bara de ieșire).""" t, steps = None, [] for i in range(start, len(cds)): if t is not None and cds[i].c < t: return steps, i if cds[i].l > cds[i - 1].h: t = cds[i].l steps.append((i, round(t, 2))) return steps, None if __name__ == "__main__": cds = serie() vols = [42, 44, 40, 46, 43, 41, 45, 42, 44, 47, 43, 45, 48, 52, 78, 46, 88, 55, 50, 48, 52, 47, 45, 49, 46, 44, 48, 51, 54, 47, 45, 46] for c, v in zip(cds, vols): c.vol = v media_vol = sum(vols) / len(vols) assert vols[DOJI] > media_vol and vols[CONF] > media_vol, "volum peste medie pe doji/confirmare" # --- aserțiunile specificației --------------------------------------------- primul_tp = next(i for i in range(CONF + 1, len(cds)) if cds[i].h >= TP1) assert primul_tp == 19, f"TP1 51.60 atins întâi la bara {primul_tp}, nu 19" steps, iesire = trail(cds) assert steps == [(21, 51.95), (27, 52.60)], steps assert iesire == 28 and abs(cds[28].c - 52.30) < 1e-9, iesire assert steps[-1][1] > ENTRY, "trail sub intrare, ieșirea n-ar fi pe profit" R_IES = round((cds[28].c - ENTRY) / R, 2) assert R_IES == 1.58, R_IES assert cds[13].c < cds[0].c - 10, "downtrend prea mic" print(f"doji {DOJI_H:.2f}/{DOJI_L:.2f} intrare {ENTRY:.2f} SL {SL:.2f} TP1 {TP1:.2f} " f"trail {steps} ieșire bara 28 @ {cds[28].c:.2f} = +{R_IES}R") cds[WAIT].color = "#777" # bara de așteptare: neutră, nu confirmă GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#666" ch = Chart( candles=cds, levels=[ Level(TP1, "ținta 1: 51.60", BLUE, label_at="left"), Level(ENTRY, "intrare 50.40", GREEN, label_at="left"), Level(SL, "stop 49.20", RED, label_at="left"), ], zones=[ Zone(4, 13, "1. scăderea: vânzătorii obosesc", RED), Zone(14, 15, "2. doji: pauză, încă nu intri", ORANGE, p_top=53.9), Zone(16, 16, "3. confirmarea", GREEN, p_top=52.9), Zone(17, 28, "4. ieșirea: o treime la țintă, restul pe trail", BLUE, p_top=54.9), ], arrows=[ Arrow(DOJI, "stopul 49.20, 5 cenți sub doji\nsub el, ideea că vânzătorii\nau obosit e moartă", where="below", price=SL, color=RED, dx=-150, dy=0), Arrow(CONF, "AICI INTRI, la close 50.40\nabia acum știi că\ncumpărătorii au câștigat", where="below", price=ENTRY, color=GREEN, dx=-10, dy=64), Arrow(19, "ținta 1: 51.60 = riscul (1.20)\nîncă o dată: vinzi o treime", where="below", price=TP1, color=BLUE, dx=95, dy=74), Arrow(27, "trailing stop: urcă după preț\nla fiecare gap, nu coboară", where="above", color=ORANGE, dx=40), Arrow(28, "ieși la 52.30 (+1.58R)\nprima închidere sub trail", where="right", color=RED, dx=40, dy=-48), ], mas=[MA(20, "#999", dash="4 3", text="MA(20)")], scale=40.0, gap=15, body_w=9, left=44, width=640, vol_h=60, vol_label="volum", title="Buy Doji — un trade de la cap la coadă (daily, exemplu construit)", ) svg = ch.render() # render() mută `top` intern (titlu, săgeți) și îl restaurează: ch._y() de aici # e mai sus cu atâta. Offsetul se citește din prima linie de Level desenată. import re as _re OFF = (float(_re.search(r'') # parantezele 1R: riscul jos, ținta sus, aceeași înălțime = „1:1" xp = ch._cx(5) br = [] for lo, hi, txt, col in ((SL, ENTRY, "1R = 1.20 (riscul)", RED), (ENTRY, TP1, "1R = 1.20 (ținta)", BLUE)): y1, y2 = Y(hi) + 2, Y(lo) - 2 br.append(f'') br.append(f'{txt}') body = svg.rsplit("", 1)[0].rstrip() svg = "\n ".join([body, step, *br]) + "\n" assert "