Files
atm-curs-dl/gen/strategii/buy_doji_doji_forma.py
Claude Agent 8b7b5f6b44 buy_doji: refacut ca tutorial pe 3 niveluri, cu diagrame adnotate
Userul a respins mostra de format tutorial: prea lunga, prea multe
trimiteri in text, termeni tehnici nedefiniti, prea multe cifre fara
desen. Cerintele lui sunt acum contract in STYLE.md (FORMAT TUTORIAL v2).

Textul: 8430 -> 4190 de cuvinte. Nivel 1 (din avion, cu mini-glosar) ->
nivel 2 (6 pasi mecanici, o diagrama fiecare) -> oglinda short -> nivel 3
(rafinari). Trimiterile fisier:linie ies din corp si se aduna la final,
cu legenda de prescurtari. Doua tabele in tot documentul.

Toate cele 14 diagrame refacute: zone dreptunghiulare de evidentiere si
sageti care spun si de ce, nu doar ce. svg_candles primeste Zone si
Arrow; render() nu mai muta obiectul la iesire.

Trei corecturi de continut:
- un singur trade prin toata procedura (doji 50.10/49.25, intrare 50.40,
  stop 49.20, 1R 1.20, TP1 51.60); seria e acum o functie comuna,
  importata de stop/transe/iesire
- short: stopul urca la 100.15, ca sa fie deasupra maximului cum cere
  regula oglindita; cifra cursului ramane in text ca observatie
- LABU: trei minime dar doua atingeri ale pivotului 86.00, ceea ce
  impaca "triple bottom" cu "al doilea test", ambele din transcriere

Reparat si bug-ul vechi din buy_doji_sell.svg: titlul peste eticheta
dojiului.

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

145 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""Ce e un doji și ce nu — patru candele cu același high 50.10 și low 49.20.
Citește `gen/strategii/buy_doji_doji_forma.spec.txt` și scrie
`gen/strategii/buy_doji_doji_forma.svg`. E prima imagine din capitol: cine o
vede nu știe ce e un doji, deci candela A primește săgeți pe corp și pe
fiecare fitil, cu explicația de ce arată așa, iar C și D sunt contraexemple.
Zonă verde peste A și B („doji"), zonă roșie peste C și D („nu e doji").
Desenat direct, nu cu `Chart`: `tag`/`note` din svg_candles pun etichetele
pe un singur rând sau în blocul de jos, iar aici fiecare etichetă trebuie să
stea lipită sub candela ei, pe 2-3 rânduri, plus săgeți cu text pe lateral.
Rulare: python3 gen/strategii/buy_doji_doji_forma.py
"""
from pathlib import Path
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, GREY, BLUE = "#188a3e", "#c00", "#555", "#1a5fb4"
H, L = 50.10, 49.20
RNG = H - L
# (nume, o, h, l, c, verdict, rânduri de etichetă sub candelă)
CANDLES = [
("A", 49.60, 50.10, 49.20, 49.55, "DA", ["DA: corp 0.05", "= 5,6 % din înălțime"]),
("B", 49.62, 50.10, 49.20, 49.53, "DA", ["DA, la limită:", "corp 0.09 = 10 %"]),
("C", 49.80, 50.10, 49.20, 49.50, "NU", ["NU: corp 0.30", "= 33 %"]),
("D", 50.05, 50.10, 49.20, 50.10, "NU", ["NU: corp mic (5,6 %),", "dar fără fitil sus"]),
]
# --- assert-urile din spec --------------------------------------------------
ratio = {n: round(abs(c - o) / (h - l), 4) for n, o, h, l, c, *_ in CANDLES}
for n, o, h, l, c, *_ in CANDLES:
assert h == H and l == L, (n, h, l) # 1
assert ratio == {"A": 0.0556, "B": 0.1000, "C": 0.3333, "D": 0.0556}, ratio # 2
for n, o, h, l, c, *_ in CANDLES[:2]: # 3
assert ratio[n] <= 0.10 and min(o, c) > l and max(o, c) < h, n
assert ratio["C"] > 0.10 # 4
_, oD, hD, _, cD, *_ = CANDLES[3]
assert max(oD, cD) == hD
# --- geometrie ---------------------------------------------------------------
SCALE, TOP, W = 260, 210, 1000
XS = [350, 490, 690, 860] # centrele candelelor: A, B (doji) | C, D (nu)
BODY_W = 26
def y(p):
return TOP + (H - p) * SCALE
oA, cA = CANDLES[0][1], CANDLES[0][4]
assert y(oA) < y(cA), "open A trebuie să fie mai sus pe ecran decât close A" # 5
out = []
t = out.append
def text(x, yy, s, color=GREY, anchor="start", bold=False, size=12):
style = f' style="font-weight:bold;font-size:{size}px"' if bold else (
f' style="font-size:{size}px"' if size != 12 else "")
t(f'<text x="{x:.1f}" y="{yy:.1f}" fill="{color}" text-anchor="{anchor}"{style}>{s}</text>')
def zone(x0, x1, y0, y1, label, color):
t(f'<rect x="{x0:.1f}" y="{y0:.1f}" width="{x1 - x0:.1f}" height="{y1 - y0:.1f}" '
f'fill="{color}" fill-opacity="0.10" stroke="{color}" stroke-width="1"/>')
text(x0 + 6, y0 - 7, label, color, bold=True)
def arrow(x1, y1, x2, y2, color, lines, anchor="start"):
"""Săgeată de la (x1,y1) [coada, lângă text] la (x2,y2) [vârf, pe țintă]."""
d = max(((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5, 1e-9)
ux, uy = (x2 - x1) / d, (y2 - y1) / d
bx, by = x2 - 8 * ux, y2 - 8 * uy
t(f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{bx:.1f}" y2="{by:.1f}" '
f'stroke="{color}" stroke-width="1.6"/>')
t(f'<polygon points="{x2:.1f},{y2:.1f} {bx - 4 * uy:.1f},{by + 4 * ux:.1f} '
f'{bx + 4 * uy:.1f},{by - 4 * ux:.1f}" fill="{color}"/>')
dx = -6 if anchor == "end" else (0 if anchor == "middle" else 6)
# la săgeata verticală textul stă deasupra cozii, altfel intră peste ea
y0 = (y1 - 10 - 14 * (len(lines) - 1) if anchor == "middle"
else y1 + 4 - 14 * (len(lines) - 1) / 2)
for k, s in enumerate(lines):
text(x1 + dx, y0 + 14 * k, s, color, anchor, bold=(k == 0))
text(24, 30, "Ce e un doji și ce nu — aceeași bară, 50.10 sus și 49.20 jos", "#333",
bold=True, size=14)
# zonele: A+B sunt doji, C+D nu
label_y0 = y(L) + 28
z_bot = label_y0 + 16 * 2 - 4
zone(XS[0] - 58, XS[1] + 62, y(H) - 30, z_bot, "doji", GREEN)
zone(XS[2] - 62, XS[3] + 74, y(H) - 30, z_bot, "nu e doji", RED)
# high și low comune: linii subțiri pe toată lățimea
for p in (H, L):
t(f'<line x1="{XS[0] - 100}" y1="{y(p):.1f}" x2="{W - 24}" y2="{y(p):.1f}" '
f'stroke="#bbb" stroke-dasharray="4 3"/>')
text(24, y(H) - 6, "cât de sus a urcat prețul: 50.10")
text(24, y(L) + 14, "cât de jos a coborât: 49.20")
for (n, o, h, l, c, verdict, lines), x in zip(CANDLES, XS):
color = GREEN if c >= o else RED
vcol = GREEN if verdict == "DA" else RED
t(f'<line x1="{x}" y1="{y(h):.1f}" x2="{x}" y2="{y(l):.1f}" stroke="#1a1a1a" stroke-width="1.4"/>')
top, bot = y(max(o, c)), y(min(o, c))
t(f'<rect x="{x - BODY_W / 2:.1f}" y="{top:.1f}" width="{BODY_W}" '
f'height="{max(bot - top, 1.6):.1f}" fill="{color}"/>')
for k, s in enumerate(lines):
text(x, label_y0 + 16 * k, s, vcol, "middle", bold=(k == 0))
# --- săgețile pe A: corp, fitil sus, fitil jos -------------------------------
xA = XS[0]
arrow(xA - 60, y(49.92), xA - 3, y(49.85), BLUE, anchor="end",
lines=["fitilul de sus", "cumpărătorii au împins, dar n-au ținut"])
arrow(xA - 60, y(49.575), xA - 15, y(49.575), BLUE, anchor="end",
lines=["corpul: aproape o linie",
"open și close aproape egale — nimeni",
"n-a câștigat ziua"])
arrow(xA - 60, y(49.30), xA - 3, y(49.375), BLUE, anchor="end",
lines=["fitilul de jos", "vânzătorii au împins, dar n-au ținut"])
# prețurile de open/close, în dreapta candelei A
text(xA + 20, y(oA) - 2, "open 49.60")
text(xA + 20, y(cA) + 10, "close 49.55")
# --- săgețile pe contraexemple ----------------------------------------------
xC, xD = XS[2], XS[3]
arrow(xC + 34, y(H) - 112, xC + 14, y(49.78), RED,
lines=["corp prea gros (33 %)", "aici una din tabere a câștigat —", "nu mai e indecizie"])
arrow(xD, y(H) - 34, xD, y(H) - 4, RED, anchor="middle",
lines=["fără fitil sus", "regula cere fitil în ambele părți"])
cap_y = z_bot + 30
text(24, cap_y, f"Doji = corpul e cel mult 10 % din înălțimea totală a barei "
f"(aici înălțimea e {RNG:.2f}) și are fitil și sus, și jos.")
height = cap_y + 16
svg = (f'<svg viewBox="0 0 {W} {height:.0f}" xmlns="http://www.w3.org/2000/svg" '
f'style="width:100%;height:auto;font:12px sans-serif">\n '
+ "\n ".join(out) + "\n</svg>\n")
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT} ({W}x{height:.0f})")