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
247 lines
14 KiB
Python
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)
|