Buy hammer: forma (patru candele cu aceeasi inaltime, DA/limita/NU), trend (aceeasi bara dupa scadere e hammer, dupa urcare e hanging man — verificat prin assert), locatie, trigger in trei panouri, stop, iesire, si oglinda pe short obtinuta prin oglindire bara cu bara. MBT-10: confluente si close peste pivotul daily. Doua serii partajate (bh_ansamblu si mbt_confluente expun serie()), ca sa nu existe doua variante ale aceluiasi trade. Capitolul se imparte in doua proceduri separate, nu una amestecata: partea A buy hammer in 5 pasi cu oglinda pe short, partea B MBT-10 in 5 pasi, cu nivelul 1 comun. Textul e neatins — agentul a atins pragul de context. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
136 lines
6.3 KiB
Python
136 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Buy hammer — ieșirea: jumătate la 1:1, restul pe trail sub swing low-uri.
|
|
|
|
Pornește din aceeași serie ca diagrama de ansamblu (barele 0-33, importate)
|
|
și îi adaugă o continuare construită (barele 34-40), pentru că seria de
|
|
ansamblu se oprește înainte ca trail-ul să apuce să urce de două ori.
|
|
|
|
Regula de trail desenată: stopul se mută sub ultimul swing low — cel mai jos
|
|
low al unui pullback, confirmat de două bare care nu-l rup. Ieși la prima
|
|
închidere sub linie.
|
|
|
|
Rulare: python3 gen/strategii/hammer_mbt10_bh_iesire.py
|
|
"""
|
|
import re
|
|
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
|
sys.path.insert(0, "/workspace/atm-curs-dl/gen/strategii")
|
|
from pathlib import Path
|
|
from hammer_mbt10_bh_ansamblu import serie
|
|
from svg_candles import Arrow, Candle, Chart, Level, Zone
|
|
|
|
OUT = Path(__file__).with_suffix(".svg")
|
|
GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555"
|
|
|
|
INTRARE, SL, R = 63.35, 62.05, 1.30
|
|
TP1 = round(INTRARE + R, 2) # 64.65 — ținta 1:1 a strategiei MBP
|
|
TP2R = round(INTRARE + 2 * R, 2) # 65.95 — ținta din exemplul lecției
|
|
|
|
cds_all, hammer, conf = serie()
|
|
conf.note = "" # nota e a diagramei de ansamblu; aici ar sta peste legendă
|
|
# continuarea construită: un pullback, o urcare, al doilea pullback, ieșirea
|
|
CONT = [Candle(*b) for b in [
|
|
(65.60, 66.10, 65.45, 65.95), # 34
|
|
(65.95, 66.80, 65.85, 66.60), # 35
|
|
(66.60, 66.90, 66.20, 66.35), # 36
|
|
(66.35, 66.55, 66.00, 66.45), # 37 -> swing low 66.00
|
|
(66.45, 67.30, 66.35, 67.10), # 38
|
|
(67.10, 67.60, 66.90, 67.30), # 39
|
|
(67.30, 67.40, 65.80, 65.90), # 40 -> close sub trail: ieși
|
|
]]
|
|
full = cds_all + CONT
|
|
assert abs(CONT[0].o - cds_all[-1].c) < 1e-9, "continuarea nu pornește din close-ul barei 33"
|
|
for i, c in enumerate(full):
|
|
assert c.l <= min(c.o, c.c) and c.h >= max(c.o, c.c), f"bara {i}: h/l nu cuprind o/c"
|
|
|
|
|
|
def swing_lows(cds, start):
|
|
"""Cel mai jos low al unui pullback, confirmat de două bare care nu-l rup."""
|
|
out = []
|
|
for i in range(start, len(cds) - 2):
|
|
if (cds[i].l < cds[i - 1].l and cds[i + 1].l >= cds[i].l and cds[i + 2].l >= cds[i].l):
|
|
out.append((i, round(cds[i].l, 2)))
|
|
return out
|
|
|
|
|
|
CONF_I = 20
|
|
sw = swing_lows(full, CONF_I + 1)
|
|
trepte, iesire = [], None
|
|
t = None
|
|
for i in range(CONF_I + 1, len(full)):
|
|
if t is not None and full[i].c < t:
|
|
iesire = i
|
|
break
|
|
nou = [p for j, p in sw if j + 2 <= i]
|
|
if nou and (t is None or nou[-1] > t):
|
|
t = nou[-1]
|
|
trepte.append((i, t))
|
|
|
|
# --- verificări -------------------------------------------------------------
|
|
assert round(full[CONF_I].c, 2) == INTRARE, "intrarea nu e close-ul barei 20"
|
|
prim_tp1 = next(i for i in range(CONF_I + 1, len(full)) if full[i].h >= TP1)
|
|
prim_2r = next(i for i in range(CONF_I + 1, len(full)) if full[i].h >= TP2R)
|
|
assert (prim_tp1, prim_2r) == (23, 28), (prim_tp1, prim_2r)
|
|
assert round(TP1 - INTRARE, 2) == R and round(TP2R - INTRARE, 2) == 2 * R
|
|
assert trepte == [(23, 63.05), (34, 65.10), (39, 66.00)], trepte
|
|
assert all(b[1] > a[1] for a, b in zip(trepte, trepte[1:])), "trail-ul trebuie doar să urce"
|
|
assert iesire == 40 and abs(full[40].c - 65.90) < 1e-9, iesire
|
|
assert trepte[0][1] > SL, "prima treaptă nu îmbunătățește stopul inițial"
|
|
assert trepte[-1][1] > INTRARE, "ultima treaptă de trail e sub intrare: n-ar proteja profitul"
|
|
R_IES = round((full[iesire].c - INTRARE) / R, 2)
|
|
assert R_IES == 1.96, R_IES
|
|
assert min(c.l for c in full[CONF_I:]) > SL, "stopul inițial n-ar fi fost atins în fereastră"
|
|
|
|
I0 = 16
|
|
w = full[I0:]
|
|
sh = lambda i: i - I0 # noqa: E731 index în fereastră
|
|
ch = Chart(
|
|
candles=w,
|
|
levels=[Level(TP2R, "65.95 = 2R — ținta din exemplul lecției", BLUE),
|
|
Level(TP1, "64.65 = 1:1 — prima jumătate", GREEN),
|
|
Level(INTRARE, "intrare 63.35", GREY, label_at="left"),
|
|
Level(SL, "SL inițial 62.05", RED, label_at="left")],
|
|
zones=[Zone(sh(30), sh(34), "pullback", ORANGE), Zone(sh(36), sh(38), "", ORANGE)],
|
|
arrows=[
|
|
Arrow(sh(23), "prima jumătate iese la 64.65:\nexact cât ai riscat (1R)",
|
|
price=TP1, where="above", color=GREEN, dx=-150, dy=-40),
|
|
Arrow(sh(40), "a doua jumătate iese la 65.90 (+1,96R):\nprima închidere sub linia de trail",
|
|
where="below", color=RED, dx=-250, dy=54),
|
|
],
|
|
scale=34.0, gap=16, body_w=9, left=48, width=700, vol_h=0, top=58,
|
|
title="Buy hammer — ieșirea (daily, ticker ilustrativ XYZ; barele de după 33 sunt construite)",
|
|
)
|
|
svg = ch.render()
|
|
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
|
W, H = int(m.group(1)), int(m.group(2))
|
|
OFF = (float(re.search(r'<line x1="24" y1="([\d.]+)"', svg).group(1)) - ch._y(ch.levels[0].price))
|
|
Y = lambda p: ch._y(p) + OFF # noqa: E731
|
|
|
|
pts, etichete = [], []
|
|
for k, (i, t) in enumerate(trepte):
|
|
x_de_la = ch._cx(sh(i)) - 6
|
|
x_pana = ch._cx(sh(trepte[k + 1][0])) - 6 if k + 1 < len(trepte) else ch._cx(sh(iesire)) + 6
|
|
if pts:
|
|
pts.append(f"{x_de_la:.1f},{Y(t):.1f}")
|
|
pts += [f"{x_de_la:.1f},{Y(t):.1f}", f"{x_pana:.1f},{Y(t):.1f}"]
|
|
if k == 0: # o singură etichetă: cifrele treptelor sunt în legendă
|
|
etichete.append(f'<text x="{x_de_la + 4:.1f}" y="{Y(t) + 16:.1f}" fill="{ORANGE}" '
|
|
f'style="font-weight:bold">linia de trail</text>')
|
|
extra_svg = (f'<polyline points="{" ".join(pts)}" fill="none" stroke="{ORANGE}" '
|
|
f'stroke-width="2"/>' + "".join(etichete))
|
|
|
|
linii = [
|
|
("Trail-ul urcă doar sub un swing low nou — 63.05, apoi 65.10, apoi 66.00 — și nu coboară "
|
|
"niciodată; ieși la prima închidere sub el.", "#333"),
|
|
("Lecția de pattern nu dă o țintă proprie — 1:1 vine de la strategia MBP, iar 2R e ținta "
|
|
"exemplului din lecție. Continuarea barelor e construită.", GREY),
|
|
]
|
|
add = 16 * len(linii) + 10
|
|
svg = svg.replace(f'viewBox="0 0 {W} {H}"', f'viewBox="0 0 {W} {H + add}"')
|
|
svg = svg.replace("</svg>", extra_svg + "".join(
|
|
f'<text x="24" y="{H + 10 + 16 * i}" fill="{c}">{t}</text>'
|
|
for i, (t, c) in enumerate(linii)) + "</svg>")
|
|
assert "\n\n" not in svg, "linie goală în SVG"
|
|
OUT.write_text(svg, encoding="utf-8")
|
|
print(f"scris {OUT.name} ({W}x{H + add}); TP1 la bara {prim_tp1}, 2R la {prim_2r}, "
|
|
f"trail {trepte}, ieșire bara {iesire} @ {full[iesire].c:.2f} = +{R_IES}R")
|