Files
atm-curs-dl/gen/strategii/intrarea_cazul_lui.py
Claude Agent edc270417d strategii: cele trei diagrame ale anexei A3
Un agent per diagramă, din `.spec.txt`-urile scrise la sesiunea trecută.
`audit_diagrams` 0 pe toate trei; fiecare randată și privită ca PNG.

Două retușuri făcute de sesiunea principală, peste ce au raportat agenții:
- `intrarea_retest_ratat` folosea alte fitiluri la candelele 12 și 14 decât
  `intrarea_doua_variante`, deși cele două împart aceeași serie 0-15.
  Aliniate după `doua_variante`, care e scris direct din spec.
- `intrarea_cazul_lui` avea un `Mark` „suport weekly" sub linia de SL, care
  dubla eticheta nivelului și părea să eticheteze SL-ul. Scos.

Abaterile de la spec pe care le-au raportat agenții și le-am acceptat:
vârful din `cazul_lui` cade pe bara 32, nu 31 (în lista de close-uri din
spec acolo e 105.20), iar bara 33 deschide chiar la 105.20, deci cerința
„toate celelalte high-uri sub 105.20" nu se poate îndeplini strict.

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

156 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Diagrama din `gen/strategii/intrarea_cazul_lui.spec.txt` (TIP: PRET).
Trade-ul userului din A3 §2.1, reconstruit din descriere: downtrend pe 15 min
până la suportul weekly 100.00, buy momentum pe o verde mare, intrare la
close 102.10, imediat o roșie mare care acoperă 83% din corpul verzii, apoi
trendul. Ieșirea userului la 102.60, înainte de TP1; vârful 105.20 după.
Pe același grafic, nivelul 1/2 = 101.20 unde s-ar fi umplut varianta B.
Toate cifrele sunt CONSTRUITE (fără ticker); mediile SMA 5 / SMA 10 sunt
ilustrative — lecția 10 nu dă lungimile reale.
Rulează:
python3 gen/strategii/intrarea_cazul_lui.py
Scrie lângă el `intrarea_cazul_lui.svg`.
"""
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
OUT = Path(__file__).resolve().parent / "intrarea_cazul_lui.svg"
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
MA5_C, MA10_C = "#8a3ea8", "#2a8a8a"
SUPORT, SL, ENTRY, HALF, TP1 = 100.00, 99.90, 102.10, 101.20, 104.30
CLOSES = [
103.80, 103.50, 103.60, 103.20, 102.90, 103.00, 102.60, 102.30, 101.90, 101.40, 101.00, # 0-10
100.70, 100.45, 100.30, # 11-13 la suport
102.10, # 14 buy momentum
100.60, # 15 roșie mare
101.10, 101.60, 101.90, # 16-18
102.60, # 19 ieșirea userului
102.90, 102.70, 103.20, 103.60, 103.40, 103.90, 104.20, 104.10, 104.60,
104.90, 104.70, 105.00, 105.20, 105.00, # 20-33
]
assert len(CLOSES) == 34, len(CLOSES)
# (sus, jos); excepțiile fixează exact low-urile/high-urile din specificație
WICKS = [(0.12, 0.10)] * 34
WICKS[10] = (0.10, 0.20) # low 100.80
WICKS[11] = (0.10, 0.30) # low 100.40
WICKS[12] = (0.10, 0.55) # low 99.90, wick sub 100.00
WICKS[13] = (0.10, 0.20) # low 100.10
WICKS[14] = (0.15, 0.10) # h 102.25, l 100.20
WICKS[15] = (0.10, 0.10) # h 102.20, l 100.50
# spec zice „candela 31 (close 105.20)", dar în lista lui 105.20 e la indexul 32;
# vârful e pus pe bara 32, unde e de fapt close-ul 105.20.
WICKS[31] = (0.10, 0.10) # h 105.10 < 105.20
WICKS[32] = (0.00, 0.10) # h 105.20 = maximul
WICKS[33] = (0.00, 0.10) # open 105.20 → high nu poate fi sub 105.20
cds = from_closes(CLOSES, wicks=WICKS, start=104.00)
R = lambda x: round(x, 2) # noqa: E731
# ── assert-urile din specificație ──────────────────────────────────────────
# 1. open == close anterior
for a, b in zip(cds, cds[1:]):
assert a.c == b.o, (a.c, b.o)
g, r = cds[14], cds[15]
assert (R(g.o), R(g.c), R(g.h), R(g.l)) == (100.30, 102.10, 102.25, 100.20), g
assert (R(r.o), R(r.c), R(r.h), R(r.l)) == (102.10, 100.60, 102.20, 100.50), r
assert (R(cds[10].l), R(cds[11].l), R(cds[12].l), R(cds[13].l)) == (100.80, 100.40, 99.90, 100.10)
# 2. SL = low 4 candele; nici verdea, nici roșia nu îl ating
assert R(min(c.l for c in cds[10:14])) == SL
assert g.l > SL and r.l > SL
# 3. culori
assert g.c > g.o and r.c < r.o
# 4. roșia acoperă 83% din corpul verzii
acop = (r.o - r.c) / (g.c - g.o)
assert abs(acop - 0.83) <= 0.01, acop
# 5. 1/2 din corp, atins de roșie
half = (g.o + g.c) / 2
assert R(half) == HALF and r.l <= half, (half, r.l)
# 6. R-urile
risc = ENTRY - SL
assert R(risc) == 2.20 and R(ENTRY + risc) == TP1
flot = (r.c - ENTRY) / risc
iesire = (cds[19].c - ENTRY) / risc
varf = (105.20 - ENTRY) / risc
assert (R(flot), R(iesire), R(varf)) == (-0.68, 0.23, 1.41), (flot, iesire, varf)
# 7. mediile mobile
sma = lambda n, i: sum(CLOSES[i + 1 - n:i + 1]) / n # noqa: E731
assert g.c > sma(5, 14) and g.c > sma(10, 14)
assert r.c < sma(5, 15) and r.c < sma(10, 15)
assert (R(sma(5, 15)), round(sma(10, 15), 3)) == (100.83, 101.335)
# 8. ieșirea înainte de TP1; vârful
assert cds[19].c < TP1 and all(c.h < TP1 for c in cds[15:20])
assert R(max(c.h for c in cds)) == 105.20 and R(cds[32].h) == 105.20 == cds[32].c
assert all(c.h < 105.20 - 1e-9 for c in cds[:32])
# varianta B (doar în legendă)
assert R(g.c - (g.c - g.o) / 3) == 101.50
assert R(HALF - SL) == 1.30 and R(HALF + (HALF - SL)) == 102.50
levels = [
Level(TP1, "TP1 104.30 (1:1)", BLUE, label_at="left"),
Level(ENTRY, "intrare la close 102.10", GREEN),
Level(HALF, "1/2 = 101.20 (B s-ar fi umplut aici)", ORANGE, dash="2 3"),
Level(SUPORT, "suport weekly 100.00", GRAY, label_at="left"),
Level(SL, "", RED), # eticheta SL sub linie, mai jos (0.10 sub suport)
Level(99.50, "", "none", dash=""), # rezervă loc sub SL pentru etichete
]
marks = [
Mark(14, "▲ buy momentum", GREEN, "above", dy=-20), # peste SMA 10
Mark(32, "vârf 105.20 = +1.41R", GRAY, "above"),
]
ch = Chart(
candles=cds, levels=levels, marks=marks,
mas=[MA(5, MA5_C), MA(10, MA10_C, dash="5 3")],
scale=60, gap=16, body_w=10, left=44, width=640,
title="Exemplu construit (fără ticker) — 15 min, suport weekly",
)
svg = ch.render()
# barele 15 și 19 au candele lipite în stânga/dreapta: un Mark centrat le-ar
# tăia, așa că eticheta lor stă în golul de lângă candelă (roșia: ▲ chiar sub
# low-urile barelor 16+, peste suport; ieșirea: deasupra barelor 14-19,
# terminată în bara 19, cu linie de legătură).
y19 = ch._y(103.30)
extra = [
f'<text x="{ch._cx(15) - 5:.0f}" y="{ch._y(100.20):.1f}" fill="{RED}" '
f'style="font-weight:bold">▲ roșie mare: −0.68R flotant</text>',
f'<text x="{ch._cx(19) + 4:.0f}" y="{y19:.1f}" fill="{ORANGE}" text-anchor="end" '
f'style="font-weight:bold">ieși aici: 102.60 = +0.23R</text>',
f'<line x1="{ch._cx(19):.1f}" y1="{y19 + 4:.1f}" x2="{ch._cx(19):.1f}" '
f'y2="{ch._y(cds[19].h) - 3:.1f}" stroke="{ORANGE}" stroke-width="1"/>',
f'<text x="30" y="{ch._y(SL) + 15:.1f}" fill="{RED}">SL 99.90 (low 4 candele)</text>',
f'<text x="24" y="{ch.top + 12:.1f}" fill="#555">'
f'<tspan fill="{MA5_C}">── SMA 5</tspan> / <tspan fill="{MA10_C}">- - SMA 10</tspan> ilustrative</text>',
]
legend = [
"Exemplu construit, fără ticker. Prețurile, mediile mobile și ieșirea sunt",
"ilustrative; secvența (verde mare → roșie mare → trend) e cea descrisă de user.",
"Varianta B (ordin la 1/2 = 101.20, risc 1.30, TP1 102.50) e aritmetică pe",
"aceleași cifre, nu un trade din curs. 1/3 din corp = 101.50.",
"Close-ul roșiei (100.60): sub ambele MA (SMA 5 = 100.83, SMA 10 = 101.34).",
]
H0 = float(re.findall(r'viewBox="0 0 \d+ ([0-9.]+)"', svg)[0])
extra += [f'<text x="24" y="{H0 + 10 + 17 * i:.1f}" fill="#555">{t}</text>'
for i, t in enumerate(legend)]
new_h = H0 + 10 + 17 * (len(legend) - 1) + 12
svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")',
lambda m: m.group(1) + f"{new_h:.0f}" + m.group(2), svg, count=1)
svg = svg.replace("\n</svg>", "\n " + "\n ".join(extra) + "\n</svg>")
svg = re.sub(r"\n <text [^>]*></text>", "", svg) # etichete goale (SL, rezervă)
svg = re.sub(r"\n <line [^>]*stroke=\"none\"[^>]*/>", "", svg)
assert "\n\n" not in svg
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name}: acoperire {acop:.3f}, flotant {flot:+.2f}R, "
f"ieșire {iesire:+.2f}R, vârf {varf:+.2f}R, SMA5/10@15 = "
f"{sma(5, 15):.2f}/{sma(10, 15):.3f}")