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
156 lines
7.2 KiB
Python
156 lines
7.2 KiB
Python
"""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}")
|