hammer_mbt10: noua diagrame noi (textul urmeaza)
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
This commit is contained in:
124
gen/strategii/hammer_mbt10_bh_locatie.py
Normal file
124
gen/strategii/hammer_mbt10_bh_locatie.py
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Buy hammer — locația: hammer la un nivel major vs. hammer în mijlocul range-ului.
|
||||
|
||||
Două panouri alăturate cu ACEEAȘI serie de 12 bare, pe aceeași scară și
|
||||
aceeași axă de preț (candelele stau la aceleași y). Stânga: suport monthly
|
||||
62.00, la 0,16 % sub low-ul hammer-ului. Dreapta: cel mai apropiat nivel de
|
||||
sub el e la 58.00, adică un gol de 7,07 %. Nivelurile se desenează de mână,
|
||||
cu coordonate absolute (liniile din Chart pornesc de la x=24 și ar trece
|
||||
peste panoul vecin).
|
||||
|
||||
Rulare: python3 gen/strategii/hammer_mbt10_bh_locatie.py
|
||||
"""
|
||||
import re
|
||||
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from pathlib import Path
|
||||
from svg_candles import Arrow, Chart, Mark, from_closes
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555"
|
||||
|
||||
CLOSES = [65.40, 64.90, 64.40, 64.70, 64.00, 63.50, 63.05,
|
||||
63.30, # 7 = hammer: o 63.05, h = c 63.30, l 62.10
|
||||
63.35, # 8 = confirmare, close peste high-ul hammer-ului
|
||||
63.80, 64.20, 64.60]
|
||||
wicks = [(0.15, 0.15)] * len(CLOSES)
|
||||
wicks[7] = (0.00, 0.95)
|
||||
cds = from_closes(CLOSES, wicks=wicks, start=65.90)
|
||||
ham = cds[7]
|
||||
SUP_DA, SUP_NU, REZ_NU = 62.00, 58.00, 67.00
|
||||
|
||||
# --- verificări -------------------------------------------------------------
|
||||
for i in range(1, len(cds)):
|
||||
assert abs(cds[i].o - cds[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]"
|
||||
assert (round(ham.h, 2), round(ham.l, 2), round(ham.c, 2)) == (63.30, 62.10, 63.30)
|
||||
corp, jos = ham.c - ham.o, ham.o - ham.l
|
||||
assert jos >= 2 * corp, "bara 7 nu e hammer"
|
||||
assert cds[8].c > ham.h, "bara 8 nu confirmă"
|
||||
d_da = round((ham.l - SUP_DA) / SUP_DA * 100, 2)
|
||||
assert d_da == 0.16 and d_da <= 2, d_da
|
||||
d_nu = round((ham.l - SUP_NU) / SUP_NU * 100, 2)
|
||||
assert d_nu == 7.07 and d_nu > 2, d_nu
|
||||
d_rez = round((REZ_NU - ham.h) / ham.h * 100, 2)
|
||||
assert d_rez == 5.85 and d_rez > 2, d_rez
|
||||
assert not any(SUP_NU < p < REZ_NU for p in (SUP_NU, REZ_NU) if False)
|
||||
|
||||
# --- layout -----------------------------------------------------------------
|
||||
W, PW = 760, 350
|
||||
PX = [24, 24 + PW + 28]
|
||||
SCALE, TOP = 34.0, 96
|
||||
|
||||
ARR = {
|
||||
PX[0]: Arrow(4, "suport monthly: nivel urmărit de mulți —\naici au cumpărători motiv să apară",
|
||||
price=SUP_DA, where="below", color=BLUE, dx=30, dy=22),
|
||||
PX[1]: Arrow(7, "aceeași formă, dar nimic sub ea:\nfitilul n-a fost oprit de un nivel,\nci s-a oprit singur",
|
||||
where="below", color=RED, dx=70, dy=30),
|
||||
}
|
||||
charts = []
|
||||
for px in PX:
|
||||
ch = Chart(candles=cds,
|
||||
marks=[Mark(7, "hammer 63.30 / 62.10", ORANGE, "above", dy=-16)],
|
||||
arrows=[ARR[px]],
|
||||
scale=SCALE, gap=17, body_w=10, left=px + 34, width=px + PW, top=TOP)
|
||||
body = ch._render()
|
||||
assert f'viewBox="0 0 {px + PW} ' in body, f"panoul {px}: textul unei săgeți iese"
|
||||
assert (ch.top, ch.left) == (TOP, px + 34), "Chart a mutat graficul: panourile s-ar dezalinia"
|
||||
charts.append((ch, body.split(">", 1)[1].rsplit("</svg>", 1)[0].strip()))
|
||||
(ch_da, body_da), (ch_nu, body_nu) = charts
|
||||
assert ch_da.scale == ch_nu.scale and ch_da._pmax == ch_nu._pmax, "scări diferite"
|
||||
y = ch_da._y
|
||||
|
||||
|
||||
def level(px, price, text, color, below=False):
|
||||
yy = y(price)
|
||||
ty, anchor, tx = (yy + 14, "end", px + PW - 4) if below else (yy - 6, "end", px + PW - 4)
|
||||
return [f'<line x1="{px}" y1="{yy:.1f}" x2="{px + PW}" y2="{yy:.1f}" stroke="{color}" '
|
||||
f'stroke-width="1.5" stroke-dasharray="4 3"/>',
|
||||
f'<text x="{tx}" y="{ty:.1f}" fill="{color}" text-anchor="{anchor}">{text}</text>']
|
||||
|
||||
|
||||
def zone(px, p_top, p_bot, label, color, label_at_bottom=False):
|
||||
y0, y1 = y(p_top), y(p_bot)
|
||||
ly, anchor, lx = ((y1 - 12, "start", px + 8) if label_at_bottom
|
||||
else (y0 - 6, "end", px + PW - 4))
|
||||
return [f'<rect x="{px}" y="{y0:.1f}" width="{PW}" height="{y1 - y0:.1f}" fill="{color}" '
|
||||
f'fill-opacity="0.12" stroke="{color}" stroke-width="1"/>',
|
||||
f'<text x="{lx}" y="{ly:.1f}" fill="{color}" text-anchor="{anchor}" '
|
||||
f'style="font-weight:bold">{label}</text>']
|
||||
|
||||
|
||||
PCT_DA, PCT_NU = f"{d_da:.2f}".replace(".", ","), f"{d_nu:.2f}".replace(".", ",")
|
||||
y_bot = y(SUP_NU)
|
||||
parts = [
|
||||
f'<svg viewBox="0 0 {W} {y_bot + 66:.0f}" xmlns="http://www.w3.org/2000/svg" '
|
||||
f'style="width:100%;height:auto;font:12px sans-serif">',
|
||||
'<text x="24" y="22" fill="#333" style="font-weight:bold">Hammer la un nivel vs. hammer '
|
||||
'în mijlocul range-ului — daily, exemplu construit</text>',
|
||||
f'<text x="{PX[0]}" y="48" fill="{GREEN}" style="font-weight:bold">DA — hammer pe suportul '
|
||||
'monthly</text>',
|
||||
f'<text x="{PX[1]}" y="48" fill="{RED}" style="font-weight:bold">NU — hammer în mijlocul '
|
||||
'range-ului</text>',
|
||||
f'<line x1="{PX[1] - 14}" y1="36" x2="{PX[1] - 14}" y2="{y_bot + 10:.0f}" stroke="#ccc"/>',
|
||||
*zone(PX[0], ham.l, SUP_DA, f"low-ul hammer-ului: {ham.l:.2f}", GREEN),
|
||||
*zone(PX[1], ham.l, SUP_NU, f"low-ul hammer-ului: {ham.l:.2f}", RED),
|
||||
f'<text x="{PX[0] + PW - 4}" y="{y(SUP_DA) + 34:.1f}" fill="{GREEN}" text-anchor="end" '
|
||||
f'style="font-weight:bold">de la {ham.l:.2f} la {SUP_DA:.2f} = {PCT_DA} % — la nivel</text>',
|
||||
f'<text x="{PX[1] + 8}" y="{y(SUP_NU) - 12:.1f}" fill="{RED}" style="font-weight:bold">'
|
||||
f'de la {ham.l:.2f} la {SUP_NU:.2f} = gol de {PCT_NU} % — hammer în aer</text>',
|
||||
*level(PX[0], SUP_DA, "suport monthly 62.00", BLUE, below=True),
|
||||
*level(PX[1], REZ_NU, "rezistența range-ului 67.00", BLUE, below=True),
|
||||
*level(PX[1], SUP_NU, "baza range-ului 58.00", BLUE, below=True),
|
||||
body_da, body_nu,
|
||||
f'<text x="24" y="{y_bot + 34:.0f}" fill="{GREY}">Completare proprie: materialul cere '
|
||||
'„un nivel major (weekly / monthly / quarterly), nu mijlocul range-ului", dar nu dă un prag '
|
||||
'numeric.</text>',
|
||||
f'<text x="24" y="{y_bot + 52:.0f}" fill="{GREY}">Pragul de 1-2 % e împrumutat de la buy '
|
||||
f'doji, ca să ai ce măsura: aici {PCT_DA} % în stânga, {PCT_NU} % în dreapta.</text>',
|
||||
"</svg>",
|
||||
]
|
||||
svg = "\n ".join(parts).replace("\n </svg>", "\n</svg>")
|
||||
assert "\n\n" not in svg, "linie goală în SVG"
|
||||
for m in re.finditer(r'<text x="([\d.]+)"[^>]*>(.*?)</text>', body_da):
|
||||
assert float(m.group(1)) < PX[1] - 14, f"text din DA trece în NU: {m.group(2)}"
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT.name} ({W}x{y_bot + 66:.0f}); DA {d_da}% NU {d_nu}% (rezistență {d_rez}%)")
|
||||
Reference in New Issue
Block a user