Files
atm-curs-dl/gen/strategii/mbp_stop.py
Claude Agent 1f91e0b5a7 08_mbp: capitol reconstruit dupa sablon
Primul capitol terminat cap-coada dupa 01_buy_doji: nivelurile 1-2-3,
cei 5 pasi cu propozitie de actiune si "Treci mai departe doar daca",
mini-glosar de 38 de termeni, oglinda pe short cu diagrama ei, checklist.
Sursele au iesit in fise/08_mbp_surse.md.

Diagrame noi pentru pasii 1, 3, 4 si 5, care nu aveau: context, trigger,
stop, iesire. Benzile Bollinger din context sunt calculate din close-uri,
nu desenate, cu 20 de bare de incalzire nedesenate.

MBP e monotimeframe, deci cerinta de corespondenta intre timeframe-uri nu
se aplica aici — nu s-a inventat o diagrama pentru ea.

Stopul e fix pe cel mai jos minim din 4 candele, fara marja: asa spune
cursul pentru strategia asta, spre deosebire de 01_buy_doji. Diferenta e
spusa explicit in text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-20 12:48:27 +00:00

160 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Diagrama de stop MBP (marcajul `stop`), Pasul 4.
Fereastra de 4 candele din care iese stopul, mărită: candela de trigger și
cele 3 dinaintea ei (deci candela M intră în fereastră). Cel mai jos minim
din ele e 623.60 — low-ul lui M — iar stopul stă fix acolo, ca în exemplul
din curs (fără marjă adăugată; e aceeași regulă în toate diagramele
capitolului).
Pe desen: zona punctată peste exact cele 4 candele (numărul din etichetă e
numărat, nu scris de mână), linia de intrare la 623.95, linia de stop la
623.60 și măsura 1R = 0.35 $ între ele, cu care se calculează mărimea
poziției.
Cifrele din curs (`05_MODUL_4/03_strategia_6_mbp_mbappe.md`): M close 623.87
sub banda 623.90, trigger → intrare 623.95, SL 623.60 = lowest low din 4
candele, trigger inclus. Structura barelor e construită de mine.
Rulează: python3 gen/strategii/mbp_stop.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Arrow, Chart, Level, Zone, from_closes # noqa: E402
OUT = Path(__file__).with_suffix(".svg")
RED, GREEN, BLUE, GRAY = "#c00", "#188a3e", "#1a5fb4", "#777"
BAND = 623.90
M = 11
CLOSES = [624.90, 624.75, 624.60, 624.68, 624.50, 624.38, 624.22, 624.30,
624.12, 624.05, 623.98, 623.87, 623.95, 624.08, 624.02, 624.15]
WICKS = ([(0.06, 0.10)] * 9
+ [(0.05, 0.20), # 9: low 623.85
(0.05, 0.16), # 10: low 623.82
(0.06, 0.27), # 11 = M: low 623.60
(0.05, 0.12), # 12 = trigger: low 623.75
(0.06, 0.08), (0.06, 0.08), (0.06, 0.08)])
assert len(CLOSES) == len(WICKS) == 16
cds = from_closes(CLOSES, wicks=WICKS, start=625.02)
TRIG = M + 1
I0 = TRIG - 3 # prima candelă din fereastră
# ── ce afirmă textul, verificat pe serie ───────────────────────────────────
entry = cds[TRIG].c
assert abs(entry - 623.95) < 1e-9, entry
assert cds[TRIG].c > cds[TRIG].o # trigger verde
assert abs(cds[M].c - 623.87) < 1e-9 and cds[M].c < BAND
assert cds[M].c < cds[M].o # M roșie
fereastra = cds[I0:TRIG + 1]
N_F = len(fereastra)
assert N_F == 4, N_F
sl = min(c.l for c in fereastra)
assert abs(sl - 623.60) < 1e-9, sl
assert sl == cds[M].l, "cel mai jos minim din fereastră e low-ul lui M"
assert all(c.l > sl for c in fereastra if c is not cds[M]), \
"nicio altă candelă din fereastră nu coboară sub low-ul lui M"
risc = entry - sl
assert abs(risc - 0.35) < 1e-9, risc
# stopul nu are marjă adăugată: e fix pe minim, ca în cifra cursului
assert sl == min(c.l for c in fereastra)
# candela dinaintea ferestrei are minim peste stop — altfel fereastra de 4 n-ar conta
assert cds[I0 - 1].l > sl, cds[I0 - 1].l
# mărimea poziției, calculată din risc (cifrele contului sunt alegerea mea)
CONT, RISC_PCT = 50_000.0, 0.5
risc_bani = CONT * RISC_PCT / 100
unitati = int(risc_bani / risc)
notional = unitati * entry
assert unitati == 714, unitati
assert abs(notional / CONT - 8.9) < 0.1, notional / CONT
SCALE, GAP, BODY, LEFT, W = 300.0, 30, 18, 64, 756
AER = 0.07 * (max(c.h for c in cds) - min(c.l for c in cds))
ch = Chart(
candles=cds,
levels=[Level(BAND, f"banda inferioară {BAND:.2f}", BLUE, dash="4 3", label_at="left"),
Level(entry, f"intrare {entry:.2f} = close-ul trigger-ului", GREEN, dash="2 3",
label_at="right"),
Level(sl, f"SL {sl:.2f}", RED, dash="4 3", label_at="left"),
Level(max(c.h for c in cds) + AER, "", "none", dash=""),
Level(sl - AER, "", "none", dash="")],
zones=[Zone(I0, TRIG, f"{N_F} candele: trigger-ul și cele 3 dinaintea lui", RED)],
arrows=[Arrow(M, f"cel mai jos minim din fereastră: {sl:.2f}\nsub el, ideea de epuizare "
f"e moartă — acolo stă stopul", where="below", color=RED),
Arrow(TRIG, "trigger: aici intri, la close", where="above", color=GREEN, dx=52, dy=-24)],
scale=SCALE, gap=GAP, body_w=BODY, left=LEFT, width=W,
title="SPY — 15 min: de unde iese stopul (fereastra de 4 candele)")
svg = ch.render()
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
assert m
W2, H = int(m.group(1)), float(m.group(2))
# ── măsura 1R între intrare și stop, lipită de liniile ei ──────────────────
xr = ch._cx(len(cds) - 1) + 26
y_e, y_s = ch._y(entry), ch._y(sl)
assert y_s > y_e
masura = [
f'<line x1="{xr:.1f}" y1="{y_e:.1f}" x2="{xr:.1f}" y2="{y_s:.1f}" stroke="{GRAY}" '
f'stroke-width="1.2"/>',
f'<line x1="{xr - 5:.1f}" y1="{y_e:.1f}" x2="{xr + 5:.1f}" y2="{y_e:.1f}" stroke="{GRAY}"/>',
f'<line x1="{xr - 5:.1f}" y1="{y_s:.1f}" x2="{xr + 5:.1f}" y2="{y_s:.1f}" stroke="{GRAY}"/>',
f'<text x="{xr + 8:.1f}" y="{(y_e + y_s) / 2 + 4:.1f}" fill="{GRAY}" '
f'style="font-weight:bold">1R = {risc:.2f} $</text>',
]
W3 = max(W2, xr + 8 + len(f"1R = {risc:.2f} $") * 6.4 + 8)
LEG = [
f"Numeri {N_F} candele înapoi începând cu cea de trigger (trigger, M și cele două "
f"dinaintea lui M) și iei cel mai jos minim:",
f"{sl:.2f}. Acolo pui stopul, fix pe minim, fără marjă adăugată — asta e cifra cursului.",
f"Riscul pe unitate iese din desen: {entry:.2f} − {sl:.2f} = {risc:.2f} $ = 1R. Din el "
f"calculezi câte unități cumperi.",
f"Completare proprie (cifrele contului sunt ale mele): cont {CONT:,.0f} $, risc "
f"{RISC_PCT} % = {risc_bani:.0f} $ → {risc_bani:.0f} / {risc:.2f} = {unitati} unități,".replace(",", "."),
f"adică un notional de {notional:,.0f} $ ≈ {notional / CONT:.1f}× contul. "
f"Un stop strâns nu înseamnă un trade mic.".replace(",", "."),
]
W3 = max(W3, max(len(t) for t in LEG) * 5.9 + 32)
ly = H + 6
svg = svg.replace("\n</svg>", "\n " + "\n ".join(
masura + [f'<text x="24" y="{ly + 17 * i:.0f}" fill="#555">{t}</text>'
for i, t in enumerate(LEG)]) + "\n</svg>")
H2 = ly + 17 * len(LEG) + 6
svg = re.sub(r'(viewBox="0 0 )\d+ [0-9.]+(")', lambda mm: mm.group(1) + f"{W3:.0f} {H2:.0f}" + mm.group(2),
svg, count=1)
# ── verificări pe SVG-ul final ─────────────────────────────────────────────
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
assert f"{N_F} candele" in svg and N_F == TRIG - I0 + 1
# liniile nu cad pe marginea cadrului
assert ch._y(sl) < H2 - 20 and ch._y(max(c.h for c in cds)) > 14
box = []
for mm in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', svg):
x, y, attrs, txt = float(mm.group(1)), float(mm.group(2)), mm.group(3), mm.group(4)
tw = len(txt) * (6.4 if "bold" in attrs else 5.9)
x0 = x - tw if 'anchor="end"' in attrs else (x - tw / 2 if 'anchor="middle"' in attrs else x)
assert x0 >= -2 and x0 + tw <= W3 + 2, f"text tăiat: {txt!r}"
assert 0 <= y <= H2 + 2, f"text în afara înălțimii: {txt!r}"
box.append((x0, x0 + tw, y, txt))
for i in range(len(box)):
for j in range(i + 1, len(box)):
(a0, a1, ay, at), (b0, b1, by, bt) = box[i], box[j]
assert not (abs(ay - by) < 13 and a0 < b1 - 4 and b0 < a1 - 4), \
f"texte suprapuse: {at!r} / {bt!r}"
# etichetele goale (nivelurile-distanțier) nu au ce căuta în SVG: auditul le
# citește ca text suprapus peste ce e sub ele
svg = re.sub(r"\n\s*<text[^>]*>\s*</text>", "", svg)
assert svg.count("\n\n") == 0
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name}: fereastră {N_F} candele [{I0}..{TRIG}], SL {sl:.2f} (= low M), "
f"intrare {entry:.2f}, 1R {risc:.2f}, {unitati} unități, notional {notional:,.0f}")