Doua proceduri complet separate, nu una amestecata, pentru ca se fac altfel: buy hammer e pattern public, desenabil si exersabil retroactiv; MBT-10 e cutie neagra din care se poate reface o singura conditie. Nivelul 1 le pune fata in fata, ca cititorul sa stie din prima de ce nu le poate trata la fel. Cate 5 pasi fiecare, oglinda pe short la hammer, checklist dublu. Sursele in fise/06_hammer_mbt10_surse.md. Zece diagrame noi: hammer-ul nu era definit vizual nicaieri, iar hanging man era doar text — acum e aceeasi bara in doua contexte, cu assert ca e identica. Cele 7 vechi si-au pierdut trimiterile fisier:linie din legende; toate le aveau. Doua serii partajate (bh_ansamblu si mbt_confluente expun serie()), deci un trade nu poate avea doua variante. Oglinda pe short e trade-ul long oglindit in jurul lui K=162.30, deci riscul ramane 1.30 in ambele. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
130 lines
6.5 KiB
Python
130 lines
6.5 KiB
Python
"""Buy hammer — diagrama de ansamblu (daily, ticker ilustrativ XYZ).
|
|
|
|
Seria de aici e SURSA UNICĂ a trade-ului din Partea A: `hammer_mbt10_bh_stop`
|
|
și `hammer_mbt10_bh_iesire` importă `serie()` și pornesc din aceleași bare
|
|
(hammer 63.30 / 62.10, intrare 63.35, SL 62.05, TP 65.95 = 2R).
|
|
|
|
Citește specificația `gen/strategii/hammer_mbt10_bh_ansamblu.spec.txt`.
|
|
Seria de 34 de candele e construită din lista de close-uri dată, cu open-ul
|
|
fiecărei bare = close-ul precedentului; wick-urile sunt fixate ca nivelurile
|
|
(hammer H=C 63.30 / L 62.10, intrare 63.35, SL 62.05, TP 65.95) să iasă
|
|
exact din serie, nu doar din etichetă.
|
|
"""
|
|
import re
|
|
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
|
from svg_candles import Chart, Level, Mark, from_closes
|
|
|
|
def serie():
|
|
"""Cele 34 de bare ale trade-ului XYZ, cu verificările specificației."""
|
|
# --- seria: 34 de close-uri, exact ca în specificație ------------------------
|
|
closes = [
|
|
72.00, 71.20, 71.50, 70.40, 69.60, 70.00, 68.90, 68.10, 68.40, 67.30, # 0-9
|
|
66.50, 66.80, 65.70, 64.90, 65.20, 64.10, 63.40, 63.70, 63.05, # 10-18
|
|
63.30, # 19 = hammer: o 63.05, c/h 63.30, l 62.10 (H = C, fitil sus 0)
|
|
63.35, # 20 = confirmare: close peste 63.30 => intrare
|
|
63.80, 64.20, 64.60, 64.40, 64.90, 65.20, 65.50, 65.80, 66.10, # 21-29
|
|
66.00, 65.70, 65.40, 65.60, # 30-33
|
|
]
|
|
assert len(closes) == 34
|
|
|
|
# wick-uri: (up, down) implicite (0.25, 0.30) + excepțiile din specificație
|
|
wicks = [(0.25, 0.30)] * 34
|
|
wicks[19] = (0.00, 0.95) # hammer: high 63.30 (= close), low 62.10
|
|
# bara 20: wu=0.15 => high 63.50. Spec-ul dă wd=0.15 cu „low 63.20", dar
|
|
# wd se măsoară de la min(o,c)=63.30, deci 0.15 dă 63.15; folosim tuplul
|
|
# literal din specificație (0.15, 0.15), low-ul barei 20 nu e etichetat.
|
|
wicks[20] = (0.15, 0.15)
|
|
|
|
# volum relativ ales de generator (materialul nu dă volum pe exemplul construit):
|
|
# normal pe downtrend, în creștere pe hammer (19) și pe confirmare (20), normal după
|
|
vols = [50, 48, 47, 46, 45, 46, 44, 43, 45, 42,
|
|
41, 43, 40, 39, 41, 38, 37, 39, 55,
|
|
90, 95,
|
|
58, 55, 53, 50, 52, 54, 56, 60, 64,
|
|
58, 55, 52, 50]
|
|
assert len(vols) == 34
|
|
media_vol = sum(vols) / len(vols)
|
|
assert vols[19] > media_vol and vols[20] > media_vol, "volum peste medie pe hammer/confirmare"
|
|
|
|
cds = from_closes(closes, wicks=wicks, vols=vols, start=72.00)
|
|
|
|
hammer, conf = cds[19], cds[20]
|
|
# confirmarea o punem ca notă sub grafic: un Mark deasupra barei 20 s-ar
|
|
# suprapune cu fitilul lung al hammer-ului (bara 19)
|
|
conf.note = "bara 20: confirmare — close 63.35 peste high-ul hammer-ului 63.30 (intrare)"
|
|
intrare, sl, tp = 63.35, 62.05, 65.95
|
|
|
|
# --- cele 8 assert-uri cerute de specificație -------------------------------
|
|
# 1. hammer: H = C = 63.30, L = 62.10
|
|
assert abs(cds[19].c - 63.30) < 1e-9 and abs(cds[19].h - 63.30) < 1e-9, "H != C la hammer"
|
|
assert abs(cds[19].l - 62.10) < 1e-9, f"low[19]={cds[19].l} != 62.10"
|
|
# 2. open[19] = close[18]; corp 0.25; fitil jos 0.95 >= 2*0.25
|
|
assert abs(cds[19].o - cds[18].c) < 1e-9 and abs(cds[19].o - 63.05) < 1e-9, "open[19] != close[18]"
|
|
corp = hammer.c - hammer.o
|
|
fitil_jos = hammer.o - hammer.l
|
|
assert abs(corp - 0.25) < 1e-9, f"corp={corp} != 0.25"
|
|
assert abs(fitil_jos - 0.95) < 1e-9, f"fitil jos={fitil_jos} != 0.95"
|
|
assert fitil_jos >= 2 * corp, "fitilul nu e cel puțin dublul corpului"
|
|
# 3. confirmarea închide peste high-ul hammer-ului
|
|
assert abs(conf.c - intrare) < 1e-9, f"close[20]={conf.c} != 63.35"
|
|
assert conf.c > hammer.h and abs(hammer.h - 63.30) < 1e-9, "close[20] nu e peste high[19]"
|
|
# 4. SL = low-ul hammer-ului - 0.05
|
|
assert abs(sl - (cds[19].l - 0.05)) < 1e-9, "SL != low[19] - 0.05"
|
|
# 5. risc 1.30, TP 2.60 = 2R exact
|
|
assert abs(abs(intrare - sl) - 1.30) < 1e-9, "risc != 1.30"
|
|
assert abs(abs(tp - intrare) - 2.60) < 1e-9, "TP != 2R exact"
|
|
# 6. primul high >= TP e bara 28 (65.80 + wick 0.25 = 66.05)
|
|
primul_tp = next(i for i in range(21, len(cds)) if cds[i].h >= tp)
|
|
assert primul_tp == 28, f"TP 65.95 atins întâi la bara {primul_tp}, nu 28"
|
|
# 7. downtrend vizibil
|
|
assert cds[18].c < cds[0].c - 8, "downtrend prea mic"
|
|
# 8. ordinea nivelurilor
|
|
assert sl < intrare < tp, "SL / intrare / TP nu sunt în ordine"
|
|
|
|
print(f"hammer H/C {hammer.h:.2f} L {hammer.l:.2f} intrare {conf.c:.2f} "
|
|
f"SL {sl:.2f} TP {tp:.2f} = {abs(tp-intrare)/abs(intrare-sl):.1f}R "
|
|
f"TP atins la bara {primul_tp} (high {cds[primul_tp].h:.2f})")
|
|
return cds, hammer, conf
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cds, hammer, conf = serie()
|
|
intrare, sl, tp = 63.35, 62.05, 65.95
|
|
|
|
# --- desenul ----------------------------------------------------------------
|
|
ch = Chart(
|
|
candles=cds,
|
|
levels=[
|
|
Level(sl, "SL 62.05 — sub low-ul hammer-ului", "#c00", label_at="left"),
|
|
Level(intrare, "intrare 63.35", "#188a3e"),
|
|
Level(tp, "TP 65.95 = 2R", "#1a5fb4"),
|
|
],
|
|
marks=[
|
|
Mark(19, "hammer 63.30 / 62.10", "#b06000", "below"),
|
|
],
|
|
scale=32.0, gap=15, body_w=9, left=44, width=720, vol_h=70,
|
|
vol_label="volum", title="Buy hammer — daily (ticker ilustrativ XYZ, din lecția de pattern)",
|
|
caption="", # legenda e pusă manual, pe două rânduri, ca să nu iasă din pânză
|
|
)
|
|
|
|
svg = ch.render()
|
|
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
|
|
|
|
# legenda din specificație e mai lată decât pânza: o rupem pe două rânduri și
|
|
# extindem viewBox-ul ca să nu fie tăiată
|
|
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
|
W, H = int(m.group(1)), int(m.group(2))
|
|
legenda = [
|
|
"Exemplu ilustrativ (XYZ) din lecția de pattern; prețurile sunt cele ale lecției,",
|
|
"structura barelor dintre ele e construită.",
|
|
]
|
|
extra = 16 * len(legenda) + 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(legenda)) + "</svg>")
|
|
assert "\n\n" not in svg, "linie goală în SVG după legendă"
|
|
|
|
with open("/workspace/atm-curs-dl/gen/strategii/hammer_mbt10_bh_ansamblu.svg", "w") as f:
|
|
f.write(svg)
|
|
print("scris gen/strategii/hammer_mbt10_bh_ansamblu.svg") |