"""Diagramele pentru summaries/08_ANALIZA_ZILNICA/08_executie_si_risc.md. Toate prețurile sunt construite (fără ticker). Rulează: .venv/bin/python gen/az_executie.py Înlocuiește în .md fiecare bloc ... cu output-ul generatorului (inclusiv tabelul eșantionului, ca totalul să vină din calcul). `PChart` e copiat din gen/az_pivoti.py: etichetele nivelurilor stau în marginea dreaptă și se împing singure în jos când sunt apropiate. Adăugat aici: `hist`, close-uri dinaintea primei bare desenate, ca SMA-200 să fie calculată pe 200 de bare reale, nu pe cele 45 vizibile. """ import math import re import sys from dataclasses import dataclass, field from pathlib import Path sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402 MD = Path("/workspace/atm-curs-dl/summaries/08_ANALIZA_ZILNICA/08_executie_si_risc.md") GRAY, RED, GREEN, BLUE, ORANGE, PURPLE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#6a3d9a" STYLE = dict(gap=15, body_w=9, left=44, width=620) RISC = 25.0 svgs = {} # `Seg` si `spread` vin din pchart.py. `PChart` NU: varianta de aici calculeaza # mediile dintr-un istoric de close-uri dinainte de bara 0 (`hist`), ca SMA-20 sa # fie deja incalzita la prima bara desenata, si si le deseneaza singura. Clasa # comuna nu are nevoie de asta, iar unirea lor ar risca cele 10 diagrame de aici. from pchart import Seg, spread # noqa: E402 @dataclass class PChart(Chart): segs: list = field(default_factory=list) seps: list = field(default_factory=list) pad_px: float = 24.0 lm: int = 104 pad_bot_px: float = 0.0 hist: list = field(default_factory=list) # close-uri înainte de bara 0, doar pentru medii def ma_vals(self, period): full = self.hist + [c.c for c in self.candles] h = len(self.hist) return [sum(full[h + i + 1 - period:h + i + 1]) / period if h + i + 1 >= period else None for i in range(len(self.candles))] def render(self) -> str: n = len(self.candles) self.left = self.lm if any(s.label_at == "lm" for s in self.segs) else self.left segs = list(self.segs) + [Seg(0, n - 1, lv.price, lv.text, lv.color, "out", dash="4 3") for lv in self.levels] saved_lv, saved_ma = list(self.levels), list(self.mas) longest = max([len(s.text) for s in segs if s.label_at == "out"] + [len(m.text) for m in saved_ma] + [0]) self.width = max(self.width, int(self._cx(n - 1) + self.gap / 2 + 6 + longest * 6.2 + 8)) ma_prices = [v for m in saved_ma for v in self.ma_vals(m.period) if v is not None] top_price = max([c.h for c in self.candles] + [s.price for s in segs] + ma_prices) self.levels = [Level(s.price, "", "none") for s in segs] + [Level(p, "", "none") for p in ma_prices] self.levels.append(Level(top_price + self.pad_px / self.scale, "", "none")) if self.pad_bot_px: low_price = min([c.l for c in self.candles] + [s.price for s in segs]) self.levels.append(Level(low_price - self.pad_bot_px / self.scale, "", "none")) self.mas = [] # mediile le desenăm noi, cu `hist` svg = self.render_base() self.levels, self.mas = saved_lv, saved_ma parts = svg.split("\n ") extra = [] for k in self.seps: x = self._cx(k) - self.gap / 2 extra.append(f'') for m in saved_ma: pts = [f"{self._cx(i):.1f},{self._y(v):.1f}" for i, v in enumerate(self.ma_vals(m.period)) if v is not None] dash = f' stroke-dasharray="{m.dash}"' if m.dash else "" extra.append(f'') xr = self._cx(n - 1) + self.gap / 2 + 6 out_lab = [(self._y(s.price) + 4, s) for s in segs if s.label_at == "out" and s.text] ma_lab = [(self._y(self.ma_vals(m.period)[-1]) + 4, Seg(0, 0, 0, m.text, m.color)) for m in saved_ma if m.text] placed = {id(s): y for y, s in spread(out_lab + ma_lab)} placed.update({id(s): y for y, s in spread([(self._y(s.price) + 4, s) for s in segs if s.label_at == "lm"])}) for s in segs: x0, x1, y = self._cx(s.i0) - self.gap / 2, self._cx(s.i1) + self.gap / 2, self._y(s.price) dash = f' stroke-dasharray="{s.dash}"' if s.dash else "" extra.append(f'') if not s.text: continue if s.label_at == "out": extra.append(f'{s.text}') elif s.label_at == "lm": extra.append(f'{s.text}') elif s.label_at == "left": extra.append(f'{s.text}') else: extra.append(f'{s.text}') for _, s in ma_lab: extra.append(f'{s.text}') k = 2 if self.title else 1 return "\n ".join(parts[:k] + extra + parts[k:]) def render_base(self): svg = Chart.render(self) svg = re.sub(r'\n ]*stroke="none"[^>]*/>\n ]*fill="none"[^>]*>', "", svg) prices = [p for c in self.candles for p in (c.o, c.h, c.l, c.c)] + [lv.price for lv in self.levels] self._body_bottom = self.top + (max(prices) - min(prices)) * self.scale return svg def f2(x): return f"{x:.2f}" def rpx(ch, entry, sl, tp): """câți R e `tp`, măsurat în pixeli pe desen""" return (ch._y(entry) - ch._y(tp)) / (ch._y(sl) - ch._y(entry)) def first(cds, a, cond): return next(i for i in range(a, len(cds)) if cond(cds[i])) def shares(risk_per_share): return math.floor(RISC / risk_per_share + 1e-9) # ================================================================ 1. risc fix 25 $ la 1:4 — long cl1 = [101.55, 101.40, 101.48, 101.25, 101.05, 101.12, 100.90, 100.72, 100.80, 100.62, 100.50, 100.46, 100.52, 100.47, 100.60, # 14 trigger 100.70, 100.62, 100.78, 100.90, 100.84, 101.00, 101.12, 101.05, 101.22, 101.35, 101.28, 101.45, 101.58, 101.66, 101.55, 101.60, 101.48, 101.52, 101.40] cds = from_closes(cl1, wick=0.06, start=101.60) T = 14 entry = cds[T].c sl = round(entry - 0.25, 2) tp = round(entry + 4 * (entry - sl), 2) n1 = shares(entry - sl) assert sl < min(c.l for c in cds[T - 3:T + 1]), "stopul trebuie să stea sub low-urile bazei" assert n1 == 100 and abs(n1 * (entry - sl) - 25) < 1e-6 and abs(n1 * (tp - entry) - 100) < 1e-6 hit = first(cds, T + 1, lambda c: c.h >= tp) assert min(c.l for c in cds[T + 1:hit + 1]) > sl, "stopul nu trebuie atins înainte de țintă" top0 = max(range(T), key=lambda i: cds[i].h) assert abs(cds[top0].h - tp) < 0.15, "ținta 4R trebuie să cadă lângă vârful de unde a plecat pullback-ul" print(f"D1 entry {f2(entry)} SL {f2(sl)} TP {f2(tp)} {n1} acțiuni; vârf anterior {f2(cds[top0].h)}; TP atins la {hit}") L1 = dict(entry=entry, sl=sl, tp=tp, n=n1, hit=hit - T, top=cds[top0].h) ch = PChart(candles=cds, segs=[Seg(T, len(cds) - 1, tp, f"TP {f2(tp)} = 4R = +100 $", BLUE, dash="4 3"), Seg(T, len(cds) - 1, entry, f"intrare {f2(entry)} ({n1} acțiuni)", GREEN), Seg(T, len(cds) - 1, sl, f"SL {f2(sl)} = 1R = −25 $", RED)], marks=[Mark(3, "pullback", GRAY, "below", dy=30), Mark(T, "trigger", GREEN, "below", dy=4), Mark(hit, "4R", BLUE, "above", dy=-2)], scale=130, title="Exemplu construit (fără ticker) — 5 min", caption=f"Risc 0.25 $/acțiune × {n1} acțiuni = 25 $. Ținta e de 4 ori mai departe decât stopul.", **STYLE) svg_ = ch.render() assert abs(rpx(ch, entry, sl, tp) - 4.0) < 0.01, "4R nu e desenat la 4x stopul" svgs["risc_long"] = svg_ # ================================================================ 2. aceeași aritmetică pe short cl2 = [180.60 - c for c in cl1] cds = from_closes(cl2, wick=0.06, start=180.60 - 101.60) entry = cds[T].c sl = round(entry + 0.25, 2) tp = round(entry - 4 * (sl - entry), 2) n2 = shares(sl - entry) assert sl > max(c.h for c in cds[T - 3:T + 1]) and n2 == 100 hit = first(cds, T + 1, lambda c: c.l <= tp) assert max(c.h for c in cds[T + 1:hit + 1]) < sl print(f"D2 short entry {f2(entry)} SL {f2(sl)} TP {f2(tp)} atins la {hit}") S2 = dict(entry=entry, sl=sl, tp=tp) ch = PChart(candles=cds, segs=[Seg(T, len(cds) - 1, sl, f"SL {f2(sl)} = 1R = −25 $", RED), Seg(T, len(cds) - 1, entry, f"intrare short {f2(entry)} ({n2} acțiuni)", GREEN), Seg(T, len(cds) - 1, tp, f"TP {f2(tp)} = 4R = +100 $", BLUE, dash="4 3")], marks=[Mark(3, "raliu", GRAY, "above", dy=-30), Mark(T, "trigger", RED, "above", dy=-4), Mark(hit, "4R", BLUE, "below", dy=2)], scale=130, title="Exemplu construit (fără ticker) — 5 min", caption="Short: stopul deasupra, ținta dedesubt, tot 0.25 $ contra 1.00 $.", **STYLE) svg_ = ch.render() assert abs(rpx(ch, entry, sl, tp) - 4.0) < 0.01 svgs["risc_short"] = svg_ # ================================================================ 3-4. break-even: DA și NU base = [25.90, 25.78, 25.82, 25.66, 25.55, 25.60, 25.46, 25.35, 25.40, 25.28, 25.24, 25.26, 25.22, 25.28, 25.40, # 14 trigger 25.48, 25.58, 25.70, 25.76, 25.68] # 17: +1R -> stop la break-even be_da = [25.60, 25.55, 25.66, 25.80, 25.74, 25.92, 26.05, 25.98, 26.15, 26.28, 26.22, 26.36, 26.45, 26.38, 26.42] be_nu = [25.58, 25.43, 25.52, 25.66, 25.80, 25.74, 25.92, 26.05, 25.98, 26.15, 26.28, 26.22, 26.36, 26.45, 26.38] def be_chart(tail, name, nu): cds = from_closes(base + tail, wick=0.04, start=26.00) entry = cds[T].c sl = round(entry - 0.25, 2) r1 = round(entry + 0.25, 2) tp = round(entry + 1.00, 2) assert sl < min(c.l for c in cds[T - 3:T + 1]) mv = first(cds, T + 1, lambda c: c.h >= r1) assert mv == 17, mv hit = first(cds, mv + 1, lambda c: c.h >= tp) out = next((i for i in range(mv + 1, hit) if cds[i].l <= entry), None) assert min(c.l for c in cds[T + 1:]) > sl, "stopul inițial nu e atins niciodată" assert (out is not None) == nu, "NU: scos la break-even înainte de țintă; DA: nu" n = len(cds) end = out if nu else hit segs = [Seg(T, n - 1, tp, f"TP {f2(tp)} = 4R", BLUE, dash="4 3"), Seg(T, mv, sl, f"SL inițial {f2(sl)} = −1R", RED), Seg(mv, end, entry, f"SL mutat la intrare {f2(entry)} = 0", ORANGE), Seg(T, mv, entry, "", GREEN), Seg(mv, n - 1, r1, f"+1R {f2(r1)}: aici muți stopul", GRAY, dash="2 3")] marks = [Mark(T, "intrare", GREEN, "below", dy=4), Mark(mv, "+1R", GRAY, "above", dy=-2)] if nu: marks += [Mark(out, "scos la 0", ORANGE, "below", dy=4), Mark(hit, "4R, fără tine", BLUE, "above", dy=-6)] cap = "Codița coboară până la intrare (25.39): ieși pe zero, apoi prețul merge la țintă." else: marks += [Mark(hit, "4R", BLUE, "above", dy=-2)] cap = "Pullback-ul se oprește deasupra intrării: stopul la zero nu se atinge, ținta da." ch = PChart(candles=cds, segs=segs, marks=marks, scale=150, title="Exemplu construit (fără ticker) — 5 min", caption=cap, **STYLE) svg_ = ch.render() assert abs(rpx(ch, entry, sl, tp) - 4.0) < 0.01 and abs(rpx(ch, entry, sl, r1) - 1.0) < 0.01 svgs[name] = svg_ low_pb = min(c.l for c in cds[mv + 1:hit]) print(f"D{'4' if nu else '3'} entry {f2(entry)} SL {f2(sl)} +1R {f2(r1)} la {mv} TP {f2(tp)} la {hit}; " f"low pullback {f2(low_pb)}; scos la {out}") return dict(entry=entry, sl=sl, r1=r1, tp=tp, low=low_pb) BE_DA = be_chart(be_da, "be_da", False) BE_NU = be_chart(be_nu, "be_nu", True) # ================================================================ 5. trei poziții: TP1 / TP2 / trail cl5 = [41.80, 41.50, 41.60, 41.10, 40.70, 40.85, 40.40, 40.10, 40.20, 39.80, 39.75, 39.70, 40.00, # 12 buy doji 40.20, 40.10, 40.35, 40.55, 40.45, 40.70, 40.90, 40.80, 41.05, 41.30, 41.20, 41.55, 41.85, 41.70, 42.05, 42.30, 42.10, 41.90, 41.80, 41.30, 41.10, 40.90, 41.00] cds = from_closes(cl5, wick=0.10, start=42.00) T = 12 entry = cds[T].c sl = round(entry - 0.50, 2) tp1, tp2 = round(entry + 0.50, 2), round(entry + 1.00, 2) n5 = shares(entry - sl) // 3 * 3 per = n5 // 3 assert sl < min(c.l for c in cds[T - 3:T + 1]) and n5 == 48 h1 = first(cds, T + 1, lambda c: c.h >= tp1) h2 = first(cds, h1 + 1, lambda c: c.h >= tp2) assert min(c.l for c in cds[T + 1:]) > sl, "stopul inițial nu e atins" # trail: linia = low-ul ultimei candele care a închis peste high-ul precedentei; urcă doar; ieși la close sub ea trail, steps, ex = None, [], None for i in range(h1 + 1, len(cds)): if trail is not None and cds[i].c < trail: ex = i break if cds[i].c > cds[i - 1].h and (trail is None or cds[i].l > trail): trail = cds[i].l steps.append((i, trail)) assert ex is not None and ex > h2 x_price = cds[ex].c pnl = [per * (tp1 - entry), per * (tp2 - entry), per * (x_price - entry)] R_tot = sum(pnl) / (n5 * (entry - sl)) print(f"D5 entry {f2(entry)} SL {f2(sl)} {n5}={per}x3; TP1 {f2(tp1)} la {h1}, TP2 {f2(tp2)} la {h2}; trail " f"{[(i, f2(p)) for i, p in steps]}; ieșire {ex} la {f2(x_price)}; P&L {[f2(p) for p in pnl]} = {f2(sum(pnl))} " f"({R_tot:.2f}R pe riscul de {f2(n5 * (entry - sl))})") TP3 = dict(entry=entry, sl=sl, tp1=tp1, tp2=tp2, n=n5, per=per, x=x_price, pnl=pnl, R=R_tot, steps=steps, risk=n5 * (entry - sl), h1=h1 - T, h2=h2 - T, ex=ex - T) segs = [Seg(T, steps[0][0] - 1, sl, f"SL inițial {f2(sl)} (toate 3)", RED), Seg(T, len(cds) - 1, entry, f"intrare {f2(entry)}", GREEN), Seg(T, h1, tp1, f"TP1 {f2(tp1)} = 1R: poziția 1", BLUE, dash="4 3"), Seg(T, h2, tp2, f"TP2 {f2(tp2)} = 2R: poziția 2", PURPLE, dash="4 3")] for k, (i, p) in enumerate(steps): i1 = steps[k + 1][0] - 1 if k + 1 < len(steps) else ex segs.append(Seg(i, i1, p, f"trail final {f2(p)}: poziția 3" if k + 1 == len(steps) else "", ORANGE)) ch = PChart(candles=cds, segs=segs, marks=[Mark(T, "buy doji", GREEN, "below", dy=4), Mark(h1, "TP1", BLUE, "above", dy=-2), Mark(h2, "TP2", PURPLE, "above", dy=-2), Mark(ex, "ieșire: close sub trail", ORANGE, "above", dy=-40)], scale=90, title="Exemplu construit (fără ticker) — daily", caption=f"{n5} acțiuni = 3 × {per}. Fiecare poziție iese altfel; stopul comun e 0.50 $ sub intrare.", **STYLE) svg_ = ch.render() assert abs(rpx(ch, entry, sl, tp1) - 1.0) < 0.01 and abs(rpx(ch, entry, sl, tp2) - 2.0) < 0.01 assert abs(rpx(ch, entry, sl, steps[-1][1]) - (steps[-1][1] - entry) / (entry - sl)) < 0.01 svgs["trei_pozitii"] = svg_ # ================================================================ 6-7. ținta realistă 1/2 vs ținta lacomă cl6 = [63.20, 64.00, 64.90, 65.80, 65.30, 64.60, 64.90, 64.00, 63.20, 63.50, 62.60, 61.80, 62.10, 61.20, 60.50, 60.35, 60.40, 60.70, # 17 trigger 60.90, 61.50, 61.30, 62.00, 62.60, 62.30, 63.00, 63.50, 63.90, 64.20, 63.70, 63.00, 62.40, 61.60, 61.00, 60.30, 59.80, 59.50, 59.90] w6 = [(0.2, 0.2)] * 15 + [(0.15, 0.35), (0.25, 0.25)] + [(0.2, 0.2)] * 20 w6[3] = (0.2, 0.2) cds = from_closes(cl6, wicks=w6, start=62.60) T, SUP = 17, 60.00 entry = cds[T].c top = max(range(T), key=lambda i: cds[i].h) hi = cds[top].h sl = round(min(c.l for c in cds[T - 3:T + 1]) - 0.10, 2) risk = entry - sl half = round(entry + (hi - entry) / 2, 2) assert abs(min(c.l for c in cds[T - 3:T + 1]) - SUP) < 0.01, "low-ul bazei stă exact pe confluență" hh = first(cds, T + 1, lambda c: c.h >= half) peak = max(c.h for c in cds[T + 1:]) stop_i = first(cds, T + 1, lambda c: c.l <= sl) assert hh < stop_i and peak < hi, "1/2 se atinge înainte de stop; vârful nu se atinge deloc" Rh, Rg = (half - entry) / risk, (hi - entry) / risk print(f"D6 vârf {f2(hi)} entry {f2(entry)} SL {f2(sl)} risc {f2(risk)} 1/2 {f2(half)} ({Rh:.2f}R) la {hh}; " f"max după {f2(peak)}; stop la {stop_i}; lacom {Rg:.2f}R") HALF = dict(hi=hi, entry=entry, sl=sl, risk=risk, half=half, Rh=Rh, Rg=Rg, peak=peak, hh=hh - T, covered=(peak - entry) / (hi - entry)) n = len(cds) common = [Seg(0, n - 1, SUP, "", PURPLE, dash="2 3"), Seg(T, n - 1, entry, f"intrare {f2(entry)}", GREEN), Seg(T, n - 1, sl, f"SL {f2(sl)} = −1R, sub confluența S2 + monthly {SUP:.2f} (mov)", RED)] ch = PChart(candles=cds, segs=common + [Seg(top, n - 1, hi, f"vârf anterior {f2(hi)}", GRAY, dash="4 3"), Seg(T, n - 1, half, f"TP = 1/2 din distanță {f2(half)} = {Rh:.1f}R", BLUE, dash="4 3")], marks=[Mark(top, "vârf", GRAY, "above", dy=-2), Mark(T, "intrare", GREEN, "below", dy=6), Mark(hh, "TP atins", BLUE, "above", dy=-2)], scale=26, title="Exemplu construit (fără ticker) — daily", caption="DA: ținta e jumătate din drumul până la vârf. Atinsă în 8 zile, înainte de întoarcere.", **STYLE) svg_ = ch.render() assert abs(rpx(ch, entry, sl, half) - Rh) < 0.01 svgs["jumatate_da"] = svg_ ch = PChart(candles=cds, segs=common + [Seg(top, n - 1, hi, f"TP lacom = vârful {f2(hi)} = {Rg:.1f}R", RED, dash="4 3")], marks=[Mark(top, "vârf", GRAY, "above", dy=-2), Mark(T, "intrare", GREEN, "below", dy=6), Mark(first(cds, T + 1, lambda c: c.h >= peak), "se întoarce", GRAY, "above", dy=-2), Mark(stop_i, "stop: −1R", RED, "below", dy=4)], scale=26, title="Exemplu construit (fără ticker) — daily", caption="NU: aceeași serie, ținta la vârf. Prețul acoperă 70% din drum, se întoarce și ia stopul.", **STYLE) svg_ = ch.render() assert abs(rpx(ch, entry, sl, hi) - Rg) < 0.01 assert abs(HALF["covered"] - 0.70) < 0.02, HALF["covered"] svgs["lacom_nu"] = svg_ # ================================================================ 8. reintrare după stop, cu același risc cl8 = [30.60, 30.48, 30.52, 30.36, 30.22, 30.26, 30.10, 30.20, # 7 intrarea 1 30.14, 30.05, 29.92, 29.80, 29.70, 29.74, 29.84, # 14 intrarea 2 29.78, 29.70, 29.58, 29.46, 29.40, 29.44, 29.56, # 21 intrarea 3 29.62, 29.55, 29.72, 29.86, 29.80, 29.98, 30.12, 30.06, 30.24, 30.38, 30.32, 30.50, 30.60, 30.52, 30.58] cds = from_closes(cl8, wick=0.03, start=30.70) E = [(7, 0.25), (14, 0.20), (21, 0.25)] trades = [] for k, (i, r) in enumerate(E): e = cds[i].c s = round(e - r, 2) tp = round(e + 4 * r, 2) ns = shares(r) assert s < min(c.l for c in cds[i - 2:i + 1]), f"stopul intrării {k + 1} nu e sub structură" st = next((j for j in range(i + 1, len(cds)) if cds[j].l <= s), 10 ** 6) th = next((j for j in range(i + 1, len(cds)) if cds[j].h >= tp), 10 ** 6) trades.append(dict(i=i, e=e, s=s, tp=tp, n=ns, stop=st if st < th else None, hit=th if th < st else None)) assert abs(ns * r - RISC) < 1e-6, "fiecare intrare riscă exact 25 $" assert trades[0]["stop"] and trades[1]["stop"] and trades[2]["hit"], trades assert trades[0]["stop"] < trades[1]["i"] and trades[1]["stop"] < trades[2]["i"] net = -RISC - RISC + trades[2]["n"] * (trades[2]["tp"] - trades[2]["e"]) assert abs(net - 50) < 1e-6 print("D8", [(t["i"], f2(t["e"]), f2(t["s"]), t["n"], t["stop"], t["hit"]) for t in trades], "net", net) REIN = dict(trades=trades, net=net) t1, t2, t3 = trades n = len(cds) ch = PChart(candles=cds, segs=[Seg(t3["i"], n - 1, t3["tp"], f"#3 TP {f2(t3['tp'])} = 4R = +100 $", BLUE, dash="4 3"), Seg(t1["i"], t1["stop"], t1["e"], f"#1 intrare {f2(t1['e'])} ({t1['n']} acț.)", GREEN, "left"), Seg(t1["i"], t1["stop"], t1["s"], f"#1 SL {f2(t1['s'])}: −25 $", RED, "lm"), Seg(t2["i"], t2["stop"], t2["e"], f"#2 intrare {f2(t2['e'])} ({t2['n']} acț.)", GREEN, "left"), Seg(t2["i"], t2["stop"], t2["s"], f"#2 SL {f2(t2['s'])}: −25 $", RED, "lm"), Seg(t3["i"], t3["hit"], t3["e"], f"#3 intrare {f2(t3['e'])} ({t3['n']} acț.)", GREEN), Seg(t3["i"], t3["hit"], t3["s"], f"#3 SL {f2(t3['s'])}", RED)], marks=[Mark(t1["stop"], "stop", RED, "below", dy=4), Mark(t2["stop"], "stop", RED, "below", dy=4), Mark(t3["i"], "#3", GREEN, "above", dy=-4), Mark(t3["hit"], "4R", BLUE, "above", dy=-2)], scale=170, title="Exemplu construit (fără ticker) — 5 min", caption="Două stopuri de câte 25 $, a treia intrare cu același risc: −25 −25 +100 = +50 $.", **STYLE) svg_ = ch.render() for t in trades: assert abs(rpx(ch, t["e"], t["s"], t["tp"]) - 4.0) < 0.01 svgs["reintrare"] = svg_ # ================================================================ 9. tranșe de 1/4 la acțiuni scumpe cl9 = [450, 462, 468, 460, 448, 452, 438, 426, 430, 414, 400, 394, 398, 410, 422, 430, 436, 428, 420, 412, 406, # 20 higher low 418, # 21 confirmare = tranșa 1 426, 432, 428, 425, 420, 416, 424, 432, 438, 435, 444, 452, 448, 458, 466, 471, 465, 468] cds = from_closes(cl9, wick=2.0, start=445) HL, CF, SH = 20, 21, 23 prev_low = min(c.l for c in cds[:HL]) assert cds[HL].l > prev_low and cds[CF].c > cds[HL].h, "higher low confirmat de candela din dreapta" top = max(range(HL), key=lambda i: cds[i].h) sh = cds[SH].h move = sh - cds[HL].l lv = [(CF, cds[CF].c, "confirmare")] for frac, name in [(1 / 3, "1/3"), (1 / 2, "1/2"), (2 / 3, "2/3")]: p = round(sh - frac * move, 2) i = first(cds, SH + 1, lambda c, p=p: c.l <= p) lv.append((i, p, name)) assert [x[0] for x in lv] == [21, 25, 26, 27], lv sl9 = cds[HL].l - 5 per9 = 2 avg = sum(p for _, p, _ in lv) / 4 risk_tr = sum(per9 * (p - sl9) for _, p, _ in lv) risk_chase = 4 * per9 * (sh - sl9) tgt = cds[top].h hit = first(cds, 28, lambda c: c.h >= tgt) assert min(c.l for c in cds[CF:]) > sl9 and hit > lv[-1][0] prof_tr = 4 * per9 * (tgt - avg) prof_ch = 4 * per9 * (tgt - sh) print(f"D9 low anterior {prev_low} HL {cds[HL].l} vârf {sh} mișcare {move}; tranșe {lv}; SL {sl9} medie {avg} " f"risc {risk_tr} vs {risk_chase}; țintă {tgt} la {hit}; profit {prof_tr} vs {prof_ch}") TR = dict(lv=lv, sl=sl9, avg=avg, risk=risk_tr, chase=risk_chase, tgt=tgt, hl=cds[HL].l, sh=sh, move=move, prev_low=prev_low, per=per9, prof=prof_tr, prof_ch=prof_ch) n = len(cds) ch = PChart(candles=cds, segs=[Seg(top, n - 1, tgt, f"țintă: vârful anterior {tgt:.0f}", BLUE, dash="4 3"), Seg(SH, n - 1, sh, f"vârful după HL {sh:.0f} (aici NU cumperi tot)", GRAY, dash="2 3")] + [Seg(lv[0][0], n - 1, lv[0][1], f"tranșele 1 și 3: câte {per9} acț. la {lv[0][1]:.0f} / {lv[2][1]:.0f}", GREEN), Seg(lv[1][0], n - 1, lv[1][1], f"tranșa 2: {per9} acț. la {lv[1][1]:.0f} (1/3)", GREEN), Seg(lv[2][0], n - 1, lv[2][1], "", GREEN), Seg(lv[3][0], n - 1, lv[3][1], f"tranșa 4: {per9} acț. la {lv[3][1]:.0f} (2/3)", GREEN)] + [Seg(CF, n - 1, sl9, f"SL comun {sl9:.0f} (sub HL {cds[HL].l:.0f})", RED)], marks=[Mark(11, "low", GRAY, "below", dy=4), Mark(HL, "HL", GREEN, "below", dy=4), Mark(SH, "vârf", GRAY, "above", dy=-2), Mark(hit, "țintă", BLUE, "above", dy=-2)], scale=3.4, title="Exemplu construit (fără ticker) — weekly, acțiune de ~420 $", caption=f"4 tranșe × {per9} acțiuni, un singur stop. Preț mediu {avg:.2f}, risc total {risk_tr:.0f} $.", **STYLE) svg_ = ch.render() assert abs(rpx(ch, avg, sl9, tgt) - (tgt - avg) / (avg - sl9)) < 0.01 svgs["transe"] = svg_ # ================================================================ 10. ținte în trepte, cu SMA-200 desenată hist10 = [55.1 + 4.3 * k / 199 for k in range(200)] cl10 = [60.40, 60.10, 60.50, 60.20, 55.20, # 4 marubozu de cădere 55.80, 55.90, 55.40, 55.00, 54.30, 54.70, 53.60, 52.80, 53.10, 52.00, 51.20, 51.50, 50.40, 49.60, 49.90, 49.10, 48.60, 48.90, 48.85, # 23 doji 49.60, # 24 buy doji 50.20, 49.90, 50.70, 51.30, 51.00, 51.80, 52.60, 52.20, 53.00, 53.80, 53.40, 54.30, 55.00, 54.60, 55.50, 56.20, 55.90, 56.70, 56.40, 55.90] w10 = [(0.2, 0.2)] * 4 + [(0.05, 0.05)] + [(0.2, 0.2)] * 18 + [(0.4, 0.4)] + [(0.2, 0.2)] * 21 cds = from_closes(cl10, wicks=w10, start=60.30) MAR, DJ, T = 4, 23, 24 R1, R2 = 51.00, 52.50 # pivotul lunii: date, calculul e în documentul 04 assert cds[MAR].o - cds[MAR].c >= 4.5, "marubozu mare" assert abs(cds[DJ].o - cds[DJ].c) <= 0.1 and cds[T].c > cds[DJ].h, "buy doji" entry = cds[T].c sl = round(min(c.l for c in cds[T - 3:T + 1]) - 0.10, 2) risk = entry - sl ph_i = max(range(MAR + 1, T), key=lambda i: cds[i].h) ph = cds[ph_i].h half_m = round((cds[MAR].o + cds[MAR].c) / 2, 2) sup = round(min(c.l for c in cds[:MAR]), 2) tmp = PChart(candles=cds, hist=hist10) sma = tmp.ma_vals(200) hits = {} for name, p in [("R1", R1), ("R2", R2), ("vârf", ph)]: hits[name] = first(cds, T + 1, lambda c, p=p: c.h >= p) hits["SMA-200"] = next(i for i in range(T + 1, len(cds)) if cds[i].h >= sma[i]) sma_hit = sma[hits["SMA-200"]] assert R1 < R2 < ph < sma_hit < half_m < sup, (R1, R2, ph, sma_hit, half_m, sup) assert hits["R1"] < hits["R2"] < hits["vârf"] < hits["SMA-200"] assert max(c.h for c in cds[T + 1:]) < half_m and min(c.l for c in cds[T + 1:]) > sl assert all(cds[i].c < sma[i] for i in range(hits["SMA-200"], len(cds))), "după atingere, nicio bară nu închide peste SMA-200" rs = {k: (p - entry) / risk for k, p in [("R1", R1), ("R2", R2), ("vârf", ph), ("SMA-200", sma_hit), ("1/2 marubozu", half_m), ("fost suport", sup)]} print(f"D10 entry {f2(entry)} SL {f2(sl)} risc {f2(risk)}; R1 {R1} R2 {R2} vârf {f2(ph)} SMA-200 la atingere " f"{f2(sma_hit)} (ultima {f2(sma[-1])}) 1/2 mar {f2(half_m)} suport {f2(sup)}; hits {hits}; " + ", ".join(f"{k} {v:.2f}R" for k, v in rs.items())) TREP = dict(entry=entry, sl=sl, risk=risk, ph=ph, sma=sma_hit, half=half_m, sup=sup, rs=rs, hits={k: v - T for k, v in hits.items()}, mar_o=cds[MAR].o, mar_c=cds[MAR].c) n = len(cds) ch = PChart(candles=cds, hist=hist10, segs=[Seg(0, n - 1, sup, f"6. fost suport {f2(sup)} = {rs['fost suport']:.1f}R", PURPLE, dash="4 3"), Seg(MAR, n - 1, half_m, f"5. 1/2 marubozu {f2(half_m)} = {rs['1/2 marubozu']:.1f}R", ORANGE, dash="4 3"), Seg(hits["SMA-200"], n - 1, sma_hit, f"4. SMA-200 (linia gri) atinsă la {f2(sma_hit)} = {rs['SMA-200']:.1f}R", GRAY, dash="2 3"), Seg(ph_i, n - 1, ph, f"3. vârf anterior {f2(ph)} = {rs['vârf']:.1f}R", BLUE, dash="4 3"), Seg(T, n - 1, R2, f"2. R2 {f2(R2)} = {rs['R2']:.1f}R", BLUE, dash="4 3"), Seg(T, n - 1, R1, f"1. R1 {f2(R1)} = {rs['R1']:.1f}R", BLUE, dash="4 3"), Seg(T, n - 1, entry, f"intrare {f2(entry)}", GREEN), Seg(T, n - 1, sl, f"SL {f2(sl)} = −1R", RED)], mas=[MA(200, GRAY)], marks=[Mark(MAR, "marubozu", RED, "below", dy=4), Mark(T, "buy doji", GREEN, "below", dy=6), Mark(hits["R1"], "1", BLUE, "above", dy=-2), Mark(hits["R2"], "2", BLUE, "above", dy=-2), Mark(hits["vârf"], "3", BLUE, "above", dy=-2), Mark(hits["SMA-200"], "4", GRAY, "above", dy=-12)], scale=24, title="Exemplu construit (fără ticker) — daily", caption="Treptele 1-4 se ating în ordine; la SMA-200, sub 1/2 marubozu, prețul e respins.", **STYLE) svg_ = ch.render() for p, r in [(R1, rs["R1"]), (R2, rs["R2"]), (ph, rs["vârf"]), (sma_hit, rs["SMA-200"]), (half_m, rs["1/2 marubozu"]), (sup, rs["fost suport"])]: assert abs(rpx(ch, entry, sl, p) - r) < 0.01 svgs["trepte"] = svg_ # ================================================================ 11. eșantionul de 25 de tranzacții la 1:4 seq = "SSSSCSSSCSSSSCSSSCSSCSSSC" # S = stop (−25 $), C = țintă 4R (+100 $) assert len(seq) == 25 rows, eq = [], 0.0 for k, ch_ in enumerate(seq, 1): eq += 100 if ch_ == "C" else -RISC rows.append((k, ch_, eq)) wins = seq.count("C") total = wins * 100 - (25 - wins) * RISC assert abs(total - eq) < 1e-9 and wins == 6 and total == 125 worst = min(r[2] for r in rows) day1 = rows[4][2] be_rate = 1 / (1 + 4) print(f"D11 {wins}/25 câștigătoare, total {total}, minim {worst}, după 5 tranzacții {day1}; prag {be_rate:.0%}") tbl = ["| # | rezultat | cumulat |", "|---|---|---|"] for k, ch_, e in rows: tbl.append(f"| {k} | {'țintă +100 $' if ch_ == 'C' else 'stop −25 $'} | {e:+.0f} $ |") svgs["esantion_tabel"] = "\n".join(tbl) SAMPLE = dict(wins=wins, total=total, worst=worst, day1=day1) # ---------------------------------------------------------------- substituție în .md if MD.exists(): md = MD.read_text(encoding="utf-8") for name, svg in svgs.items(): pat = re.compile(rf".*?", re.S) assert pat.search(md), f"marcatorul {name} lipsește din .md" md = pat.sub(lambda _m: f"\n{svg}\n", md) MD.write_text(md, encoding="utf-8") print("scris", MD)