diagrame: trei desene care aratau altceva decat spune regula
m2d_exemplu (Riot): eticheta zicea 'close peste bear trail = iesi' pe o bara
care se inchidea SUB linie - doar fitilul trecea, adica exact cazul pe care
capitolul il interzice de trei ori. Seria refacuta, cu assert care recalculeaza
trail-ul din serie si cere ca bara de iesire sa inchida peste el.
mbp_exemplu: candela M se inchidea la 660.50 sub banda de 660.90, iar
generatorul chiar afirma relatia inversa ('assert BAND > c'); desenul arata un
setup invalid ca valid. Plus stopul 662.71, care plutea 1.71 deasupra extremei
celor 4 bare - exact marja pe care capitolul o declara absenta la MBP.
Ambele reparate, cu assert pe fiecare.
atmi_orizont: trei etichete purtau 'fisier:linie' in desen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
This commit is contained in:
@@ -13,6 +13,12 @@ Legenda spune explicit lucrul ăsta.
|
||||
Serie continuă de 30 de candele din lista de close-uri din specificație,
|
||||
open = close-ul precedentului, wick-uri alese ca să scoată exact nivelurile
|
||||
(candela 13: high 100.85 = SL), panou de volum dedesubt.
|
||||
|
||||
Bear trail-ul e calculat din serie cu regula capitolului (linia sare pe
|
||||
high-ul barei care se închide sub low-ul precedentei; violare = o bară care
|
||||
se închide PESTE linie) și desenat ca scară portocalie. Seria e construită
|
||||
astfel încât fitilele să atingă linia de trei ori (barele 16, 20, 24) fără
|
||||
s-o violeze, iar ieșirea să vină abia la bara 29, care închide peste linie.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
@@ -34,22 +40,36 @@ closes = [
|
||||
97.6, 97.1, 96.7, 96.3, 95.9, 95.6, 95.9, # 0-6 coborâre
|
||||
96.4, 96.9, 97.5, 98.2, 99.0, 99.7, 100.3, # 7-13 pullback Buy (M)
|
||||
100.00, # 14 trigger Sell (A)
|
||||
99.5, 99.0, 98.5, 98.0, 97.6, 97.2, 96.9, 96.6, 96.3, 96.1,
|
||||
95.8, 95.5, 95.2, 95.35, 95.45, # 15-29 bear trail
|
||||
99.45, 99.80, 98.58, 98.10, 97.60, 97.95, 97.40, 96.90, 96.40, 96.75,
|
||||
96.20, 95.70, 95.20, 95.00, 95.45, # 15-29 bear trail
|
||||
]
|
||||
assert len(closes) == 30, len(closes)
|
||||
|
||||
# wick-uri: candela 13 are nevoie de un wick sus până la 100.85 (SL-ul de 4
|
||||
# candele); restul sunt mici, ca seria să arate ca un grafic real.
|
||||
# candele); barele 16, 20 și 24 sunt retestări ale liniei de trail, deci au
|
||||
# fitil lung în sus care atinge linia, dar se închid sub ea. Restul sunt mici,
|
||||
# ca seria să arate ca un grafic real.
|
||||
wicks = [
|
||||
(0.15, 0.20), (0.12, 0.18), (0.15, 0.15), (0.10, 0.20), (0.12, 0.25),
|
||||
(0.10, 0.30), (0.25, 0.15),
|
||||
(0.20, 0.15), (0.15, 0.20), (0.20, 0.15), (0.20, 0.20), (0.20, 0.18),
|
||||
(0.25, 0.15), (SL - 100.3, 0.15), # 13: high = 100.85
|
||||
(0.15, 0.25), # 14: trigger Sell
|
||||
(0.15, 0.20), (0.15, 0.18), (0.12, 0.20), (0.15, 0.18), (0.15, 0.20),
|
||||
(0.12, 0.18), (0.15, 0.20), (0.12, 0.18), (0.15, 0.20), (0.12, 0.18),
|
||||
(0.15, 0.20), (0.12, 0.18), (0.15, 0.20), (0.20, 0.15), (0.30, 0.10),
|
||||
(0.15, 0.15), # 15
|
||||
(0.45, 0.10), # 16 retest trail
|
||||
(0.15, 0.15), # 17
|
||||
(0.15, 0.15), # 18
|
||||
(0.15, 0.15), # 19
|
||||
(0.40, 0.10), # 20 retest trail
|
||||
(0.15, 0.15), # 21
|
||||
(0.15, 0.15), # 22
|
||||
(0.15, 0.15), # 23
|
||||
(0.40, 0.10), # 24 retest trail
|
||||
(0.15, 0.15), # 25
|
||||
(0.15, 0.15), # 26
|
||||
(0.15, 0.15), # 27
|
||||
(0.05, 0.05), # 28: coboară trail-ul
|
||||
(0.20, 0.10), # 29: close peste trail
|
||||
]
|
||||
assert len(wicks) == 30, len(wicks)
|
||||
|
||||
@@ -95,6 +115,53 @@ for a, b in zip(cds, cds[1:]):
|
||||
assert cds[14].c < cds[13].c, (cds[13].c, cds[14].c)
|
||||
assert any(cds[i].c > cds[i].o for i in range(7, 14)), "pullback Buy absent"
|
||||
|
||||
# 10. bara 17 e la 1.42% profit (procentul real din curs)
|
||||
assert abs((ENTRY - cds[17].c) / ENTRY - 0.0142) < 1e-4, cds[17].c
|
||||
|
||||
|
||||
# ── bear trail, recalculat din serie cu regula capitolului ─────────────────
|
||||
# Linia sare pe high-ul fiecărei bare care se închide sub low-ul precedentei;
|
||||
# violarea = prima bară care se închide PESTE linie (fitilul singur nu scoate).
|
||||
def bear_trail(candles, trig):
|
||||
"""linie[i] = nivelul trail-ului în vigoare la începutul barei i (None până
|
||||
se formează linia); iesire = prima bară închisă peste linie."""
|
||||
linie, linie_la, iesire = None, [], None
|
||||
for i, c in enumerate(candles):
|
||||
linie_la.append(linie)
|
||||
if i > trig and linie is not None and c.c > linie:
|
||||
iesire = i
|
||||
break
|
||||
if i > trig:
|
||||
p = candles[i - 1]
|
||||
if c.c < p.l:
|
||||
linie = c.h
|
||||
while len(linie_la) < len(candles):
|
||||
linie_la.append(linie)
|
||||
return linie_la, iesire
|
||||
|
||||
|
||||
LINIE, EXIT_BAR = bear_trail(cds, 14)
|
||||
|
||||
# 11. ieșirea e pe bara 29, care se închide PESTE linia de trail (nu doar fitilul)
|
||||
assert EXIT_BAR == 29, f"ieșirea trebuie să fie pe bara 29, nu {EXIT_BAR}"
|
||||
TRAIL_EXIT = LINIE[29]
|
||||
assert TRAIL_EXIT is not None and cds[29].c > TRAIL_EXIT, (cds[29].c, TRAIL_EXIT)
|
||||
|
||||
# 12. linia nu urcă niciodată (e trail, nu nivel fix)
|
||||
pas = [v for v in LINIE[15:30] if v is not None]
|
||||
assert all(b <= a + 1e-9 for a, b in zip(pas, pas[1:])), pas
|
||||
|
||||
# 13. nicio bară dinainte de ieșire nu închide peste linie
|
||||
for i in range(15, 29):
|
||||
if LINIE[i] is not None:
|
||||
assert cds[i].c < LINIE[i], (i, cds[i].c, LINIE[i])
|
||||
|
||||
# 14. fitilele au atins linia de mai multe ori fără s-o violeze pe close
|
||||
atingeri = [i for i in range(15, 29) if LINIE[i] is not None
|
||||
and cds[i].h >= LINIE[i] - 1e-9 and cds[i].c < LINIE[i]]
|
||||
assert len(atingeri) >= 2, f"fitilele ating linia doar la barele {atingeri}"
|
||||
assert set(atingeri) >= {16, 20, 24}, f"retestările lipsesc: {atingeri}"
|
||||
|
||||
# ── geometria, ca să așez marks-urile „below" fără suprapunere ─────────────
|
||||
SCALE = 42
|
||||
levels = [
|
||||
@@ -177,8 +244,11 @@ definiții = [
|
||||
|
||||
|
||||
def natural_y(i: int, where: str) -> float:
|
||||
# „below": sub candelă, dar înainte de panoul de volum. Plafonul e cu 1 px
|
||||
# sub limita de jos a candidaților, altfel când bara i are chiar minimul
|
||||
# seriei nu mai rămâne niciun y întreg de testat și plasarea pică.
|
||||
return (y(cds[i].h) - 10 if where == "above"
|
||||
else min(y(cds[i].l) + 20, body_bottom + 14))
|
||||
else min(y(cds[i].l) + 20, body_bottom + 13))
|
||||
|
||||
|
||||
# candidați: toate y-urile care nu ating niciun corp de candelă, ordonate
|
||||
@@ -239,6 +309,33 @@ ch = Chart(
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and svg.count("\n\n") == 0, "linie goală în SVG"
|
||||
|
||||
# ── linia de trail, desenată ca scară portocalie ───────────────────────────
|
||||
# Nivelul desenat la bara i e cel în vigoare după bara i (linia sare pe bara
|
||||
# care a închis sub low-ul precedentei). Eticheta stă la stânga primului
|
||||
# palier, ca să nu treacă peste fitilul lung al barei 13.
|
||||
def nivel_desen(i: int):
|
||||
return LINIE[i + 1] if i + 1 < len(LINIE) else LINIE[i]
|
||||
|
||||
|
||||
trail_parts, prev_y = [], None
|
||||
for i in range(15, len(cds)):
|
||||
v = nivel_desen(i)
|
||||
if v is None:
|
||||
continue
|
||||
x0, x1, yv = cx(i) - GAP / 2, cx(i) + GAP / 2, y(v)
|
||||
trail_parts.append(
|
||||
f'<line x1="{x0:.1f}" y1="{yv:.1f}" x2="{x1:.1f}" y2="{yv:.1f}" '
|
||||
f'stroke="#b06000" stroke-width="2"/>')
|
||||
if prev_y is not None and abs(prev_y - yv) > 1e-9:
|
||||
trail_parts.append(
|
||||
f'<line x1="{x0:.1f}" y1="{prev_y:.1f}" x2="{x0:.1f}" y2="{yv:.1f}" '
|
||||
f'stroke="#b06000" stroke-width="1.2" stroke-dasharray="2 2"/>')
|
||||
prev_y = yv
|
||||
trail_parts.append(
|
||||
f'<text x="{cx(13) - 6:.1f}" y="{y(nivel_desen(15)) - 14:.1f}" fill="#b06000" '
|
||||
f'text-anchor="end" style="font-weight:bold">bear trail</text>')
|
||||
svg = svg.replace("\n</svg>", "\n " + "\n ".join(trail_parts) + "\n</svg>")
|
||||
|
||||
# ── verificare: fiecare etichetă e în pânză și nu se suprapune alta ────────
|
||||
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
||||
W, H = int(m.group(1)), int(m.group(2))
|
||||
|
||||
Reference in New Issue
Block a user