Files
atm-curs-dl/gen/strategii/mdsrt_stop.py
Claude Agent f8dd2f7bd0 07_mdsrt: capitol reconstruit dupa sablon
Nivelurile 1-2-3, cei 6 pasi cu diagrama fiecare, mini-glosar de 33 de
termeni, oglinda pe short cu diagrama ei, checklist. Sursele in
fise/07_mdsrt_surse.md.

Capitolul avea TREI reguli numerice diferite in acelasi loc: stop fix pe
low in ansamblu, 0.10 sub low in aplus si exemplu, plus doua reguli de
intrare. Acum mdsrt_ansamblu.py tine seria si regulile, iar picior,
trigger, stop, iesire si short le importa — nu mai pot diverge.

Noi: zona (daily -> 60 min ca descompunere reala, 2 candele daily = 14
bare, cu caseta, ghidaje si pivotul pe ambele panouri), picior, trigger,
stop, iesire, short (seria long rasturnata, deci fiecare cifra are
perechea ei).

Verificare numerica text-diagrama: trei nepotriviri reparate, inclusiv o
cifra din proza care nu exista pe niciun desen.

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

139 lines
6.6 KiB
Python

"""MDSRT, Pasul 5 — unde pui stopul și de ce nu mai aproape.
Sus: cum numeri cele 4 candele înapoi de la candela de intrare, care e cel mai
jos low dintre ele și unde ajunge stopul (0.10 sub el).
Jos: același trade, cu o continuare construită în care prețul retestează
nivelul. Stopul „la ochi", pus sub candela de intrare, e măturat de retest;
stopul din regulă ține, iar trade-ul ajunge la țintă.
Cursul lasă două variante de stop (extrema ultimelor N candele sau ATR) și nu
dă valoarea lui N; N = 4 e valoarea folosită la celelalte strategii mecanice
ale cursului, iar marja de 0.10 e regula acestui capitol.
Rulează: python3 gen/strategii/mdsrt_stop.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
sys.path.insert(0, str(Path(__file__).parent))
from svg_candles import Candle, Chart, Level, Zone, Arrow # noqa: E402
from mdsrt_ansamblu import (serie, SUPORT, TRIG, CONF, ENTRY, SL, R, TP1, # noqa: E402
MARJA, STOP_BARS)
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, BLUE, GRAY, ORANGE = "#188a3e", "#c00", "#1a5fb4", "#555", "#b06000"
W, VGAP = 720, 16
S = 9 # prima bară arătată
base = serie()[S:CONF + 1]
t, cf = TRIG - S, CONF - S
low4 = min(c.l for c in base[cf - STOP_BARS + 1:cf + 1])
OCHI = round(base[cf].l - MARJA, 2) # stopul „la ochi", sub candela de intrare
# continuare construită: un retest care coboară sub candela de intrare
after = [Candle(*b) for b in [
(590.20, 590.35, 589.60, 589.85), # +1: retestul; mătură stopul „la ochi"
(589.85, 590.45, 589.80, 590.35),
(590.35, 590.75, 590.25, 590.65),
(590.65, 591.10, 590.55, 591.05), # +4: atinge ținta
]]
jos = base + after
# ----------------------------------------------------------------- assert-uri
for i, c in enumerate(jos):
assert c.h >= max(c.o, c.c) and c.l <= min(c.o, c.c), i
if i:
assert abs(c.o - jos[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]"
# 1. cel mai jos low din cele 4 candele e al piciorului 2, iar stopul e sub el
assert low4 == base[t].l == 589.50, (low4, base[t].l)
assert round(low4 - MARJA, 2) == SL == 589.40
assert round(ENTRY - SL, 2) == R == 0.80
# 2. cu 5 candele în loc de 4 iese același stop (minimul e tot al piciorului 2)
assert min(c.l for c in base[cf - 4:cf + 1]) == low4
# 3. stopul „la ochi" e mai aproape de intrare decât cel din regulă
assert SL < OCHI < ENTRY and OCHI == 589.65, OCHI
# 4. retestul mătură stopul „la ochi", dar nu-l atinge pe cel din regulă
assert min(c.l for c in after) == 589.60
assert SL < 589.60 < OCHI
assert next(k for k, c in enumerate(after) if c.l <= OCHI) == 0
assert all(c.l > SL for c in after), "stopul din regulă ar fi fost atins"
# 5. ținta e atinsă abia după retest, la a 4-a bară de după intrare
assert next(k for k, c in enumerate(after, 1) if c.h >= TP1) == 4
print(f"stop: low4 {low4:.2f} → SL {SL:.2f} (marjă {MARJA:.2f}); la ochi {OCHI:.2f} "
f"măturat de {min(c.l for c in after):.2f}; țintă {TP1:.2f} la a 4-a bară")
common: dict = dict(scale=52.0, gap=22, body_w=11, left=180, width=W)
sus = Chart(
candles=base,
levels=[Level(SUPORT, f"pivot daily {SUPORT:.2f}", BLUE, label_at="left"),
Level(SL, f"stopul din regulă: {SL:.2f}", RED, "4 3", label_at="left")],
zones=[Zone(cf - STOP_BARS + 1, cf, f"numeri {STOP_BARS} candele înapoi, de la intrare", BLUE)],
arrows=[
Arrow(t, f"cel mai jos low din cele {STOP_BARS}: {low4:.2f}\n"
f"stopul merge cu {MARJA:.2f} sub el",
where="below", price=low4, color=GRAY, dx=170, dy=34),
Arrow(cf, f"intrare {ENTRY:.2f}\nde aici numeri înapoi",
where="above", color=GREEN, dx=60, dy=-44),
],
title=f"Pasul 5 — stopul: {STOP_BARS} candele înapoi de la intrare, minus {MARJA:.2f}",
**common,
)
gata = Chart(
candles=jos,
levels=[Level(TP1, f"ținta 1:1: {TP1:.2f}", GREEN, "2 2", label_at="left"),
Level(ENTRY, f"intrarea: {ENTRY:.2f}", GRAY, "4 3", label_at="left"),
Level(OCHI, f"stop pus la ochi: {OCHI:.2f}", ORANGE, "4 3"),
Level(SL, f"stopul din regulă: {SL:.2f}", RED, "4 3", label_at="left")],
zones=[Zone(cf + 1, cf + 1, "", RED, p_top=OCHI, p_bot=SL)],
arrows=[
Arrow(cf + 1, f"retestul coboară la {min(c.l for c in after):.2f}:\n"
f"mătură stopul de {OCHI:.2f},\nnu-l atinge pe cel de {SL:.2f}",
where="below", price=589.60, color=ORANGE, dx=150, dy=26),
Arrow(len(jos) - 1, "cu stopul din regulă mai erai în trade\ncând a venit ținta",
where="above", price=TP1, color=GREEN, dx=40, dy=-14),
],
title="Același trade, cu un retest: stopul „la ochi” e măturat, cel din regulă nu",
caption="Barele de după intrare sunt construite, ca să arate retestul.",
**common,
)
def panou(ch, sus_de=0.0):
ch.top += sus_de
svg = ch.render()
h = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1))
off = float(re.search(r'<line x1="24" y1="([\d.]+)"', svg).group(1)) - ch._y(ch.levels[0].price)
return svg[svg.index(">") + 1:svg.rindex("</svg>")].strip(), h, off
body_sus, h_sus, off_sus = panou(sus)
# numerele 4 3 2 1 sub barele numărate
yb = sus._y(SL) + off_sus + 20
nums = [f'<text x="{sus._cx(k):.1f}" y="{yb:.1f}" fill="{BLUE}" text-anchor="middle" '
f'style="font-weight:bold">{n}</text>'
for k, n in zip(range(cf - STOP_BARS + 1, cf + 1), "4321")]
nums.append(f'<text x="{sus._cx(cf - STOP_BARS):.1f}" y="{yb:.1f}" fill="#999" '
f'text-anchor="middle">(5)</text>')
y_sub = max([yb] + [float(m) for m in re.findall(r'<text[^>]*\sy="([\d.]+)"', body_sus)]) + 24
nums.append(f'<text x="24" y="{y_sub:.1f}" fill="#555">cu 4 sau cu 5 candele iese același stop: '
f'după o coborâre, low-ul piciorului 2 e oricum cel mai jos</text>')
h_sus = max(h_sus, y_sub + 12)
body_jos, h_jos, _ = panou(gata, h_sus + VGAP)
H = h_jos + 8
svg = "\n ".join([
f'<svg viewBox="0 0 {W} {H:.0f}" xmlns="http://www.w3.org/2000/svg" '
f'style="width:100%;height:auto;font:12px sans-serif">',
body_sus, *nums,
f'<line x1="24" y1="{h_sus + VGAP / 2:.0f}" x2="{W - 14}" y2="{h_sus + VGAP / 2:.0f}" stroke="#ddd"/>',
body_jos,
]) + "\n</svg>"
assert "\n\n" not in svg, "linie goală în SVG"
for x, y in re.findall(r'<text x="([-\d.]+)" y="([-\d.]+)"', svg):
assert 0 <= float(x) <= W and 0 <= float(y) <= H, f"text în afara pânzei la ({x}, {y})"
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name} ({W}x{H:.0f})")