Cea mai grava, si cea care a cerut refacerea seriei: exemplul central de MBT-10 era luat de pe 60 de minute si bagat in procedura de daily, cu barele numite "zile", desi sursa spune explicit de ce nu merge pe daily. Pe langa asta, suportul fusese schimbat din 88.00 (testat de trei ori, cu compresie, cum e in sursa) in 87.50 cu un double bottom si o divergenta de volum imprumutate din alt exemplu. Seria e refacuta pe exemplul materialului, cu assert pe cele trei atingeri ale lui 88.00 si pe compresia varfurilor. Adaosurile au fost scoase, nu doar marcate: cu ele in desen, suportul din sursa n-ar fi avut sens. Aritmetica a supravietuit neatinsa. Restul: stopul de 60.40 sta acum sub o structura reala; pivotul daily nu mai e o linie fixa peste tot graficul, ci un segment peste ziua intrarii; SL-ul de pe LABU respecta regula proprie de 5 centi; citatul de la A2 nu mai e dat ca al autorului; pragul de 1-2% isi spune originea (cursul da 0,5%, pe 60 min); o singura conventie de distanta, impartirea la nivel; swing low definit la fel in pas si in glosar. eticheta_libera a prins 7 suprapuneri reale. La bh_aplus a trebuit rulat pe fiecare panou separat: wrapper-ul le muta cu <g transform>, pe care unealta nu-l citeste - de notat pentru data viitoare. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
199 lines
9.0 KiB
Python
199 lines
9.0 KiB
Python
"""După gap down — primul MBT-10 te scoate, al doilea merge (exemplu construit).
|
||
|
||
Citește `gen/strategii/hammer_mbt10_mbt_gapdown.spec.txt`
|
||
(marcajul `<!-- DIAGRAMA: mbt_gapdown -->`, Partea B §2.3 Gotchas).
|
||
|
||
Serie continuă de 33 candele daily, construită din close-urile date în
|
||
specificație, cu open-ul fiecărei bare = close-ul precedentei (STYLE.md),
|
||
cu două excepții impuse de exemplu:
|
||
|
||
- bara 13 (ultima dinainte de gap): ca să iasă ȘI ``low[13] = 70.60``
|
||
(nivelul de închidere a gap-ului, cerut de assert 1 și 6) ȘI
|
||
``high[13] = 71.00`` (structura seriei, „de la ~71"), bara trebuie să
|
||
fie un doji ``o = c = 70.60``. De aceea am ridicat ``close[12]`` de la
|
||
69.80 (cât dă lista) la 70.60: altfel ``min(open,close)=69.80`` și
|
||
low-ul ar ieși 69.80, nu 70.60. Wu=0.40 => high 71.00, wd=0.00 => low
|
||
70.60. (Linia „Mecioni" cere wu=0.20 => high 70.80; am ales 71.00,
|
||
cifra din structura seriei și din textul „~71".)
|
||
- bara 14 (gap down): ``open[14] = 66.40``, NU close[13]=70.60 — singura
|
||
bară cu open diferit de close-ul precedent. O construim manual:
|
||
o=c=66.40, wu=0.30 => high 66.70, wd=0.30 => low 66.10.
|
||
|
||
Wick-urile speciale: bara 16 wd=0.20 (low 64.60), bara 18 wd=0.20 (low
|
||
63.20), bara 21 wd=1.60 (low 60.60 = noul low al coborârii, ca SL-ul 60.40 din
|
||
material să stea sub o structură desenată); restul (0.30, 0.40).
|
||
|
||
Rulează: python3 gen/strategii/hammer_mbt10_mbt_gapdown.py
|
||
Scrie: gen/strategii/hammer_mbt10_mbt_gapdown.svg
|
||
"""
|
||
import re
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||
sys.path.insert(0, "/workspace/atm-curs-dl/gen")
|
||
from eticheta_libera import eticheta_libera # noqa: E402
|
||
from svg_candles import Candle, Chart, Level, Mark, from_closes # noqa: E402
|
||
|
||
OUT = Path(__file__).resolve().parent / "hammer_mbt10_mbt_gapdown.svg"
|
||
|
||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||
|
||
# --- seria: 33 de close-uri, exact ca în specificație ------------------------
|
||
# Singura abatere: closes[12] 69.80 -> 70.60, ca bara 13 să poată avea
|
||
# low 70.60 (vezi docstring-ul).
|
||
closes = [
|
||
60.00, 60.80, 61.60, 62.50, 63.30, 64.20, 65.00, 65.80, 66.60, 67.40, # 0-9
|
||
68.20, 69.00, 70.60, 70.60, # 10-13
|
||
66.40, # 14 = bara de GAP DOWN (open impus 66.40, NU close[13])
|
||
65.50, # 15
|
||
64.80, # 16 = MBT-10 #1, închidere => intrare #1
|
||
64.20, # 17
|
||
63.40, # 18 = low 63.20 => stop #1
|
||
62.90, # 19
|
||
62.50, # 20
|
||
62.20, # 21 = MBT-10 #2, închidere => intrare #2
|
||
62.80, 63.60, 64.50, 65.50, 66.50, 67.50, 68.50, 69.50, # 22-29
|
||
70.60, # 30 = gap închis (TP)
|
||
70.90, # 31
|
||
70.50, # 32
|
||
]
|
||
assert len(closes) == 33, len(closes)
|
||
|
||
WICKS = [(0.30, 0.40)] * 33 # restul barelor
|
||
WICKS[13] = (0.40, 0.00) # high 71.00, low 70.60
|
||
WICKS[14] = (0.30, 0.30) # high 66.70, low 66.10 (bara de gap)
|
||
WICKS[16] = (0.30, 0.20) # low 64.60
|
||
WICKS[18] = (0.30, 0.20) # low 63.20
|
||
# bara lui MBT-10 #2 face și noul low al coborârii: flush la 60.60, închidere
|
||
# înapoi la 62.20. Fără el, SL-ul 60.40 din material (`mbt`:729-732) ar pluti
|
||
# sub orice structură desenată — exact reproșul din verificare.
|
||
WICKS[21] = (0.30, 1.60) # low 60.60 = noul low; SL 60.40 stă sub el
|
||
|
||
# Volume relative, alese de generator (materialul nu dă volum aici):
|
||
# în scădere pe piciorul coborârii după gap (14 -> 21), apoi recuperare.
|
||
vols = [
|
||
48, 46, 44, 45, 43, 42, 44, 41, 40, 42, # 0-9
|
||
43, 41, 46, 58, # 10-13
|
||
90, # 14 = gap down
|
||
76, 66, 57, 50, 45, 42, # 15-20
|
||
40, # 21 = MBT-10 #2 (minim)
|
||
46, 52, 58, 63, 68, 73, 78, 82, 86, 88, 80, # 22-32
|
||
]
|
||
assert len(vols) == 33, len(vols)
|
||
coborare = vols[14:22]
|
||
assert all(coborare[i] >= coborare[i + 1] for i in range(len(coborare) - 1)), \
|
||
f"volumul nu scade pe piciorul coborârii: {coborare}"
|
||
assert vols[-1] > vols[21], "volumul nu recuperează după bara 21"
|
||
|
||
cds = from_closes(closes, wicks=WICKS, vols=vols, start=60.00)
|
||
# bara de gap: open = 66.40, nu close[13] = 70.60
|
||
cds[14] = Candle(66.40, 66.70, 66.10, 66.40, vol=vols[14], color=RED)
|
||
|
||
# --- assert-urile din specificație -----------------------------------------
|
||
# 1. gap down real
|
||
assert abs(cds[14].o - 66.40) < 1e-9, cds[14].o
|
||
assert abs(cds[13].l - 70.60) < 1e-9, f"low[13]={cds[13].l} != 70.60"
|
||
assert abs(cds[13].c - 70.60) < 1e-9, cds[13].c
|
||
assert cds[14].o < cds[13].l and cds[14].h < cds[13].l, "gapul nu e real"
|
||
assert abs(cds[14].o - cds[13].c) > 1e-9, "open[14] nu e excepția de la serie"
|
||
|
||
# 2. #1
|
||
intrare1, sl1 = 64.80, 63.20
|
||
assert abs(cds[16].c - intrare1) < 1e-9, f"close[16]={cds[16].c} != 64.80"
|
||
assert abs(sl1 - 63.20) < 1e-9
|
||
assert min(c.l for c in cds[16:19]) <= sl1, "stopul #1 nu e atins"
|
||
assert cds[16].c > sl1, "intrarea #1 e sub stopul ei"
|
||
|
||
# 3. #2
|
||
intrare2, sl2 = 62.20, 60.40
|
||
assert abs(cds[21].c - intrare2) < 1e-9, f"close[21]={cds[21].c} != 62.20"
|
||
assert abs(sl2 - 60.40) < 1e-9
|
||
assert not any(c.l <= sl2 for c in cds[22:]), "un low atinge SL2 după bara 21"
|
||
# stopul #2 numește o structură: e sub cel mai jos low al coborârii de după gap
|
||
nou_low = min(c.l for c in cds[14:22])
|
||
assert abs(nou_low - cds[21].l) < 1e-9, "noul low nu e al barei lui MBT-10 #2"
|
||
assert sl2 < nou_low <= sl2 + 0.30, \
|
||
f"SL2 {sl2} nu stă chiar sub noul low {nou_low:.2f}: nu mai numește o structură"
|
||
assert nou_low < min(c.l for c in cds[14:21]), "low-ul barei 21 nu e cel mai jos"
|
||
|
||
# 4. riscuri
|
||
risc1 = intrare1 - sl1 # 1.60
|
||
risc2 = intrare2 - sl2 # 1.80
|
||
assert abs(risc1 - 1.60) < 1e-9 and abs(risc2 - 1.80) < 1e-9, (risc1, risc2)
|
||
|
||
# 5. TP = închiderea gap-ului
|
||
TP = 70.60
|
||
reward2 = TP - intrare2 # 8.40
|
||
assert abs(reward2 - 8.40) < 1e-9, reward2
|
||
assert round(reward2 / risc2, 1) == 4.7, round(reward2 / risc2, 1)
|
||
|
||
# 6. low[13] e nivelul de închidere a gap-ului și TP-ul e atins în urcare
|
||
assert abs(cds[13].l - TP) < 1e-9, "low[13] != 70.60 (nivelul gap-ului)"
|
||
assert max(c.h for c in cds[22:33]) >= TP, "TP 70.60 nu e atins după #2"
|
||
primul_tp = next(i for i in range(22, 33) if cds[i].h >= TP)
|
||
|
||
# 7. același risc în bani: mărimea #2 = mărimea #1 × (risc1 / risc2)
|
||
marime1 = 1.0
|
||
marime2 = marime1 * (risc1 / risc2)
|
||
assert abs(marime2 - 1.60 / 1.80) < 1e-9, marime2
|
||
|
||
print(f"gap {cds[13].l:.2f} -> {cds[14].o:.2f} "
|
||
f"#1 intrare {intrare1:.2f} SL {sl1:.2f} (risc {risc1:.2f}) "
|
||
f"#2 intrare {intrare2:.2f} SL {sl2:.2f} (risc {risc2:.2f}) "
|
||
f"TP {TP:.2f} = {reward2 / risc2:.1f}R "
|
||
f"atins la bara {primul_tp} (high {cds[primul_tp].h:.2f}) "
|
||
f"mărime #2 = {marime2:.3f} × mărime #1")
|
||
|
||
# --- desenul ----------------------------------------------------------------
|
||
ch = Chart(
|
||
candles=cds,
|
||
levels=[
|
||
# toate etichetele stau în dreapta (x > ultima candelă), fără suprapuneri
|
||
Level(intrare1, "#1 intrare 64.80", GRAY, label_at="right"),
|
||
Level(sl1, "#1 SL 63.20", GRAY, label_at="right"),
|
||
Level(intrare2, "#2 intrare 62.20", GRAY, label_at="right"),
|
||
Level(sl2, "#2 SL 60.40, sub noul low 60.60", GRAY, label_at="right"),
|
||
Level(TP, "TP 70.60 = 4.7R (gap închis)", BLUE, label_at="right"),
|
||
],
|
||
marks=[
|
||
# decalate pe verticală cât să nu atingă candelele din coborâre
|
||
Mark(14, "gap down", RED, "above", dy=-22),
|
||
Mark(16, "MBT-10 #1", GRAY, "above", dy=-25),
|
||
Mark(18, "stop #1 atins", RED, "below", dy=30),
|
||
Mark(21, "MBT-10 #2: flush la 60.60, închidere 62.20", GREEN, "below", dy=25),
|
||
],
|
||
scale=28.0, gap=15, body_w=9, left=44, width=820, vol_h=70,
|
||
vol_label="volum",
|
||
title="După gap down — primul MBT-10 te scoate, al doilea merge (exemplu construit)",
|
||
caption="",
|
||
)
|
||
|
||
svg = ch.render()
|
||
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
|
||
eticheta_libera(svg, "mbt_gapdown",
|
||
{"MBT-10 #2: flush la 60.60, închidere 62.20",
|
||
"#2 SL 60.40, sub noul low 60.60", "stop #1 atins", "gap down",
|
||
"MBT-10 #1"}, rects=True, segmente=False)
|
||
|
||
# --- blocul de text de jos: cine are același risc + legenda -----------------
|
||
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
||
W, H = int(m.group(1)), int(m.group(2))
|
||
linii = [
|
||
"MBT-10 #1: intrare 64.80 / SL 63.20 — risc 1R în bani",
|
||
"MBT-10 #2: intrare 62.20 / SL 60.40, sub noul low 60.60 — risc 1R în bani "
|
||
"(mărime ajustată)",
|
||
"Exemplu construit: #1 și #2 au același risc în bani; "
|
||
"#2 singur dă 4.7R.",
|
||
"De aceea mărimea se împarte pe 2-3 încercări.",
|
||
]
|
||
extra = 16 * len(linii) + 6
|
||
svg = svg.replace(f'viewBox="0 0 {W} {H}"', f'viewBox="0 0 {W} {H + extra}"')
|
||
svg = svg.replace("</svg>", "".join(
|
||
f'<text x="24" y="{H + 12 + 16 * i}" fill="#555">{t}</text>'
|
||
for i, t in enumerate(linii)) + "</svg>")
|
||
assert "\n\n" not in svg, "linie goală în SVG după legendă"
|
||
|
||
OUT.write_text(svg)
|
||
print(f"scris {OUT.relative_to('/workspace/atm-curs-dl')}")
|