Context (DA/NU: cadere in suport major vs. consolidare stransa, cu benzile Bollinger calculate din close-uri), trigger (aceeasi candela M in ambele panouri), stop (fereastra de 4 candele, 1R si sizing derivate) si iesire (TP1 -> break even -> trail, fata de capcana iesirii la 50 %, unde prețul urca apoi la +2R). Aceeasi regula de stop in toate cinci diagramele ale capitolului, si banda inferioara calibrata explicit ca sa nu existe doua valori pentru acelasi nivel. Textul capitolului e neatins — agentul a atins pragul de context. SVG-urile sunt deocamdata fisiere in plus, fara marcaje care sa le ceara. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
155 lines
7.4 KiB
Python
155 lines
7.4 KiB
Python
"""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}"
|
||
|
||
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}")
|