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
8.1 KiB
Python
156 lines
8.1 KiB
Python
"""Diagrama din `gen/strategii/intrarea_doua_variante.spec.txt` (TIP: PRET).
|
|
|
|
Același setup cu cele două intrări pe același grafic: A la close-ul candelei
|
|
de semnal, B la retestul de 1/3 și 1/2 din corpul ei. Un singur stop, comun
|
|
(sub structură), și trei paranteze de risc în dreapta, la aceeași scară cu
|
|
prețul — diferența de risc vine din intrare, nu din stop.
|
|
|
|
Seria e CONSTRUITĂ (fără ticker), 32 de candele de 15 min, fără volum.
|
|
Etichetele de sub candele (suport atins / atinge 1/3 / atinge 1/2) sunt
|
|
desenate de mână sub linia de SL, cu linii de legătură: ca Mark-uri „below"
|
|
ar cădea peste liniile de 1/2 și 1/3 și peste wick-ul candelei vecine.
|
|
|
|
Rulează: .venv/bin/python gen/strategii/intrarea_doua_variante.py
|
|
-> gen/strategii/intrarea_doua_variante.svg
|
|
"""
|
|
import math
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, "/workspace/atm-curs-dl")
|
|
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
|
|
|
OUT = Path(__file__).resolve().parent / "intrarea_doua_variante.svg"
|
|
GRI, ROSU, VERDE, PORT, ALB = "#777", "#c00", "#188a3e", "#b06000", "#1a5fb4"
|
|
|
|
closes = [103.60, 103.35, 103.45, 103.10, 102.80, 102.90, 102.50, 102.20, 101.85, 101.95, 101.50, 101.10, # 0-11
|
|
100.60, 100.40, 100.35, # 12-14 suport
|
|
102.15, # 15 semnal
|
|
101.70, 101.40, 101.60, # 16-18 retest
|
|
102.00, 102.40, 102.30, 102.80, 103.20, 103.00, 103.50, 103.90, 104.20, 104.10, 104.50, 104.80, 105.00]
|
|
wicks = [(0.15, 0.15)] * 12 \
|
|
+ [(0.10, 0.15), (0.10, 0.50), (0.10, 0.15)] \
|
|
+ [(0.15, 0.10)] \
|
|
+ [(0.10, 0.20), (0.10, 0.20), (0.10, 0.10)] \
|
|
+ [(0.10, 0.03)] + [(0.15, 0.12)] * 12
|
|
assert len(closes) == len(wicks) == 32
|
|
cds = from_closes(closes, wicks=wicks, start=103.80)
|
|
|
|
# ── assert-urile din spec ───────────────────────────────────────────────────
|
|
S = cds[15]
|
|
for a, b in zip(cds, cds[1:]): # 1
|
|
assert abs(a.c - b.o) < 1e-9
|
|
assert abs(S.o - 100.35) < 1e-9 and abs(S.c - 102.15) < 1e-9 # 2
|
|
assert abs(S.h - 102.30) < 1e-9 and abs(S.l - 100.25) < 1e-9
|
|
SL = min(c.l for c in cds[11:15]) # 3
|
|
assert abs(SL - 99.90) < 1e-9 and abs(cds[13].l - SL) < 1e-9 and S.l > SL
|
|
assert all(c.l > SL for i, c in enumerate(cds[11:15], 11) if i != 13)
|
|
half = (S.o + S.c) / 2 # 4
|
|
third = S.c - (S.c - S.o) / 3
|
|
assert abs(half - 101.25) < 1e-9 and abs(third - 101.55) < 1e-9
|
|
assert min(c.l for c in cds[16:19]) <= half and cds[16].l <= third # 5
|
|
assert cds[16].l > half # 16 atinge doar 1/3, 17 atinge 1/2
|
|
assert all(c.l > third for c in cds[19:]) # după retest nu mai revine la 1/3
|
|
RISK_PER_TRADE = 50.0
|
|
intrari = {"A": S.c, "1/3": third, "1/2": half}
|
|
riscuri = {k: e - SL for k, e in intrari.items()} # 6
|
|
for k, r in zip(intrari, (2.25, 1.65, 1.35)):
|
|
assert abs(riscuri[k] - r) < 1e-6, (k, riscuri[k])
|
|
actiuni = {k: math.floor(RISK_PER_TRADE / r + 1e-9) for k, r in riscuri.items()}
|
|
assert list(actiuni.values()) == [22, 30, 37], actiuni
|
|
tp = {k: e + (e - SL) for k, e in intrari.items()} # 7
|
|
for k, v in zip(intrari, (104.40, 103.20, 102.60)):
|
|
assert abs(tp[k] - v) < 1e-9, (k, tp[k])
|
|
assert max(c.h for c in cds) >= tp["A"] # 9
|
|
|
|
# ── desenul ─────────────────────────────────────────────────────────────────
|
|
W = 880
|
|
levels = [
|
|
Level(100.00, "suport 100.00", GRI),
|
|
Level(SL, "", ROSU), # eticheta SL e scrisă sub linie, mai jos
|
|
Level(S.c, f"A: intrare la close {S.c:.2f}", VERDE),
|
|
Level(third, f"B: 1/3 = {third:.2f}", PORT),
|
|
Level(half, f"B: 1/2 = {half:.2f}", PORT),
|
|
Level(tp["A"], f"TP1 A {tp['A']:.2f}", ALB),
|
|
Level(tp["1/2"], f"TP1 B (1/2) {tp['1/2']:.2f}", ALB, dash="2 3"),
|
|
Level(tp["1/3"], f"TP1 B (1/3) {tp['1/3']:.2f}", ALB, dash="2 3"),
|
|
]
|
|
ch = Chart(candles=cds, levels=levels,
|
|
marks=[Mark(15, "semnal: candela verde mare", VERDE, "above", dy=-14)],
|
|
scale=60, gap=15, body_w=9, left=44, width=W,
|
|
title="Exemplu construit (fără ticker) — 15 min")
|
|
svg = ch.render()
|
|
svg = re.sub(r'\s*<text [^>]*></text>', "", svg) # eticheta goală a SL-ului
|
|
y, cx = ch._y, ch._cx
|
|
ySL = y(SL)
|
|
|
|
extra = []
|
|
def txt(x, yy, s, fill, anchor="start", bold=False):
|
|
st = ' style="font-weight:bold"' if bold else ""
|
|
extra.append(f'<text x="{x:.1f}" y="{yy:.1f}" fill="{fill}" text-anchor="{anchor}"{st}>{s}</text>')
|
|
def ln(x1, y1, x2, y2, stroke, w=1.2, dash=""):
|
|
d = f' stroke-dasharray="{dash}"' if dash else ""
|
|
extra.append(f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" stroke="{stroke}" stroke-width="{w}"{d}/>')
|
|
|
|
# eticheta SL, sub linia lui, în dreapta
|
|
txt(W - 28, ySL + 16, f"SL {SL:.2f} = low-ul ultimelor 4 candele (comun A și B)", ROSU, "end")
|
|
|
|
# etichetele de sub candele, sub linia de SL, cu linii de legătură spre wick
|
|
R1, R2 = ySL + 16, ySL + 31
|
|
txt(cx(13), R1, "suport atins", GRI, "middle", True)
|
|
ln(cx(16), y(cds[16].l) + 2, cx(16), R2 - 11, PORT, 1, "2 2")
|
|
txt(cx(16) + 4, R2, "atinge 1/3", PORT, "end", True)
|
|
ln(cx(17), y(cds[17].l) + 2, cx(17), R1 - 11, PORT, 1, "2 2")
|
|
txt(cx(17) - 4, R1, "atinge 1/2", PORT, "start", True)
|
|
|
|
# parantezele de risc: intrare → SL, la aceeași scară cu prețul
|
|
BX = {"A": 560, "1/3": 605, "1/2": 650}
|
|
col = {"A": VERDE, "1/3": PORT, "1/2": PORT}
|
|
eticheta = {"A": "A", "1/3": "B 1/3", "1/2": "B 1/2"}
|
|
px = {}
|
|
for k, e in intrari.items():
|
|
x, y0, y1 = BX[k], y(e), ySL
|
|
px[k] = y1 - y0
|
|
ln(x, y0, x, y1, col[k], 2.2)
|
|
ln(x - 6, y0, x + 6, y0, col[k], 2.2)
|
|
ln(x - 6, y1, x + 6, y1, ROSU, 2.2)
|
|
txt(x, y0 - 6, eticheta[k], col[k], "middle", True)
|
|
txt(x + 6, (y0 + y1) / 2 + 4, f"{riscuri[k]:.2f}", col[k], "start")
|
|
# 8. scara: raportul pixelilor == raportul riscurilor, toleranță 1%
|
|
for k in intrari:
|
|
assert abs(px[k] / px["A"] - riscuri[k] / riscuri["A"]) < 0.01 * riscuri[k] / riscuri["A"], k
|
|
|
|
# blocul cu riscurile, sub SL, sub paranteze
|
|
bloc = [(f"risc A {riscuri['A']:.2f} → {actiuni['A']} acțiuni la 50 $", VERDE),
|
|
(f"risc B 1/3 {riscuri['1/3']:.2f} → {actiuni['1/3']} acțiuni", PORT),
|
|
(f"risc B 1/2 {riscuri['1/2']:.2f} → {actiuni['1/2']} acțiuni", PORT)]
|
|
for i, (s, c) in enumerate(bloc):
|
|
txt(530, ySL + 34 + 15 * i, s, c)
|
|
ylegenda = ySL + 34 + 15 * len(bloc) + 12
|
|
txt(24, ylegenda, "Exemplu construit, fără ticker. Cifrele nu vin din curs. "
|
|
"Risc permis 50 $ (0,5% din 10.000 $).", "#555")
|
|
H = ylegenda + 12
|
|
|
|
svg = re.sub(r'viewBox="0 0 \d+ \d+"', f'viewBox="0 0 {W} {H:.0f}"', svg)
|
|
svg = svg.replace("\n</svg>", "\n " + "\n ".join(extra) + "\n</svg>")
|
|
assert "\n\n" not in svg, "linie goală în SVG"
|
|
|
|
# ── verificare: etichete în pânză, fără suprapuneri între ele ─────────────
|
|
cutii = []
|
|
for xs, ys, attrs, s in re.findall(r'<text x="([\d.-]+)" y="([\d.-]+)"([^>]*)>([^<]*)</text>', svg):
|
|
x, yy = float(xs), float(ys)
|
|
w = len(s) * (7.0 if "bold" in attrs else 6.3)
|
|
x0 = x - w if 'anchor="end"' in attrs else (x - w / 2 if 'anchor="middle"' in attrs else x)
|
|
assert -2 <= x0 and x0 + w <= W + 2 and 0 <= yy <= H, f"în afara pânzei: {s!r}"
|
|
cutii.append((x0, x0 + w, yy, s))
|
|
for i, a in enumerate(cutii):
|
|
for b in cutii[i + 1:]:
|
|
if abs(a[2] - b[2]) < 13 and a[0] < b[1] - 2 and b[0] < a[1] - 2:
|
|
raise AssertionError(f"texte suprapuse: {a[3]!r} / {b[3]!r}")
|
|
|
|
OUT.write_text(svg + "\n", encoding="utf-8")
|
|
print(f"SL {SL:.2f} | 1/3 {third:.2f} | 1/2 {half:.2f} | riscuri "
|
|
+ ", ".join(f"{k} {r:.2f}→{actiuni[k]}" for k, r in riscuri.items())
|
|
+ " | px " + ", ".join(f"{v:.1f}" for v in px.values()))
|