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
105 lines
4.8 KiB
Python
105 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Trigger-ul buy hammer: un singur DA și două feluri de NU.
|
|
|
|
Trei panouri cu ACELEAȘI șase bare (scădere + hammer 63.30 / 62.10) și cu o
|
|
singură bară diferită la final — candela următoare. Axa de preț e comună:
|
|
toate trei folosesc același `pmax`, verificat prin assert, deci liniile
|
|
63.30 și 62.10 cad la același y pe toate panourile.
|
|
|
|
Rulare: python3 gen/strategii/hammer_mbt10_bh_trigger.py
|
|
"""
|
|
import re
|
|
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
|
from pathlib import Path
|
|
from svg_candles import Arrow, Chart, from_closes
|
|
|
|
OUT = Path(__file__).with_suffix(".svg")
|
|
GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555"
|
|
|
|
BAZA = [65.20, 64.60, 64.10, 63.60, 63.05, 63.30] # 5 = hammer
|
|
H_HAM, L_HAM = 63.30, 62.10
|
|
ULTIMA = {"DA": 63.35, "DEVREME": 63.20, "MORT": 62.00}
|
|
|
|
|
|
def serie(ultim):
|
|
closes = BAZA + [ultim]
|
|
w = [(0.15, 0.15)] * len(closes)
|
|
w[5] = (0.00, 0.95)
|
|
return from_closes(closes, wicks=w, start=65.70)
|
|
|
|
|
|
S = {k: serie(v) for k, v in ULTIMA.items()}
|
|
|
|
# --- verificări -------------------------------------------------------------
|
|
for k, cds in S.items():
|
|
for i in range(1, len(cds)):
|
|
assert abs(cds[i].o - cds[i - 1].c) < 1e-9, f"{k}: open[{i}] != close[{i-1}]"
|
|
h = cds[5]
|
|
assert (round(h.h, 2), round(h.l, 2)) == (H_HAM, L_HAM), (k, h)
|
|
assert (h.o - h.l) >= 2 * (h.c - h.o), f"{k}: bara 5 nu e hammer"
|
|
assert [round(c.c, 2) for c in cds[:6]] == BAZA, k
|
|
assert S["DA"][6].c > H_HAM, "DA: candela următoare nu închide peste high"
|
|
assert L_HAM <= S["DEVREME"][6].c <= H_HAM, "DEVREME: închiderea trebuie între low și high"
|
|
assert S["MORT"][6].c < L_HAM, "MORT: candela următoare nu închide sub low"
|
|
|
|
# --- layout -----------------------------------------------------------------
|
|
W, PW = 760, 240
|
|
PX = [12, 12 + PW + 14, 12 + 2 * (PW + 14)]
|
|
SCALE, TOP, SHIFT = 46.0, 96, 30
|
|
|
|
CFG = [
|
|
("DA", PX[0], GREEN, "DA — închide peste high",
|
|
Arrow(6, "close 63.35, peste 63.30:\naici intri", where="above", color=GREEN, dx=-70, dy=-18)),
|
|
("DEVREME", PX[1], RED, "NU — încă sub high",
|
|
Arrow(6, "close 63.20, sub 63.30:\nnu s-a întâmplat nimic,\naștepți mai departe",
|
|
where="above", color=RED, dx=-78, dy=-18)),
|
|
("MORT", PX[2], RED, "NU — închide sub low",
|
|
Arrow(6, "close 62.00, sub 62.10:\nvânzătorii au dus prețul\nsub fitil — setup mort",
|
|
where="below", color=RED, dx=-84, dy=26)),
|
|
]
|
|
|
|
bodies, inalt, charts = [], [], {}
|
|
for k, px, col, _, arr in CFG:
|
|
ch = Chart(candles=S[k], arrows=[arr], scale=SCALE, gap=20, body_w=11,
|
|
left=px + 46, width=px + PW, top=TOP)
|
|
body = ch._render()
|
|
assert f'viewBox="0 0 {px + PW} ' in body, f"panoul {k}: textul iese din cadru"
|
|
assert (ch.top, ch.left) == (TOP, px + 46), f"panoul {k}: Chart a mutat graficul"
|
|
charts[k] = ch
|
|
inalt.append(float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', body).group(1)))
|
|
bodies.append(f'<g transform="translate(0,{SHIFT})">'
|
|
+ body.split(">", 1)[1].rsplit("</svg>", 1)[0].strip() + '</g>')
|
|
pmax = {k: c._pmax for k, c in charts.items()}
|
|
assert len(set(pmax.values())) == 1, f"panourile n-au aceeași axă: {pmax}"
|
|
y = lambda p: charts["DA"]._y(p) + SHIFT # noqa: E731
|
|
|
|
H = int(max(inalt)) + SHIFT + 44
|
|
parts = [
|
|
f'<svg viewBox="0 0 {W} {H}" xmlns="http://www.w3.org/2000/svg" '
|
|
f'style="width:100%;height:auto;font:12px sans-serif">',
|
|
'<text x="12" y="20" fill="#333" style="font-weight:bold">Trigger-ul: contează unde se '
|
|
'închide candela următoare — daily, exemplu construit</text>',
|
|
]
|
|
for (k, px, col, titlu, _), corp in zip(CFG, bodies):
|
|
parts.append(f'<text x="{px}" y="{40}" fill="{col}" style="font-weight:bold">{titlu}</text>')
|
|
for p, c, txt in ((H_HAM, ORANGE, "high 63.30"), (L_HAM, BLUE, "low 62.10")):
|
|
yy = y(p)
|
|
parts.append(f'<line x1="{px}" y1="{yy:.1f}" x2="{px + PW}" y2="{yy:.1f}" '
|
|
f'stroke="{c}" stroke-width="1.3" stroke-dasharray="4 3"/>')
|
|
parts.append(f'<text x="{px + PW - 2}" y="{yy - 5:.1f}" fill="{c}" text-anchor="end">'
|
|
f'{txt}</text>')
|
|
parts.append(corp)
|
|
for px in PX[1:]:
|
|
parts.append(f'<line x1="{px - 7}" y1="34" x2="{px - 7}" y2="{H - 40}" stroke="#ccc"/>')
|
|
parts += [
|
|
f'<text x="12" y="{H - 22}" fill="{GREY}">Regula e a lecției de pattern: intri la close-ul '
|
|
'candelei următoare, dacă acea închidere e peste high-ul hammer-ului. O atingere</text>',
|
|
f'<text x="12" y="{H - 6}" fill="{GREY}">intraday peste 63.30, urmată de o închidere sub el, '
|
|
'nu e trigger. Prețurile sunt construite.</text>',
|
|
"</svg>",
|
|
]
|
|
svg = "\n ".join(parts).replace("\n </svg>", "\n</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})")
|