diff --git a/gen/strategii/atmi_ansamblu.py b/gen/strategii/atmi_ansamblu.py
index f045744..5e7621a 100644
--- a/gen/strategii/atmi_ansamblu.py
+++ b/gen/strategii/atmi_ansamblu.py
@@ -1,6 +1,6 @@
"""Diagrama de ansamblu ATMI (strategii/05_atmi.md, marcajul `ansamblu`).
-„SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 %".
+„SPY — daily: corecție ≥4 % → zona PPQ → BIM + verde → intrare → TP +10 %".
Structura seriei și nivelurile vin din spec-ul
`gen/strategii/atmi_ansamblu.spec.txt`. Toate cifrele desenate (100.00 swing
@@ -27,29 +27,32 @@ PURPLE = "#7b2fbf" # folosit în materialul de curs pentru PPQ
# ---------------------------------------------------------------- seria (33 bare)
# 15 bare de urcare ~88 -> 100, 8 de corecție 100 -> 96 (ultima = intrarea),
# 10 de urcare 96 -> 105.60. open-ul fiecărei bare = close-ul precedentei.
-UP_A = [88.2, 88.8, 89.5, 90.1, 90.8, 91.6, 92.5, 93.3, 94.2, 95.1,
- 96.0, 96.9, 97.8, 98.9, 100.00] # 0-14
-CORR = [99.3, 98.5, 97.8, 97.2, 96.7, 96.3, 96.00, 96.00] # 15-22
-UP_B = [96.6, 97.2, 97.9, 98.6, 99.4, 100.3, 101.4, 102.6, 104.0, 105.60] # 23-32
+UP_A = [96.60, 97.10, 97.60, 98.00, 98.50, 98.20, 98.70, 99.20,
+ 99.60, 99.30, 99.70, 100.00] # 0-11
+CORR = [99.30, 98.50, 97.80, 97.20, 96.70, 96.30, 95.90, 96.00] # 12-19 (18 = BIM)
+UP_B = [96.60, 97.20, 97.90, 98.60, 99.40, 100.30, 101.40, 102.60,
+ 103.40, 104.20, 104.90, 105.60] # 20-31
CLOSES = UP_A + CORR + UP_B
-assert len(CLOSES) == 33, len(CLOSES)
-PEAK, BIM, ENTRY, LAST = 14, 21, 22, 32
+assert len(CLOSES) == 32, len(CLOSES)
+PEAK, BIM, ENTRY, LAST = 11, 18, 19, 31
# fitile (sus, jos) per bară; jos pe corecție ținut ≥ 0 ca minimul să fie fix 96.00
-WICKS = ([(0.30, 0.30)] * 14
- + [(0.00, 0.30)] # 14: vârful atinge exact 100.00
- + [(0.00, 0.00), (0.05, 0.10), (0.05, 0.10), (0.05, 0.15),
- (0.05, 0.15), (0.05, 0.20), (0.05, 0.00), (0.12, 0.00)] # 15-22
- + [(0.25, 0.25)] * 9
- + [(0.00, 0.20)]) # 32: închide/atinge exact 105.60
-assert len(WICKS) == len(CLOSES)
+WICKS = ([(0.25, 0.25)] * 11
+ + [(0.00, 0.25)] # 11: vârful atinge exact 100.00
+ + [(0.00, 0.20), (0.05, 0.20), (0.05, 0.20), (0.05, 0.20),
+ (0.05, 0.20), (0.05, 0.20),
+ (0.05, 0.35), # 18: bara de BIM, fundul la 95.55
+ (0.15, 0.05)] # 19: candela verde de intrare
+ + [(0.25, 0.00)] + [(0.25, 0.25)] * 10
+ + [(0.00, 0.20)]) # 31: închide/atinge exact 105.60
+assert len(WICKS) == len(CLOSES), (len(WICKS), len(CLOSES))
-VOLS = ([44, 47, 42, 50, 45, 41, 48, 43, 46, 51, 44, 49, 46, 52, 58] # 0-14 urcare
- + [64, 70, 66, 61, 58, 74, 96, 88] # 15-22 corecție + BIM/intrare
- + [55, 58, 52, 60, 57, 63, 59, 66, 72, 84]) # 23-32 urcare spre TP
-assert len(VOLS) == len(CLOSES)
+VOLS = ([44, 47, 42, 50, 45, 41, 48, 43, 46, 51, 44, 58] # 0-11 urcare
+ + [64, 70, 66, 61, 58, 74, 96, 88] # 12-19 corecție + BIM/intrare
+ + [55, 58, 52, 60, 57, 63, 59, 66, 70, 74, 78, 84]) # 20-31 urcare spre TP
+assert len(VOLS) == len(CLOSES), (len(VOLS), len(CLOSES))
-cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=88.0)
+cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=96.20)
# ---------------------------------------------------------------- assert-urile din spec
high = 100.00
@@ -58,9 +61,9 @@ tp = cds[LAST].h
assert abs((high - 96.00) / high * 100 - 4.0) < 0.05 # corecția e 4.0 %
assert abs(entry - 96.00) < 0.05 # intrarea = close-ul candelei verzi
assert abs(tp / entry - 1.10) < 0.005 # TP = +10 %
-assert abs(tp - max(c.h for c in cds[-10:])) < 1e-9 # TP atins la final, e maximul barelor
+assert abs(tp - max(c.h for c in cds[-12:])) < 1e-9 # TP atins la final, e maximul barelor
assert BIM < ENTRY # BIM înainte de candela verde
-assert abs(min(c.l for c in cds[15:23]) - 96.00) < 1e-9 # minimul corecției = 96.00
+assert abs(min(c.l for c in cds[12:20]) - 95.55) < 1e-9 # fundul corecției = 95.55
assert cds[ENTRY].c >= cds[ENTRY].o # candela de intrare e verde
assert abs(cds[PEAK].h - high) < 1e-9 # swing high = 100.00
assert abs(cds[LAST].c - tp) < 1e-9 # ultima bară închide pe TP
@@ -75,16 +78,17 @@ assert all("stop" not in lv.text.lower() for lv in levels)
assert all(lv.price >= entry - 1e-9 for lv in levels)
marks = [
- Mark(15, "corecție 4.0 %", ORANGE, "above"),
- Mark(BIM, "BIM", ORANGE, "above"),
- Mark(ENTRY, "BIM + candelă verde → intrare", GREEN, "below", dy=20),
+ Mark(13, "corecție 4,0 %", ORANGE, "below", dy=26),
+ Mark(BIM, "BIM", "#0a7f9e", "above"),
+ Mark(ENTRY, "prima candelă verde → intrare", GREEN, "below", dy=30),
+ Mark(LAST, "ieși cu tot", BLUE, "below", dy=22),
]
ch = Chart(candles=cds, levels=levels, marks=marks,
- mas=[MA(20, ORANGE, dash="4 3", text="SMA-20 (ilustrativ)")],
- scale=18.0, gap=16, body_w=9, left=44, width=720, vol_h=70, top=70,
+ scale=22.0, gap=17, body_w=9, left=44, width=720, vol_h=70, vol_gap=72, top=88,
vol_label="volum",
- caption="Niveluri (100.00 / 96.00 / 105.60) din curs; PPQ/SQ schematice, fără cifre în material.")
+ caption="100.00 / 96.00 / 105.60 sunt cifrele exemplului din curs; PPQ 95.80 e calculat "
+ "în diagrama pasului 1 (cursul nu dă valorile pivoților).")
svg = ch.render()
@@ -98,26 +102,30 @@ def band(price: float, half: float, color: str, label: str) -> str:
y_top, y_bot = ch._y(price + half), ch._y(price - half)
return (f''
- f'{label}')
+ f'{label}')
-# SQ e zona joasă (intrarea exact pe ea), PPQ zona mai sus, retragere mai mică
-assert (entry + 1.60 - 0.35) > (entry + 0.35), "benzile SQ și PPQ nu se suprapun"
-sq = band(entry, 0.35, GREEN, "SQ — zonă quarterly")
-ppq = band(entry + 1.60, 0.35, PURPLE, "PPQ — zonă quarterly")
+# PPQ 95.80 e pivotul trimestrial calculat în diagrama pasului 1; banda e
+# zona de cumpărare din jurul lui. SQ 90.80 e sub scara acestui desen, deci
+# nu se desenează aici — apare pe diagrama pasului 1.
+PPQ = 95.80
+assert abs(min(c.l for c in cds) - 95.55) < 1e-9
+assert PPQ - 0.30 <= min(c.l for c in cds) <= PPQ + 0.30, "fundul nu cade în banda PPQ"
+assert entry > PPQ, "candela verde trebuie să închidă peste pivot"
+ppq = band(PPQ, 0.30, PURPLE, f"zona PPQ {PPQ:.2f} — aici cumperi")
+sq = ""
# „fără stop-loss standard" stă sub zona de intrare, unde nu are candele
-no_sl = (f'fără stop-loss standard'
- f'(03_strategia_2_atmi.md:164-169)')
+no_sl = (f'fără stop-loss standard — nicio linie sub intrare')
# titlul e injectat deasupra, ca rând separat de eticheta TP (care stă chiar
# sub marginea de sus a panoului); altfel cele două texte se calcă la dreapta
titlu = (''
- 'SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 %')
+ 'SPY — daily: corecție ≥4 % → zona PPQ → BIM + verde → intrare → TP +10 %')
i = svg.index(">") + 1
-svg = svg[:i] + "\n " + "\n ".join([sq, ppq, no_sl, titlu]) + svg[i:]
+svg = svg[:i] + "\n " + "\n ".join([ppq, no_sl, titlu]) + svg[i:]
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
+assert ".md:" not in svg, "trimitere fișier:linie în SVG"
OUT.write_text(svg)
print(f"scris {OUT.name}: {len(cds)} candele, intrare {entry:.2f}, TP {tp:.2f}")
diff --git a/gen/strategii/atmi_ansamblu.svg b/gen/strategii/atmi_ansamblu.svg
index fec12fe..15eddb3 100644
--- a/gen/strategii/atmi_ansamblu.svg
+++ b/gen/strategii/atmi_ansamblu.svg
@@ -1,119 +1,114 @@
-", "\n " + "\n ".join(overlay) + "\n") # traseele, peste
assert "\n\n" not in svg, "linie goală în SVG"
+assert ".md:" not in svg, "trimitere fișier:linie în SVG"
assert "" in svg and "stroke-dasharray=\"5 4\"" in svg, "traseul NU nu a fost inserat"
-with open("/workspace/atm-curs-dl/gen/strategii/atmi_aplus.svg", "w") as f:
+with open("/workspace/atm-curs-dl/gen/strategii/atmi_corectie.svg", "w") as f:
f.write(svg)
-print("scris gen/strategii/atmi_aplus.svg")
+print("scris gen/strategii/atmi_corectie.svg")
diff --git a/gen/strategii/atmi_corectie.svg b/gen/strategii/atmi_corectie.svg
new file mode 100644
index 0000000..546ccf3
--- /dev/null
+++ b/gen/strategii/atmi_corectie.svg
@@ -0,0 +1,119 @@
+
+
+ SPY — daily: aceeași corecție, 4.0 % (DA) vs. 3.9 % (NU)
+
+ 100.00 — swing high
+
+ 96.00 = 4.0 % — DA, intri
+
+ TP +10 % = 105.60
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ volum
+ BIM
+ candelă verde → trigger
+ Regula e pe procent, nu pe cât de dramatică arată linia: calea NU pare o cădere mare, dar e 3.9 %.
+ Calea DA (candele): 4.0 % la 96.00. Calea NU (punctat): 3.9 % („3.93 %” în curs) — fără intrare.
+
+
+
+ 96.10 = 3.9 % — NU, sub prag
+ zona PPQ 95.80 — aici cumperi
+ calea DA (candelele)
+ calea NU (continuă în jos)
+
\ No newline at end of file
diff --git a/gen/strategii/atmi_exemplu.py b/gen/strategii/atmi_exemplu.py
index 806b23b..fb7cd39 100644
--- a/gen/strategii/atmi_exemplu.py
+++ b/gen/strategii/atmi_exemplu.py
@@ -24,10 +24,10 @@ ENTRY = 117.43 # 04_MODUL_3/03_strategia_2_atmi.md:358-363
TP = 129.17 # TP1 = +10 % pe 117.43 → 129.17 (":170-171, 358-363")
SWING_HIGH = 123.00 # construit de mine ca să conțină corecția ≥4 %
ENTRY_I = 17 # candela de intrare (verde, precedată de BIM)
-TP_I = 37 # ultima candelă: TP1 atins
+TP_I = 30 # ultima candelă: TP1 atins
BIM_I = ENTRY_I - 1 # BIM (bară de inversare bearish) înaintea candelei verzi
-# ── structura seriei: 12 sus → 6 corecție → intrare → 20 spre TP ───────────
+# ── structura seriei: 12 sus → 5 corecție → intrare → 13 luni spre TP ─────
closes = [
# ~12 candele de urcare spre swing high ~123.00
115.80, 116.50, 117.20, 117.90, 117.60, 118.30,
@@ -35,13 +35,11 @@ closes = [
# ~6 candele de corecție, de la 123.00 în zona de intrare
122.00, 121.10, 120.10, 119.00, 117.00,
117.43, # candela de intrare (verde)
- # ~20 candele de urcare de la 117.43 spre 129.17
- 118.00, 118.55, 119.05, 119.50, 119.95,
- 120.40, 120.85, 121.25, 121.65, 122.10,
- 122.50, 122.95, 123.40, 123.85, 124.30,
- 125.00, 126.00, 127.10, 128.40, 129.17,
+ # 13 candele lunare de urcare de la 117.43 spre 129.17 — „aproximativ un an"
+ 118.20, 119.10, 119.90, 120.60, 121.50, 122.40, 123.30,
+ 124.20, 125.30, 126.40, 127.40, 128.30, 129.17,
]
-assert len(closes) == 38, len(closes)
+assert len(closes) == 31, len(closes)
wicks = [(0.25, 0.30)] * len(closes)
wicks[0] = (0.35, 0.35)
@@ -53,8 +51,8 @@ wicks[TP_I] = (0.00, 0.30) # high = 129.17 exact, ca TP să fie max(high)
vols = [42, 40, 44, 47, 39, 45, 48, 52, 50, 55, 58, 62,
57, 61, 66, 72, 84,
70,
- 58, 55, 53, 57, 54, 56, 59, 55, 57, 60, 58, 61,
- 63, 60, 64, 66, 68, 72, 76, 88]
+ 58, 55, 53, 57, 54, 56, 59, 62, 64, 68, 72, 78, 88]
+assert len(vols) == len(closes), (len(vols), len(closes))
cds = from_closes(closes, wicks=wicks, vols=vols, start=115.30)
@@ -68,7 +66,7 @@ assert abs(tp - 129.17) < 0.05, tp
assert abs(tp / entry - 1.10) < 0.005, tp / entry
assert (swing_high - entry) / swing_high >= 0.04, (swing_high, entry)
assert abs(entry - cds[ENTRY_I].c) < 0.05
-assert abs(max(c.h for c in cds[-10:]) - tp) < 0.05, max(c.h for c in cds[-10:])
+assert abs(max(c.h for c in cds[-13:]) - tp) < 0.05, max(c.h for c in cds[-13:])
# structura care susține afirmațiile din text
assert abs(swing_high - SWING_HIGH) < 0.05, swing_high
@@ -76,14 +74,15 @@ assert cds[ENTRY_I].c > cds[ENTRY_I].o, "candela de intrare trebuie să fie verd
assert cds[BIM_I].c < cds[BIM_I].o, "BIM-ul dinaintea intrării trebuie să fie bearish"
assert any(c.vol is not None for c in cds), "lipsește panoul de volum"
-n_entry_to_tp = TP_I - ENTRY_I + 1
-assert 18 <= n_entry_to_tp <= 23, n_entry_to_tp
+LUNI = TP_I - ENTRY_I # câte candele lunare de la intrare la TP1
+assert 12 <= LUNI <= 14, LUNI # „aproximativ un an" din material
-TP_LABEL = "TP1 atins ~1 an mai târziu (data exactă neclară în transcriere — ASR)"
-assert "~1 an" in TP_LABEL
+TP_LABEL = f"TP1 atins {LUNI} luni mai târziu — „aproximativ un an\" în material "\
+ "(ziua exactă e neclară în transcriere)"
+assert "aproximativ un an" in TP_LABEL
levels = [
- Level(ENTRY, "intrare 117.43", "#188a3e", label_at="left"),
+ Level(ENTRY, "intrare 117.43", "#188a3e", label_at="right"),
Level(TP, "TP1 129.17 (+10 %)", "#1a5fb4"),
]
@@ -101,6 +100,8 @@ assert len(levels) == 2, "doar intrare și TP; strategic nu există stop standar
# etichetele lungi merg ca linii de text sub grafic, ca să nu se bată cu
# candelele din dreapta-sus (unde urcarea spre TP nu lasă loc liber)
+cds[TP_I - 2].note = ("trigger-ul (BIM + candela verde daily) se petrece în interiorul "
+ "lunii de intrare — vezi diagrama pasului 4")
cds[TP_I - 1].note = "după TP: capital + profit → cash până la următorul trigger valid"
cds[TP_I].note = TP_LABEL
@@ -110,16 +111,17 @@ ch = Chart(
marks=marks,
mas=[MA(20, "#b06000", dash="4 3")],
scale=9.5, width=660, gap=15, body_w=9, left=40, vol_h=70,
- vol_label="volum", title="SPY — weekly: intrare 117.43 (27.01.2005) → TP +10 % = 129.17",
- caption="Swing high ~123.00 construit de mine; cifrele de intrare/TP sunt din material.",
+ vol_label="volum", title="SPY — monthly: intrare ianuarie 2005 la 117.43 → TP1 +10 % = 129.17",
+ caption="Grafic lunar, ca să încapă orizontul poziției. Swing high-ul e al meu; "
+ "intrarea și ținta sunt din material.",
)
svg = ch.render()
assert svg.count("\n\n") == 0, "linie goală în SVG — rupe markdown-ul"
OUT.write_text(svg)
-print(f"serie {len(cds)} candele | entry {entry:.2f} @ {ENTRY_I} | "
+print(f"serie {len(cds)} candele lunare | entry {entry:.2f} @ {ENTRY_I} | "
f"tp {tp:.2f} @ {TP_I} | swing high {swing_high:.2f} "
f"(corecție {(swing_high - entry) / swing_high * 100:.2f} %) | "
- f"entry→TP {n_entry_to_tp} candele (~1 an)")
+ f"entry→TP {LUNI} luni")
print(f"scris {OUT}")
diff --git a/gen/strategii/atmi_exemplu.svg b/gen/strategii/atmi_exemplu.svg
index 56917a0..4e12c44 100644
--- a/gen/strategii/atmi_exemplu.svg
+++ b/gen/strategii/atmi_exemplu.svg
@@ -1,10 +1,10 @@
-
- SPY — weekly: intrare 117.43 (27.01.2005) → TP +10 % = 129.17
+
+ SPY — monthly: intrare ianuarie 2005 la 117.43 → TP1 +10 % = 129.17
- intrare 117.43
+ intrare 117.43
TP1 129.17 (+10 %)
-
+
@@ -41,46 +41,32 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -107,25 +93,19 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
volum
swing high ~123.00
BIM
27.01.2005
- TP1
- după TP: capital + profit → cash până la următorul trigger valid
- TP1 atins ~1 an mai târziu (data exactă neclară în transcriere — ASR)
- Swing high ~123.00 construit de mine; cifrele de intrare/TP sunt din material.
+ TP1
+ trigger-ul (BIM + candela verde daily) se petrece în interiorul lunii de intrare — vezi diagrama pasului 4
+ după TP: capital + profit → cash până la următorul trigger valid
+ TP1 atins 13 luni mai târziu — „aproximativ un an" în material (ziua exactă e neclară în transcriere)
+ Grafic lunar, ca să încapă orizontul poziției. Swing high-ul e al meu; intrarea și ținta sunt din material.
\ No newline at end of file
diff --git a/gen/strategii/atmi_perechi_tf.py b/gen/strategii/atmi_perechi_tf.py
new file mode 100644
index 0000000..a05d295
--- /dev/null
+++ b/gen/strategii/atmi_perechi_tf.py
@@ -0,0 +1,260 @@
+#!/usr/bin/env python3
+"""ATMI — Pasul 3: nivelul trăiește pe weekly, declanșatorul pe daily.
+
+Panoul din stânga (weekly) arată de unde pleacă corecția și unde stă zona de
+cumpărare. Panoul din dreapta NU e o serie care vine după el: e
+**descompunerea reală** a ultimelor 3 candele weekly — fiecare candelă
+weekly = 5 bare daily, cu open, close, max(high) și min(low) care coincid,
+verificat bară cu bară.
+
+Sursa de adevăr e seria daily (`FINE`); candelele weekly din casetă se
+calculează din ea prin agregare, deci corespondența nu poate rămâne în urmă.
+
+Nivelul comun e PPQ 95.80 (calculat în `atmi_pivoti.py`): e desenat pe
+ambele panouri, cu același număr, și legat printr-un conector — scările au
+pante diferite, e o lupă, deci același preț cade la înălțimi diferite.
+
+Rulare: python3 gen/strategii/atmi_perechi_tf.py
+"""
+from pathlib import Path
+
+OUT = Path(__file__).with_suffix(".svg")
+W = 720
+K = 5 # 5 zile de bursă într-o săptămână
+NZ = 3 # câte candele weekly se sparg în dreapta
+PPQ, SQ = 95.80, 90.80 # din atmi_pivoti.py
+SWING = 100.00
+
+GREEN, RED, GRAY, INK, BLUE = "#188a3e", "#c00", "#888", "#333", "#1a5fb4"
+PURPLE, CYAN, LINK = "#7b2fbf", "#0a7f9e", "#aaa"
+
+# --- seria daily: 15 bare = 3 săptămâni ------------------------------------
+D_CLOSES = [98.60, 98.20, 97.90, 97.60, 97.90, # săptămâna 1
+ 97.50, 97.10, 96.80, 97.00, 96.60, # săptămâna 2
+ 96.30, 96.00, 95.90, 95.70, 96.00] # săptămâna 3 (13 = BIM, 14 = intrare)
+D_START = 98.90
+BIM_I, ENTRY_I = 13, 14
+
+wick = [(0.15, 0.15)] * 15
+wick[BIM_I] = (0.10, 0.15) # bara de BIM coboară sub PPQ: 95.70 - 0.15 = 95.55
+wick[ENTRY_I] = (0.15, 0.05)
+
+FINE, o = [], D_START
+for i, c in enumerate(D_CLOSES):
+ wu, wd = wick[i]
+ FINE.append((o, max(o, c) + wu, min(o, c) - wd, c))
+ o = c
+
+# --- barele weekly dinaintea lupei ----------------------------------------
+# (open, high, low, close); ultima închide exact pe open-ul primei bare daily
+BASE = [(93.60, 94.80, 93.30, 94.50), (94.50, 95.90, 94.20, 95.60),
+ (95.60, 97.10, 95.30, 96.80), (96.80, 98.30, 96.50, 98.00),
+ (98.00, 99.50, 97.70, 99.20), (99.20, 100.00, 98.90, 100.00),
+ (100.00, 100.00, 98.60, 98.90)]
+SWING_I = 5
+
+
+def aggregate(fine, k, n):
+ out_ = []
+ for j in range(n):
+ part = fine[j * k:(j + 1) * k]
+ out_.append((part[0][0], max(b[1] for b in part),
+ min(b[2] for b in part), part[-1][3]))
+ return out_
+
+
+ZOOM = aggregate(FINE, K, NZ)
+BIG = BASE + ZOOM
+
+# --- ce trebuie să fie adevărat -------------------------------------------
+eq = lambda a, b: abs(a - b) < 0.005 # noqa: E731
+assert len(FINE) == K * NZ
+for name, bars in (("weekly", BIG), ("daily", FINE)):
+ for i, (o_, h, l, c) in enumerate(bars):
+ assert h >= max(o_, c) and l <= min(o_, c), f"{name}[{i}] h/l incoerent"
+ if i:
+ assert eq(o_, bars[i - 1][3]), f"{name}[{i}] open != close precedent"
+# lupa se leagă de restul graficului mare
+assert eq(FINE[0][0], BASE[-1][3]), "open-ul primei bare daily != close-ul ultimei weekly"
+# ce pretinde textul: corecția se întinde peste mai multe candele weekly, iar
+# trigger-ul cade în ultima
+assert eq(max(b[1] for b in BASE), SWING), max(b[1] for b in BASE)
+assert BIM_I // K == ENTRY_I // K == NZ - 1, "BIM și intrarea nu cad în aceeași săptămână"
+assert (BIM_I // K) > 0, "trigger-ul ar încăpea în prima candelă weekly a lupei"
+ENTRY = FINE[ENTRY_I][3]
+assert eq(ENTRY, 96.00) and eq((SWING - ENTRY) / SWING * 100, 4.0), ENTRY
+assert FINE[ENTRY_I][3] > FINE[ENTRY_I][0], "candela de intrare nu e verde"
+assert FINE[BIM_I][2] < PPQ < ENTRY, "prețul nu intră în zona PPQ înainte de intrare"
+assert eq(ZOOM[-1][3], ENTRY), "close-ul săptămânii = close-ul barei daily de intrare"
+assert eq(ZOOM[-1][2], FINE[BIM_I][2]), "low-ul săptămânii != low-ul barei de BIM"
+assert min(b[2] for b in BIG) > SQ, "seria desenată atinge deja SQ"
+# cât de aproape e fundul de PPQ, în procente — asta face zona „atinsă"
+DIST = abs(FINE[BIM_I][2] - PPQ) / PPQ * 100
+assert DIST < 0.5, DIST
+
+# --- desen -----------------------------------------------------------------
+PLOT_H = 190
+BX0, BGAP = 132, 16
+SX0, SGAP = 392, 14
+BODY_W = 9
+FRAME_L = SX0 - 12
+COL_X = 608
+MIDX = FRAME_L - 20
+PAD = 0.07
+
+out, LABELS = [], []
+
+
+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.5 if size < 12 else 5.7)
+ x0 = x if anchor == "start" else (x - w / 2 if anchor == "middle" else x - w)
+ assert 4 <= x0 and x0 + w <= W - 4, f"text iese din viewBox: {s!r} ({x0:.0f}..{x0 + w:.0f})"
+ LABELS.append((x0, x0 + w, y, s))
+ out.append(f'{s}')
+
+
+def hline(x1, x2, y, color, dash, width=1.5):
+ out.append(f'')
+
+
+def line(x1, y1, x2, y2, color, dash="", width=1.2, marker=""):
+ d = f' stroke-dasharray="{dash}"' if dash else ""
+ m = f' marker-end="url(#{marker})"' if marker else ""
+ out.append(f'')
+
+
+def rect(x0_, y0_, x1, y1, color, fill_op=0.10, dash=""):
+ d = f' stroke-dasharray="{dash}"' if dash else ""
+ out.append(f'')
+
+
+def candles(bars, x0, gap, yf):
+ for i, (o_, h, l, c) in enumerate(bars):
+ cx = x0 + i * gap
+ line(cx, yf(h), cx, yf(l), "#1a1a1a", width=1.2)
+ top_ = yf(max(o_, c))
+ hh = max(yf(min(o_, c)) - top_, 1.6)
+ out.append(f'')
+
+
+out.append(''
+ '')
+text(20, 22, "Nivelul trăiește pe weekly, declanșatorul pe daily", bold=True, size=13)
+text(20, 38, f"Fiecare candelă weekly din casetă = {K} bare daily (o săptămână de bursă):",
+ fill="#555", size=11)
+text(20, 52, "același open, același close, max(high) și min(low) identice.",
+ fill="#555", size=11)
+
+top = 104
+text(20, top - 12, "weekly: de unde pleacă corecția", fill="#555", size=11)
+text(FRAME_L, top - 12, f"daily: ultimele {NZ} candele weekly, desfăcute", fill="#555", size=11)
+
+zlo_, zhi_ = min(b[2] for b in FINE), max(b[1] for b in FINE)
+pad = PAD * (zhi_ - zlo_)
+lo, hi = zlo_ - pad, zhi_ + pad
+
+bmax, bmin = max(b[1] for b in BIG), min(min(b[2] for b in BIG), SQ, lo)
+bs, ss = PLOT_H / (bmax - bmin), PLOT_H / (hi - lo)
+yb = lambda p: top + (bmax - p) * bs # noqa: E731
+ys = lambda p: top + (hi - p) * ss # noqa: E731
+
+# ---- panoul weekly ---------------------------------------------------------
+lx0, lx1 = BX0 + len(BASE) * BGAP - 7, BX0 + (len(BIG) - 1) * BGAP + 9
+ly0, ly1 = yb(hi), yb(lo)
+rect(lx0, ly0, lx1, ly1, BLUE, fill_op=0.06, dash="4 3")
+hline(BX0 - 22, lx1, yb(PPQ), PURPLE, "4 3")
+hline(BX0 - 22, lx1, yb(SQ), GREEN, "4 3")
+hline(BX0 - 22, lx1, yb(SWING), "#555", "3 3", width=1.0)
+text(24, yb(SWING) + 13, f"swing high {SWING:.2f}", fill="#555", size=11)
+text(24, yb(PPQ) - 6, f"PPQ {PPQ:.2f}", fill=PURPLE, size=11, bold=True)
+text(24, yb(SQ) - 6, f"SQ {SQ:.2f}", fill=GREEN, size=11, bold=True)
+text(24, yb(SQ) + 13, "zona 2, dacă", fill=GREEN, size=10)
+text(24, yb(SQ) + 25, "panica merge", fill=GREEN, size=10)
+text(24, yb(SQ) + 37, "mai jos", fill=GREEN, size=10)
+candles(BIG, BX0, BGAP, yb)
+# paranteza corecției: de la swing high până la intrare, pe panoul mare
+brx = BX0 - 16
+out.append(f'')
+text(brx - 3, (yb(SWING) + yb(ENTRY)) / 2 + 4,
+ f"-{(SWING - ENTRY) / SWING * 100:.1f} %".replace(".", ","),
+ fill=RED, anchor="end", bold=True, size=11)
+text(BX0 + 2 * BGAP, top + PLOT_H + 20,
+ "corecția de minim 4 % se măsoară aici, pe weekly", fill=RED, size=11)
+
+# ---- ghidajele de lupă -----------------------------------------------------
+guides = [((lx1, ly0), (FRAME_L, top)), ((lx1, ly1), (FRAME_L, top + PLOT_H))]
+for a_, b_ in guides:
+ line(a_[0], a_[1], b_[0], b_[1], BLUE, dash="5 3", width=1.1)
+
+# ---- conectorul nivelului comun -------------------------------------------
+ycb, ycs = yb(PPQ), ys(PPQ)
+mid = (lx1 + FRAME_L) / 2
+out.append(f'')
+text(mid + 4, (ycb + ycs) / 2 + 4, "același", fill="#999", size=10)
+text(mid + 4, (ycb + ycs) / 2 + 16, "preț", fill="#999", size=10)
+
+# ---- panoul daily ----------------------------------------------------------
+frame_r = SX0 + (len(FINE) - 1) * SGAP + 10
+rect(FRAME_L, top, frame_r, top + PLOT_H, BLUE, fill_op=0.03)
+y_ppq, y_e = ys(PPQ), ys(ENTRY)
+assert top + PLOT_H - ys(zlo_) > 6, "minimul e lipit de marginea de jos a cadrului"
+assert ys(zhi_) - top > 6, "maximul e lipit de marginea de sus a cadrului"
+# zona de decizie: bara de BIM + candela verde
+sx0, sx1 = SX0 + BIM_I * SGAP - 10, SX0 + ENTRY_I * SGAP + 10
+rect(sx0, ys(FINE[ENTRY_I][1]) - 6, sx1, ys(min(b[2] for b in FINE)) + 6, GREEN)
+hline(FRAME_L, frame_r, y_ppq, PURPLE, "4 3")
+hline(FRAME_L, frame_r, y_e, GREEN, "6 3")
+candles(FINE, SX0, SGAP, ys)
+text(COL_X, y_ppq + 4, f"PPQ {PPQ:.2f}", fill=PURPLE, size=10, bold=True)
+text(COL_X, y_ppq + 16, "același nivel,", fill=PURPLE, size=10)
+text(COL_X, y_ppq + 28, "văzut de aproape", fill=PURPLE, size=10)
+text(COL_X, y_e - 8, f"intrare {ENTRY:.2f}", fill=GREEN, size=10, bold=True)
+line(SX0 + BIM_I * SGAP, ys(FINE[BIM_I][2]) + 4, SX0 + BIM_I * SGAP,
+ top + PLOT_H + 14, CYAN, width=1)
+text(SX0 + BIM_I * SGAP - 6, top + PLOT_H + 28, "BIM: aici se oprește vânzarea",
+ fill=CYAN, anchor="middle", size=11, bold=True)
+text(SX0 + BIM_I * SGAP - 6, top + PLOT_H + 42,
+ f"(fundul e la {DIST:.2f} % de PPQ)".replace(".", ","),
+ fill=CYAN, anchor="middle", size=10)
+text(20, top + PLOT_H + 58,
+ "Pe weekly nu vezi unde s-a oprit căderea; pe daily, da — de aceea intri de aici.",
+ fill="#555", size=11)
+
+# --- prețurile rămân fiecare pe panoul lui, cu excepția nivelului comun ----
+COMUN = {f"{PPQ:.2f}"}
+MARE = {f"{SWING:.2f}", f"{SQ:.2f}"} - COMUN
+MIC = {f"{ENTRY:.2f}"} - COMUN
+for x0_, _x1, _y, s_ in LABELS:
+ rau = MARE if x0_ >= MIDX else MIC
+ for v in rau:
+ assert v not in s_, f"eticheta {s_!r} poartă prețul {v} al celuilalt panou"
+st_ = [s for x0_, _x1, _y, s in LABELS if f"{PPQ:.2f}" in s and x0_ < MIDX]
+dr_ = [s for x0_, _x1, _y, s in LABELS if f"{PPQ:.2f}" in s and x0_ >= MIDX]
+assert st_ and dr_, f"nivelul comun {PPQ} lipsește de pe un panou ({st_} / {dr_})"
+
+H = top + PLOT_H + 76
+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'\n ' + "\n ".join(out) + "\n")
+assert "\n\n" not in svg and ".md:" not in svg
+OUT.write_text(svg, encoding="utf-8")
+print(f"weekly {len(BIG)} bare (ultimele {NZ} = {len(FINE)} daily) | swing {SWING:.2f} "
+ f"| PPQ {PPQ} | intrare {ENTRY:.2f} | fund {min(b[2] for b in FINE):.2f} "
+ f"({DIST:.2f} % sub PPQ) | scris {OUT.name} ({W}x{H:.0f})")
diff --git a/gen/strategii/atmi_perechi_tf.svg b/gen/strategii/atmi_perechi_tf.svg
new file mode 100644
index 0000000..c094257
--- /dev/null
+++ b/gen/strategii/atmi_perechi_tf.svg
@@ -0,0 +1,88 @@
+
+
+ Nivelul trăiește pe weekly, declanșatorul pe daily
+ Fiecare candelă weekly din casetă = 5 bare daily (o săptămână de bursă):
+ același open, același close, max(high) și min(low) identice.
+ weekly: de unde pleacă corecția
+ daily: ultimele 3 candele weekly, desfăcute
+
+
+
+
+ swing high 100.00
+ PPQ 95.80
+ SQ 90.80
+ zona 2, dacă
+ panica merge
+ mai jos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -4,0 %
+ corecția de minim 4 % se măsoară aici, pe weekly
+
+
+
+ același
+ preț
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PPQ 95.80
+ același nivel,
+ văzut de aproape
+ intrare 96.00
+
+ BIM: aici se oprește vânzarea
+ (fundul e la 0,26 % de PPQ)
+ Pe weekly nu vezi unde s-a oprit căderea; pe daily, da — de aceea intri de aici.
+
\ No newline at end of file
diff --git a/gen/strategii/atmi_pivoti.py b/gen/strategii/atmi_pivoti.py
new file mode 100644
index 0000000..c6b9d15
--- /dev/null
+++ b/gen/strategii/atmi_pivoti.py
@@ -0,0 +1,117 @@
+"""ATMI — Pasul 1: PPQ și SQ, calculați din trimestrul precedent.
+
+Un singur grafic daily care acoperă sfârșitul trimestrului precedent (zona
+gri) și începutul celui nou. Din H/L/C-ul trimestrului precedent ies, prin
+formula standard de pivot point, cele două niveluri pe care ATMI cumpără:
+PPQ (pivotul trimestrial) și SQ (suportul trimestrial, S1). Liniile se
+prelungesc peste trimestrul nou — acolo le folosești.
+
+Cifrele PPQ/SQ sunt COMPLETARE PROPRIE: cursul spune explicit că nu dă
+valorile lor. Formula e cea confirmată „1 la 1" în material. Aici sunt
+calculate în cod din seria desenată, nu scrise de mână.
+
+Rulează: python3 gen/strategii/atmi_pivoti.py
+"""
+import sys
+from pathlib import Path
+
+sys.path.insert(0, "/workspace/atm-curs-dl")
+from svg_candles import Chart, Level, Mark, Zone, Arrow, from_closes # noqa: E402
+
+OUT = Path(__file__).resolve().parent / "atmi_pivoti.svg"
+GRAY, RED, GREEN, BLUE, ORANGE, INK = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#333"
+PURPLE = "#7b2fbf"
+
+# --- seria: 22 de bare din trimestrul precedent + 12 din cel nou -----------
+# Trimestrul precedent trebuie să aibă H = 100.80, L = 90.40, C = 96.20.
+Q_PREV = [95.40, 94.10, 93.00, 92.10, 90.90, 90.70, 92.20, 93.60, 95.00, 96.40,
+ 97.90, 99.20, 100.40, 99.80, 98.60, 97.40, 96.20, 95.10, 95.90, 96.80,
+ 97.30, 96.20]
+Q_NOU = [97.00, 97.70, 98.40, 99.00, 99.60, 100.00, 99.20, 98.30, 97.40, 96.90,
+ 96.50, 96.20]
+CLOSES = Q_PREV + Q_NOU
+NP = len(Q_PREV)
+assert len(CLOSES) == 34
+
+wicks = [(0.25, 0.25)] * len(CLOSES)
+wicks[4] = (0.20, 0.50) # minimul trimestrului precedent: 90.90 - 0.50 = 90.40
+wicks[12] = (0.40, 0.20) # maximul trimestrului precedent: 100.40 + 0.40 = 100.80
+wicks[NP - 1] = (0.20, 0.20)
+wicks[NP] = (0.25, 0.00) # prima bară a trimestrului nou pleacă de pe close-ul C
+wicks[NP + 5] = (0.00, 0.25) # swing high-ul trimestrului nou: exact 100.00
+wicks[NP + 6] = (0.00, 0.25) # nicio bară de după nu depășește swing high-ul
+wicks[-3] = (0.20, 0.00)
+wicks[-2] = (0.20, 0.00)
+wicks[-1] = (0.15, 0.20) # low-ul ultimei bare: 96.20 - 0.20 = 96.00
+
+cds = from_closes(CLOSES, wicks=wicks,
+ vols=[40 + (i * 7) % 33 for i in range(len(CLOSES))], start=96.00)
+
+# --- pivoții, calculați din seria desenată ---------------------------------
+H = round(max(c.h for c in cds[:NP]), 2)
+L = round(min(c.l for c in cds[:NP]), 2)
+C = round(cds[NP - 1].c, 2)
+PP = round((H + L + C) / 3, 2)
+S1 = round(2 * PP - H, 2)
+R1 = round(2 * PP - L, 2)
+
+assert (H, L, C) == (100.80, 90.40, 96.20), (H, L, C)
+assert PP == 95.80, PP
+assert S1 == 90.80, S1
+assert R1 == 101.20, R1
+assert S1 < PP < R1
+assert abs(S1 - L) > 0.3, "S1 a ieșit chiar pe low — ar părea o coincidență, nu un calcul"
+
+# ce afirmă textul capitolului despre trimestrul NOU
+SWING = round(max(c.h for c in cds[NP:]), 2)
+FUND = round(min(c.l for c in cds[NP:]), 2)
+assert abs(SWING - 100.00) < 1e-9, SWING
+ZONA = (PP - 0.30, PP + 0.30) # banda de cumpărare din jurul pivotului
+assert abs(FUND - 96.00) < 1e-9, FUND
+assert ZONA[0] <= FUND <= ZONA[1], (FUND, ZONA) # ultima bară intră în zona PPQ
+assert abs((SWING - 96.00) / SWING * 100 - 4.0) < 0.05 # corecție de exact 4,0 %
+assert all(c.l > S1 for c in cds[NP:]), "trimestrul nou nu trebuie să atingă SQ"
+
+levels = [
+ Level(PP, f"PPQ {PP:.2f} — zona 1", PURPLE),
+ Level(S1, f"SQ {S1:.2f} — zona 2", GREEN),
+]
+marks = [
+ Mark(12, f"H {H:.2f}", INK, "above"),
+ Mark(4, f"L {L:.2f}", INK, "below"),
+ Mark(NP - 1, f"C {C:.2f}", INK, "above", dy=-16),
+ Mark(NP + 5, f"swing high {SWING:.2f}", RED, "above"),
+ Mark(len(cds) - 4, "corecție 4,0 % — prețul intră în zona PPQ: treci la pasul 2",
+ PURPLE, "below", dy=34),
+]
+arrows = []
+zones = [Zone(0, NP - 1, "", GRAY), Zone(NP, len(cds) - 1, "", BLUE)]
+
+ch = Chart(candles=cds, levels=levels, marks=marks, zones=zones, arrows=arrows,
+ scale=13.0, gap=16, body_w=9, left=52, width=720, vol_h=60, top=124,
+ label_gap=54,
+ caption="Cifrele PPQ/SQ sunt completarea mea: cursul dă regula, nu valorile. "
+ "Formula e cea standard, confirmată în material pentru pivoți.")
+svg = ch.render()
+
+# formula, scrisă cu cifrele desenului, ca să nu rămână o regulă abstractă
+form = [f"PP = (H + L + C) / 3 = ({H:.2f} + {L:.2f} + {C:.2f}) / 3 = {PP:.2f} → PPQ",
+ f"S1 = 2 x PP - H = 2 x {PP:.2f} - {H:.2f} = {S1:.2f} → SQ",
+ f"R1 = 2 x PP - L = 2 x {PP:.2f} - {L:.2f} = {R1:.2f}"]
+box = [f''
+ f'SPY — daily: pivoții trimestrului nou, calculați din trimestrul precedent',
+ f'',
+ f''
+ f'trimestrul precedent',
+ f''
+ f'trimestrul nou: aici tranzacționezi']
+for i, s in enumerate(form):
+ box.append(f'{s}')
+
+i = svg.index(">") + 1
+svg = svg[:i] + "\n " + "\n ".join(box) + svg[i:]
+assert "\n\n" not in svg
+assert ".md:" not in svg
+OUT.write_text(svg)
+print(f"scris {OUT.name}: H {H} L {L} C {C} -> PPQ {PP}, SQ {S1}, R1 {R1}")
diff --git a/gen/strategii/atmi_pivoti.svg b/gen/strategii/atmi_pivoti.svg
new file mode 100644
index 0000000..5aeb1cd
--- /dev/null
+++ b/gen/strategii/atmi_pivoti.svg
@@ -0,0 +1,125 @@
+
+ SPY — daily: pivoții trimestrului nou, calculați din trimestrul precedent
+
+ trimestrul precedent
+ trimestrul nou: aici tranzacționezi
+ PP = (H + L + C) / 3 = (100.80 + 90.40 + 96.20) / 3 = 95.80 → PPQ
+ S1 = 2 x PP - H = 2 x 95.80 - 100.80 = 90.80 → SQ
+ R1 = 2 x PP - L = 2 x 95.80 - 90.40 = 101.20
+
+
+
+ PPQ 95.80 — zona 1
+
+ SQ 90.80 — zona 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ volum
+ H 100.80
+ L 90.40
+ C 96.20
+ swing high 100.00
+ corecție 4,0 % — prețul intră în zona PPQ: treci la pasul 2
+ Cifrele PPQ/SQ sunt completarea mea: cursul dă regula, nu valorile. Formula e cea standard, confirmată în material pentru pivoți.
+
\ No newline at end of file
diff --git a/gen/strategii/atmi_reinvestire.py b/gen/strategii/atmi_reinvestire.py
new file mode 100644
index 0000000..048036e
--- /dev/null
+++ b/gen/strategii/atmi_reinvestire.py
@@ -0,0 +1,134 @@
+#!/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, "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'\n ' + "\n ".join(out) + "\n")
+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})")
diff --git a/gen/strategii/atmi_reinvestire.svg b/gen/strategii/atmi_reinvestire.svg
new file mode 100644
index 0000000..4ac9e90
--- /dev/null
+++ b/gen/strategii/atmi_reinvestire.svg
@@ -0,0 +1,38 @@
+
+
+ Ciclul ATMI: ieși cu tot la +10 %, stai în cash, reintri cu tot plus contribuția
+ Contribuție 600 $ la fiecare trigger valid; ținta +10 % pe poziție.
+ ciclul 1
+
+ 600 $
+ intri cu tot
+
+ 660,00 $
+ ieși la +10 %
+
+
+ cash, apoi + 600 $ nou
+ ciclul 2
+
+ 1.260 $
+ intri cu tot
+
+ 1.386,00 $
+ ieși la +10 %
+
+
+ cash, apoi + 600 $ nou
+ ciclul 3
+
+ 1.986 $
+ intri cu tot
+
+ 2.184,60 $
+ ieși la +10 %
+
+
+
+ Ai pus în total 1.800 $, în trei contribuții egale.
+ Cu reinvestire integrală: 2.184,60 $ · dacă lași profitul deoparte: 1.980 $ · diferența: 204,60 $
+ Cifrele astea sunt calculul meu pe trei cicluri, nu backtest-ul cursului.
+
\ No newline at end of file
diff --git a/gen/strategii/atmi_risc.py b/gen/strategii/atmi_risc.py
new file mode 100644
index 0000000..b3d9375
--- /dev/null
+++ b/gen/strategii/atmi_risc.py
@@ -0,0 +1,100 @@
+"""ATMI — Pasul 5: ce înseamnă „fără stop-loss", desenat.
+
+Același trade ca în restul capitolului (intrare 96.00, țintă +10 % = 105.60),
+dar arătând drumul, nu doar capetele: între intrare și țintă poziția trece
+printr-un drawdown — pierdere flotantă — de 20 % pe poziție, fără ca nimic
+să te scoată din ea. Cifra de 20-30 % pe poziție fără levier e a cursului
+(COVID 2020, 2018); traseul barelor e construit de mine.
+
+Coloana din dreapta traduce aceeași cădere de 20 % a indicelui în pierdere
+pe poziție, pe trei variante de instrument — calculul e în cod.
+
+Rulează: python3 gen/strategii/atmi_risc.py
+"""
+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 / "atmi_risc.svg"
+GRAY, RED, GREEN, BLUE, INK, PURPLE = "#777", "#c00", "#188a3e", "#1a5fb4", "#333", "#7b2fbf"
+
+ENTRY, TP, PPQ = 96.00, 105.60, 95.80
+DD_PCT = 20.0 # drawdown pe poziție, fără levier
+FUND = round(ENTRY * (1 - DD_PCT / 100), 2) # 76.80
+
+# 3 bare de context + intrarea + 14 bare în jos + 17 bare de revenire
+CLOSES = ([98.20, 97.10, 96.40, ENTRY]
+ + [94.60, 92.80, 90.90, 89.40, 90.70, 88.60, 86.30, 84.40,
+ 85.60, 83.10, 81.00, 79.20, 78.10, FUND]
+ + [78.40, 80.60, 82.90, 84.50, 86.80, 88.40, 90.10, 92.30,
+ 94.00, ENTRY, 97.80, 99.40, 100.90, 102.20, 103.40, 104.50, TP])
+ENTRY_I, FUND_I, BE_I, TP_I = 3, 17, 27, len(CLOSES) - 1
+assert CLOSES[ENTRY_I] == ENTRY and CLOSES[FUND_I] == FUND
+assert CLOSES[BE_I] == ENTRY and CLOSES[TP_I] == TP
+
+wicks = [(0.35, 0.35)] * len(CLOSES)
+wicks[ENTRY_I] = (0.30, 0.10)
+wicks[FUND_I] = (0.30, 0.00) # fundul e exact -20 %
+wicks[FUND_I + 1] = (0.35, 0.00) # nicio bară de după nu coboară sub fund
+wicks[TP_I] = (0.00, 0.35) # ținta e exact maximul seriei
+cds = from_closes(CLOSES, wicks=wicks, vols=[44 + (i * 11) % 41 for i in range(len(CLOSES))],
+ start=98.90)
+
+# --- ce susține textul -----------------------------------------------------
+fund = round(min(c.l for c in cds), 2)
+assert fund == FUND, fund
+assert round((ENTRY - fund) / ENTRY * 100, 1) == DD_PCT
+assert round(max(c.h for c in cds), 2) == TP
+assert round(TP / ENTRY, 3) == 1.100
+assert cds[FUND_I].l == fund and all(c.l >= fund for c in cds)
+assert FUND < PPQ, "fundul trebuie să fie sub pivotul pe care ai cumpărat"
+assert BE_I > FUND_I and CLOSES[BE_I] == ENTRY, "lipsește revenirea pe zero"
+DURATA_JOS = BE_I - ENTRY_I # câte bare stai sub prețul de intrare
+assert DURATA_JOS == 24, DURATA_JOS
+
+# aceeași cădere de 20 % pe indice, tradusă pe trei instrumente
+VARIANTE = [("ETF simplu (SPY)", 1), ("ETF cu levier 2x", 2), ("ETF cu levier 3x (SPXL)", 3)]
+PIERDERI = [(n, round(DD_PCT * k, 1)) for n, k in VARIANTE]
+assert PIERDERI[-1][1] == 60.0, PIERDERI
+COVID = 34.0 # SPY în 2020, cifra cursului
+assert round(COVID * 3, 1) > 100.0, "3x pe -34 % trebuie să treacă de -100 %"
+
+levels = [
+ Level(TP, f"TP +10 % = {TP:.2f}", BLUE),
+ Level(ENTRY, f"intrare {ENTRY:.2f}", GREEN, label_at="right"),
+ Level(fund, f"fund {fund:.2f} = -{DD_PCT:.0f} % pe poziție", RED, label_at="left"),
+]
+marks = [
+ Mark(FUND_I, f"-{DD_PCT:.0f} %: nimic nu te scoate", RED, "below"),
+ Mark(BE_I, "abia aici ești pe zero", GRAY, "above", dy=-22),
+ Mark(TP_I, "TP", BLUE, "below"),
+]
+
+ch = Chart(candles=cds, levels=levels, marks=marks,
+ scale=5.6, gap=16, body_w=9, left=56, width=720, vol_h=60, top=152,
+ label_gap=46,
+ caption="Cifra de 20-30 % pe poziție fără levier e a cursului; traseul barelor "
+ "e construit de mine, ca să se vadă cât durează.")
+svg = ch.render()
+
+titlu = (''
+ 'SPY — daily: fără stop, între intrare și țintă treci prin toată căderea')
+sub = (f''
+ f'{DURATA_JOS} de bare sub prețul de intrare, fără niciun ordin care să te scoată '
+ f'— ăsta e costul regulii „fără stop-loss".')
+box = [f'',
+ f''
+ f'aceeași cădere de {DD_PCT:.0f} % pe indice, pe poziție:']
+for i, (nume, pct) in enumerate(PIERDERI):
+ box.append(f''
+ f'{nume}: -{pct:.0f} %')
+
+i = svg.index(">") + 1
+svg = svg[:i] + "\n " + "\n ".join([titlu, sub] + box) + svg[i:]
+assert "\n\n" not in svg and ".md:" not in svg
+OUT.write_text(svg)
+print(f"scris {OUT.name}: intrare {ENTRY} fund {fund} (-{DD_PCT} %) TP {TP}, "
+ f"{DURATA_JOS} bare sub apă")
diff --git a/gen/strategii/atmi_risc.svg b/gen/strategii/atmi_risc.svg
new file mode 100644
index 0000000..f0b7714
--- /dev/null
+++ b/gen/strategii/atmi_risc.svg
@@ -0,0 +1,126 @@
+
+ SPY — daily: fără stop, între intrare și țintă treci prin toată căderea
+ 24 de bare sub prețul de intrare, fără niciun ordin care să te scoată — ăsta e costul regulii „fără stop-loss".
+
+ aceeași cădere de 20 % pe indice, pe poziție:
+ ETF simplu (SPY): -20 %
+ ETF cu levier 2x: -40 %
+ ETF cu levier 3x (SPXL): -60 %
+
+ TP +10 % = 105.60
+
+ intrare 96.00
+
+ fund 76.80 = -20 % pe poziție
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ volum
+ -20 %: nimic nu te scoate
+ abia aici ești pe zero
+ TP
+ Cifra de 20-30 % pe poziție fără levier e a cursului; traseul barelor e construit de mine, ca să se vadă cât durează.
+
\ No newline at end of file
diff --git a/gen/strategii/atmi_trigger.py b/gen/strategii/atmi_trigger.py
new file mode 100644
index 0000000..778236e
--- /dev/null
+++ b/gen/strategii/atmi_trigger.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python3
+"""ATMI — Pasul 4: trigger-ul. DA (BIM, apoi candela verde) vs. NU (verde fără BIM).
+
+Două panouri, unul sub altul, cu exact aceleași patru bare de corecție. Sus:
+prețul intră în zona PPQ, apare săgeata BIM, iar prima candelă verde daily de
+după ea închide la 96.00 — acolo intri. Jos: candela verde vine ÎNAINTE de
+orice BIM, deci nu e trigger; prețul continuă să cadă spre SQ — „cuțitul în
+cădere" din curs.
+
+Numerele sunt cele ale capitolului: PPQ 95.80, SQ 90.80 (calculate în
+`atmi_pivoti.py`), intrare 96.00 la close-ul candelei verzi.
+
+Rulare: python3 gen/strategii/atmi_trigger.py
+"""
+import re
+import sys
+from pathlib import Path
+
+sys.path.insert(0, "/workspace/atm-curs-dl")
+from svg_candles import Arrow, Chart, from_closes # noqa: E402
+
+OUT = Path(__file__).with_suffix(".svg")
+W = 660
+SCALE = 46.0
+LEFT, GAP = 66, 44
+RED, GREEN, BLUE, GREY, CYAN, PURPLE = "#c00", "#188a3e", "#1a5fb4", "#555", "#0a7f9e", "#7b2fbf"
+
+PPQ, SQ, ENTRY = 95.80, 90.80, 96.00
+
+COMUN = [98.60, 97.80, 97.00, 96.40] # corecția, identică pe ambele panouri
+CLOSES_DA = COMUN + [95.70, 96.00, 96.60] # 4: BIM 5: prima verde = intrare
+CLOSES_NU = COMUN + [96.80, 96.10, 95.10, 94.00, 92.90] # 4: verde fără BIM
+
+w_da = [(0.15, 0.15)] * len(CLOSES_DA)
+w_da[4] = (0.10, 0.15) # bara BIM coboară sub PPQ: 95.55
+w_da[5] = (0.10, 0.10)
+w_nu = [(0.15, 0.15)] * len(CLOSES_NU)
+w_nu[4] = (0.10, 0.10)
+
+START = COMUN[0] + 0.30
+cds_da = from_closes(CLOSES_DA, wicks=w_da, start=START)
+cds_nu = from_closes(CLOSES_NU, wicks=w_nu, start=START)
+
+BIM_I, VERDE_DA, VERDE_NU = 4, 5, 4
+
+# --- ce trebuie să fie adevărat ca desenul să susțină textul ---------------
+ser = lambda c: (round(c.o, 2), round(c.h, 2), round(c.l, 2), round(c.c, 2)) # noqa: E731
+assert [ser(c) for c in cds_da[:4]] == [ser(c) for c in cds_nu[:4]], \
+ "panourile nu pleacă din aceeași corecție"
+# DA: BIM înainte de verde; verdele e chiar verde; close-ul lui = intrarea
+assert BIM_I < VERDE_DA
+assert cds_da[BIM_I].c < cds_da[BIM_I].o, "bara de BIM e bara de epuizare, roșie"
+assert cds_da[VERDE_DA].c > cds_da[VERDE_DA].o, "candela de intrare nu e verde"
+assert abs(cds_da[VERDE_DA].c - ENTRY) < 1e-9, cds_da[VERDE_DA].c
+# criteriul A+: candela de intrare închide DEASUPRA pivotului
+assert cds_da[VERDE_DA].c > PPQ, "candela de intrare nu închide peste PPQ"
+# prețul a intrat efectiv în zona PPQ (a coborât sub pivot și a fost respins)
+assert cds_da[BIM_I].l < PPQ < cds_da[VERDE_DA].c, (cds_da[BIM_I].l, PPQ)
+assert min(c.l for c in cds_da) == cds_da[BIM_I].l, "fundul nu e pe bara de BIM"
+# NU: verdele vine primul, și după el prețul face minime tot mai joase
+assert cds_nu[VERDE_NU].c > cds_nu[VERDE_NU].o, "bara 4 de pe panoul NU nu e verde"
+lows = [c.l for c in cds_nu[VERDE_NU + 1:]]
+assert all(b < a for a, b in zip(lows, lows[1:])), "cuțitul nu cade continuu"
+assert cds_nu[-1].c < PPQ - 2.5, "căderea de pe panoul NU e prea mică ca să se vadă"
+assert min(c.l for c in cds_nu) > SQ, "căderea desenată trece deja de SQ"
+# cele două panouri au aceeași scară, deci căderile se compară cu ochiul
+PIERDERE = round((cds_nu[-1].c - cds_nu[VERDE_NU].c) / cds_nu[VERDE_NU].c * 100, 1)
+assert PIERDERE < -3.0, PIERDERE
+
+ch_da = Chart(
+ candles=cds_da,
+ marks=[],
+ arrows=[
+ Arrow(BIM_I, "BIM: săgeata indicatorului\ncyan = momentul în care\ntrendul se schimbă",
+ where="below", color=CYAN),
+ Arrow(VERDE_DA, f"AICI INTRI, la close {ENTRY:.2f}\nînchide peste pivot, deci\n"
+ f"cumpărătorii au ținut zona",
+ price=ENTRY, where="right", color=GREEN, dx=18, dy=40),
+ ],
+ scale=SCALE, gap=GAP, body_w=13, left=LEFT, width=W,
+ title="DA — BIM, apoi prima candelă verde daily",
+)
+ch_nu = Chart(
+ candles=cds_nu,
+ marks=[],
+ arrows=[
+ Arrow(VERDE_NU, "aici NU intri: verdele a venit\nînainte de BIM, deci nimic\n"
+ "nu confirmă că vânzarea s-a oprit",
+ where="below", color=RED, dx=40),
+ Arrow(len(cds_nu) - 1, f"căderea continuă: {abs(PIERDERE):.1f} % mai jos\nde la candela verde —\n"
+ f"„cuțitul prins în cădere”",
+ where="below", color=RED, dx=-30),
+ ],
+ scale=SCALE, gap=GAP, body_w=13, left=LEFT, width=W,
+ title="NU — candelă verde înainte de BIM",
+)
+
+
+def panel(ch):
+ svg = ch._render()
+ m = re.search(r'viewBox="0 0 ([\d.]+) ([\d.]+)"', svg)
+ assert float(m.group(1)) == W, f"o etichetă iese din {W}px"
+ return svg[svg.index(">") + 1:svg.rindex("")], float(m.group(2))
+
+
+def level(ch, price, text, color, below=False):
+ y = ch._y(price)
+ out = [f'']
+ for k, s in enumerate(text):
+ ty = (y + 15 + 14 * k) if below else (y - 6 - 14 * (len(text) - 1 - k))
+ out.append(f'{s}')
+ return out
+
+
+def zone(ch, i0, i1, label, color, lift=0.0):
+ cds = ch.candles[i0:i1 + 1]
+ x0, x1 = ch._cx(i0) - 17, ch._cx(i1) + 17
+ y0 = ch._y(max(c.h for c in cds)) - 8 - lift
+ y1 = ch._y(min(c.l for c in cds)) + 8
+ return [f'',
+ f'{label}']
+
+
+body_da, h_da = panel(ch_da)
+body_nu, h_nu = panel(ch_nu)
+
+extra_da = (zone(ch_da, BIM_I, VERDE_DA, "zona de decizie: BIM + verde", BLUE, lift=16)
+ + level(ch_da, PPQ, [f"PPQ {PPQ:.2f} — pivotul trimestrial"], PURPLE, below=True))
+extra_nu = (zone(ch_nu, VERDE_NU, len(cds_nu) - 1, "fără BIM, nimic nu confirmă", RED, lift=16)
+ + level(ch_nu, PPQ, [f"PPQ {PPQ:.2f} — pivotul trimestrial"], PURPLE, below=True))
+
+CAP = ["Aceleași patru bare de corecție pe ambele panouri, aceeași scară.",
+ "PPQ 95.80 e calculat în diagrama pasului 1; intrarea 96.00 e close-ul candelei verzi."]
+assert all(len(s) * 6.3 < W - 40 for s in CAP), "caption prea lung"
+cap_top = h_da + h_nu + 20
+H = cap_top + 24
+
+svg = "\n ".join([
+ f'',
+ f'{"".join(extra_da)}{body_da}',
+ f'{"".join(extra_nu)}{body_nu}',
+ f'{CAP[0]}',
+ f'{CAP[1]}',
+ ""])
+assert "\n\n" not in svg and ".md:" not in svg
+
+TEXT = re.compile(r']*)>(.*?)', re.S)
+
+
+def labels(body, y_off, tag):
+ out = []
+ for m in TEXT.finditer(body):
+ x, y, attrs, s = float(m.group(1)), float(m.group(2)) + y_off, m.group(3), m.group(4)
+ anchor = "end" if 'text-anchor="end"' in attrs else (
+ "middle" if 'text-anchor="middle"' in attrs else "start")
+ w = len(s) * 12 * (0.62 if "bold" in attrs else 0.52)
+ lf = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
+ out.append((lf, lf + w, y, s, tag))
+ return out
+
+
+LB = (labels(body_da + "".join(extra_da), 0.0, "DA")
+ + labels(body_nu + "".join(extra_nu), h_da, "NU"))
+for lf, rt, y, s, tag in LB:
+ assert -2 <= lf and rt <= W + 2, f"[{tag}] text iese din panză: {s!r}"
+ assert 0 <= y <= H, f"[{tag}] text în afara înălțimii: {s!r}"
+for i in range(len(LB)):
+ for j in range(i + 1, len(LB)):
+ li, ri, yi, si, ti = LB[i]
+ lj, rj, yj, sj, tj = LB[j]
+ assert not (ti == tj and abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4), \
+ f"[{ti}] texte suprapuse: {si!r} / {sj!r}"
+
+# niciun preț scris într-o etichetă care nu e un nivel desenat pe panoul lui
+for lf, rt, y, s, tag in LB:
+ for p in re.findall(r"\d+\.\d\d", s):
+ assert float(p) in (PPQ, ENTRY), f"[{tag}] prețul {p} din {s!r} nu e un nivel desenat"
+
+OUT.write_text(svg)
+print(f"DA: BIM la {BIM_I} (low {cds_da[BIM_I].l:.2f} < PPQ {PPQ}), intrare {ENTRY:.2f}")
+print(f"NU: verde la {VERDE_NU} fără BIM, apoi {PIERDERE:.1f} % în jos "
+ f"(ultimul close {cds_nu[-1].c:.2f}, peste SQ {SQ})")
+print(f"scris {OUT.name} ({W}x{H:.0f})")
diff --git a/gen/strategii/atmi_trigger.svg b/gen/strategii/atmi_trigger.svg
new file mode 100644
index 0000000..f59e163
--- /dev/null
+++ b/gen/strategii/atmi_trigger.svg
@@ -0,0 +1,62 @@
+
+ zona de decizie: BIM + verdePPQ 95.80 — pivotul trimestrial
+ DA — BIM, apoi prima candelă verde daily
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ BIM: săgeata indicatorului
+ cyan = momentul în care
+ trendul se schimbă
+
+
+ AICI INTRI, la close 96.00
+ închide peste pivot, deci
+ cumpărătorii au ținut zona
+
+ fără BIM, nimic nu confirmăPPQ 95.80 — pivotul trimestrial
+ NU — candelă verde înainte de BIM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ aici NU intri: verdele a venit
+ înainte de BIM, deci nimic
+ nu confirmă că vânzarea s-a oprit
+
+
+ căderea continuă: 4.0 % mai jos
+ de la candela verde —
+ „cuțitul prins în cădere”
+
+ Aceleași patru bare de corecție pe ambele panouri, aceeași scară.
+ PPQ 95.80 e calculat în diagrama pasului 1; intrarea 96.00 e close-ul candelei verzi.
+
\ No newline at end of file
diff --git a/gen/strategii/hammer_mbt10_bh_ansamblu.py b/gen/strategii/hammer_mbt10_bh_ansamblu.py
index 2971154..52b2abd 100644
--- a/gen/strategii/hammer_mbt10_bh_ansamblu.py
+++ b/gen/strategii/hammer_mbt10_bh_ansamblu.py
@@ -1,5 +1,9 @@
"""Buy hammer — diagrama de ansamblu (daily, ticker ilustrativ XYZ).
+Seria de aici e SURSA UNICĂ a trade-ului din Partea A: `hammer_mbt10_bh_stop`
+și `hammer_mbt10_bh_iesire` importă `serie()` și pornesc din aceleași bare
+(hammer 63.30 / 62.10, intrare 63.35, SL 62.05, TP 65.95 = 2R).
+
Citește specificația `gen/strategii/hammer_mbt10_bh_ansamblu.spec.txt`.
Seria de 34 de candele e construită din lista de close-uri dată, cu open-ul
fiecărei bare = close-ul precedentului; wick-urile sunt fixate ca nivelurile
@@ -10,109 +14,117 @@ import re
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Chart, Level, Mark, from_closes
-# --- seria: 34 de close-uri, exact ca în specificație ------------------------
-closes = [
- 72.00, 71.20, 71.50, 70.40, 69.60, 70.00, 68.90, 68.10, 68.40, 67.30, # 0-9
- 66.50, 66.80, 65.70, 64.90, 65.20, 64.10, 63.40, 63.70, 63.05, # 10-18
- 63.30, # 19 = hammer: o 63.05, c/h 63.30, l 62.10 (H = C, fitil sus 0)
- 63.35, # 20 = confirmare: close peste 63.30 => intrare
- 63.80, 64.20, 64.60, 64.40, 64.90, 65.20, 65.50, 65.80, 66.10, # 21-29
- 66.00, 65.70, 65.40, 65.60, # 30-33
-]
-assert len(closes) == 34
+def serie():
+ """Cele 34 de bare ale trade-ului XYZ, cu verificările specificației."""
+ # --- seria: 34 de close-uri, exact ca în specificație ------------------------
+ closes = [
+ 72.00, 71.20, 71.50, 70.40, 69.60, 70.00, 68.90, 68.10, 68.40, 67.30, # 0-9
+ 66.50, 66.80, 65.70, 64.90, 65.20, 64.10, 63.40, 63.70, 63.05, # 10-18
+ 63.30, # 19 = hammer: o 63.05, c/h 63.30, l 62.10 (H = C, fitil sus 0)
+ 63.35, # 20 = confirmare: close peste 63.30 => intrare
+ 63.80, 64.20, 64.60, 64.40, 64.90, 65.20, 65.50, 65.80, 66.10, # 21-29
+ 66.00, 65.70, 65.40, 65.60, # 30-33
+ ]
+ assert len(closes) == 34
-# wick-uri: (up, down) implicite (0.25, 0.30) + excepțiile din specificație
-wicks = [(0.25, 0.30)] * 34
-wicks[19] = (0.00, 0.95) # hammer: high 63.30 (= close), low 62.10
-# bara 20: wu=0.15 => high 63.50. Spec-ul dă wd=0.15 cu „low 63.20", dar
-# wd se măsoară de la min(o,c)=63.30, deci 0.15 dă 63.15; folosim tuplul
-# literal din specificație (0.15, 0.15), low-ul barei 20 nu e etichetat.
-wicks[20] = (0.15, 0.15)
+ # wick-uri: (up, down) implicite (0.25, 0.30) + excepțiile din specificație
+ wicks = [(0.25, 0.30)] * 34
+ wicks[19] = (0.00, 0.95) # hammer: high 63.30 (= close), low 62.10
+ # bara 20: wu=0.15 => high 63.50. Spec-ul dă wd=0.15 cu „low 63.20", dar
+ # wd se măsoară de la min(o,c)=63.30, deci 0.15 dă 63.15; folosim tuplul
+ # literal din specificație (0.15, 0.15), low-ul barei 20 nu e etichetat.
+ wicks[20] = (0.15, 0.15)
-# volum relativ ales de generator (materialul nu dă volum pe exemplul construit):
-# normal pe downtrend, în creștere pe hammer (19) și pe confirmare (20), normal după
-vols = [50, 48, 47, 46, 45, 46, 44, 43, 45, 42,
- 41, 43, 40, 39, 41, 38, 37, 39, 55,
- 90, 95,
- 58, 55, 53, 50, 52, 54, 56, 60, 64,
- 58, 55, 52, 50]
-assert len(vols) == 34
-media_vol = sum(vols) / len(vols)
-assert vols[19] > media_vol and vols[20] > media_vol, "volum peste medie pe hammer/confirmare"
+ # volum relativ ales de generator (materialul nu dă volum pe exemplul construit):
+ # normal pe downtrend, în creștere pe hammer (19) și pe confirmare (20), normal după
+ vols = [50, 48, 47, 46, 45, 46, 44, 43, 45, 42,
+ 41, 43, 40, 39, 41, 38, 37, 39, 55,
+ 90, 95,
+ 58, 55, 53, 50, 52, 54, 56, 60, 64,
+ 58, 55, 52, 50]
+ assert len(vols) == 34
+ media_vol = sum(vols) / len(vols)
+ assert vols[19] > media_vol and vols[20] > media_vol, "volum peste medie pe hammer/confirmare"
-cds = from_closes(closes, wicks=wicks, vols=vols, start=72.00)
+ cds = from_closes(closes, wicks=wicks, vols=vols, start=72.00)
-hammer, conf = cds[19], cds[20]
-# confirmarea o punem ca notă sub grafic: un Mark deasupra barei 20 s-ar
-# suprapune cu fitilul lung al hammer-ului (bara 19)
-conf.note = "bara 20: confirmare — close 63.35 peste high-ul hammer-ului 63.30 (intrare)"
-intrare, sl, tp = 63.35, 62.05, 65.95
+ hammer, conf = cds[19], cds[20]
+ # confirmarea o punem ca notă sub grafic: un Mark deasupra barei 20 s-ar
+ # suprapune cu fitilul lung al hammer-ului (bara 19)
+ conf.note = "bara 20: confirmare — close 63.35 peste high-ul hammer-ului 63.30 (intrare)"
+ intrare, sl, tp = 63.35, 62.05, 65.95
-# --- cele 8 assert-uri cerute de specificație -------------------------------
-# 1. hammer: H = C = 63.30, L = 62.10
-assert abs(cds[19].c - 63.30) < 1e-9 and abs(cds[19].h - 63.30) < 1e-9, "H != C la hammer"
-assert abs(cds[19].l - 62.10) < 1e-9, f"low[19]={cds[19].l} != 62.10"
-# 2. open[19] = close[18]; corp 0.25; fitil jos 0.95 >= 2*0.25
-assert abs(cds[19].o - cds[18].c) < 1e-9 and abs(cds[19].o - 63.05) < 1e-9, "open[19] != close[18]"
-corp = hammer.c - hammer.o
-fitil_jos = hammer.o - hammer.l
-assert abs(corp - 0.25) < 1e-9, f"corp={corp} != 0.25"
-assert abs(fitil_jos - 0.95) < 1e-9, f"fitil jos={fitil_jos} != 0.95"
-assert fitil_jos >= 2 * corp, "fitilul nu e cel puțin dublul corpului"
-# 3. confirmarea închide peste high-ul hammer-ului
-assert abs(conf.c - intrare) < 1e-9, f"close[20]={conf.c} != 63.35"
-assert conf.c > hammer.h and abs(hammer.h - 63.30) < 1e-9, "close[20] nu e peste high[19]"
-# 4. SL = low-ul hammer-ului - 0.05
-assert abs(sl - (cds[19].l - 0.05)) < 1e-9, "SL != low[19] - 0.05"
-# 5. risc 1.30, TP 2.60 = 2R exact
-assert abs(abs(intrare - sl) - 1.30) < 1e-9, "risc != 1.30"
-assert abs(abs(tp - intrare) - 2.60) < 1e-9, "TP != 2R exact"
-# 6. primul high >= TP e bara 28 (65.80 + wick 0.25 = 66.05)
-primul_tp = next(i for i in range(21, len(cds)) if cds[i].h >= tp)
-assert primul_tp == 28, f"TP 65.95 atins întâi la bara {primul_tp}, nu 28"
-# 7. downtrend vizibil
-assert cds[18].c < cds[0].c - 8, "downtrend prea mic"
-# 8. ordinea nivelurilor
-assert sl < intrare < tp, "SL / intrare / TP nu sunt în ordine"
+ # --- cele 8 assert-uri cerute de specificație -------------------------------
+ # 1. hammer: H = C = 63.30, L = 62.10
+ assert abs(cds[19].c - 63.30) < 1e-9 and abs(cds[19].h - 63.30) < 1e-9, "H != C la hammer"
+ assert abs(cds[19].l - 62.10) < 1e-9, f"low[19]={cds[19].l} != 62.10"
+ # 2. open[19] = close[18]; corp 0.25; fitil jos 0.95 >= 2*0.25
+ assert abs(cds[19].o - cds[18].c) < 1e-9 and abs(cds[19].o - 63.05) < 1e-9, "open[19] != close[18]"
+ corp = hammer.c - hammer.o
+ fitil_jos = hammer.o - hammer.l
+ assert abs(corp - 0.25) < 1e-9, f"corp={corp} != 0.25"
+ assert abs(fitil_jos - 0.95) < 1e-9, f"fitil jos={fitil_jos} != 0.95"
+ assert fitil_jos >= 2 * corp, "fitilul nu e cel puțin dublul corpului"
+ # 3. confirmarea închide peste high-ul hammer-ului
+ assert abs(conf.c - intrare) < 1e-9, f"close[20]={conf.c} != 63.35"
+ assert conf.c > hammer.h and abs(hammer.h - 63.30) < 1e-9, "close[20] nu e peste high[19]"
+ # 4. SL = low-ul hammer-ului - 0.05
+ assert abs(sl - (cds[19].l - 0.05)) < 1e-9, "SL != low[19] - 0.05"
+ # 5. risc 1.30, TP 2.60 = 2R exact
+ assert abs(abs(intrare - sl) - 1.30) < 1e-9, "risc != 1.30"
+ assert abs(abs(tp - intrare) - 2.60) < 1e-9, "TP != 2R exact"
+ # 6. primul high >= TP e bara 28 (65.80 + wick 0.25 = 66.05)
+ primul_tp = next(i for i in range(21, len(cds)) if cds[i].h >= tp)
+ assert primul_tp == 28, f"TP 65.95 atins întâi la bara {primul_tp}, nu 28"
+ # 7. downtrend vizibil
+ assert cds[18].c < cds[0].c - 8, "downtrend prea mic"
+ # 8. ordinea nivelurilor
+ assert sl < intrare < tp, "SL / intrare / TP nu sunt în ordine"
-print(f"hammer H/C {hammer.h:.2f} L {hammer.l:.2f} intrare {conf.c:.2f} "
- f"SL {sl:.2f} TP {tp:.2f} = {abs(tp-intrare)/abs(intrare-sl):.1f}R "
- f"TP atins la bara {primul_tp} (high {cds[primul_tp].h:.2f})")
+ print(f"hammer H/C {hammer.h:.2f} L {hammer.l:.2f} intrare {conf.c:.2f} "
+ f"SL {sl:.2f} TP {tp:.2f} = {abs(tp-intrare)/abs(intrare-sl):.1f}R "
+ f"TP atins la bara {primul_tp} (high {cds[primul_tp].h:.2f})")
+ return cds, hammer, conf
-# --- desenul ----------------------------------------------------------------
-ch = Chart(
- candles=cds,
- levels=[
- Level(sl, "SL 62.05 — sub low-ul hammer-ului", "#c00", label_at="left"),
- Level(intrare, "intrare 63.35", "#188a3e"),
- Level(tp, "TP 65.95 = 2R", "#1a5fb4"),
- ],
- marks=[
- Mark(19, "hammer 63.30 / 62.10", "#b06000", "below"),
- ],
- scale=32.0, gap=15, body_w=9, left=44, width=720, vol_h=70,
- vol_label="volum", title="Buy hammer — daily (ticker ilustrativ XYZ, din lecția de pattern)",
- caption="", # legenda e pusă manual, pe două rânduri, ca să nu iasă din pânză
-)
-svg = ch.render()
-assert "", "".join(
- f'{t}'
- for i, t in enumerate(legenda)) + "")
-assert "\n\n" not in svg, "linie goală în SVG după legendă"
+ # --- desenul ----------------------------------------------------------------
+ ch = Chart(
+ candles=cds,
+ levels=[
+ Level(sl, "SL 62.05 — sub low-ul hammer-ului", "#c00", label_at="left"),
+ Level(intrare, "intrare 63.35", "#188a3e"),
+ Level(tp, "TP 65.95 = 2R", "#1a5fb4"),
+ ],
+ marks=[
+ Mark(19, "hammer 63.30 / 62.10", "#b06000", "below"),
+ ],
+ scale=32.0, gap=15, body_w=9, left=44, width=720, vol_h=70,
+ vol_label="volum", title="Buy hammer — daily (ticker ilustrativ XYZ, din lecția de pattern)",
+ caption="", # legenda e pusă manual, pe două rânduri, ca să nu iasă din pânză
+ )
-with open("/workspace/atm-curs-dl/gen/strategii/hammer_mbt10_bh_ansamblu.svg", "w") as f:
- f.write(svg)
-print("scris gen/strategii/hammer_mbt10_bh_ansamblu.svg")
+ svg = ch.render()
+ assert "", "".join(
+ f'{t}'
+ for i, t in enumerate(legenda)) + "")
+ assert "\n\n" not in svg, "linie goală în SVG după legendă"
+
+ with open("/workspace/atm-curs-dl/gen/strategii/hammer_mbt10_bh_ansamblu.svg", "w") as f:
+ f.write(svg)
+ print("scris gen/strategii/hammer_mbt10_bh_ansamblu.svg")
\ No newline at end of file
diff --git a/gen/strategii/mdsrt_ansamblu.py b/gen/strategii/mdsrt_ansamblu.py
index 4114e65..a811ec2 100644
--- a/gen/strategii/mdsrt_ansamblu.py
+++ b/gen/strategii/mdsrt_ansamblu.py
@@ -1,166 +1,203 @@
-"""Diagrama de ansamblu MDSRT long (strategii, marcajul `ansamblu`).
+"""MDSRT — diagrama de ansamblu (Nivelul 1) și seria canonică a capitolului.
-„SPY — 5 min: al doilea picior mai jos la 589.80, sub suportul 590.00 →
-divergență bull (M) preț-vs-volum → candela-trigger Buy Hammer/Doji →
-intrare 590.20 → SL 589.40 (risc 0.80) → TP1 591.00 (1:1) → poziția rămasă
-pe trail, ieșire la close 592.40 (≈ 2.75R)."
+Aici trăiește singurul exemplu construit al capitolului: SPY pe 5 minute,
+suport/pivot daily 590.00, piciorul 1 la 590.10 (ține nivelul), piciorul 2 la
+589.50 (îl rupe, cu divergență pe volum), candela-trigger = Buy Hammer,
+intrare 590.20, SL 589.40, risc 0.80, TP1 591.00 (1:1), ieșire pe trail la
+592.40 (2.75R).
-Toate close-urile, wick-urile speciale și nivelurile vin din
-`gen/strategii/mdsrt_ansamblu.spec.txt`. Structura M→A→P, suportul 590.00,
-minimul 589.80 și riscul 0.80 sunt din exemplul cursului
-(`05_MODUL_4/02_strategia_5_mdsrt.md:159-177, 246, 308-311`). Intrare/SL/TP
-sunt exemplul construit al autorului sumarului, marcat explicit ca NU citat
-din curs (`...:166-177, 439-445`); transcrierea se întrerupe înainte de
-planul numeric. Volumele sunt relative, alese de generator (materialul nu dă
-volum pentru acest exemplu).
+Celelalte generatoare de preț ale capitolului (picior, trigger, stop, iesire)
+IMPORTĂ seria de aici. Așa o regulă numerică — marja stopului, distanța până
+la țintă — nu poate să difere de la o diagramă la alta.
-Rulează:
- python3 gen/strategii/mdsrt_ansamblu.py
-Scrie lângă el `mdsrt_ansamblu.svg`.
+Regulile numerice ale capitolului, definite o singură dată, aici:
+ MARJA = 0.10 — stopul stă cu atât sub low-ul piciorului 2
+ intrarea = close-ul candelei care sparge high-ul candelei-trigger
+ TP1 = intrare + (intrare − SL)
+
+Ce e din curs și ce nu: structura M→A→P e a cursului; suportul 590.00 și
+minimul piciorului 2 vin din exemplul construit al autorului sumarului
+(`05_MODUL_4/02_strategia_5_mdsrt.md:166-177, 439-445`), nu din transcriere —
+transcrierea se întrerupe înainte de planul numeric.
+
+Rulează: python3 gen/strategii/mdsrt_ansamblu.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
-from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
+from svg_candles import Chart, Level, Mark, MA, Zone, Arrow, from_closes # noqa: E402
-OUT = Path(__file__).resolve().parent / "mdsrt_ansamblu.svg"
+OUT = Path(__file__).with_suffix(".svg")
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
-# ---------------------------------------------------------------- seria (30 bare)
+# ------------------------------------------------- regulile numerice ale capitolului
+MARJA = 0.10 # stopul, sub low-ul piciorului 2
+SUPORT = 590.00 # pivotul daily (nivelul de pe TF-ul mare)
+LEG1, TRIG, CONF = 10, 18, 19 # indicii: piciorul 1, candela-trigger, candela de intrare
+STOP_BARS = 4 # low-ul se caută pe ultimele 4 candele, ca la Buy Doji
+
CLOSES = [
596.00, 595.20, 595.60, 594.80, 594.10, 593.40, 593.70, 592.90, # 0-7
- 592.20, 591.60, 591.90, 591.30, 590.80, 590.30, 590.55, 590.10, # 8-15
- 589.85, 590.05, # 16-17
- 589.80, # 18 = al doilea picior mai jos + candela-trigger (hammer)
- 590.20, # 19 = intrare (close = entry; sparge high-ul trigger-ului)
- 590.60, 590.90, 591.00, # 20-22 (22 = TP1 1:1)
+ 592.20, 591.60,
+ 590.40, # 10 = piciorul 1
+ 590.90, 591.40, 591.10, 591.30, 590.90, 590.50, 590.05, # 11-17
+ 589.80, # 18 = piciorul 2 (Buy Hammer)
+ 590.20, # 19 = intrarea
+ 590.60, 590.90, 591.00, # 20-22 (22 = TP1)
591.30, 591.10, 591.45, 591.70, 591.95, 592.15, # 23-28
- 592.40, # 29 = ieșire pe trail (close)
+ 592.80, 593.10, 592.90, # 29-31 = ultimul impuls
+ 592.40, # 32 = ieșire pe trail
]
-assert len(CLOSES) == 30, len(CLOSES)
+WICKS = [(0.25, 0.30)] * 33
+WICKS[LEG1] = (0.25, 0.30) # piciorul 1: low 590.10, deasupra suportului
+WICKS[16] = (0.20, 0.15)
+WICKS[17] = (0.15, 0.10) # low 589.95 — prețul intră în zonă, fără să facă minim nou
+WICKS[TRIG] = (0.10, 0.30) # hammer: high 590.15, low 589.50
+WICKS[CONF] = (0.10, 0.05)
+WICKS[20] = (0.25, 0.15) # retestul nivelului ține: low 590.05, peste pivot
+WICKS[21] = (0.05, 0.15) # high 590.95 < TP1: ținta e atinsă prima dată la bara 22
-# fitile (sus, jos): (0.25, 0.30) peste tot, cu excepțiile din spec.
-WICKS = [(0.25, 0.30)] * 30
-WICKS[18] = (0.10, 0.40) # hammer: high 590.15, low 589.40 (= SL)
-WICKS[19] = (0.10, 0.05) # intrare: high 590.30, low 589.75
-# bara 21 coborâtă sub TP1 (high 590.95 < 591.00), ca TP1 să fie atins exact
-# prima dată la bara 22 (altfel assert-ul 5 ar pica pe „primul high").
-WICKS[21] = (0.05, 0.30)
-
-# volum relativ (ales de generator): 16-19 crescător, 18-19 peste medie,
-# vol[18] > vol[10] (higher low pe volum la lower low pe preț).
VOLS = [
- 48, 46, 44, 45, 43, 41, 40, 42, 44, 46, # 0-9
- 52, 38, 40, 42, 39, 44, # 10-15
- 50, 58, 72, 85, # 16-19 absorbție la minim
- 70, 64, 58, 60, 55, 52, 50, 48, 46, 44, # 20-29
+ 48, 46, 44, 45, 43, 41, 40, 42, 44, 46, # 0-9
+ 52, # 10 = volumul piciorului 1
+ 38, 40, 42, 39, 44, 50, 58, # 11-17
+ 88, 80, # 18-19 absorbție la minim
+ 70, 64, 58, 60, 55, 52, 50, 48, 46, # 20-28
+ 52, 48, 42, 46, # 29-32
]
-assert len(VOLS) == 30, len(VOLS)
+assert len(CLOSES) == len(WICKS) == len(VOLS) == 33
-cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=596.60)
-# ---------------------------------------------------------------- assert-urile din spec
-entry = cds[19].c
-sl = cds[18].l
-tp1 = cds[22].c
+EXIT_I = 32 # bara pe care se închide poziția, sub linia de trail
-# 1. minimul și SL-ul pe bara 18
-assert cds[18].c == 589.80, cds[18].c
-assert cds[18].l == 589.40, cds[18].l
-# 2. al doilea picior rupe suportul/pivotul 590.00
-assert cds[18].l < 590.00, cds[18].l
-# 3. intrarea = close-ul barei 19 și sparge high-ul trigger-ului
-assert cds[19].c == 590.20, cds[19].c
-assert cds[19].c == entry
-assert cds[19].c > cds[18].h, (cds[19].c, cds[18].h)
-# 4. TP1 exact 1:1
-assert abs((entry - sl) - (tp1 - entry)) < 1e-9, (entry - sl, tp1 - entry)
-assert abs((entry - sl) - 0.80) < 1e-9
-# 5. TP1 atins la bara 22 (și nu mai devreme)
-assert cds[22].c == 591.00, cds[22].c
-assert cds[22].c == tp1
-assert max(c.h for c in cds[20:22]) < tp1, [c.h for c in cds[20:22]]
-# 6. ieșirea pe trail e pe profit
-assert cds[29].c == 592.40, cds[29].c
-assert cds[29].c > entry, (cds[29].c, entry)
-# 7. hammer: corp mic, coadă lungă în jos
-body = abs(cds[18].o - cds[18].c)
-rng = cds[18].h - cds[18].l
-tail = min(cds[18].o, cds[18].c) - cds[18].l
-assert body == 0.25, body
-assert body <= 0.5 * rng, (body, rng)
-assert tail > body, (tail, body)
-# 8. downtrend vizibil
-assert cds[13].c < cds[0].c - 5, (cds[13].c, cds[0].c)
-# 9. divergență pe volum: lower low pe preț, higher low pe volum
-assert cds[18].l < cds[10].l, (cds[18].l, cds[10].l)
-assert VOLS[18] > VOLS[10], (VOLS[18], VOLS[10])
-# barele 16-19 au volum crescător; 18-19 peste media seriei
-assert VOLS[16] < VOLS[17] < VOLS[18] < VOLS[19], VOLS[16:20]
-avg_vol = sum(VOLS) / len(VOLS)
-assert VOLS[18] > avg_vol and VOLS[19] > avg_vol, (VOLS[18], VOLS[19], avg_vol)
-# 10. fiecare open == close-ul barei anterioare
-for a, b in zip(cds, cds[1:]):
- assert a.c == b.o, (a.c, b.o)
-r_mult = (cds[29].c - entry) / (entry - sl)
-assert abs(r_mult - 2.75) < 1e-9, r_mult
+def bull_trail(cds, start):
+ """Bull Trail, definiția cursului: low-ul fiecărei candele care se închide
+ peste high-ul candelei dinainte; linia urcă, nu coboară niciodată.
+ Întoarce lista [(index, nivel)] de la bara `start` încolo."""
+ out, cur = [], None
+ for i in range(start, len(cds)):
+ if cds[i].c > cds[i - 1].h:
+ cur = round(cds[i].l, 2) if cur is None else max(cur, round(cds[i].l, 2))
+ if cur is not None:
+ out.append((i, cur))
+ return out
-# ---------------------------------------------------------------- niveluri, marks
-levels = [
- Level(590.00, "suport / pivot 590.00", BLUE, label_at="left"),
- Level(sl, f"SL {sl:.2f} — risc 0.80", RED, label_at="left"),
- # stânga: altfel eticheta din dreapta se calcă pe textul „SMA-20" al mediei
- Level(tp1, f"TP1 {tp1:.2f} = 1:1", GREEN, label_at="left"),
-]
-marks = [
- Mark(18, f"minim 589.80 — divergență (M)", RED, "below"),
- # ridicat deasupra vârfurilor candelelor de urcare (altfel textul cade peste
- # fitilele barelor 20-22) — bara 19 e totuși exact sub el
- Mark(19, f"intrare {entry:.2f}", GREEN, "above", dy=-46),
- Mark(29, f"ieșire trail {cds[29].c:.2f}", GREEN, "above"),
-]
+def serie():
+ """Seria canonică a capitolului, 33 de candele de 5 minute."""
+ return from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=596.60)
-# titlul e injectat pe două linii: textul din spec (82 caractere) nu încape
-# pe un singur rând în 640 px fără să fie tăiat la margine
-TITLE = ["SPY — 5 min (exemplu construit pe scara lecției;",
- "cifrele planului nu vin din curs)"]
-ch = Chart(
- candles=cds, levels=levels, marks=marks,
- mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")],
- scale=48.0, gap=15, body_w=9, left=44, width=640, top=64, vol_h=70,
- vol_label="volum",
- title="", caption="",
-)
-svg = ch.render()
-title_svg = "\n ".join(
- f'{t}'
- for i, t in enumerate(TITLE))
-i = svg.index(">") + 1
-svg = svg[:i] + "\n " + title_svg + svg[i:]
+cds = serie()
+leg1_l = cds[LEG1].l
+leg2_l = cds[TRIG].l
+ENTRY = cds[CONF].c
+SL = round(leg2_l - MARJA, 2)
+R = round(ENTRY - SL, 2)
+TP1 = round(ENTRY + R, 2)
-# ---------------------------------------------------------------- legenda (onestitate)
-m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
-W, H0 = int(m.group(1)), float(m.group(2))
-legend = [
- "Legendă: entry/SL/TP/trail sunt exemplul construit al autorului sumarului,",
- "nu citate din curs (transcrierea se întrerupe înainte de planul numeric).",
-]
-legend_svg = [f'{t}'
- for i, t in enumerate(legend)]
-new_h = H0 + 16 + 17 * (len(legend) - 1) + 14
-svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")',
- lambda mm: mm.group(1) + f"{new_h:.0f}" + mm.group(2), svg, count=1)
-svg = svg.replace("\n", "\n " + "\n ".join(legend_svg) + "\n")
+# ------------------------------------------------------------------ assert-urile
+for i, c in enumerate(cds):
+ assert c.h >= max(c.o, c.c) and c.l <= min(c.o, c.c), f"bara {i}: h/l incoerent"
+ if i:
+ assert abs(c.o - cds[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]"
-assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
-assert f"TP1 {tp1:.2f}" in svg and f"intrare {entry:.2f}" in svg
-OUT.write_text(svg)
-print(f"scris {OUT.name}: {len(cds)} candele, suport 590.00, minim {cds[18].l:.2f}, "
- f"intrare {entry:.2f}, SL {sl:.2f} (risc 0.80), TP1 {tp1:.2f} (1:1), "
- f"ieșire {cds[29].c:.2f} = {r_mult:.2f}R, vol[18]={VOLS[18]} > vol[10]={VOLS[10]}")
+# 1. piciorul 1 ține nivelul, piciorul 2 îl rupe — asta e povestea capitolului
+assert leg1_l == 590.10 and leg1_l > SUPORT, leg1_l
+assert leg2_l == 589.50 and leg2_l < SUPORT, leg2_l
+assert leg2_l < leg1_l, (leg2_l, leg1_l)
+# 2. cele două picioare sunt minimele locale, nu doar două bare oarecare
+assert leg1_l == min(c.l for c in cds[:LEG1 + 6]), "piciorul 1 nu e minimul zonei lui"
+assert leg2_l == min(c.l for c in cds), "piciorul 2 nu e minimul seriei"
+# 3. distanța dintre picioare depășește minimul de 5 candele cerut de curs
+assert TRIG - LEG1 == 8, TRIG - LEG1
+# 4. hammer: corp mic sus, coadă lungă în jos
+body = abs(cds[TRIG].o - cds[TRIG].c)
+rng = cds[TRIG].h - cds[TRIG].l
+tail = min(cds[TRIG].o, cds[TRIG].c) - cds[TRIG].l
+assert body <= 0.5 * rng and tail > body, (body, rng, tail)
+# 5. intrarea sparge high-ul candelei-trigger (regula de intrare a capitolului)
+assert ENTRY == 590.20 and ENTRY > cds[TRIG].h, (ENTRY, cds[TRIG].h)
+# 6. stopul: marja sub cel mai jos low din ultimele 4 candele
+low4 = min(c.l for c in cds[CONF - STOP_BARS + 1:CONF + 1])
+assert low4 == leg2_l, (low4, leg2_l)
+assert SL == 589.40 and R == 0.80, (SL, R)
+# 7. TP1 e 1:1 exact și e atins prima dată la bara 22
+assert abs((ENTRY - SL) - (TP1 - ENTRY)) < 1e-9
+assert TP1 == 591.00
+assert next(i for i in range(CONF + 1, len(cds)) if cds[i].h >= TP1 - 1e-9) == 22
+# 8. downtrend vizibil înainte de piciorul 1
+assert cds[LEG1].c < cds[0].c - 5, (cds[LEG1].c, cds[0].c)
+# 9. divergența: lower low pe preț, higher low pe volum
+assert cds[TRIG].l < cds[LEG1].l and VOLS[TRIG] > VOLS[LEG1]
+assert VOLS[16] < VOLS[17] < VOLS[TRIG] == max(VOLS)
+# 10. după intrare, retestul ține: nicio bară nu mai închide sub pivot
+assert all(c.c > SUPORT for c in cds[CONF + 1:]), "prețul recade sub pivot după intrare"
+assert min(c.l for c in cds[CONF + 1:]) > SUPORT, "o bară de după intrare rupe iar pivotul"
+# 11. ieșirea pe trail: ultimul swing low înainte de întoarcere e linia de trail,
+# iar bara de ieșire e prima care se închide sub ea
+_trail = bull_trail(cds, CONF)
+TRAIL = dict(_trail)[EXIT_I] # nivelul liniei de trail când vine bara de ieșire
+assert TRAIL == 592.50, TRAIL
+assert [p for _, p in _trail] == sorted(p for _, p in _trail), "linia de trail coboară"
+assert all(cds[i].c > p for i, p in _trail if i < EXIT_I), "trail-ul era rupt mai devreme"
+assert cds[EXIT_I].c == 592.40 < TRAIL, (cds[EXIT_I].c, TRAIL)
+r_mult = round((cds[EXIT_I].c - ENTRY) / R, 2)
+assert r_mult == 2.75, r_mult
+
+if __name__ == "__main__":
+ levels = [
+ Level(SUPORT, f"pivot daily {SUPORT:.2f} — zona (M)", BLUE, label_at="left"),
+ Level(SL, f"SL {SL:.2f} = 0.10 sub piciorul 2 (risc {R:.2f})", RED),
+ Level(TP1, f"TP1 {TP1:.2f} = 1:1", GREEN, label_at="left"),
+ ]
+ marks = [
+ Mark(LEG1, f"piciorul 1: low {leg1_l:.2f}", GRAY, "below"),
+ Mark(TRIG, f"piciorul 2: low {leg2_l:.2f}", RED, "below", dy=16),
+
+ ]
+ arrows = [
+ Arrow(TRIG, "Buy Hammer: coadă lungă în jos\nvânzătorii au dus prețul sub nivel\nși nu l-au putut ține acolo",
+ where="below", price=leg2_l, color=RED, dx=210, dy=58),
+ Arrow(CONF, f"intrare {ENTRY:.2f}: intri abia aici,\nla close peste high-ul candelei-trigger",
+ where="above", color=GREEN, dx=40, dy=-44),
+ Arrow(EXIT_I, f"ieșire pe trail {cds[EXIT_I].c:.2f} = {r_mult:.2f}R\nprima închidere sub linia de trail",
+ where="right", color=GREEN, dy=-6),
+ ]
+ ch = Chart(
+ candles=cds, levels=levels, marks=marks, zones=[
+ Zone(LEG1, CONF, "M (zonă + divergență) → A (trigger) → P (plan)", BLUE)],
+ arrows=arrows,
+ mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")],
+ scale=48.0, gap=15, body_w=9, left=44, width=660, top=74, vol_h=70,
+ vol_label="volum: la piciorul 2, volum mai mare decât la piciorul 1 = divergență",
+ )
+ svg = ch.render()
+ TITLE = ["SPY — 5 min (exemplu construit pe scara lecției;",
+ "cifrele planului nu vin din curs)"]
+ title_svg = "\n ".join(
+ f'{t}'
+ for i, t in enumerate(TITLE))
+ i = svg.index(">") + 1
+ svg = svg[:i] + "\n " + title_svg + svg[i:]
+
+ m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
+ W, H0 = int(m.group(1)), float(m.group(2))
+ legend = [
+ "Legendă: structura M → A → P e a cursului; nivelurile (590.00, 589.50) și planul",
+ "(intrare, SL, TP) sunt exemplul construit al autorului sumarului, nu citate din curs.",
+ ]
+ legend_svg = [f'{t}'
+ for i, t in enumerate(legend)]
+ new_h = H0 + 16 + 17 * (len(legend) - 1) + 14
+ svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")',
+ lambda mm: mm.group(1) + f"{new_h:.0f}" + mm.group(2), svg, count=1)
+ svg = svg.replace("\n", "\n " + "\n ".join(legend_svg) + "\n")
+ assert "\n\n" not in svg, "linie goală în SVG"
+ OUT.write_text(svg)
+ print(f"scris {OUT.name}: picior 1 {leg1_l:.2f} / picior 2 {leg2_l:.2f} sub {SUPORT:.2f}, "
+ f"intrare {ENTRY:.2f}, SL {SL:.2f} (risc {R:.2f}), TP1 {TP1:.2f}, ieșire {r_mult:.2f}R")
diff --git a/gen/strategii/mdsrt_ansamblu.svg b/gen/strategii/mdsrt_ansamblu.svg
index 0718c56..9b46a44 100644
--- a/gen/strategii/mdsrt_ansamblu.svg
+++ b/gen/strategii/mdsrt_ansamblu.svg
@@ -1,109 +1,132 @@
-
+
SPY — 5 min (exemplu construit pe scara lecției;
cifrele planului nu vin din curs)
-
- suport / pivot 590.00
-
- SL 589.40 — risc 0.80
-
- TP1 591.00 = 1:1
-
- SMA-20
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- volum
- minim 589.80 — divergență (M)
- intrare 590.20
- ieșire trail 592.40
- Legendă: entry/SL/TP/trail sunt exemplul construit al autorului sumarului,
- nu citate din curs (transcrierea se întrerupe înainte de planul numeric).
+
+ M (zonă + divergență) → A (trigger) → P (plan)
+
+ pivot daily 590.00 — zona (M)
+
+ SL 589.40 = 0.10 sub piciorul 2 (risc 0.80)
+
+ TP1 591.00 = 1:1
+
+ SMA-20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ volum: la piciorul 2, volum mai mare decât la piciorul 1 = divergență
+ piciorul 1: low 590.10
+ piciorul 2: low 589.50
+
+
+ Buy Hammer: coadă lungă în jos
+ vânzătorii au dus prețul sub nivel
+ și nu l-au putut ține acolo
+
+
+ intrare 590.20: intri abia aici,
+ la close peste high-ul candelei-trigger
+
+
+ ieșire pe trail 592.40 = 2.75R
+ prima închidere sub linia de trail
+ Legendă: structura M → A → P e a cursului; nivelurile (590.00, 589.50) și planul
+ (intrare, SL, TP) sunt exemplul construit al autorului sumarului, nu citate din curs.
\ No newline at end of file
diff --git a/gen/strategii/mdsrt_exemplu.py b/gen/strategii/mdsrt_exemplu.py
index 8423bf2..0e49000 100644
--- a/gen/strategii/mdsrt_exemplu.py
+++ b/gen/strategii/mdsrt_exemplu.py
@@ -30,9 +30,10 @@ GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
PIVOT = 622.00 # pivot daily, suportul
LEG1 = 622.40 # swing low picior 1
LEG2 = 621.40 # picior 2 (mai jos, rupe pivotul)
-ENTRY = 622.60 # close-ul Buy Hammer (construit)
+HAMMER_C = 622.60 # close-ul Buy Hammer — cifră reală, din sesiunea Q&A
+ENTRY = 623.00 # close-ul candelei care sparge high-ul hammer-ului (construit)
SL = 621.30 # 0.10 sub low-ul piciorului 2 (construit)
-TP1 = 623.90 # entry + risc = 1:1 (construit)
+TP1 = 624.70 # entry + risc = 1:1 (construit)
# ── seria: 30 de close-uri, exact ca în specificație ───────────────────────
CLOSES = [
@@ -100,11 +101,13 @@ assert abs(corp - 0.30) < 1e-9, corp
assert abs(coada - 0.90) < 1e-9, coada
assert coada > corp, (coada, corp)
-# 6. entry = close-ul trigger-ului (construit), SL 0.10 sub low-ul piciorului 2
-assert cds[18].c == ENTRY
+# 6. regula de intrare a capitolului: close-ul candelei care sparge high-ul
+# candelei-trigger (aici bara 19), nu close-ul hammer-ului însuși
+assert cds[18].c == HAMMER_C
+assert cds[19].c == ENTRY and cds[19].c > cds[18].h, (cds[19].c, cds[18].h)
assert abs(cds[18].l - SL - 0.10) < 1e-9, (cds[18].l, SL)
-risk = ENTRY - SL
-assert abs(risk - 1.30) < 1e-9, risk
+risk = round(ENTRY - SL, 2)
+assert risk == 1.70, risk
# 7. TP1 simetric 1:1
assert abs((ENTRY - SL) - (TP1 - ENTRY)) < 1e-9
@@ -112,9 +115,9 @@ assert abs((ENTRY - SL) - (TP1 - ENTRY)) < 1e-9
# 8. după trigger, primul close care atinge 623.90 e bara 21 (nu mai devreme).
# Seria pornește la 627.00, deci „atinge" se citește de jos în sus, din zona
# picioarelor: barele 10-20 rămân sub TP1, bara 21 îl atinge.
-assert all(c.c < TP1 for c in cds[10:21]), [c.c for c in cds[10:21]]
-assert max(c.h for c in cds[19:21]) < TP1, [c.h for c in cds[19:21]]
-assert cds[21].c == TP1, cds[21].c
+assert max(c.h for c in cds[19:25]) < TP1, [c.h for c in cds[19:25]]
+assert cds[25].h >= TP1 > cds[25].o, (cds[25].h, cds[25].o)
+TP_BAR = 25
# 9. divergență: lower low pe preț, higher low pe volum
assert cds[18].l < cds[10].l, (cds[18].l, cds[10].l)
@@ -136,9 +139,8 @@ def cx(i: int) -> float:
levels = [
Level(PIVOT, "pivot daily 622.00", ORANGE, label_at="left"),
Level(SL, "SL 621.30 (construit)", RED, label_at="left"),
- Level(ENTRY, "intrare 622.60 (construit)", GREEN, label_at="left"),
- Level(TP1, "TP1 623.90 = 1:1 (construit)", GREEN, label_at="left"),
- Level(LEG1, "picior 1 = 622.40", BLUE, label_at="right"),
+ Level(ENTRY, "intrare 623.00 (construit)", GREEN, label_at="right"),
+ Level(TP1, "TP1 624.70 = 1:1 (construit)", GREEN, label_at="left"),
]
# dy-uri calculate manual ca etichetele „below" să nu calce linia pivotului
@@ -146,7 +148,8 @@ levels = [
marks = [
Mark(10, "picior 1 = 622.40", BLUE, "below", dy=15),
Mark(18, "picior 2 = 621.40 (mai jos) · Buy Hammer", RED, "below"),
- Mark(18, "hammer închide 622.60, înapoi peste pivot", GREEN, "above", dy=-20),
+ Mark(18, "hammer închide 622.60, înapoi peste pivot", GREEN, "above", dy=-42),
+ Mark(19, "intri aici: close peste high-ul hammer-ului", GREEN, "above", dy=-62),
]
ch = Chart(
@@ -162,7 +165,7 @@ svg = ch.render()
# ── legenda de onestitate (assert 11): ce e real, ce e construit ───────────
legend = [
"Legendă: nivelurile 622.40 / 622.00 / 621.40 / 622.60 și cele 8 candele dintre",
- "picioare sunt reale, din curs. Intrarea 622.60, SL-ul 621.30 și TP-ul 623.90",
+ "picioare sunt reale, din curs. Intrarea 623.00, SL-ul 621.30 și TP-ul 624.70",
"sunt construcția mea — materialul nu le dă.",
]
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
@@ -182,8 +185,8 @@ assert "sunt reale, din curs" in svg and "construcția mea" in svg, \
for eticheta in ("pivot daily 622.00", "picior 1 = 622.40",
"picior 2 = 621.40 (mai jos) · Buy Hammer",
"hammer închide 622.60, înapoi peste pivot",
- "intrare 622.60 (construit)", "SL 621.30 (construit)",
- "TP1 623.90 = 1:1 (construit)"):
+ "intrare 623.00 (construit)", "SL 621.30 (construit)",
+ "TP1 624.70 = 1:1 (construit)"):
assert eticheta in svg, f"lipsește eticheta: {eticheta!r}"
assert "SPY — 5 min" in svg, "lipsește titlul"
@@ -218,5 +221,5 @@ OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name}: {len(cds)} candele | picior 1 {cds[10].l:.2f} (close "
f"{cds[10].c:.2f}) | picior 2 {cds[18].l:.2f} (mai jos) | hammer închide "
f"{cds[18].c:.2f} | intrare {ENTRY:.2f} | SL {SL:.2f} (risc {risk:.2f}) | "
- f"TP1 {TP1:.2f} = {(TP1 - ENTRY) / risk:.2f}:1 la bara 21 | "
+ f"TP1 {TP1:.2f} = {(TP1 - ENTRY) / risk:.2f}:1 la bara {TP_BAR} | "
f"vol[18]={VOLS[18]} > vol[10]={VOLS[10]}")
diff --git a/gen/strategii/mdsrt_exemplu.svg b/gen/strategii/mdsrt_exemplu.svg
index 5e14419..e56e0fe 100644
--- a/gen/strategii/mdsrt_exemplu.svg
+++ b/gen/strategii/mdsrt_exemplu.svg
@@ -4,12 +4,10 @@
pivot daily 622.00
SL 621.30 (construit)
-
- intrare 622.60 (construit)
-
- TP1 623.90 = 1:1 (construit)
-
- picior 1 = 622.40
+
+ intrare 623.00 (construit)
+
+ TP1 624.70 = 1:1 (construit)
SMA-20
@@ -106,8 +104,9 @@
volum
picior 1 = 622.40
picior 2 = 621.40 (mai jos) · Buy Hammer
- hammer închide 622.60, înapoi peste pivot
+ hammer închide 622.60, înapoi peste pivot
+ intri aici: close peste high-ul hammer-ului
Legendă: nivelurile 622.40 / 622.00 / 621.40 / 622.60 și cele 8 candele dintre
- picioare sunt reale, din curs. Intrarea 622.60, SL-ul 621.30 și TP-ul 623.90
+ picioare sunt reale, din curs. Intrarea 623.00, SL-ul 621.30 și TP-ul 624.70
sunt construcția mea — materialul nu le dă.
\ No newline at end of file
diff --git a/gen/strategii/mdsrt_trinity.py b/gen/strategii/mdsrt_trinity.py
index 5987e46..47605ad 100644
--- a/gen/strategii/mdsrt_trinity.py
+++ b/gen/strategii/mdsrt_trinity.py
@@ -113,12 +113,7 @@ add(W / 2, 468,
add(W / 2, 486,
"aliniere în cadranul 1, nu divergență (contradicția 2.4).",
13, "normal", GRAY)
-add(W / 2, 512,
- "Sursa: 05_MODUL_4/02_strategia_5_mdsrt.md:6-8, 34-42, 59-61, 85-88;",
- 13, "normal", GRAY)
-add(W / 2, 530,
- "08_ANALIZA_ZILNICA/11_selectia_simbolului.md:612-627.",
- 13, "normal", GRAY)
+# trimiterile la material stau în fișa de surse a capitolului, nu pe desen
# --------------------------------------------------------------- emitere
head = (f'')
parts.insert(0, f'')
+assert ".md:" not in "".join(parts), "trimiteri fișier:linie pe desen"
parts.insert(0, head)
body = []
diff --git a/gen/strategii/mdsrt_trinity.svg b/gen/strategii/mdsrt_trinity.svg
index 5268a12..1e9ea1b 100644
--- a/gen/strategii/mdsrt_trinity.svg
+++ b/gen/strategii/mdsrt_trinity.svg
@@ -25,6 +25,4 @@
La MDSRT, „Trinity” se folosește local cu sensul „toți trei în divergență”.
Lecția de selecție dă aceluiași cuvânt alt sens:
aliniere în cadranul 1, nu divergență (contradicția 2.4).
-Sursa: 05_MODUL_4/02_strategia_5_mdsrt.md:6-8, 34-42, 59-61, 85-88;
-08_ANALIZA_ZILNICA/11_selectia_simbolului.md:612-627.
diff --git a/summaries/17_STRATEGII/05_atmi.md b/summaries/17_STRATEGII/05_atmi.md
index e87ad66..d063abc 100644
--- a/summaries/17_STRATEGII/05_atmi.md
+++ b/summaries/17_STRATEGII/05_atmi.md
@@ -389,7 +389,7 @@ formulă de tip „risc % din cont"** în material pentru ATMI.
Aceeași corecție, două deznodăminte. Diagrama le pune alături.
-
+
SPY — daily: aceeași corecție, 4.0 % (DA) vs. 3.9 % (NU)
@@ -509,7 +509,7 @@ Aceeași corecție, două deznodăminte. Diagrama le pune alături.
calea DA (candelele)
calea NU (continuă în jos)
-
+
| | **A+** | **Mediocru / NU** |
|---|---|---|
diff --git a/summaries/17_STRATEGII/fise/05_atmi_surse.md b/summaries/17_STRATEGII/fise/05_atmi_surse.md
new file mode 100644
index 0000000..e9206c5
--- /dev/null
+++ b/summaries/17_STRATEGII/fise/05_atmi_surse.md
@@ -0,0 +1,94 @@
+# Surse — 5. ATMI
+
+Trimiterile la material pentru [`05_atmi.md`](../05_atmi.md). Stau aici, nu în
+capitol: în corpul tutorialului îngreunează cititul, dar fără ele nu se mai
+poate verifica nimic. Cifrele sunt linii din fișierele de sumarizare, nu din
+transcrieri.
+
+Textul fără trimitere e explicația mea și nu adaugă reguli; ce completez eu
+începe cu **Completare proprie**. Toate diagramele sunt exemple construite:
+din curs vin regulile (corecție minim 4 %, zona quarterly, BIM înainte de
+candela verde, ținta +10 %, fără stop) și cifrele exemplului SPY 2005
+(117.43 → 129.17). Valorile pivoților — **PPQ 95.80, SQ 90.80** — sunt
+calculate de mine cu formula standard, pentru că materialul spune explicit că
+nu le dă.
+
+**Prescurtări:**
+`main` = [`03_strategia_2_atmi.md`](../../04_MODUL_3/03_strategia_2_atmi.md)
+(lecția principală, integral) ·
+`bim` = [`10_buysell_momentum_trigger.md`](../../03_MODUL_2/10_buysell_momentum_trigger.md)
+(definiția indicatorului BIM) ·
+`tf` = [`03_timeframes_set_up.md`](../../03_MODUL_2/03_timeframes_set_up.md)
+(pivoți quarterly) ·
+`filtre` = [`01_filtre_scaner_bull_bear_reversal_pp.md`](../../04_MODUL_3/01_filtre_scaner_bull_bear_reversal_pp.md)
+(câmpul de scaner „PP or Support") ·
+`DECIZII` = [`triaj/DECIZII.md`](../../../triaj/DECIZII.md) (registrul de triaj
+al materialului) ·
+fișierele datate sunt sesiuni Q&A din `../../09_QA/`.
+
+**Nivelul 1 — ce face strategia** — `main`: 4-5, 5-6, 26-29, 31-45, 39-45,
+164-169, 170-179, 505-508 · `27_2025-05-28`: 466-467.
+
+**Pasul 1 — instrumentul, trimestrul și pivoții** — `main`: 6-8, 26-29,
+47-54 (formula nu e explicitată cifric), 143 (cursul nu dă cifrele PPQ/SQ),
+146-154 · `tf`: 73-79 · `filtre`: 29-34 · `14_2025-10-21`: 501-535
+(indici precalculați în TradeStation) · `27_2025-05-28`: 466-467
+(~2 tranzacții pe trimestru) · `09_2025-12-09`: 1177-1180 (pivoții clasici,
+confirmați „1 la 1").
+
+**Pasul 2 — corecția de minim 4 %** — `main`: 153-154, 249-319 (cazul de
+3,93 % / 3,9 %), 502-504.
+
+**Pasul 3 — zona SQ / PPQ** — `main`: 146-149 (intrarea sub SQ, rară, doar
+în crize reale), 146-154, 151-179 (harta MAPP) · `12_2025-11-18`: 294-296 ·
+`25_2025-07-08`: 840-843 · `13_2025-10-28`: 104 · `DECIZII`: 234, 324.
+
+**Pasul 4 — BIM, apoi prima candelă verde** — `main`: 156-159 (verdele de
+dinainte de BIM nu e trigger), 156-162, 159-162 (ideal închide deasupra
+pivotului), 16-21 (ATMI trimite la BIM fără să-l definească), 497-501
+(„cuțitul prins în cădere") · `bim`: 3-12 (definiția: `jbv2`, două moving
+averages, săgeți cyan/crimson).
+
+**Pasul 5 — fără stop, mărimea contribuției** — `main`: 10-11, 164-169
+(absența stopului e regulă), 166-169 (opțional -100 % pe poziție), 173-175
+(1 unitate SPY ≈ 600 $, ajustabil 0.2-0.5), 470-489 (levierul), 491-494
+(drawdown 20-30 % pe poziție, ~3 momente în 20 de ani), 509-512 ·
+`11_2025-11-25`: 489-500, 498-500 (dimensionezi ca și cum poți pierde 100 %) ·
+`04_2026-08-04`: 194-195 (lichiditatea ETF-urilor cu levier), 217-220
+(riscul e emoțional, nu matematic) · `13_2025-10-28`: 69-72 (CFD-urile, doar
+1-3 zile).
+
+**Pasul 6 — ieșirea la +10 % și reinvestirea** — `main`: 170-171 (ieși cu
+toată poziția), 176-177 (cash până la următorul trigger), 178-179
+(reinvestești tot + contribuția nouă), 321-356 (dobânda compusă), 349-356
+(poziții în paralel, cu maturități diferite), 465-468 (backtest: ~16.300 $ în
+20 de ani → ~52-53.000 $) · `27_2025-05-28`: 456-467, 462-467 (aceleași cifre,
+altă formulare; estimarea de 16.000 → 100.000 după a doua reinvestire).
+
+**A+ vs. mediocru** — `main`: 31-45 (mecanismul Buffett), 146-154, 156-162,
+159-162, 185-247 (exemplul „DA"), 249-319 (exemplul „NU"), 39-45, 505-508 ·
+`DECIZII`: 234, 324.
+
+**Exemplul real (SPY 2005)** — `main`: 358-363 (intrare 117.43, TP1 +10 % =
+129.17, atins „aproximativ un an mai târziu"; ziua exactă e neclară în
+transcriere), 164-171, 465-468.
+
+**Transfer pe prop** — `26_2025-06-11`: 214-222 (limita zilnică se calculează
+și pe pozițiile deschise; cazul participantului căruia i s-au închis
+tranzacții aflate în profit), 216-222, 225-227 (varianta „swing, 2 săptămâni",
+pentru calificare — vezi contradicția 1) · `main`: 470-494.
+
+**Transfer pe BVB** — `09_2025-12-09`: 1177-1180 (pivoții clasici, „1 la 1") ·
+`28_2025-05-27`: 374-377 (pragurile US de lichiditate: volum > 500k, preț
+> 30-40 $) · `main`: 39-45, 505-508 (premisa cade pe acțiuni individuale),
+497-501 (fără BIM, trigger-ul devine cuțitul în cădere).
+
+**Contradicții și puncte neclare** — (1) orizontul: `main`: 26-29, 358-363
+vs. `26_2025-06-11`: 225-227; (2) aritmetica contribuției: `main`: 173-175 vs.
+465-468; (3) formula PPQ/SQ, nedată cifric: `main`: 47-54, 143; (4) trailing /
+tranșe / ieșire anticipată, neacoperite: `main`: 170-179; (5) data exactă a
+atingerii TP1 (ASR): `main`: 358-363; (6) extinderea acronimului „ATMI" nu
+apare nicăieri în material.
+
+**Legături** — `26_2025-06-11`: 915-917 (autorul recomandă combinarea ATMI cu
+MP² la suport/pivot important).