"""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'") + 1:svg.rindex("")].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'{n}' for k, n in zip(range(cf - STOP_BARS + 1, cf + 1), "4321")] nums.append(f'(5)') y_sub = max([yb] + [float(m) for m in re.findall(r']*\sy="([\d.]+)"', body_sus)]) + 24 nums.append(f'cu 4 sau cu 5 candele iese același stop: ' f'după o coborâre, low-ul piciorului 2 e oricum cel mai jos') 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'', body_sus, *nums, f'', body_jos, ]) + "\n" assert "\n\n" not in svg, "linie goală în SVG" for x, y in re.findall(r'