#!/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'{s}')
def bar(x, val, color, label, sub):
h = val * PXPU
out.append(f'')
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(''
'')
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'')
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'')
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'')
# comparația de la final
yc = BASE_Y + 92
out.append(f'')
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'")
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})")