Un agent care nu a scris documentul l-a verificat fata de harvest/ si
transcrieri. Toate cele 9 puncte confirmate in surse inainte de reparatie,
niciunul contestat.
Conținut, in ordinea gravitatii:
- regula "nu intri inainte de raport" era extinsa nedeclarat la NFP si Fed.
Restransa la CPI si declarata ca adaugire; la NFP si minuta Fed scrie acum
doar ce spune sursa (volatilitate mare, atentie la ora). Alte patru
extinderi nedeclarate marcate pe loc si trecute in completari.
- rândul COT spunea "tinta se poate largi", care nu apare in sursa.
Rescris dupa Q&A 18.08.2025: pe valute comercianții trec peste zero
INAINTE de un bottom important, apoi revin net short, semnalul e rar si
nu se ia singur.
- rândul CPI atribuia sursei lucruri pe care ea nu le spune. Rescris dupa
Q&A 11.06.2025: in ziua CPI corelatiile clasice n-au tinut, FedWatch
arata ~100% menținere.
- afirmatie falsa despre documentul 01: transele de acolo cumpara
retesturile de 1/3 si 2/3 din ultima miscare, nu retestul de 1/2.
- o parafraza a recoltei era in ghilimele ca vorba lui. Pus citatul real:
"ar putea macar jumatate din distanta asta sa o acopere".
- doua durate nesursate si inconsecvente intre ele ("inca doua saptamani"
fata de "inca o saptamana"), scoase amandoua.
Desen: bara numita "hammer" in diagrama de spike ATR era roșie, cu codita
de 1.1x corpul. Acum are close 56.10 cu low tot pe 54.60, codita de 7.5x
corpul si close la 83% din range, cu assert in generator (codita >= 3x
corpul, close in treimea de sus). True range-ul barei e neschimbat, deci
nicio cifra din text nu s-a modificat.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
523 lines
29 KiB
Python
523 lines
29 KiB
Python
"""Diagramele pentru summaries/08_ANALIZA_ZILNICA/07_context_de_piata.md.
|
|
|
|
Toate prețurile sunt construite (fără ticker). Rulează:
|
|
.venv/bin/python gen/az_context.py
|
|
Înlocuiește în .md fiecare bloc <!--SVG:nume--> ... <!--/SVG--> cu output-ul
|
|
generatorului. Ca să schimbi o cifră, o schimbi aici și rulezi din nou.
|
|
|
|
`PChart` e copiat din gen/az_pivoti.py: etichetele nivelurilor stau în marginea
|
|
din dreapta, după ultima candelă, împinse în jos când se ating.
|
|
McClellan, AAII, Fear & Greed, breadth și COT nu sunt preț: în .md sunt tabele.
|
|
"""
|
|
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/07_context_de_piata.md")
|
|
GRAY, RED, GREEN, BLUE, ORANGE, PURPLE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#6a3d9a"
|
|
STYLE = dict(gap=15, body_w=9, left=44, width=620)
|
|
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"
|
|
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):
|
|
"""copiat din gen/az_pivoti.py (vezi acolo explicațiile)"""
|
|
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
|
|
|
|
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))
|
|
top_price = max([c.h for c in self.candles] + [s.price for s in segs])
|
|
self.levels = [Level(s.price, "", "none") for s in segs]
|
|
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 = [MA(m.period, m.color, m.dash) for m in saved_ma]
|
|
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"/>')
|
|
closes = [c.c for c in self.candles]
|
|
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"]
|
|
out_lab += [(self._y(sum(closes[-m.period:]) / m.period) + 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)}
|
|
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 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 y, s in out_lab:
|
|
if s.i1 == 0 and s.price == 0:
|
|
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 px_ratio(ch, a, b, c):
|
|
"""raportul (c-b)/(b-a) măsurat în pixeli, după render"""
|
|
return (ch._y(b) - ch._y(c)) / (ch._y(a) - ch._y(b))
|
|
|
|
|
|
def f2(x):
|
|
return f"{x:.2f}"
|
|
|
|
|
|
def near(a, b, tol=0.011):
|
|
return abs(a - b) <= tol
|
|
|
|
|
|
def wicks(n, special, default=(0.3, 0.3)):
|
|
w = [default] * n
|
|
for i, v in special.items():
|
|
w[i] = v
|
|
return w
|
|
|
|
|
|
def true_range(cds):
|
|
out = [cds[0].h - cds[0].l]
|
|
for p, c in zip(cds, cds[1:]):
|
|
out.append(max(c.h, p.c) - min(c.l, p.c))
|
|
return out
|
|
|
|
|
|
V = {} # cifrele folosite în text, tipărite la final
|
|
|
|
# ================================================================ 1. VIX DA: reject în fostul suport + lower low
|
|
vix_head = [15.2, 14.8, 15.4, 15.0, 14.6, 15.1, # 0-5 calm
|
|
17.0, 19.5, 23.8, 27.5, 26.0, # 6-10 spike
|
|
24.1, 22.0, 20.2, 19.6, 20.4, 19.8, 20.6, 19.5, # 11-18 se sprijină pe 19.20
|
|
17.9, 16.9, 16.4] # 19-21 sparge suportul
|
|
vw_head = {8: (0.8, 0.3), 9: (0.9, 0.4), 13: (0.3, 1.0), 14: (0.3, 0.4), 15: (0.3, 0.4), 16: (0.3, 0.6),
|
|
17: (0.3, 0.6), 18: (0.3, 0.3)}
|
|
vix_da = vix_head + [17.4, 18.5, 17.6, 16.8, 16.2, 15.6, 15.9, 15.1, 14.7, 15.0, 14.5, 14.3]
|
|
SUP = 19.20
|
|
RJ = 23
|
|
cds = from_closes(vix_da, wicks=wicks(len(vix_da), {**vw_head, 23: (0.7, 0.2)}), start=15.0)
|
|
assert all(near(cds[i].l, SUP) for i in (13, 14, 15, 18)), [cds[i].l for i in range(13, 19)]
|
|
assert all(c.l >= SUP - 0.01 for c in cds[11:19]), "suportul 19.20 ține pe barele 11-18"
|
|
assert cds[19].c < SUP, "bara 19 închide sub suport"
|
|
assert all(c.h < SUP for c in cds[20:RJ]), "după bara de spargere nimic nu revine la 19.20 până la retest"
|
|
assert near(cds[RJ].h, SUP) and cds[RJ].c < SUP, "retestul atinge 19.20 din jos și închide sub"
|
|
assert cds[RJ + 1].c < cds[RJ].c, "bara de după retest confirmă rejectul"
|
|
LL0 = min(c.l for c in cds[19:22])
|
|
LL = next(i for i in range(RJ + 1, len(cds)) if cds[i].l < LL0)
|
|
assert all(c.h < SUP for c in cds[RJ + 1:])
|
|
peak_v = max(range(len(cds)), key=lambda i: cds[i].h)
|
|
V["vix"] = dict(sup=SUP, peak=cds[peak_v].h, rj_h=cds[RJ].h, rj_c=cds[RJ].c, ll0=LL0, ll=LL, ll_l=cds[LL].l,
|
|
end=cds[-1].c)
|
|
print("D1 VIX", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["vix"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(SUP, f"fost suport = rezistență {SUP:.2f}", ORANGE)],
|
|
marks=[Mark(peak_v, f"spike {cds[peak_v].h:.1f}", RED, "above"),
|
|
Mark(16, "suport", ORANGE, "below", dy=4),
|
|
Mark(RJ, "reject", RED, "above", dy=-4),
|
|
Mark(LL, "lower low", GREEN, "below", dy=4)],
|
|
scale=12, title="Exemplu construit (fără ticker) — daily, VIX (citit invers)",
|
|
caption="VIX respins sub fostul suport și lower low: frica scade, favorabil long-urilor pe indice.",
|
|
**STYLE)
|
|
svgs["vix_da"] = ch.render()
|
|
|
|
# ================================================================ 2. indicele, aceeași perioadă: HL + long
|
|
idx = [55.0, 55.3, 54.9, 55.2, 55.5, 55.2,
|
|
54.2, 53.0, 51.2, 49.8, 50.3,
|
|
51.0, 51.9, 52.4, 52.7, 52.2, 52.6, 52.1, 52.6,
|
|
53.4, 54.0, 54.3,
|
|
53.8, 53.2, 54.3,
|
|
54.8, 55.3, 55.9, 55.6, 56.2, 56.8, 56.5, 57.1, 57.5]
|
|
assert len(idx) == len(vix_da), "aceeași perioadă, bară cu bară"
|
|
cds = from_closes(idx, wicks=wicks(len(idx), {8: (0.3, 0.8), 9: (0.3, 0.9)}), start=55.2)
|
|
HL, TRG = RJ, RJ + 1
|
|
bottom = min(range(len(cds)), key=lambda i: cds[i].l)
|
|
assert bottom == peak_v, "bottom-ul indicelui = spike-ul VIX"
|
|
assert cds[HL].l > max(c.l for c in cds[11:19]), "pullback-ul face higher low peste consolidarea de după bottom"
|
|
assert cds[HL].l == min(c.l for c in cds[20:]), "bara 23 e low-ul pullback-ului de după bara 19"
|
|
assert cds[TRG].c > cds[HL].h, "trigger: close peste high-ul barei de higher low"
|
|
entry = cds[TRG].c
|
|
sl = round(cds[HL].l - 0.10, 2)
|
|
risk = entry - sl
|
|
tp = round(entry + 2 * risk, 2)
|
|
hit = next(i for i in range(TRG + 1, len(cds)) if cds[i].h >= tp)
|
|
assert min(c.l for c in cds[TRG + 1:]) > sl
|
|
V["idx"] = dict(bottom=cds[bottom].l, hl=cds[HL].l, entry=entry, sl=sl, risk=risk, tp=tp, hit=hit - TRG)
|
|
print("D2 indice", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["idx"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(tp, f"TP {f2(tp)} = 2R", BLUE),
|
|
Level(entry, f"intrare {f2(entry)}", GREEN),
|
|
Level(sl, f"SL {f2(sl)}", RED)],
|
|
marks=[Mark(bottom, "bottom (VIX în spike)", GRAY, "below", dy=4),
|
|
Mark(HL, "HL = reject pe VIX", GREEN, "below", dy=14),
|
|
Mark(hit, "TP", BLUE, "above")],
|
|
scale=24, title="Exemplu construit (fără ticker) — daily, indicele, aceeași perioadă",
|
|
caption="Bara 23: VIX respins la 19.20, indicele face higher low. Intrarea vine pe close-ul următor.",
|
|
**STYLE)
|
|
svg_ = ch.render()
|
|
assert abs(px_ratio(ch, sl, entry, tp) - 2.0) < 0.01, "2R nu e desenat la 2x risc"
|
|
svgs["vix_indice"] = svg_
|
|
|
|
# ================================================================ 3. VIX NU: higher low pe VIX
|
|
vix_nu = vix_head + [17.4, 18.5, 17.6, 17.2, 18.4, 19.8, 21.6, 23.4, 22.5, 24.6, 23.8, 25.2]
|
|
cds = from_closes(vix_nu, wicks=wicks(len(vix_nu), {**vw_head, 23: (0.7, 0.2), 25: (0.2, 0.3)}), start=15.0)
|
|
assert [c.c for c in cds[:24]] == [c.c for c in from_closes(vix_da, start=15.0)[:24]], "până la bara 23 identic cu DA"
|
|
HLV = 25
|
|
assert cds[HLV].l > LL0 and cds[HLV].l == min(c.l for c in cds[RJ:]), "VIX face higher low"
|
|
back = next(i for i in range(HLV, len(cds)) if cds[i].c > SUP)
|
|
V["vixnu"] = dict(hl=cds[HLV].l, back=back, back_c=cds[back].c, end=cds[-1].c)
|
|
print("D3 VIX NU", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["vixnu"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(SUP, f"fost suport {SUP:.2f}", ORANGE),
|
|
Level(LL0, f"low anterior {f2(LL0)}", GRAY)],
|
|
marks=[Mark(RJ, "reject?", GRAY, "above", dy=-4),
|
|
Mark(HLV, "higher low", RED, "below", dy=4)],
|
|
scale=12, title="Exemplu construit (fără ticker) — daily, VIX (citit invers)",
|
|
caption="Același început, dar VIX face higher low și închide peste 19.20: risc de nouă cădere pe indice.",
|
|
**STYLE)
|
|
svgs["vix_nu"] = ch.render()
|
|
|
|
# ================================================================ 4. ATR în spike la locul crimei: bounce
|
|
cl4 = [50.0, 50.6, 51.1, 50.8, 51.6, 52.3, 52.0, 53.0, 53.8, 54.4, # 0-9 swing high la 9
|
|
53.9, 53.4, 53.8, # 10-12
|
|
54.8, 55.7, 55.4, 56.5, 57.4, 57.1, 58.3, 59.2, 58.9, 60.1, 61.2, 62.0, # 13-24 vârf
|
|
61.4, 61.7, 60.6, 59.8, # 25-28 început de corecție
|
|
58.0, 56.3, 56.1, # 29-31 bare mari, hammer la 31
|
|
56.6, # 32 trigger
|
|
57.2, 57.9, 57.6, 58.6, 59.3, 59.0, 60.0, 60.8] # 33-40
|
|
cds = from_closes(cl4, wicks=wicks(len(cl4), {29: (0.4, 0.6), 30: (0.4, 0.7), 31: (0.1, 1.5)}, default=(0.2, 0.2)), start=49.7)
|
|
LC = cds[9].h
|
|
HM, TG = 31, 32
|
|
PK = max(range(len(cds)), key=lambda i: cds[i].h)
|
|
tr = true_range(cds)
|
|
for c, t in zip(cds, tr):
|
|
c.vol = t
|
|
tr_norm = sum(tr[10:29]) / len(tr[10:29])
|
|
assert PK == 24
|
|
assert all(c.h < LC for c in cds[:9]) and cds[13].c > LC, "bara 9 e swing high-ul spart = locul crimei"
|
|
assert near(cds[HM].l, LC, 0.051) and cds[HM].c > LC, "hammer-ul atinge locul crimei și închide deasupra"
|
|
_hb = abs(cds[HM].c - cds[HM].o)
|
|
assert (min(cds[HM].o, cds[HM].c) - cds[HM].l) >= 3 * _hb and cds[HM].c >= cds[HM].l + 2 / 3 * (cds[HM].h - cds[HM].l), "hammer adevărat: codița de jos >= 3x corpul, close în treimea de sus"
|
|
assert min(tr[29:31]) >= 2 * tr_norm, ("barele 29-30: range de 2x normalul", tr[25:33], tr_norm)
|
|
assert cds[TG].c > cds[HM].h, "trigger: close peste high-ul hammer-ului"
|
|
entry = cds[TG].c
|
|
sl = round(cds[HM].l - 0.10, 2)
|
|
risk = entry - sl
|
|
tp1 = round((cds[PK].h + cds[HM].l) / 2, 2) # jumătatea distanței până la vârf
|
|
R1 = (tp1 - entry) / risk
|
|
tp2 = round(entry + 2 * risk, 2)
|
|
hit1 = next(i for i in range(TG + 1, len(cds)) if cds[i].h >= tp1)
|
|
hit2 = next(i for i in range(TG + 1, len(cds)) if cds[i].h >= tp2)
|
|
assert tp2 < cds[PK].h and min(c.l for c in cds[TG + 1:]) > sl
|
|
V["atr"] = dict(lc=LC, pk=cds[PK].h, hm_l=cds[HM].l, tr_norm=tr_norm, tr_spike=max(tr[29:32]),
|
|
spike_x=max(tr[29:32]) / tr_norm, entry=entry, sl=sl, risk=risk, tp1=tp1, R1=R1, tp2=tp2,
|
|
hit1=hit1 - TG, hit2=hit2 - TG)
|
|
print("D4 ATR", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["atr"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(tp2, f"TP2 {f2(tp2)} = 2R", BLUE),
|
|
Level(tp1, f"TP1 {f2(tp1)} = 1/2 până la vârf ({R1:.1f}R)", BLUE),
|
|
Level(entry, f"intrare {f2(entry)}", GREEN),
|
|
Level(LC, f"locul crimei {f2(LC)}", ORANGE),
|
|
Level(sl, f"SL {f2(sl)}", RED)],
|
|
marks=[Mark(9, "swing high", ORANGE, "above", dy=-4),
|
|
Mark(PK, f"vârf {f2(cds[PK].h)}", GRAY, "above"),
|
|
Mark(30, "spike de range", RED, "below", dy=26),
|
|
Mark(TG, "trigger", GREEN, "above", dy=-4)],
|
|
mas=[MA(20, GRAY, dash="4 3", text="SMA-20")],
|
|
scale=18, vol_h=70, vol_label="true range pe bară (volatilitate)",
|
|
title="Exemplu construit (fără ticker) — daily, indicele",
|
|
caption="Range-ul barelor sare la peste 2x normalul exact când prețul atinge locul crimei.",
|
|
**STYLE)
|
|
svg_ = ch.render()
|
|
assert abs(px_ratio(ch, sl, entry, tp2) - 2.0) < 0.01
|
|
assert abs(px_ratio(ch, sl, entry, tp1) - R1) < 0.01
|
|
svgs["atr_spike"] = svg_
|
|
|
|
# ================================================================ 5. ATR în cădere liberă: nu e bottom
|
|
cl5 = [50.0, 50.7, 51.2, 50.9, 51.8, 52.5, 52.2, 53.2, 53.9, 54.5,
|
|
54.0, 55.0, 55.6, 56.3, 57.0,
|
|
56.6, 56.3, 55.9, 56.0, 55.6, 55.3, 55.4, 55.0, 54.7, 54.8, 54.5, 54.2, 54.3, 54.0, 53.8, 53.9,
|
|
53.6, 53.4, 53.5, 53.2, 53.0]
|
|
w5 = [(0.3, 0.3)] * 15 + [(0.12, 0.12)] * 21
|
|
cds = from_closes(cl5, wicks=w5, start=49.7)
|
|
LC5 = cds[9].h
|
|
tr = true_range(cds)
|
|
for c, t in zip(cds, tr):
|
|
c.vol = t
|
|
up_tr = sum(tr[1:15]) / 14
|
|
dn_tr = sum(tr[15:]) / len(tr[15:])
|
|
touch = next(i for i in range(15, len(cds)) if cds[i].l <= LC5)
|
|
assert all(c.h < LC5 for c in cds[:9]) and cds[11].c > LC5
|
|
assert max(tr[15:]) < up_tr, "fără nicio bară de spike în corecție"
|
|
assert cds[-1].c < LC5 - 1.5, "prețul trece prin locul crimei fără reacție"
|
|
V["atrnu"] = dict(lc=LC5, up_tr=up_tr, dn_tr=dn_tr, touch=touch, end=cds[-1].c)
|
|
print("D5 ATR NU", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["atrnu"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(LC5, f"locul crimei {f2(LC5)}", ORANGE)],
|
|
marks=[Mark(9, "swing high", ORANGE, "above", dy=-4),
|
|
Mark(touch, "atins, fără spike", RED, "below", dy=4),
|
|
Mark(33, "lower lows", RED, "below", dy=4)],
|
|
mas=[MA(20, GRAY, dash="4 3", text="SMA-20")],
|
|
scale=26, vol_h=70, vol_label="true range pe bară (volatilitate)",
|
|
title="Exemplu construit (fără ticker) — daily, indicele",
|
|
caption="Range-ul se micșorează pe cădere: nimeni nu capitulează, deci încă nu e bottom.",
|
|
**STYLE)
|
|
svgs["atr_nu"] = ch.render()
|
|
|
|
# ================================================================ 6-7. top purtat de mega-cap-uri: indice vs RSP
|
|
ci = [50.0, 50.5, 50.2, 51.0, 51.6, 51.3, 52.1, 52.8, 52.5, 53.3, 53.9, 53.6, 54.4, 55.0, 54.7, 55.5, 56.0,
|
|
55.4, 54.8, 55.1, 54.5, 54.2,
|
|
54.9, 55.6, 56.1, 56.7, 56.8,
|
|
55.9,
|
|
55.4, 55.7, 54.9, 54.3, 54.6, 53.8, 53.2, 53.5, 52.6, 52.4, 52.8, 52.1]
|
|
cds = from_closes(ci, wicks=wicks(len(ci), {26: (0.6, 0.1)}), start=49.8)
|
|
P1, P2, ST, EN = 16, 26, 26, 27
|
|
assert cds[P1].h == max(c.h for c in cds[:22]) and cds[P2].h == max(c.h for c in cds)
|
|
assert cds[P2].h > cds[P1].h, "indicele: al doilea vârf mai sus"
|
|
assert abs(cds[ST].c - cds[ST].o) <= 0.15 and cds[ST].h - max(cds[ST].o, cds[ST].c) >= 0.5, "shooting star"
|
|
assert cds[25].c - cds[25].o >= 0.5 and cds[EN].o - cds[EN].c >= 0.8, "evening star: bull mare, stea, bear mare"
|
|
assert cds[EN].c < cds[ST].l, "trigger short: close sub low-ul stelei"
|
|
entry = cds[EN].c
|
|
sl = round(cds[P2].h + 0.10, 2)
|
|
risk = sl - entry
|
|
tp = round(entry - 2 * risk, 2)
|
|
hit = next(i for i in range(EN + 1, len(cds)) if cds[i].l <= tp)
|
|
assert max(c.h for c in cds[EN + 1:]) < sl
|
|
V["top"] = dict(p1=cds[P1].h, p2=cds[P2].h, star_l=cds[ST].l, entry=entry, sl=sl, risk=risk, tp=tp,
|
|
hit=hit - EN)
|
|
print("D6 top indice", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["top"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(sl, f"SL {f2(sl)}", RED),
|
|
Level(cds[P1].h, f"vârf 1 {f2(cds[P1].h)}", GRAY),
|
|
Level(entry, f"intrare short {f2(entry)}", GREEN),
|
|
Level(tp, f"TP {f2(tp)} = 2R", BLUE)],
|
|
marks=[Mark(P1, "vârf 1", GRAY, "above", dy=-4),
|
|
Mark(ST, "vârf 2 mai sus", RED, "above", dy=-4),
|
|
Mark(EN, "evening star", RED, "below", dy=4),
|
|
Mark(hit, "TP", BLUE, "below", dy=4)],
|
|
scale=22, title="Exemplu construit (fără ticker) — daily, indicele (ponderat)",
|
|
caption="Indicele face vârf nou. Singur, arată a continuare. Verificarea e pe RSP, dedesubt.",
|
|
**STYLE)
|
|
svg_ = ch.render()
|
|
assert abs(1 / px_ratio(ch, tp, entry, sl) - 2.0) < 0.01, "2R nu e desenat la 2x risc"
|
|
svgs["top_indice"] = svg_
|
|
|
|
cr = [40.0, 40.3, 40.1, 40.6, 41.0, 40.8, 41.3, 41.7, 41.5, 42.0, 42.4, 42.2, 42.7, 43.1, 42.9, 43.4, 43.7,
|
|
43.1, 42.6, 42.8, 42.3, 42.0,
|
|
42.4, 42.8, 43.0, 43.3, 43.2,
|
|
42.8,
|
|
42.4, 42.6, 42.0, 41.6, 41.8, 41.2, 40.8, 41.0, 40.4, 40.2, 40.5, 40.0]
|
|
assert len(cr) == len(ci)
|
|
cds = from_closes(cr, wicks=wicks(len(cr), {26: (0.3, 0.1)}, default=(0.25, 0.25)), start=39.8)
|
|
r1 = cds[P1].h
|
|
r2 = max(c.h for c in cds[22:28])
|
|
i_r2 = max(range(22, 28), key=lambda i: cds[i].h)
|
|
assert r1 == max(c.h for c in cds) and r2 < r1, "RSP: al doilea vârf mai jos = nu confirmă"
|
|
V["rsp"] = dict(r1=r1, r2=r2, i_r2=i_r2, diff=(r2 - r1) / r1 * 100)
|
|
print("D7 RSP", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["rsp"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(r1, f"vârf 1 {f2(r1)}", GRAY)],
|
|
marks=[Mark(P1, "vârf 1", GRAY, "above", dy=-4),
|
|
Mark(i_r2, f"vârf 2 mai jos {f2(r2)}", RED, "above", dy=-20)],
|
|
scale=40, title="Exemplu construit (fără ticker) — daily, varianta equal weight (tip RSP)",
|
|
caption="Aceleași zile. Compania medie nu mai face vârf nou: urcarea o duc câteva mega-cap-uri.",
|
|
**STYLE)
|
|
svgs["top_rsp"] = ch.render()
|
|
|
|
# ================================================================ 8-9. CPI: breakout din compresie DA / NU
|
|
head8 = [50.0, 50.5, 50.3, 51.0, 51.6, 51.4, 52.1, 52.7, 52.5, 53.2, 53.8, 53.6, 54.3, 54.9, # 0-13
|
|
54.8, 55.1, 54.6, 54.9, 55.2, 54.7, 55.0, 54.8, 55.1, 54.9, 55.0] # 14-24 compresie
|
|
hv8 = [44, 47, 42, 48, 50, 43, 49, 51, 44, 50, 52, 45, 50, 48,
|
|
38, 36, 35, 33, 32, 31, 30, 29, 28, 27, 26]
|
|
hw8 = [(0.3, 0.3)] * 14 + [(0.2, 0.2)] * 11
|
|
C0, CP = 14, 25
|
|
tail_da = [56.4, 56.9, 57.3, 57.0, 57.8, 58.4, 58.1, 58.9, 59.5, 59.2, 60.0, 60.5, 60.2, 60.9]
|
|
tv_da = [96, 62, 55, 44, 52, 54, 42, 50, 53, 41, 49, 51, 40, 47]
|
|
tw_da = [(0.2, 0.3), (0.2, 0.2)] + [(0.3, 0.3)] * 12
|
|
cds = from_closes(head8 + tail_da, wicks=hw8 + tw_da, vols=hv8 + tv_da, start=49.8)
|
|
RH = max(c.h for c in cds[C0:CP])
|
|
RL = min(c.l for c in cds[C0:CP])
|
|
vavg = sum(hv8[C0:CP]) / (CP - C0)
|
|
CF = CP + 1
|
|
assert near(RH, 55.40) and near(RL, 54.40), (RH, RL)
|
|
assert cds[CP].c > RH and cds[CP].vol >= 3 * vavg, "bara de CPI închide peste range cu volum de 3x"
|
|
assert cds[CF].l > RH and cds[CF].c > cds[CP].c, "a doua zi rămâne deasupra range-ului: confirmare"
|
|
entry = cds[CF].c
|
|
sl = round(RH - 0.20, 2)
|
|
risk = entry - sl
|
|
tp = round(entry + 2 * risk, 2)
|
|
hit = next(i for i in range(CF + 1, len(cds)) if cds[i].h >= tp)
|
|
assert min(c.l for c in cds[CF + 1:]) > sl
|
|
V["cpi"] = dict(rh=RH, rl=RL, cp_c=cds[CP].c, vol=cds[CP].vol, vavg=vavg, entry=entry, sl=sl, risk=risk,
|
|
tp=tp, hit=hit - CF)
|
|
print("D8 CPI DA", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["cpi"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(tp, f"TP {f2(tp)} = 2R", BLUE),
|
|
Level(entry, f"intrare {f2(entry)}", GREEN),
|
|
Level(RH, f"range high {f2(RH)}", ORANGE),
|
|
Level(sl, f"SL {f2(sl)}", RED),
|
|
Level(RL, f"range low {f2(RL)}", ORANGE)],
|
|
marks=[Mark(19, "compresie înainte de CPI", GRAY, "below", dy=26),
|
|
Mark(CP, "CPI", PURPLE, "below", dy=4),
|
|
Mark(CF, "confirmare", GREEN, "above", dy=-4)],
|
|
scale=22, vol_h=70, title="Exemplu construit (fără ticker) — daily, indicele",
|
|
caption="Nu intri înainte de raport. Intri după ce ziua de după CPI rămâne deasupra range-ului.",
|
|
**STYLE)
|
|
svg_ = ch.render()
|
|
assert abs(px_ratio(ch, sl, entry, tp) - 2.0) < 0.01
|
|
svgs["cpi_da"] = svg_
|
|
|
|
tail_nu = [55.1, 54.2, 53.8, 54.1, 53.4, 52.9, 53.2, 52.5, 52.0, 52.3, 51.7, 51.2, 51.5, 50.9]
|
|
tv_nu = [92, 70, 58, 44, 60, 62, 45, 58, 60, 44, 55, 57, 42, 52]
|
|
tw_nu = [(1.4, 0.3)] + [(0.3, 0.3)] * 13
|
|
cds = from_closes(head8 + tail_nu, wicks=hw8 + tw_nu, vols=hv8 + tv_nu, start=49.8)
|
|
assert near(max(c.h for c in cds[C0:CP]), RH) and near(min(c.l for c in cds[C0:CP]), RL), "aceeași compresie"
|
|
assert cds[CP].h > RH + 0.8 and RL < cds[CP].c < RH, "CPI: spike peste range, close înapoi înăuntru"
|
|
BD = next(i for i in range(CP + 1, len(cds)) if cds[i].c < RL)
|
|
assert BD == CP + 1
|
|
V["cpinu"] = dict(cp_h=cds[CP].h, cp_c=cds[CP].c, bd_c=cds[BD].c, end=cds[-1].c)
|
|
print("D9 CPI NU", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["cpinu"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(RH, f"range high {f2(RH)}", ORANGE),
|
|
Level(RL, f"range low {f2(RL)}", ORANGE)],
|
|
marks=[Mark(19, "compresie înainte de CPI", GRAY, "below", dy=26),
|
|
Mark(CP, f"CPI: spike {f2(cds[CP].h)}", PURPLE, "above", dy=-4),
|
|
Mark(BD, "close sub range", RED, "below", dy=40)],
|
|
scale=22, vol_h=70, title="Exemplu construit (fără ticker) — daily, indicele",
|
|
caption="Același range. Spike-ul de pe CPI se închide înapoi înăuntru: cine a cumpărat la 56 e prins.",
|
|
**STYLE)
|
|
svgs["cpi_nu"] = ch.render()
|
|
|
|
# ================================================================ 10. bond-urile: hammer în suportul quarterly
|
|
cl10 = [46.0, 45.6, 45.9, 45.2, 44.7, 45.0, 44.3, 43.8, 44.1, 43.4, 43.0, 43.3, # 0-11
|
|
43.9, 44.4, 44.1, 44.7, 44.3, 43.9, # 12-17
|
|
43.4, 42.9, 42.6, 42.5, # 18-21 (21 hammer)
|
|
43.0, 43.4, 43.2, 43.8, 44.3, 44.0, 44.6, 45.1, 44.8, 45.4, 45.9, 45.6] # 22-33
|
|
cds = from_closes(cl10, wicks=wicks(len(cl10), {21: (0.1, 0.7)}, default=(0.2, 0.2)), start=46.3)
|
|
L1 = min(range(0, 12), key=lambda i: cds[i].l)
|
|
H10 = 21
|
|
SQ = 41.80
|
|
assert near(cds[H10].l, SQ) and cds[H10].l == min(c.l for c in cds), "hammer-ul face low-ul seriei, în suportul quarterly"
|
|
assert cds[H10].l < cds[L1].l, "al doilea picior mai jos"
|
|
assert min(cds[H10].o, cds[H10].c) - cds[H10].l >= 2 * abs(cds[H10].o - cds[H10].c), "hammer: codiță >= 2x corpul"
|
|
TG10 = H10 + 1
|
|
assert cds[TG10].c > cds[H10].h
|
|
V["tlt"] = dict(l1=cds[L1].l, sq=SQ, hm_l=cds[H10].l, hm_c=cds[H10].c, tg=TG10, tg_c=cds[TG10].c)
|
|
print("D10 TLT", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["tlt"].items()})
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(SQ, f"suport quarterly {SQ:.2f}", ORANGE)],
|
|
marks=[Mark(3, "downtrend", GRAY, "above", dy=-4),
|
|
Mark(L1, "picior 1", GRAY, "below", dy=4),
|
|
Mark(H10, "hammer, picior 2 mai jos", GREEN, "below", dy=4)],
|
|
scale=26, pad_bot_px=10, title="Exemplu construit (fără ticker) — daily, ETF de bond-uri pe termen lung",
|
|
caption="Spike low sub picior 1, hammer în suportul quarterly: bond-urile își pot face bottom-ul.",
|
|
**STYLE)
|
|
svgs["tlt"] = ch.render()
|
|
|
|
# ================================================================ 11. acțiunea sensibilă la dobândă: ascending triangle
|
|
cl11 = [49.0, 49.6, 50.3, 50.1, 51.0, 51.8, 52.6, 53.5, # 0-7
|
|
54.8, 54.0, 53.1, 53.8, 54.4, 54.8, 54.2, 53.6, 54.2, 54.8, 54.4, 54.0, 54.5, 54.8, 54.5, 54.3, 54.6, # 8-24
|
|
55.7, # 25 breakout
|
|
56.2, 55.9, 56.6, 57.2, 56.9, 57.6, 58.2, 57.9, 58.6, 59.2, 58.9, 59.5, 60.0, 59.7] # 26-39
|
|
cds = from_closes(cl11, wicks=[(0.2, 0.2)] * len(cl11), start=48.7)
|
|
RT = 55.00
|
|
BO = 25
|
|
assert near(max(c.h for c in cds[8:BO]), RT), "vârful triunghiului e plat la 55.00"
|
|
assert sum(1 for c in cds[8:BO] if near(c.h, RT)) >= 4, "cel puțin 4 atingeri ale rezistenței"
|
|
sw = [min(cds[a:b], key=lambda c: c.l).l for a, b in ((9, 13), (14, 17), (18, 21), (22, 25))]
|
|
assert all(x < y for x, y in zip(sw, sw[1:])), f"low-urile nu urcă: {sw}"
|
|
assert cds[BO].c > RT and all(c.c <= RT for c in cds[8:BO])
|
|
assert BO > TG10, "breakout-ul acțiunii vine după trigger-ul de pe bond-uri"
|
|
entry = cds[BO].c
|
|
sl = round(sw[-1] - 0.10, 2)
|
|
risk = entry - sl
|
|
tp = round(entry + 2 * risk, 2)
|
|
hit = next(i for i in range(BO + 1, len(cds)) if cds[i].h >= tp)
|
|
assert min(c.l for c in cds[BO + 1:]) > sl
|
|
V["dhi"] = dict(rt=RT, lows=[f2(x) for x in sw], entry=entry, sl=sl, risk=risk, tp=tp, hit=hit - BO)
|
|
print("D11 actiune", {k: (f2(v) if isinstance(v, float) else v) for k, v in V["dhi"].items()})
|
|
lowbars = [min(range(a, b), key=lambda i: cds[i].l) for a, b in ((9, 13), (14, 17), (18, 21), (22, 25))]
|
|
ch = PChart(candles=cds,
|
|
levels=[Level(tp, f"TP {f2(tp)} = 2R", BLUE),
|
|
Level(entry, f"intrare {f2(entry)}", GREEN),
|
|
Level(RT, f"rezistență {RT:.2f}", ORANGE),
|
|
Level(sl, f"SL {f2(sl)}", RED)],
|
|
marks=[Mark(lowbars[0], "low", GRAY, "below", dy=4),
|
|
Mark(lowbars[1], "HL", GREEN, "below", dy=4),
|
|
Mark(lowbars[2], "HL", GREEN, "below", dy=4),
|
|
Mark(BO, "breakout", GREEN, "above", dy=-4),
|
|
Mark(hit, "TP", BLUE, "above")],
|
|
scale=20, title="Exemplu construit (fără ticker) — daily, acțiune sensibilă la dobândă",
|
|
caption="Aceleași zile ca bond-urile: ascending triangle, breakout la 3 zile după trigger-ul de pe bond-uri.",
|
|
**STYLE)
|
|
svg_ = ch.render()
|
|
assert abs(px_ratio(ch, sl, entry, tp) - 2.0) < 0.01
|
|
svgs["dhi"] = svg_
|
|
|
|
# ---------------------------------------------------------------- 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)
|