Analiza Zilnica 07: context de piata (11 diagrame)
Al optulea document de strategie. Acoperite prin desen: un top pe indice cu RSP care nu confirma varful, VIX citit invers (DA/NU, plus indicele in aceleasi zile), ATR in spike fata de ATR in cadere libera, bond-urile (TLT) ca semnal inter-market urmate de breakout pe o actiune sensibila la dobanda, reactia la CPI (DA/NU). Ce rămâne tabel si de ce, spus explicit intr-o secțiune proprie: breadth pe SMA 10/20/50/200, McClellan, advance/decline, AAII, Fear & Greed, COT. Niciunul nu are OHLC, deci nu se pot desena cu svg_candles - nu s-a forțat nicio diagrama pe ele. Plus un checklist de bottom si de top. Marcat ca adaugire proprie: CPI si PPI nu apar in recolte, deci descrierea lor si regula "intri a doua zi" sunt ale autorului; ATR% din platforma lui nu e explicat in transcrieri, asa ca panoul arata true range cu pragul de 2x media; pragurile AAII si procentele peste medii vin din titlurile din INDEX_TEMATIC, cu link la Q&A, fara sa fi fost citite paginile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
This commit is contained in:
520
gen/az_context.py
Normal file
520
gen/az_context.py
Normal file
@@ -0,0 +1,520 @@
|
||||
"""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, 55.5, # 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, 0.9)}, 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"
|
||||
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)
|
||||
Reference in New Issue
Block a user