Files
atm-curs-dl/gen/az_buy_doji_mici.py
Claude Agent 97d0613392 Primul document de strategie din Analiza Zilnica: buy doji + MICI
Sase diagrame generate cu svg_candles.py, cu assert pe fiecare nivel din
text; audit curat; screenshot-uri citite si reparate de agent, verificate
prin sondaj de mine (R-urile 3.6 si 2.0 se confirma pe prețurile desenate).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
2026-09-13 21:50:21 +00:00

247 lines
14 KiB
Python

"""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 <!--SVG:nume--> ... <!--/SVG--> 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"<!--SVG:{name}-->.*?<!--/SVG-->", re.S)
assert pat.search(md), f"marcatorul {name} lipsește din .md"
md = pat.sub(lambda _m: f"<!--SVG:{name}-->\n{svg}\n<!--/SVG-->", md)
MD.write_text(md, encoding="utf-8")
print("scris", MD)