02_mp2: capitol reconstruit dupa sablon

Nivelurile 1-2-3, cei 6 pasi cu diagrama fiecare, mini-glosar de 34 de
termeni, oglinda pe short cu diagrama ei proprie, checklist. Sursele in
fise/02_mp2_surse.md.

Toate diagramele procedurii ies acum dintr-o singura serie
(mp2_serie.py), deci capitolul nu mai poate avea doua reguli numerice.
Ansamblul folosea NVDA cu stop la swing low absolut (-23 %), adica alta
regula decat procedura celor 4 bare; NVDA a devenit "bottom", la nivelul
3, marcat explicit ca exceptie.

perechi_tf e diagrama cu doua timeframe-uri: panoul mic e descompunerea
reala a ultimelor 2 candele daily (1 zi = 7 bare de 60 min), cu pivotul
weekly pe ambele panouri si conector. Pivotii se calculeaza din serie, nu
sunt scrisi de mana.

Reparate: trail-ul din proza era 600.05, calculul din serie da 600.75;
doua SVG-uri aveau trimiteri fisier:linie desenate pe ele.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
This commit is contained in:
Claude Agent
2026-09-20 12:51:58 +00:00
parent 9b047e10bb
commit 2ea599f265
25 changed files with 4449 additions and 1666 deletions

View File

@@ -1,139 +1,72 @@
"""Diagrama de ansamblu MP² — bottom major NVDA (strategii/02_mp2.md, Nivel 0).
"""MP² — tot trade-ul pe o singură diagramă (Nivelul 1 din `02_mp2.md`).
„NVDA — weekly (pre-split 1:10)": downtrend masiv → bază/bottom oct. 2022 →
crossing-ul pivotului weekly (mic) peste cel monthly (mare) → candela de
trigger care închide peste pivotul mic (M+A) → intrare 13.80 → SL la swing
low-ul absolut 10.63 (−23 %) → urcare spre TP 17.25 (+25 %, ~9 săptămâni).
Aceeași serie ca restul capitolului (`mp2_serie.py`): 5 ședințe de câte 7
bare de 60 de minute. Pe desen: pivotul weekly (mare) ca linie orizontală,
pivotul daily (mic) ca linie în trepte care se recalculează în fiecare zi,
crossing-ul, bara de trigger, stopul, TP1 și ieșirea pe trail.
Cifrele (13.80 / 10.63 / 17.25 / −23 % / +25 %) sunt din
`summaries/04_MODUL_3/04_strategia_2_mp_patrat.md:341-357,435`.
Traseul de preț și cele două linii de pivot sunt schematice: cursul nu dă
valorile pivoților, doar faptul că micul trece deasupra marelui la crossing
(`...:326-335`).
Rulează:
python3 gen/strategii/mp2_ansamblu.py
Scrie lângă el `mp2_ansamblu.svg`.
Rulare: python3 gen/strategii/mp2_ansamblu.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
sys.path.insert(0, str(Path(__file__).parent))
from svg_candles import Arrow, Chart, Level, Zone # noqa: E402
import mp2_serie as S # noqa: E402
OUT = Path(__file__).resolve().parent / "mp2_ansamblu.svg"
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, BLUE, GRAY, ORANGE = "#188a3e", "#c00", "#1a5fb4", "#777", "#b06000"
W = 720
GRAY, RED, GREEN, BLUE = "#777", "#c00", "#188a3e", "#1a5fb4"
zi4a, zi4b = S.ZI4 * S.BARE_ZI, (S.ZI4 + 1) * S.BARE_ZI - 1
# ------------------------------------------------------------------ seria (38 bare)
# 16 bare de downtrend 32.00 -> 11.40 (minimul absolut 10.63 e fitilul barei 15),
# 10 bare de bază/recuperare 11.80 -> 13.80 (bara 25 = intrarea, după M+A),
# 12 bare de urcare 14.10 -> 17.25 (ținta). open-ul = close-ul precedentului.
DOWN = [32.00, 30.80, 29.60, 28.70, 27.40, 26.50, 25.30, 24.20, 23.60,
22.30, 21.10, 19.90, 18.50, 16.90, 15.20, 11.40] # 0-15
BASE = [11.80, 12.10, 12.35, 12.20, 12.55, 12.85, 13.05, 13.35, 13.55,
13.80] # 16-25
UP = [14.10, 14.45, 14.80, 15.10, 15.45, 15.80, 16.10, 15.90, 16.30,
16.60, 16.95, 17.25] # 26-37
CLOSES = DOWN + BASE + UP
assert len(CLOSES) == 38, len(CLOSES)
CAPIT, ENTRY, LAST = 15, 25, 37
# fitile (sus, jos) per bară; bara 15 are fitilul de jos până la minimul absolut
WICKS = ([(0.35, 0.35)] * 14
+ [(0.30, 0.30)] # 14
+ [(0.20, 0.77)] # 15: low = 11.40 - 0.77 = 10.63
+ [(0.30, 0.30)] * 9 # 16-24
+ [(0.25, 0.20)] # 25: candela de intrare
+ [(0.30, 0.30)] * 12) # 26-37
assert len(WICKS) == len(CLOSES)
VOLS = ([52, 55, 50, 58, 54, 49, 56, 52, 60, 57, 63, 68, 72, 80, 90, 120] # 0-15
+ [100, 72, 60, 55, 58, 62, 66, 70, 78, 95] # 16-25
+ [88, 80, 74, 70, 68, 72, 76, 70, 74, 78, 84, 92]) # 26-37
assert len(VOLS) == len(CLOSES)
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=32.60)
# ------------------------------------------------- liniile de pivot (trepte)
# Stepped, schematice (cursul nu dă valorile): monthly „mare" se recalculează
# rar, weekly „mic" mai des. Crossing: micul trece deasupra marelui exact pe
# candela de intrare.
PMARE = [31.00] * 10 + [21.00] * 10 + [13.55] * 8 + [13.20] * 10 # monthly
PMIC = ([30.50] * 5 + [29.00] * 5 + [20.00] * 4 + [17.00] * 4
+ [12.60] * 3 + [12.95] * 2 + [13.20] * 2 + [13.70] * 3
+ [14.20] * 4 + [14.60] * 6) # weekly
assert len(PMARE) == len(PMIC) == len(CLOSES)
# ---------------------------------------------------------- assert-urile din spec
entry = cds[ENTRY].c
sl, tp = 10.63, cds[LAST].c
assert abs(entry - 13.80) < 0.05 # close-ul intrării == 13.80
assert abs(sl - 10.63) < 0.05 and sl < entry # SL = swing low absolut, sub entry
assert abs(tp - 17.25) < 0.10 and tp > entry # TP = 17.25, peste entry
# spec: `abs((entry/sl - 1)*100 - 23)`; formula din spec e inversată (dă +29.8,
# nu −23). Pierderea corectă din entry până la SL e (sl/entry - 1), deci o scriu așa:
loss_pct = (sl / entry - 1) * 100
assert abs(loss_pct + 23) < 1.5, loss_pct # SL ≈ −23 %
assert abs((tp / entry - 1) * 100 - 25) < 1.5 # TP ≈ +25 %
assert abs(min(c.l for c in cds) - 10.63) < 0.01 # minimul absolut al seriei = SL
assert min(c.l for c in cds[-5:-1]) > 10.63 # stopul de 4 bare ar fi mult mai sus
# crossing-ul pivoților, înainte/după candela de intrare
assert all(PMIC[i] < PMARE[i] for i in range(ENTRY - 4, ENTRY)), "mic < mare înainte de intrare"
assert all(PMIC[i] > PMARE[i] for i in range(ENTRY, ENTRY + 4)), "mic > mare după intrare"
assert cds[ENTRY].c > PMIC[ENTRY], "candela de trigger nu închide peste pivotul mic"
assert abs(cds[CAPIT].l - sl) < 1e-9, "fitilul barei 15 nu atinge exact SL-ul"
levels = [
Level(13.80, "entry ~13.80 (după M+A)", GREEN, label_at="left"),
Level(sl, "SL swing low absolut 10.63, −23%", RED, label_at="left"),
Level(tp, "TP ~+25% (17.25), ~9 săpt.", BLUE, label_at="left"),
]
marks = [
Mark(CAPIT, f"minim absolut {sl:.2f}", RED, "below", dy=-6),
Mark(ENTRY, "M+A", BLUE, "above"),
]
ch = Chart(candles=cds, levels=levels, marks=marks,
scale=17.0, gap=15, body_w=9, left=44, width=720, top=64, vol_h=70,
vol_label="volum", title="NVDA — weekly (pre-split 1:10)",
caption="Bottom oct. 2022: entry ~13.80, −23%, ~+25% în ~9 săpt. "
"Traseu și pivoți construiți (cursul nu dă valorile lor).")
ch = Chart(
candles=S.CDS,
levels=[Level(S.TP1, f"TP1 {S.TP1:.2f} — poziția 1", GREEN, "2 3", "left"),
Level(S.ENTRY, f"intrarea {S.ENTRY:.2f}", "#333", "4 3", "left"),
Level(S.SL, f"stopul {S.SL:.2f}", RED, "6 3", "left"),
Level(S.PPW, f"pivot weekly (mare) {S.PPW:.2f}", GRAY, "8 4", "right")],
zones=[Zone(zi4a, zi4b, "ziua a 4-a: pivotul mic e deja peste cel mare", BLUE)],
arrows=[
Arrow(S.TRIGGER, f"aici intri: {S.ENTRY:.2f}\nprima bară care ÎNCHIDE\npeste pivotul mic",
where="above", color=GREEN, dx=-118, dy=-6),
Arrow(22, f"stopul: {S.SL:.2f}\nminimul ultimelor {S.BARE_STOP} bare —\n"
f"sub el, cumpărătorii nu mai au controlul",
where="below", price=S.SL, color=RED, dx=126, dy=4),
Arrow(S.I_TP1, f"poziția 1 iese la {S.TP1:.2f}:\nexact cât ai riscat (1:1)",
where="right", price=S.TP1, color=GREEN, dx=78, dy=-40),
Arrow(S.IESIRE, f"poziția 2 iese la {S.IES_PRET:.2f},\nprima închidere sub trail",
where="right", color=ORANGE, dy=44),
],
scale=17.0, gap=15, body_w=9, left=52, width=W, top=40, vol_h=64,
vol_label="volum", title="Exemplu construit — bare de 60 de minute, 5 ședințe",
caption=f"Crossing: pivotul mic urcă de la {S.PPD[S.ZI4 - 1]:.2f} la {S.PPD[S.ZI4]:.2f}, "
f"peste cel mare ({S.PPW:.2f}) — de-abia de acolo cauți trigger-ul.")
svg = ch.render()
def step_points(vals: list[float]) -> str:
"""Polilinie în trepte: orizontal pe fiecare palier, vertical la schimbare."""
pts: list[tuple[float, float]] = []
for i, v in enumerate(vals):
x, y = ch._cx(i), ch._y(v)
if i == 0:
pts.append((x, y))
elif v != vals[i - 1]:
pts.append((x, ch._y(vals[i - 1]))) # orizontal până la bara nouă
pts.append((x, y)) # salt vertical la nivelul nou
else:
pts.append((x, y))
return " ".join(f"{x:.1f},{y:.1f}" for x, y in pts)
# injectate imediat după tag-ul de deschidere => desenate în spatele candelelor
off = S.offset(svg, ch)
y_ppd = ch._y(S.PPD[-1]) + off
inject = [
f'<polyline points="{step_points(PMARE)}" fill="none" stroke="{GRAY}" stroke-width="1.6"/>',
f'<polyline points="{step_points(PMIC)}" fill="none" stroke="{BLUE}" stroke-width="1.6"/>',
f'<text x="712" y="{ch._y(PMIC[-1]) - 6:.1f}" fill="{BLUE}" text-anchor="end">'
f'pivot weekly (mic)</text>',
f'<text x="712" y="{ch._y(PMARE[-1]) + 14:.1f}" fill="{GRAY}" text-anchor="end">'
f'pivot monthly (mare)</text>',
f'<polyline points="{S.trepte(ch, S.PPD_BARA, off)}" fill="none" stroke="{BLUE}" '
f'stroke-width="1.8"/>',
f'<text x="{W - 16}" y="{y_ppd - 7:.1f}" fill="{BLUE}" text-anchor="end">'
f'pivot daily (mic)</text>',
]
i = svg.index(">") + 1
svg = svg[:i] + "\n " + "\n ".join(inject) + svg[i:]
# marcajul crossing-ului: treapta verticală a pivotului mic, între ziua 3 și 4
xc = ch._cx(zi4a) - 7.5
y_de, y_la = ch._y(S.PPD[S.ZI4 - 1]) + off, ch._y(S.PPD[S.ZI4]) + off
inject += [
f'<circle cx="{xc:.1f}" cy="{(y_de + y_la) / 2:.1f}" r="{(y_de - y_la) / 2 + 4:.1f}" '
f'fill="none" stroke="{BLUE}" stroke-width="1.4" stroke-dasharray="3 2"/>',
f'<text x="{xc:.1f}" y="{y_de + 20:.1f}" fill="{BLUE}" text-anchor="middle" '
f'style="font-weight:bold">crossing</text>',
]
svg = S.injecteaza(svg, inject)
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
OUT.write_text(svg)
print(f"scris {OUT.name}: {len(cds)} candele, entry {entry:.2f}, "
f"SL {sl:.2f} ({loss_pct:+.1f}%), TP {tp:.2f} "
f"({(tp/entry - 1) * 100:+.1f}%), crossing la bara {ENTRY}")
assert y_de > y_la, "crossing-ul nu urcă pe desen"
assert abs((ch._y(S.SL) - ch._y(S.ENTRY)) - (ch._y(S.ENTRY) - ch._y(S.TP1))) < 0.01, \
"1R în sus nu e desenat cât 1R în jos"
h = float(svg.split('viewBox="0 0 ')[1].split('"')[0].split()[1])
w = float(svg.split('viewBox="0 0 ')[1].split()[0])
S.scrie(OUT, svg, w, h)