Files
atm-curs-dl/gen/az_executie.py
Claude Agent 4383e500af 08 si 09: reparate 11 defecte de continut plus pasul vizual
Verificare independenta pe continut, aritmetica si, separat, pe 31 de
screenshot-uri citite. Niciun punct contestat.

08:
- TP1/TP2 era atribuit la Strategia 3, care are 1:1 / trail / lasata sa
  curga, fara TP2. Schema 1:1 / 2:1 / trail e insa a lui, din Q&A
  11.06.2025 - atribuire corectata cu ancora verificata, si scoasa din
  lista de completari, unde figura greșit ca adaugire a autorului. La Surse,
  Strategia 3 rămâne, cu mențiunea "TP la 1:1 si trail, fara TP2".
- "dupa TP1 stopul pozitiilor 2 si 3 trece la break-even" nu era in nicio
  sursa si contrazicea sursa: acolo a treia poate sta pe stopul initial,
  fiindca dupa 1R si 2R totalul ramane pe profit. Inlocuit cu calculul lui
  (-8 $ fata de +24 $). Agentul a prins si o fraza vecina falsa pe desen.
- gotcha de volatilitate era inversul regulii din 02.09 (stop pe break-even
  pana se lamureste). Corectat dupa sursa.
- "pretul e respins chiar la SMA-200" era fals pe seria desenata: o bara
  inchidea peste medie. Reparat DESENUL, nu textul - doua close-uri
  schimbate, cu assert ca nicio bara nu mai inchide peste SMA dupa
  atingere. Cifrele din tabel neschimbate.

09:
- "pullback-ul il atinge a doua zi dupa intrare" nu corespundea desenului:
  stopul e atins la a doua bara, nu in ziua urmatoare. Corectat in ambele
  locuri, cu assert pe bara.
- eticheta "stop" era scrisa peste linia "sus gap 66.70", deci pe imagine
  parea ca stopul a fost acolo, nu la 68.60. Mark se ancoreaza doar pe
  candele si nu incapea nicaieri curat, asa ca informatia a trecut in
  eticheta nivelului, cu assert pe bara de atingere.
- trei parafraze ale recoltei nu mai apar ca citate ale lui, iar condiția de
  la pre-earnings e atribuita explicit recoltei din 25.08, cu mențiunea ca
  nu e in citatul lui.
- rezerva lui la pattern-ul pre-earnings ("nu suta la suta") e adaugata si
  in enumerarea edge-urilor, singurul loc care o slabea.
- "-6%" apare acum la toate trei raportarile, cum spune textul.

Doua limite lasate cu bună știință, nu reparate: la transe liniile de 418 si
419 se vad ca una, iar la tinta de 1/2 linia de confluenta si SL-ul sunt la
0.10 $ - in ambele cazuri apropierea vine din date, si depărtarea ar cere
schimbarea cifrelor din text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
2026-09-14 06:31:29 +00:00

547 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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 <!--SVG:nume--> ... <!--/SVG--> 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 = {}
@dataclass
class Seg:
"""nivel valabil doar pe barele i0..i1"""
i0: int
i1: int
price: float
text: str
color: str = ORANGE
label_at: str = "out" # "out" marginea dreaptă | "lm" marginea stângă | "left"/"right" pe linie
dy: float = -5.0
dash: str = ""
def spread(items, gap=14):
"""[(y, ...)] -> aceleași, cu y-urile împinse ca două etichete să nu se atingă"""
items = sorted(items, key=lambda t: t[0])
out = []
for it in items:
y = it[0] if not out else max(it[0], out[-1][0] + gap)
out.append((y,) + tuple(it[1:]))
return out
@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'<line x1="{x:.1f}" y1="{self.top - 4:.1f}" x2="{x:.1f}" '
f'y2="{self._body_bottom:.1f}" stroke="#bbb" stroke-dasharray="2 3"/>')
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'<polyline points="{" ".join(pts)}" fill="none" stroke="{m.color}" stroke-width="1.5"{dash}/>')
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'<line x1="{x0:.1f}" y1="{y:.1f}" x2="{x1:.1f}" y2="{y:.1f}" '
f'stroke="{s.color}" stroke-width="{1.5 if s.dash else 2}"{dash}/>')
if not s.text:
continue
if s.label_at == "out":
extra.append(f'<text x="{xr:.1f}" y="{placed[id(s)]:.1f}" fill="{s.color}">{s.text}</text>')
elif s.label_at == "lm":
extra.append(f'<text x="{x0 - 6:.1f}" y="{placed[id(s)]:.1f}" fill="{s.color}" '
f'text-anchor="end">{s.text}</text>')
elif s.label_at == "left":
extra.append(f'<text x="{x0 + 3:.1f}" y="{y + s.dy:.1f}" fill="{s.color}">{s.text}</text>')
else:
extra.append(f'<text x="{x1 - 3:.1f}" y="{y + s.dy:.1f}" fill="{s.color}" '
f'text-anchor="end">{s.text}</text>')
for _, s in ma_lab:
extra.append(f'<text x="{xr:.1f}" y="{placed[id(s)]:.1f}" fill="{s.color}">{s.text}</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 <line [^>]*stroke="none"[^>]*/>\n <text [^>]*fill="none"[^>]*></text>', "", 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"<!--SVG:{name}-->.*?<!--/SVG-->", re.S)
assert pat.search(md), f"marcatorul {name} lipsește din .md"
md = pat.sub(lambda _m: f"<!--SVG:{name}-->\n{svg}\n<!--/SVG-->", md)
MD.write_text(md, encoding="utf-8")
print("scris", MD)