"""Diagramele pentru summaries/08_ANALIZA_ZILNICA/01_buy_doji_si_strategia_mici.md. Toate prețurile sunt construite (fără ticker). Rulează: .venv/bin/python gen/az_buy_doji_mici.py Înlocuiește în .md fiecare bloc ... cu output-ul generatorului. Ca să schimbi o cifră, o schimbi aici și rulezi din nou. """ import re import sys from pathlib import Path sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402 MD = Path("/workspace/atm-curs-dl/summaries/08_ANALIZA_ZILNICA/01_buy_doji_si_strategia_mici.md") GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000" STYLE = dict(gap=15, body_w=9, left=44, width=620) svgs = {} def px_ratio(ch, a, b, c): """raportul (c-b)/(b-a) măsurat în pixeli, după render""" return (ch._y(b) - ch._y(c)) / (ch._y(a) - ch._y(b)) # ---------------------------------------------------------------- 1. buy doji DA down = [60.2, 59.0, 59.4, 58.1, 57.0, 57.5, 56.2, 55.1, 55.6, 54.3, 53.2, 52.4] closes = down + [52.3, # 12 doji 52.6, # 13 în fereastră, nu închide peste high 53.7, # 14 buy doji: close > high doji 54.3, 54.0, 55.0, 56.1, 55.6, 56.4, 57.2, 56.8, 57.6, 58.3, 57.9, 58.6, 59.0] wicks = [(0.3, 0.4)] * 12 + [(0.6, 0.7), (0.3, 0.5), (0.2, 0.3)] + [(0.3, 0.3)] * 13 cds = from_closes(closes, wicks=wicks, start=61.0) D, T = 12, 14 doji_hi, doji_lo = cds[D].h, cds[D].l assert abs(cds[D].o - cds[D].c) <= 0.15 and doji_hi - doji_lo >= 1.2, "doji-ul nu arată a doji" assert cds[D - 10].c > cds[D - 1].c + 5, "trendul anterior nu e descendent" assert cds[13].c <= doji_hi and cds[T].c > doji_hi, "trigger-ul nu e prima închidere peste high-ul doji" entry = cds[T].c sl = round(min(c.l for c in cds[T - 3:T + 1]) - 0.05, 2) risk = entry - sl tp1 = round(entry + risk, 2) hit = next(i for i in range(T + 1, len(cds)) if cds[i].h >= tp1) assert hit - T == 4, "textul spune: TP1 atins la a 4-a candelă după intrare" print(f"D1 doji hi {doji_hi:.2f} lo {doji_lo:.2f} entry {entry:.2f} SL {sl:.2f} risc {risk:.2f} TP1 {tp1:.2f} atins la {hit}") ch = Chart(candles=cds, levels=[Level(sl, f"SL {sl:.2f} (low 4 candele)", RED), Level(entry, f"intrare {entry:.2f}", GREEN, label_at="left"), Level(tp1, f"TP1 {tp1:.2f} = 1R", BLUE)], marks=[Mark(8, "downtrend", GRAY, "above", dy=-40), Mark(D, "doji", "#333", "below"), Mark(T, "buy doji", GREEN, "above", dy=-4), Mark(hit, "TP1 atins", BLUE, "above")], scale=22, title="Exemplu construit (fără ticker) — daily", caption=f"Trigger = prima închidere peste high-ul doji-ului ({doji_hi:.2f}), în max. 3 candele.", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, sl, entry, tp1) - 1.0) < 0.01 svgs["buy_doji_da"] = svg_ # ---------------------------------------------------------------- 2. buy doji NU closes = down + [52.3, # 12 doji 52.7, 52.1, 52.5, # 13-15: fereastra de 3 candele, nicio închidere peste high 51.2, 50.4, 50.9, 49.8, 49.0, 49.5, 48.6, 48.1, 48.7, 47.9] wicks = [(0.3, 0.4)] * 12 + [(0.6, 0.7), (0.25, 0.3), (0.3, 0.3), (0.3, 0.3)] + [(0.3, 0.3)] * 10 cds = from_closes(closes, wicks=wicks, start=61.0) doji_hi, doji_lo = cds[D].h, cds[D].l assert all(c.c <= doji_hi for c in cds[D + 1:D + 4]), "o candelă din fereastră a închis peste high" brk = next(i for i in range(D + 1, len(cds)) if cds[i].c < doji_lo) assert brk == D + 4, brk print(f"D2 doji hi {doji_hi:.2f} lo {doji_lo:.2f} spart la {brk} close {cds[brk].c:.2f}") ch = Chart(candles=cds, levels=[Level(doji_hi, f"high doji {doji_hi:.2f}", GREEN), Level(doji_lo, f"low doji {doji_lo:.2f}", RED, label_at="left")], marks=[Mark(8, "downtrend", GRAY, "above", dy=-40), Mark(D, "doji", "#333", "below", dy=4), Mark(D + 5, "3 candele, niciuna peste high", ORANGE, "above", dy=-60)], scale=22, title="Exemplu construit (fără ticker) — daily", caption=f"A 4-a candelă după doji închide la {cds[brk].c:.2f}, sub low: setup mort, nu intri.", **STYLE) svgs["buy_doji_nu"] = ch.render() # ---------------------------------------------------------------- 3. sell doji (short) up = [40.8, 42.0, 41.6, 42.9, 44.0, 43.5, 44.8, 45.9, 45.4, 46.7, 47.8, 48.6] closes = up + [48.7, # 12 doji 48.4, # 13 în fereastră, nu închide sub low 47.3, # 14 sell doji: close < low doji 46.7, 47.0, 46.0, 44.9, 45.4, 44.6, 43.8, 44.2, 43.4, 42.7, 43.1, 42.4, 42.0] wicks = [(0.4, 0.3)] * 12 + [(0.7, 0.6), (0.5, 0.3), (0.3, 0.2)] + [(0.3, 0.3)] * 13 cds = from_closes(closes, wicks=wicks, start=40.0) doji_hi, doji_lo = cds[D].h, cds[D].l assert abs(cds[D].o - cds[D].c) <= 0.15 assert cds[13].c >= doji_lo and cds[T].c < doji_lo, "trigger-ul short nu e prima închidere sub low" entry = cds[T].c sl = round(max(c.h for c in cds[T - 3:T + 1]) + 0.05, 2) risk = sl - entry tp1 = round(entry - risk, 2) hit = next(i for i in range(T + 1, len(cds)) if cds[i].l <= tp1) assert hit - T <= 8 print(f"D3 doji hi {doji_hi:.2f} lo {doji_lo:.2f} entry {entry:.2f} SL {sl:.2f} risc {risk:.2f} TP1 {tp1:.2f} atins la {hit}") ch = Chart(candles=cds, levels=[Level(sl, f"SL {sl:.2f} (high 4 candele)", RED), Level(entry, f"intrare short {entry:.2f}", GREEN, label_at="left"), Level(tp1, f"TP1 {tp1:.2f} = 1R", BLUE)], marks=[Mark(5, "uptrend", GRAY, "below"), Mark(D, "doji", "#333", "below", dy=4), Mark(T, "sell doji", RED, "below", dy=4), Mark(hit, "TP1 atins", BLUE, "below")], scale=22, title="Exemplu construit (fără ticker) — daily", caption=f"Oglinda: trigger = prima închidere sub low-ul doji-ului ({doji_lo:.2f}).", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, tp1, entry, sl) - 1.0) < 0.01 svgs["sell_doji"] = svg_ # ---------------------------------------------------------------- 4. MICI complet (weekly) PIVOT = 46.50 mici = [57.6, 56.4, 55.1, 53.8, # 0-3 downtrend 55.6, # 4 bounce: lower high (vârful anterior) 53.8, 52.0, 50.3, 48.6, 46.3, # 5-9 downtrend continuă sub pivot 45.2, 44.6, # 10-11 picior 1 45.8, 46.2, # 12-13 revenire sub pivot 45.1, 44.3, # 14-15 picior 2 (mai jos) 44.4, # 16 doji 47.6, # 17 buy doji weekly, close peste pivot 49.1, 50.4, 51.2, # 18-20 raliu 50.1, 49.2, 48.9, # 21-23 pullback: higher low deasupra pivotului 49.8, # 24 candela din dreapta: confirmă swing low-ul, intrare 50.6, 51.8, 52.6, 52.1, 53.2, 54.1, 53.6, 54.8, 55.7] # 25-33, 33 atinge ținta mw = [(0.4, 0.4)] * 4 + [(0.3, 0.3), (0.2, 0.4)] + [(0.4, 0.4)] * 4 + [(0.4, 0.5), (0.4, 0.7), (0.4, 0.4), (0.4, 0.4), (0.4, 0.4), (0.3, 0.9), (0.6, 0.7), (0.3, 0.3), (0.3, 0.3), (0.3, 0.3), (0.5, 0.3), (0.3, 0.3), (0.3, 0.4), (0.3, 0.6), (0.2, 0.3)] + [(0.3, 0.3)] * 9 mv = [40, 42, 44, 41, 30, 45, 47, 50, 52, 55, 50, 58, 38, 36, 44, 60, 52, 88, 70, 64, 60, 38, 34, 30, 55, 50, 58, 54, 40, 56, 52, 42, 55, 60] cds = from_closes(mici, wicks=mw, vols=mv, start=58.8) DJ, BD, HL, CF = 16, 17, 23, 24 leg1, leg2 = min(c.l for c in cds[10:13]), min(c.l for c in cds[14:17]) prior_high = cds[4].h assert prior_high > cds[3].h and prior_high > cds[5].h, "bara 4 nu e swing high" swing1 = max(c.h for c in cds[18:22]) hl = min(c.l for c in cds[21:24]) assert leg2 < leg1, "al doilea picior al double bottom-ului trebuie să fie mai jos" assert abs(cds[DJ].o - cds[DJ].c) <= 0.15, "bara 16 nu e doji" assert cds[BD].c > cds[DJ].h and cds[BD].c > PIVOT, "buy doji-ul nu închide peste high-ul doji și peste pivot" assert all(c.c < PIVOT for c in cds[9:17]), "înainte de buy doji prețul trebuie să fie sub pivot" assert hl == cds[HL].l and hl > PIVOT and hl > leg2, "higher low-ul nu e deasupra pivotului" assert cds[CF].c > cds[HL].h and cds[CF].l > hl, "candela din dreapta nu confirmă swing low-ul" assert sum(mv[BD:BD + 3]) / 3 > sum(mv[21:24]) / 3 * 1.8, "volumul pe raliu nu e clar peste cel de pe pullback" entry = cds[CF].c sl = round(hl - 0.2, 2) risk = entry - sl tp = round(prior_high, 2) R = (tp - entry) / risk hit = next(i for i in range(CF + 1, len(cds)) if cds[i].h >= tp) assert R >= 2.5, R assert hit - CF == 9 and hit == len(cds) - 1, "textul spune: ținta atinsă după 9 candele; seria se oprește acolo" assert mv[BD] == max(mv), "textul spune: buy doji-ul are cel mai mare volum din serie" print(f"D4 leg1 {leg1:.2f} leg2 {leg2:.2f} buydoji close {cds[BD].c:.2f} swing1 {swing1:.2f} HL {hl:.2f} " f"entry {entry:.2f} SL {sl:.2f} risc {risk:.2f} TP {tp:.2f} = {R:.2f}R atins la {hit}") MICI_ENTRY, MICI_SL, MICI_TP, MICI_R, MICI_SWING1 = entry, sl, tp, R, swing1 ch = Chart(candles=cds, levels=[Level(PIVOT, f"pivot monthly {PIVOT:.2f}", ORANGE, label_at="left"), Level(sl, f"SL {sl:.2f}", RED), Level(entry, f"intrare {entry:.2f}", GREEN), Level(tp, f"TP {tp:.2f} = {R:.1f}R", BLUE)], marks=[Mark(4, "vârf anterior", BLUE, "above", dy=-30), Mark(11, "picior 1", RED, "below"), Mark(15, "picior 2", RED, "below"), Mark(BD, "buy doji W", GREEN, "above", dy=-50), Mark(HL, "higher low", GREEN, "below", dy=2)], scale=18, vol_h=70, title="Exemplu construit (fără ticker) — weekly", caption="Intrarea vine la close-ul candelei care confirmă higher low-ul, nu la buy doji.", **{**STYLE, "width": 700}) svg_ = ch.render() assert abs(px_ratio(ch, sl, entry, tp) - R) < 0.01 svgs["mici_complet"] = svg_ # ---------------------------------------------------------------- 5. reintrare după primul target re_cl = mici[18:] + [ # continuă aceeași serie (bara 0 aici = bara 18 din D4) 55.2, 54.1, 53.3, 52.6, # pullback după TP 52.5, # doji în locul crimei 53.4, # buy doji daily/weekly: confirmă higher low-ul 54.5, 55.6, 56.4, 55.9, 57.1, 57.9, 57.4, 58.9] re_w = mw[18:] + [(0.3, 0.3), (0.3, 0.3), (0.3, 0.4), (0.3, 0.6), (0.5, 0.8), (0.2, 0.3)] + [(0.3, 0.3)] * 8 cds = from_closes(re_cl, wicks=re_w, start=mici[17]) off = 18 crime = MICI_SWING1 # fostul vârf al raliului, spart pe drum spre TP k_doji, k_bd = len(mici) - off + 4, len(mici) - off + 5 low2 = min(c.l for c in cds[k_doji - 2:k_doji + 1]) assert abs(cds[k_doji].o - cds[k_doji].c) <= 0.15 assert abs(low2 - crime) <= 0.05, "codița retestului nu atinge fostul vârf (locul crimei)" assert low2 > hl, "a doua intrare nu mai e higher low" assert cds[k_bd].c > cds[k_doji].h entry2 = cds[k_bd].c sl2 = round(crime - 0.7, 2) risk2 = entry2 - sl2 tp2 = 58.30 R2 = (tp2 - entry2) / risk2 hit2 = next(i for i in range(k_bd + 1, len(cds)) if cds[i].h >= tp2) assert R2 >= 2, R2 print(f"D5 locul crimei {crime:.2f} low pullback {low2:.2f} entry2 {entry2:.2f} SL {sl2:.2f} " f"risc {risk2:.2f} TP2 {tp2:.2f} = {R2:.2f}R atins la {hit2}") ch = Chart(candles=cds, levels=[Level(crime, f"locul crimei {crime:.2f}", ORANGE, label_at="left"), Level(sl2, f"SL {sl2:.2f}", RED), Level(entry2, f"intrare 2: {entry2:.2f}", GREEN), Level(tp2, f"TP2 {tp2:.2f} = {R2:.1f}R", BLUE, label_at="left")], marks=[Mark(2, "fost vârf", ORANGE, "above", dy=-16), Mark(len(mici) - off - 1, "TP1 atins", BLUE, "above", dy=-2), Mark(k_doji, "retest + buy doji", ORANGE, "below", dy=16)], scale=26, title="Exemplu construit (fără ticker) — weekly, continuarea", caption="Căderea după TP1 e doar retestul fostului vârf; higher low + nou trigger = a doua intrare.", **STYLE) svg_ = ch.render() assert abs(px_ratio(ch, sl2, entry2, tp2) - R2) < 0.01 svgs["mici_reintrare"] = svg_ # ---------------------------------------------------------------- 6. MICI NU nu = mici[:21] + [50.1, 49.2, # 21-22 pullback ca în D4 48.4, # 23 "arată a higher low", candela din dreapta nu e închisă 46.1, # 24 candela din dreapta închide sub pivot 46.4, 45.8, 45.2, 45.9, 45.0, 44.6, 45.3, 44.8] nw = mw[:21] + [(0.3, 0.3), (0.3, 0.4), (0.3, 0.6), (0.3, 0.4)] + [(0.3, 0.3)] * 8 nv = mv[:21] + [40, 44, 52, 66, 48, 58, 54, 40, 56, 50, 38, 52] cds = from_closes(nu, wicks=nw, vols=nv, start=58.8) assert cds[23].l > PIVOT, "bara 23 trebuie să pară încă higher low deasupra pivotului" assert cds[24].c < PIVOT and cds[24].l < cds[23].l, "candela din dreapta nu anulează higher low-ul" assert sum(nv[21:25]) / 4 > sum(nv[18:21]) / 3 * 0.75, "pe NU volumul vânzătorilor nu scade" early = cds[23].c assert round(cds[23].l, 2) == 47.80 and round(early, 2) == 48.40 and round(cds[24].c, 2) == 46.10 print(f"D6 bara 23 low {cds[23].l:.2f} close {early:.2f}; bara 24 close {cds[24].c:.2f} sub pivot {PIVOT}") ch = Chart(candles=cds, levels=[Level(PIVOT, f"pivot monthly {PIVOT:.2f}", ORANGE, label_at="left"), Level(cds[23].l, f"HL? {cds[23].l:.2f} (neconfirmat)", GRAY)], marks=[Mark(BD, "buy doji W", GREEN, "above", dy=-50), Mark(24, "sub pivot", RED, "below", dy=4), Mark(15, "picior 2", RED, "below")], scale=18, vol_h=70, title="Exemplu construit (fără ticker) — weekly", caption="Candela din dreapta a închis sub pivot: nu există higher low, nu există MICI.", **STYLE) svgs["mici_nu"] = ch.render() # ---------------------------------------------------------------- substituție în .md if MD.exists(): md = MD.read_text(encoding="utf-8") for name, svg in svgs.items(): pat = re.compile(rf".*?", re.S) assert pat.search(md), f"marcatorul {name} lipsește din .md" md = pat.sub(lambda _m: f"\n{svg}\n", md) MD.write_text(md, encoding="utf-8") print("scris", MD)