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
This commit is contained in:
Claude Agent
2026-09-18 19:22:13 +00:00
parent af448dda69
commit 01599d2e9d
60 changed files with 9035 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
"""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')}")