Files
atm-curs-dl/gen/strategii/atmi_reinvestire.py
Claude Agent 76011566ee diagrame: ultimele patru etichete ramase in urma textului corectat
selectia_rvol: 'sub jumatate fata de normal' pentru 0,61 -> '61 % dintr-o zi
normala'. mdsrt_exemplu: legenda spunea ca nivelurile SPY sunt 'reale, din
curs'; sunt reconstruite dupa ilustratia unui sumar Q&A. mbt_iesire: 2.04R ->
2,0R, cifra sursei, prin rotunjire la afisare, fara sa se atinga nicio serie;
assert-ul verifica acum textul din SVG-ul randat, nu doar valoarea bruta.
atmi_exemplu: '13 luni mai tarziu' era citit ca fapt al materialului - acum
spune ca 13 e cifra desenului, materialul da 'aproximativ un an'; si cele
patru marcaje de completare din desenele atmi sunt uniformizate.

Plus cei trei agenti opencode scrisi pentru rundele astea: verif_strat,
corector_strat, reparator_grafic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-22 20:13:12 +00:00

135 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""ATMI — Pasul 6: ieșirea la +10 %, cash, apoi reinvestirea integrală.
Nu e un grafic de preț: e ciclul banilor. Trei cicluri, fiecare cu aceeași
regulă — ieși cu TOATĂ poziția la +10 %, banii stau în cash până la
următorul trigger valid, iar acolo intră înapoi integral, plus contribuția
nouă. Coloana din dreapta arată ce iese dacă reinvestești doar contribuțiile
și lași profitul deoparte: aceeași sumă pusă, mai puțin la final.
Contribuția de 600 $/trimestru e cifra cursului („1 unitate SPY"); ținta de
+10 % e regula lui. Suma finală e calculată aici, nu preluată din backtest —
backtest-ul cursului (~16.300 $ → ~52-53.000 $ în 20 de ani) are alt
orizont și e citat în text, nu desenat.
Rulează: python3 gen/strategii/atmi_reinvestire.py
"""
from pathlib import Path
OUT = Path(__file__).with_suffix(".svg")
W = 700
CONTRIB, TINTA = 600.0, 0.10
N = 3
GREEN, RED, BLUE, INK, GRAY, PURPLE = "#188a3e", "#c00", "#1a5fb4", "#333", "#777", "#7b2fbf"
# --- cele două scenarii ----------------------------------------------------
cicluri, capital = [], 0.0
for i in range(N):
intrat = round(capital + CONTRIB, 2)
iesit = round(intrat * (1 + TINTA), 2)
cicluri.append((i + 1, intrat, iesit))
capital = iesit
fara = round(N * CONTRIB * (1 + TINTA), 2) # fiecare contribuție câștigă 10 % o dată
pus = round(N * CONTRIB, 2)
assert cicluri[0] == (1, 600.00, 660.00), cicluri[0]
assert cicluri[1] == (2, 1260.00, 1386.00), cicluri[1]
assert cicluri[2] == (3, 1986.00, 2184.60), cicluri[2]
assert fara == 1980.00 and pus == 1800.00
CASTIG = round(cicluri[-1][2] - fara, 2)
assert CASTIG == 204.60, CASTIG
# fiecare ciclu intră cu exact ce a ieșit din precedentul plus contribuția
for (_, intr, _), (_, _, ies) in zip(cicluri[1:], cicluri):
assert round(intr - ies, 2) == CONTRIB
assert all(round(ies / intr, 3) == 1.100 for _, intr, ies in cicluri)
# --- desen -----------------------------------------------------------------
out, LABELS = [], []
BASE_Y = 250 # linia de zero a coloanelor
PXPU = 0.072 # px per dolar
COL_W, X0, STEP = 46, 60, 190
assert cicluri[-1][2] * PXPU < BASE_Y - 80, "coloana cea mai înaltă iese din desen"
def text(x, y, s, fill=INK, anchor="start", bold=False, size=12):
st = f"font-size:{size}px" + (";font-weight:bold" if bold else "")
a = f' text-anchor="{anchor}"' if anchor != "start" else ""
w = len(s) * (size * 0.52)
x0 = x if anchor == "start" else (x - w / 2 if anchor == "middle" else x - w)
assert 2 <= x0 and x0 + w <= W - 2, f"text iese din panză: {s!r} ({x0:.0f}..{x0 + w:.0f})"
LABELS.append((x0, x0 + w, y, s))
out.append(f'<text x="{x:.1f}" y="{y:.1f}" fill="{fill}"{a} style="{st}">{s}</text>')
def bar(x, val, color, label, sub):
h = val * PXPU
out.append(f'<rect x="{x:.1f}" y="{BASE_Y - h:.1f}" width="{COL_W}" height="{h:.1f}" '
f'fill="{color}" fill-opacity="0.85"/>')
text(x + COL_W / 2, BASE_Y - h - 8, label, fill=color, anchor="middle", bold=True, size=11)
text(x + COL_W / 2, BASE_Y + 15, sub, fill=GRAY, anchor="middle", size=10)
out.append('<defs><marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" '
f'markerHeight="7" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="{BLUE}"/></marker>'
'</defs>')
text(20, 22, "Ciclul ATMI: ieși cu tot la +10 %, stai în cash, reintri cu tot plus contribuția",
bold=True, size=13)
text(20, 40, f"Contribuție {CONTRIB:.0f} $ la fiecare trigger valid; ținta +10 % pe poziție.",
fill="#555", size=11)
for i, (nr, intrat, iesit) in enumerate(cicluri):
xa = X0 + i * STEP
xb = xa + COL_W + 26
text(xa, 72, f"ciclul {nr}", fill=INK, bold=True, size=12)
bar(xa, intrat, BLUE, f"{intrat:,.0f} $".replace(",", "."), "intri cu tot")
bar(xb, iesit, GREEN, f"{iesit:,.2f} $".replace(",", "x").replace(".", ",").replace("x", "."),
"ieși la +10 %")
# săgeata dintre intrare și ieșire (ce face trade-ul)
out.append(f'<line x1="{xa + COL_W + 4:.1f}" y1="{BASE_Y - 30:.1f}" x2="{xb - 6:.1f}" '
f'y2="{BASE_Y - 30:.1f}" stroke="{GREEN}" stroke-width="1.4" '
f'marker-end="url(#ar)"/>')
if i + 1 < len(cicluri):
# săgeata spre ciclul următor: cash + contribuție nouă
xn = X0 + (i + 1) * STEP
y = BASE_Y + 46
out.append(f'<path d="M{xb + COL_W / 2:.1f},{BASE_Y + 22:.1f} V{y:.1f} '
f'H{xn + COL_W / 2:.1f} V{BASE_Y + 22:.1f}" fill="none" stroke="{BLUE}" '
f'stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#ar)"/>')
text((xb + xn) / 2 + COL_W / 2, y + 16,
f"cash, apoi + {CONTRIB:.0f} $ nou", fill=BLUE,
anchor="middle", size=11, bold=True)
out.append(f'<line x1="24" y1="{BASE_Y:.1f}" x2="{W - 24}" y2="{BASE_Y:.1f}" stroke="#bbb"/>')
# comparația de la final
yc = BASE_Y + 92
out.append(f'<rect x="24" y="{yc - 16:.0f}" width="{W - 48}" height="56" fill="#f6f6f9" '
f'stroke="#ccc"/>')
text(34, yc, f"Ai pus în total {pus:,.0f} $".replace(",", ".") + ", în trei contribuții egale.",
fill=INK, size=11, bold=True)
text(34, yc + 17, f"Cu reinvestire integrală: "
f"{cicluri[-1][2]:,.2f} $".replace(",", "x").replace(".", ",").replace("x", ".")
+ f" · dacă lași profitul deoparte: {fara:,.0f} $".replace(",", ".")
+ f" · diferența: {CASTIG:,.2f} $".replace(",", "x").replace(".", ",").replace("x", "."),
fill=INK, size=11)
text(34, yc + 34, "Completare proprie: cifrele astea sunt calculate pe trei cicluri, nu backtest-ul cursului.",
fill=GRAY, size=10)
H = yc + 56
for i in range(len(LABELS)):
for j in range(i + 1, len(LABELS)):
xa0, xa1, ya, sa = LABELS[i]
xb0, xb1, yb_, sb = LABELS[j]
assert not (abs(ya - yb_) < 12 and xa0 < xb1 - 4 and xb0 < xa1 - 4), \
f"etichete suprapuse: {sa!r} / {sb!r}"
assert 0 < LABELS[i][2] < H, f"etichetă în afara înălțimii: {LABELS[i][3]!r}"
svg = (f'<svg viewBox="0 0 {W} {H:.0f}" xmlns="http://www.w3.org/2000/svg" '
f'style="width:100%;height:auto;font:12px sans-serif">\n ' + "\n ".join(out) + "\n</svg>")
assert "\n\n" not in svg and ".md:" not in svg
OUT.write_text(svg, encoding="utf-8")
print(" | ".join(f"c{n}: {a:.0f} -> {b:.2f}" for n, a, b in cicluri)
+ f" | fără reinvestire {fara:.0f} | diferență {CASTIG:.2f} | scris {OUT.name} ({W}x{H:.0f})")