Files
atm-curs-dl/gen/strategii/hammer_mbt10_mbt_gapdown.py
Claude Agent 01599d2e9d strategii: primele 30 de diagrame, cu generatoarele lor
Cate un agent grafician per diagrama, fiecare cu generatorul si SVG-ul lui.
Graficele de pret trec prin svg_candles, cu assert-urile din specificatie
scrise in generator — verifica structura, nu doar ca ruleaza: ca al doilea
picior e mai jos, ca intrarea e close-ul barei de confirmare, ca 1:1 e exact.

Verificare prin sondaj pe buy_doji_ansamblu, cu screenshot citit: serie
continua de 32 de candele, panou de volum, MA(20), doji-ul si ieșirea pe
trail etichetate, cifrele identice cu specificatia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
2026-09-18 19:22:13 +00:00

182 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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=0.20 (low 62.00); 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")
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
WICKS[21] = (0.30, 0.20) # low 62.00
# 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"
# 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", 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", 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"
# --- 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 — risc 1R în bani (mărime ajustată)",
"Exemplu construit (02_mbt10.md:728-734): #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')}")