Files
atm-curs-dl/gen/strategii/buy_doji_sell.py
Claude Agent a92a12a65c buy_doji: reparatiile de diagrame din verificare (11 puncte)
doji_forma nu mai foloseste 49.20 ca low al dojiului — era fix pretul
stopului, in prima diagrama pe care o vede un incepator. Bara e pe
50.10/49.25, cu procentele recalculate.

LABU: stopul e etichetat "pe cel mai jos dintre cele trei minime", nu
"sub minime"; zonele arata doua teste ale pivotului, cu "revenire peste
pivot 88.90" intre ele; numarul barei a iesit din desen, nota spune
"a 6-a bara dupa intrare", derivat din serie.

perechi_tf nu mai pune pretul TF-ului mare pe panoul TF-ului mic. aplus
nu mai numeste un nivel care nu e desenat. directie nu mai zice
"saptamanile" pe un panou daily. sell acopera toata cresterea, 14 bare.

transe: cele trei sageti nu mai converg intr-un ghem — fiecare are linia
ei cu pretul la capat; linia de intrare e desenata, ca parantezele de
risc sa plece de undeva. locatie arata 49.25, cifra din care iese 0,51 %.
stop si iesire: linii si sageti care taiau texte, mutate.

Jumatate din probleme erau aceeasi greseala: un pret scris de mana intr-o
eticheta, ramas in urma cand se schimba seria. Sunt acum aserttiuni care
o fac imposibila; PROGRESS.md spune care merita generalizata.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-19 21:33:15 +00:00

152 lines
8.3 KiB
Python

"""Sell Doji — oglinda short a ansamblului Buy Doji (daily, exemplu construit).
Citește specificația `gen/strategii/buy_doji_sell.spec.txt`. Seria de 31 de
candele e construită din lista de close-uri dată, cu open-ul fiecărei bare =
close-ul precedentului; wick-urile sunt fixate ca nivelurile (doji 99.20 /
100.10, intrare short 99.00, SL 100.15 = 5 cenți peste high-ul de 4 candele,
TP 97.85 = 1R de 1.15, trail 96.45, ieșire la 96.60)
să iasă exact din serie, nu doar din etichetă.
"""
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Arrow, Chart, Level, Zone, from_closes
# --- seria: 31 de close-uri, exact ca în specificație -----------------------
closes = [
93.00, 94.10, 93.70, 95.00, 96.10, 95.80, 97.00, 98.10, 97.80, 98.90, # 0-9
99.80, 100.20, 99.90, 99.65, # 10-13 uptrend
99.60, # 14 doji
99.40, # 15 +1 (nu confirmă)
99.00, # 16 +2 confirmare => intrare short
98.50, 98.30, 97.80, 97.40, 97.00, 96.70, 96.40, 96.10, 95.80, # 17-25
96.20, 96.60, 96.40, 96.10, 96.30, # 26-30
]
assert len(closes) == 31
# wick-uri: (up, down) implicite (0.30, 0.25) + excepțiile din specificație
wicks = [(0.30, 0.25)] * 31
# ajustat: cu wu=0.30, high[13] = 99.90+0.30 = 100.20 > SL; îl coborâm la
# 100.10 ca max(high[13:17]) == 100.10 (high-ul ultimelor 4 candele = SL)
wicks[13] = (0.20, 0.25)
wicks[14] = (0.45, 0.40) # doji: high 100.10, low 99.20
wicks[15] = (0.20, 0.15) # high 99.80, low 99.25 (peste 99.20 => nu confirmă)
wicks[16] = (0.20, 0.15) # high 99.60, low 98.85
wicks[25] = (0.35, 0.20) # high 96.45 (= trail), low 95.60 (minimul corecției)
# ajustat: cu wd=0.25, low[26] = 95.80-0.25 = 95.55 < 95.60, adică bara 26 ar
# fi minimul, nu bara 25; wd=0.10 păstrează bara 25 ca minim (low 95.60)
wicks[26] = (0.30, 0.10)
# volum relativ ales de generator (materialul nu dă volum pe exemplu);
# peste medie pe doji (14) și pe confirmare (16) — distribuție — apoi normal
vols = [44, 42, 46, 40, 45, 48, 43, 47, 50, 45,
52, 58, 49, 55, 90, 62, 84,
56, 52, 50, 48, 46, 44, 42, 40, 45,
50, 46, 44, 43, 45]
assert len(vols) == 31
media_vol = sum(vols) / len(vols)
assert vols[14] > media_vol and vols[16] > media_vol, "volum peste medie pe doji/confirmare"
cds = from_closes(closes, wicks=wicks, vols=vols, start=closes[0])
DOJI_I = 14 # indexul dojiului; barele 0..DOJI_I-1 sunt creșterea
doji, wait, conf = cds[DOJI_I], cds[15], cds[16]
# stopul 5 cenți DEASUPRA high-ului, oglinda pasului 5 (51.60 -> 51.55 pe long);
# cifrele vechi (SL 100.10, TP 97.90) puneau stopul exact pe high
entry, sl, tp = 99.00, 100.15, 97.85
risc = round(sl - entry, 2)
# --- cele 9 assert-uri cerute de specificație ------------------------------
# 1. SL-ul e 5 cenți peste high-ul ultimelor 4 candele (100.10)
assert abs(max(cd.h for cd in cds[13:17]) - 100.10) < 1e-9, "max high[13:17] != 100.10"
assert abs(sl - 100.10 - 0.05) < 1e-9, "SL nu e 5 cenți peste high"
# 2. intrarea e close-ul barei 16
assert abs(conf.c - entry) < 1e-9, f"close[16]={conf.c} != 99.00"
# 3. confirmarea închide sub low-ul dojiului
assert abs(doji.l - 99.20) < 1e-9 and conf.c < doji.l, "close[16] nu e sub low[14]"
# 4. bara +1 NU confirmă
assert wait.c >= doji.l, "bara 15 ar confirma, fereastra n-ar avea sens"
# 5. 1:1 exact
assert abs(abs(sl - entry) - abs(entry - tp)) < 1e-9 and abs((entry - tp) - 1.15) < 1e-9, \
"risc != recompensă (nu e 1:1 exact)"
# 6. primul low <= TP după intrare e bara 19
primul_tp = next(i for i in range(17, len(cds)) if cds[i].l <= tp)
assert primul_tp == 19, f"TP 97.85 atins întâi la bara {primul_tp}, nu 19"
assert abs(cds[18].l - 98.05) < 1e-9 and abs(cds[19].l - 97.55) < 1e-9, "low-urile 18/19 nu ies"
# 7. trailing = high[25] = 96.45; primul close peste el e bara 27; ieșirea e pe profit
trail = cds[25].h
assert abs(trail - 96.45) < 1e-9, f"high[25]={trail} != 96.45"
primul_exit = next(i for i in range(26, len(cds)) if cds[i].c > trail)
assert primul_exit == 27 and abs(cds[27].c - 96.60) < 1e-9, "ieșirea nu e bara 27 @ 96.60"
assert cds[27].c < entry, "ieșirea n-ar fi pe profit"
# 8. doji: corp <= 10% din amplitudine
assert abs(doji.o - doji.c) <= 0.10 * (doji.h - doji.l), "bara 14 nu e doji"
# 9. uptrend vizibil
assert cds[13].c > cds[0].c + 5, "uptrend prea mic"
# 10. creșterea ține de la bara 0 până imediat înainte de doji — asta acoperă
# zona „1. creștere de N candele", cu N = DOJI_I
assert cds[DOJI_I - 1].c > cds[0].c + 5, "creșterea nu urcă până la doji"
print(f"doji {doji.l:.2f}/{doji.h:.2f} intrare short {conf.c:.2f} "
f"SL {sl:.2f} TP {tp:.2f} trail {trail:.2f} ieșire bara 27 @ {cds[27].c:.2f}")
# --- desenul ----------------------------------------------------------------
RED, GREEN, BLUE, ORANGE, GRAY = "#c00", "#188a3e", "#1a5fb4", "#b06000", "#555"
ch = Chart(
candles=cds,
levels=[
# etichetele în dreapta, dincolo de ultima candelă (width > ultima
# candelă); titlul stă în stânga sus, deci nu mai are cu ce să se lovească
Level(sl, f"stop {sl:.2f}", RED),
Level(entry, f"intrare short {entry:.2f}", ORANGE),
Level(tp, f"ținta {tp:.2f}", GREEN),
],
zones=[
# zona acoperă TOATĂ creșterea (barele 0..13), iar numărul din etichetă
# iese din indici; p_top = high-ul dojiului, altfel eticheta urcă peste titlu
Zone(0, DOJI_I - 1, f"1. creșterea: {DOJI_I} candele",
BLUE, p_top=doji.h),
Zone(DOJI_I, DOJI_I, "2. doji", ORANGE),
# ca unealta „short position" din TradingView: roșu = risc, SUS;
# verde = câștig, JOS — pe short totul e întors față de long
Zone(16, 19, "", RED, p_top=sl, p_bot=entry),
Zone(16, 19, "", GREEN, p_top=entry, p_bot=tp),
# linia de trail doar unde contează (înălțime 0 nu se randează)
# (de la bara 25), nu pe toată lățimea
Zone(25, 30, "", GRAY, p_top=trail, p_bot=trail - 0.02),
],
arrows=[
Arrow(18, f"STOPUL SUS, la {sl:.2f} — 5 cenți peste maximul ultimelor 4 candele:\n"
"dacă prețul urcă până aici, creșterea continuă și ideea e moartă",
where="above", price=sl, color=RED, dx=110),
Arrow(16, f"AICI INTRI SHORT, la {entry:.2f}:\na doua candelă după doji e prima\n"
f"care se ÎNCHIDE sub low-ul lui ({doji.l:.2f})",
where="below", color=ORANGE, dx=-70),
Arrow(19, f"ținta JOS, la {tp:.2f}: tot {risc:.2f}, exact cât ai riscat;\n"
"aici închizi prima treime",
where="right", price=tp, color=GREEN, dx=50, dy=-32),
Arrow(27, f"trail-ul a coborât după preț ({trail:.2f});\nprima închidere peste el: "
f"ieși cu restul, {cds[27].c:.2f}",
where="below", color=GRAY, dx=-40),
],
scale=45.0, gap=20, body_w=9, left=44, width=790, vol_h=60,
vol_label="volum", title="Sell Doji pe short — daily (exemplu construit)",
caption="Pe short câștigi când prețul COBOARĂ: stopul stă SUS, ținta JOS, "
"trail-ul coboară după preț. Roșu = ce riști, verde = ce câștigi.",
)
svg = ch.render()
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
# fiecare preț din etichete e o valoare calculată din serie, nu una scrisă de mână
import re # noqa: E402
PERMISE = {f"{v:.2f}" for v in (sl, entry, tp, doji.l, doji.h, trail, cds[27].c, risc)}
for t in re.findall(r"<text[^>]*>([^<]*)</text>", svg):
for n in re.findall(r"\d+\.\d\d", t):
assert n in PERMISE, f"eticheta {t!r} numește {n}, care nu iese din serie"
# numărul din eticheta zonei 1 == câte bare acoperă efectiv zona (0..DOJI_I-1)
z1 = ch.zones[0]
assert f"{z1.i1 - z1.i0 + 1} candele" in z1.text, "eticheta zonei 1 nu spune câte bare acoperă"
assert z1.i1 == DOJI_I - 1 and cds[z1.i1].c > cds[z1.i0].c + 5, "zona 1 nu acoperă creșterea"
with open("/workspace/atm-curs-dl/gen/strategii/buy_doji_sell.svg", "w") as f:
f.write(svg)
print("scris gen/strategii/buy_doji_sell.svg")