#!/usr/bin/env python3 """M2D, pasul 4 — stopul: extrema ultimelor 4 bare, și alternativa 2 × ATR(10). Pentru `summaries/17_STRATEGII/03_m2d.md`, marcajul ``. Sus: exemplul construit din tot capitolul (3 min). Numeri 4 bare înapoi de la bara de trigger, iei cel mai jos low și acolo stă stopul — fix pe el, fără marjă; asta e regula folosită în TOATE diagramele capitolului. Jos: exemplul SPY din curs, singurul loc din material unde apar cifre absolute pentru stop. Reale din curs: close-ul de trigger 517.78, ATR(10) 2.88, stopul 2×ATR 512.02 și low-ul metodei pe 4 candele 512.87. Restul seriei e construit ca să scoată exact acel low. Rulare: python3 gen/strategii/m2d_stop.py """ import sys from pathlib import Path sys.path.insert(0, "/workspace/atm-curs-dl") sys.path.insert(0, str(Path(__file__).resolve().parent)) from svg_candles import Arrow, Candle, Chart # noqa: E402 import m2d_duo as duo # noqa: E402 OUT = Path(__file__).with_suffix(".svg") W = 680 GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555" STOP_BARS = 4 # --- panoul 1: exemplul construit, 3 min (aceleași bare ca în ansamblu) ---- MIC = [(100.60, 100.70, 100.40, 100.45), (100.45, 100.50, 100.25, 100.30), (100.30, 100.35, 100.15, 100.20), (100.20, 100.25, 100.05, 100.10), (100.10, 100.15, 100.00, 100.05), (100.05, 100.10, 99.95, 100.00), (100.00, 100.05, 99.92, 99.95), (99.95, 100.00, 99.90, 99.98), (99.98, 100.08, 99.94, 100.02), (100.02, 100.45, 100.00, 100.40), # 9: trigger (100.40, 100.60, 100.35, 100.55), (100.55, 100.75, 100.50, 100.70)] TRIG_M = 9 # --- panoul 2: SPY, 15 min. Reale: 517.78, 2.88, 512.02, 512.87 ----------- SPY_CLOSE_TRIG = 517.78 SPY_ATR = 2.88 SPY_ATR_STOP = 512.02 SPY_LOW4 = 512.87 SPY = [(521.40, 521.80, 520.10, 520.50), (520.50, 520.90, 519.20, 519.60), (519.60, 519.80, 518.10, 518.40), (518.40, 518.70, 516.90, 517.20), (517.20, 517.40, 515.70, 516.10), (516.10, 516.40, 514.90, 515.30), (515.30, 515.60, 513.40, 514.60), (514.60, 514.90, 513.40, 514.00), # 7 (514.00, 514.30, SPY_LOW4, 513.50), # 8: cel mai jos low din 4 (513.50, 514.20, 513.20, 513.90), # 9 (513.90, 518.10, 513.80, SPY_CLOSE_TRIG)] # 10: trigger TRIG_S = 10 # ---------------------------------------------------------------- verificări for nume, bars in (("mic", MIC), ("SPY", SPY)): for i, (o, h, l, c) in enumerate(bars): assert h >= max(o, c) and l <= min(o, c), f"{nume}[{i}]: high/low incoerent" if i: assert abs(o - bars[i - 1][3]) < 1e-9, f"{nume}[{i}]: open != close precedent" ENTRY_M = MIC[TRIG_M][3] SL_M = min(b[2] for b in MIC[TRIG_M - STOP_BARS + 1:TRIG_M + 1]) RISK_M = round(ENTRY_M - SL_M, 2) assert (ENTRY_M, SL_M, RISK_M) == (100.40, 99.90, 0.50), (ENTRY_M, SL_M, RISK_M) # stopul nu e „undeva pe-aproape": e minimul celor 4 bare, și e minimul întregii serii assert abs(SL_M - min(b[2] for b in MIC)) < 1e-9, "stopul nu e minimul seriei mici" # a cincea bară înapoi are un low mai mare — deci numărul 4 chiar contează assert MIC[TRIG_M - STOP_BARS][2] > SL_M, "a 5-a bară strică demonstrația celor 4" PCT_M = RISK_M / ENTRY_M * 100 SL_S = min(b[2] for b in SPY[TRIG_S - STOP_BARS + 1:TRIG_S + 1]) assert abs(SL_S - SPY_LOW4) < 1e-9, f"low-ul pe 4 bare iese {SL_S}, nu {SPY_LOW4}" assert abs(SPY[TRIG_S][3] - SPY_CLOSE_TRIG) < 1e-9 assert abs((SPY_CLOSE_TRIG - 2 * SPY_ATR) - SPY_ATR_STOP) < 1e-9, \ "2×ATR nu dă stopul din curs" MARJA = round(SPY_LOW4 - SPY_ATR_STOP, 2) assert MARJA == 0.85, MARJA RISK_S4 = round(SPY_CLOSE_TRIG - SPY_LOW4, 2) RISK_SA = round(SPY_CLOSE_TRIG - SPY_ATR_STOP, 2) assert (RISK_S4, RISK_SA) == (4.91, 5.76), (RISK_S4, RISK_SA) assert SPY_ATR_STOP < SPY_LOW4, "ATR ar trebui să dea un stop mai larg" def chart(bars, scale, gap, left, body_w, arrows, top=64): return Chart(candles=[Candle(*b) for b in bars], arrows=arrows, scale=scale, gap=gap, body_w=body_w, left=left, width=W, top=top) ch_m = chart(MIC, 150.0, 34, 62, 13, [ Arrow(TRIG_M, "bara de trigger: de la ea numeri înapoi", where="above", color=GREEN, dx=-30, dy=-6), Arrow(TRIG_M - STOP_BARS + 2, "cel mai jos low dintre cele 4 bare: 99.90.\n" "Stopul stă FIX acolo, fără marjă", where="below", color=RED, dx=-40, dy=40)]) ch_s = chart(SPY, 17.0, 34, 62, 13, [ Arrow(TRIG_S, "close-ul barei de trigger 517.78", where="above", color=GREEN, dx=-60, dy=-4), Arrow(8, "2 × ATR(10) = 5.76 sub close:\n" "stop 512.02, cu 0.85 sub low-ul celor 4 bare", where="below", color=ORANGE, dx=-20, dy=52)]) body_m, h_m = duo.panel(ch_m, W) body_s, h_s = duo.panel(ch_s, W) extra_m = (duo.titlu("Metoda de bază — cele 4 bare", color=RED) + duo.zone(ch_m, TRIG_M - STOP_BARS + 1, TRIG_M, f"{STOP_BARS} bare, numărate de la trigger înapoi", RED, pad=13, lift=30) + duo.level(ch_m, ENTRY_M, ["intrare 100.40"], GREEN, W, at_left=False) + duo.level(ch_m, SL_M, ["stop 99.90 — risc 0.50 pe acțiune (0,50 %)"], RED, W, below=True)) extra_s = (duo.titlu("Alternativa din curs — 2 × ATR(10), pe SPY", color=ORANGE) + duo.zone(ch_s, TRIG_S - STOP_BARS + 1, TRIG_S, "", RED, pad=13) + duo.level(ch_s, SPY_CLOSE_TRIG, ["intrare 517.78"], GREEN, W, at_left=False) + duo.level(ch_s, SPY_LOW4, ["stop pe 4 bare 512.87 — risc 4.91"], RED, W, below=False) + duo.level(ch_s, SPY_ATR_STOP, ["stop 2×ATR 512.02 — risc 5.76"], ORANGE, W, below=True, dash="2 3")) svg, H, n = duo.build( [("mic", body_m, h_m, extra_m), ("SPY", body_s, h_s, extra_s)], ["Sus: serie construită, aceleași cifre ca în restul capitolului.", "Jos: din curs sunt reale doar 517.78, ATR(10) = 2.88, 512.02 și 512.87; " "restul barelor sunt construite.", "ATR-ul dă un stop mai larg, deci un risc mai mare pe acțiune — și, la același", "risc în dolari, mai puține acțiuni."], W) duo.fara_preturi_straine(svg, [ENTRY_M, SL_M, RISK_M, SPY_CLOSE_TRIG, SPY_LOW4, SPY_ATR_STOP, SPY_ATR, RISK_S4, RISK_SA, MARJA, 2 * SPY_ATR, PCT_M]) OUT.write_text(svg, encoding="utf-8") print(f"scris {OUT} ({W}x{H:.0f}), {n} etichete | mic: intrare {ENTRY_M:.2f} stop {SL_M:.2f} " f"risc {RISK_M:.2f} ({PCT_M:.2f} %) | SPY: intrare {SPY_CLOSE_TRIG:.2f}, 4 bare " f"{SPY_LOW4:.2f} (risc {RISK_S4:.2f}), 2×ATR {SPY_ATR_STOP:.2f} (risc {RISK_SA:.2f}), " f"diferență {MARJA:.2f}")