Files
atm-curs-dl/gen/strategii/atmi_corectie.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

166 lines
8.8 KiB
Python

"""ATMI — aceeași corecție: 4.0 % (DA) vs. 3.9 % (NU; cursul zice „3.93 %").
Citește specificația `gen/strategii/atmi_corectie.spec.txt`.
O singură serie de 32 de candele daily (open-ul fiecărei bare = close-ul
precedentei, deci seria e continuă), care urmează calea DA: urcare la swing
high 100.00, corecție exact 4.0 % până la 96.00 (BIM + candelă verde =
intrare), apoi urcare la TP +10 % = 105.60. Peste ea, un traseu punctat
(calea NU) care se oprește la 96.10 = 3.9 % și apoi coboară ilustrativ — fără
intrare, pentru că nu atinge pragul de 4 %.
Traseele punctate / benzile se inserează în SVG-ul scos de `svg_candles`
folosind coordonatele lui (`ch._cx`, `ch._y`), la fel ca în `az_structura.py`
— candelele rămân desenate de `svg_candles`, nu de mână.
"""
import sys
sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
GRAY, RED, GREEN, BLUE, ORANGE, INK = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#333"
PURPLE = "#7b2fbf"
# --- seria: 32 de close-uri, structura din specificație ----------------------
# 0-11 urcare 90.00 -> 100.00 (swing high la bara 11)
# 12-17 corecție (cădere)
# 18 BIM (hammer de reversie, codiță lungă în jos)
# 19 prima candelă verde daily => intrare, close 96.00
# 20-31 urcare 96.00 -> 105.60 (TP +10 %)
closes = [
90.00, 90.90, 91.70, 92.40, 93.20, 94.00, 94.70, 95.40, 96.10, 97.00, 98.30, 100.00, # 0-11
99.30, 98.50, 97.70, 97.10, 96.60, 96.20, # 12-17
95.98, # 18 BIM
96.00, # 19 intrare (verde)
97.00, 97.90, 98.80, 99.70, 100.50, 101.30, 102.10, 102.90, 103.70, 104.50, 105.10, 105.60, # 20-31
]
assert len(closes) == 32
# wick-uri (sus, jos): implicite (0.25, 0.30), cu excepțiile care fixează nivelurile
wicks = [(0.25, 0.30)] * 32
wicks[11] = (0.00, 0.30) # swing high: high = close = 100.00
wicks[18] = (0.20, 0.43) # BIM: codiță lungă în jos, fundul la 95.55
wicks[19] = (0.20, 0.15) # candela de intrare
wicks[31] = (0.00, 0.30) # TP: high = close = 105.60
# volum relativ ales de generator (materialul nu dă volum pe exemplu):
# crește pe corecție (vânzătorii), vârf pe candela de intrare, scade pe urcare.
vols = [48, 50, 46, 52, 54, 50, 56, 52, 58, 60, 64, 72,
70, 74, 78, 82, 86, 80, 84,
88,
72, 66, 62, 58, 54, 50, 46, 62, 68, 72, 78, 86]
assert len(vols) == 32
cds = from_closes(closes, wicks=wicks, vols=vols, start=89.40)
# --- indicii-cheie și nivelurile ---------------------------------------------
SWING_I = 11 # swing high
BIM_I = 18 # primul BIM
ENTRY_I = 19 # candela verde de intrare
SWING = 100.00
PRAG4 = 96.00 # prag corecție 4.0 % => DA
NU_STOP = 96.10 # cazul real 3.9 % => NU
TP = 105.60 # +10 % față de 96.00
entry = cds[ENTRY_I].c
# --- cele 8 assert-uri cerute de specificație --------------------------------
# 1. pragul de 4 % corespunde lui 96.00
assert abs((SWING - PRAG4) / SWING * 100 - 4.00) < 0.05, "96.00 nu e 4.0 %"
# 2. cazul NU: 96.10 = 3.9 % (graficul cursului scrie 3.9 %; textul „3.93 %")
assert abs((SWING - NU_STOP) / SWING * 100 - 3.90) < 0.02, "96.10 nu e 3.9 %"
# 3. calea DA coboară mai jos decât calea NU
assert PRAG4 < NU_STOP, "pragul DA nu e sub oprirea NU"
# 4. intrarea = close-ul candelei verzi = 96.00
assert abs(entry - 96.00) < 1e-9, f"intrarea {entry} != 96.00"
assert cds[ENTRY_I].c >= cds[ENTRY_I].o, "candela de intrare nu e verde"
# 5. TP = +10 %
assert abs(TP / entry - 1.10) < 0.005, f"TP/entry = {TP / entry:.4f}, nu 1.10"
# 6. BIM înainte de candela de intrare
assert BIM_I < ENTRY_I, "BIM nu e înainte de candela de intrare"
# 7. traseul NU nu atinge pragul de trigger: se oprește la 96.10 > 96.00, deci
# rămâne fără intrare; pe traseul punctat nu există nicio candelă / BIM
nu_route = [(SWING_I, 100.00), (12, 99.40), (13, 98.65), (14, 97.95),
(15, 97.35), (16, 96.80), (17, NU_STOP)]
assert min(p for _, p in nu_route) == NU_STOP, "oprirea traseului NU nu e 96.10"
assert min(p for _, p in nu_route) > PRAG4, "traseul NU atinge pragul de 4 %"
assert not any(i >= BIM_I for i, _ in nu_route), "traseul NU ajunge la candela BIM"
# 8. ambele trasee pleacă din același swing high 100.00
assert abs(cds[SWING_I].h - SWING) < 1e-9, f"swing high {cds[SWING_I].h} != 100.00"
assert abs(nu_route[0][1] - cds[SWING_I].h) < 1e-9, "traseul NU nu pleacă din swing high"
# verificări de context cerute de STYLE.md (structura susține eticheta)
assert abs(TP - max(c.h for c in cds[-10:])) < 1e-9, "TP nu e maximul de la final"
assert min(closes[12:20]) < 96.10 and min(closes[12:20]) >= 95.90, \
"corecția DA nu coboară în jurul lui 96.00"
assert all(b > a for a, b in zip(closes[20:31], closes[21:32])), "urcarea spre TP nu e monotonă"
print(f"swing {SWING:.2f} prag DA {PRAG4:.2f} (4.0 %) stop NU {NU_STOP:.2f} (3.9 %) "
f"intrare {entry:.2f} TP {TP:.2f} (+{TP / entry - 1:.1%}) BIM bara {BIM_I}")
# --- desenul -----------------------------------------------------------------
cds[ENTRY_I].note = ("Regula e pe procent, nu pe cât de dramatică arată linia: "
"calea NU pare o cădere mare, dar e 3.9 %.")
# traseul NU: decizia (până la 96.10) + continuarea ilustrativă în jos
nu_cont = [(17, NU_STOP), (18, 95.70), (19, 95.35), (21, 94.95), (23, 94.65), (25, 94.45)]
nu_pts = nu_route + nu_cont[1:]
ch = Chart(
candles=cds,
levels=[
Level(SWING, "100.00 — swing high", INK),
Level(PRAG4, "96.00 = 4.0 % — DA, intri", GREEN),
Level(TP, "TP +10 % = 105.60", BLUE),
],
marks=[
Mark(BIM_I, "BIM", GREEN, "above", dy=-2),
Mark(ENTRY_I, "candelă verde → trigger", GREEN, "below", dy=20),
],
scale=21.0, gap=16, body_w=9, left=44, width=680, vol_h=70,
vol_label="volum", title="SPY — daily: aceeași corecție, 4.0 % (DA) vs. 3.9 % (NU)",
caption="Calea DA (candele): 4.0 % la 96.00. Calea NU (punctat): 3.9 % („3.93 %” în curs) — fără intrare.",
)
svg = ch.render()
# --- elemente suplimentare, pe coordonatele graficului -----------------------
x0, x1 = 24, ch.width - 14
PPQ = 95.80 # pivotul trimestrial, calculat în atmi_pivoti.py
FUND = round(min(c.l for c in cds[12:20]), 2)
assert FUND == 95.55, FUND
assert PPQ - 0.30 <= FUND <= PPQ + 0.30, "fundul căii DA nu cade în banda PPQ"
assert NU_STOP >= PPQ + 0.30, "calea NU ar coborî în zona de cumpărare"
band_top, band_bot = ch._y(PPQ + 0.30), ch._y(PPQ - 0.30)
band = (f'<rect x="{x0}" y="{band_top:.1f}" width="{x1 - x0}" height="{band_bot - band_top:.1f}" '
f'fill="{PURPLE}" opacity="0.12"/>')
nu_line = ('<polyline points="' + " ".join(f"{ch._cx(i):.1f},{ch._y(p):.1f}" for i, p in nu_pts) + '" '
f'fill="none" stroke="{RED}" stroke-width="1.8" stroke-dasharray="5 4"/>')
# linia pragului NU (96.10) se desenează manual, ca Level-ul gol să nu lase text vid
nu_hline = (f'<line x1="24" y1="{ch._y(NU_STOP):.1f}" x2="{ch.width - 14}" y2="{ch._y(NU_STOP):.1f}" '
f'stroke="{RED}" stroke-width="1.5" stroke-dasharray="4 3"/>')
# punctul de oprire al traseului NU (96.10, strict deasupra pragului de 4 %)
stop_dot = (f'<circle cx="{ch._cx(17):.1f}" cy="{ch._y(NU_STOP):.1f}" r="3.5" fill="{RED}"/>')
# eticheta traseului NU, sub linia 96.10 (nu se suprapune cu eticheta verde de 96.00)
nu_lab = (f'<text x="30" y="{ch._y(NU_STOP) - 8:.1f}" fill="{RED}">'
f'{NU_STOP:.2f} = 3.9 % — NU, sub prag</text>')
# eticheta zonei quarterly, sub banda (fără cifre: cursul nu le dă)
zone_lab = (f'<text x="30" y="{ch._y(PPQ - 0.30) + 34:.1f}" fill="{PURPLE}">'
f'zona PPQ {PPQ:.2f} — aici cumperi</text>')
# legende scurte ale celor două trasee
da_lab = (f'<text x="24" y="{ch._y(97.60):.1f}" fill="{GREEN}" '
f'style="font-weight:bold">calea DA (candelele)</text>')
nu_leg = (f'<text x="{ch._cx(25):.1f}" y="{ch._y(94.45) + 30:.1f}" fill="{RED}" '
f'style="font-weight:bold">calea NU (continuă în jos)</text>')
overlay = [nu_line, nu_hline, stop_dot, nu_lab, zone_lab, da_lab, nu_leg]
svg = svg.replace(">", ">\n " + band, 1) # banda, sub candele
svg = svg.replace("\n</svg>", "\n " + "\n ".join(overlay) + "\n</svg>") # traseele, peste
assert "\n\n" not in svg, "linie goală în SVG"
assert ".md:" not in svg, "trimitere fișier:linie în SVG"
assert "</svg>" in svg and "stroke-dasharray=\"5 4\"" in svg, "traseul NU nu a fost inserat"
with open("/workspace/atm-curs-dl/gen/strategii/atmi_corectie.svg", "w") as f:
f.write(svg)
print("scris gen/strategii/atmi_corectie.svg")