Files
atm-curs-dl/gen/strategii/atmi_reinvestire.py
Claude Agent c08c857274 atmi: diagramele refacute, cinci noi, si fisa de surse
Noi: pivoti (PPQ/SQ calculati din trimestrul precedent, cu formula pe
desen), perechi_tf (weekly -> daily ca descompunere reala, cu caseta,
ghidaje si PPQ pe ambele panouri), trigger (BIM apoi verde, fata de
verde inainte de BIM — cutitul in cadere), risc (ce inseamna "fara
stop": 24 de bare sub apa, -20 % pe pozitie), reinvestire (ciclul
banilor pe trei cicluri).

aplus devine corectie: diagrama arata un prag de corectie, nu un setup
A+, si numele vechi trimitea la altceva.

Trei bug-uri reale: exemplul era etichetat weekly cu 20 de bare, dar
textul spunea "un an mai tarziu" — trecut pe monthly cu assert pe numarul
de luni; ansamblul avea o trimitere fisier:linie desenata in SVG; si
benzile PPQ/SQ erau inversate ca sens, cu SQ fix pe intrare.

PPQ 95.80 si SQ 90.80 sunt completare proprie — cursul spune explicit ca
nu da valorile; derivate cu formula standard, cu assert.

Textul capitolului e neatins — agentul a atins pragul de context.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-20 12:46:55 +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, "Cifrele astea sunt calculul meu 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})")