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,136 @@
"""Diagrama de ansamblu M2D (strategii/03_m2d.md, marcajul `ansamblu`).
„Setup M2D LONG complet pe TF mic (3 min), cu TF mare (15 min) în Buy":
urcare -> pullback (M = Sell Momentum) -> trigger (A = primul Buy) -> intrare
-> SL de 4 candele -> TP1 la 1:1 -> continuare -> vârf -> ieșire pe bull trail.
Toate close-urile, wick-urile, volumele și nivelurile vin din
`gen/strategii/m2d_ansamblu.spec.txt`. Seria e construită (exemplu generic,
fără ticker), la scara 1.00 $ risc / 2.2R randament — nu e un trade real.
Rulează:
python3 gen/strategii/m2d_ansamblu.py
Scrie lângă el `m2d_ansamblu.svg`.
"""
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
OUT = Path(__file__).resolve().parent / "m2d_ansamblu.svg"
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
# ---------------------------------------------------------------- seria (32 bare)
CLOSES = [
96.8, 97.2, 97.7, 98.1, 98.6, 99.1, 99.5, 100.0, # 0-7 urcare
99.8, 99.4, 99.0, 99.2, 98.9, 99.15, 99.3, # 8-14 pullback (M)
99.8, 100.2, # 15-16 trigger (A)
100.7, 101.2, 101.6, 102.0, 102.4, 102.7, 103.0, # 17-23 continuare
103.3, 103.1, 102.8, 102.7, 102.9, 102.5, 102.2, 102.0, # 24-31 vârf + violare
]
assert len(CLOSES) == 32, len(CLOSES)
# fitile (sus, jos); jos mai mari pe 10-12, ca minimul de 4 candele (11:15) să
# fie clar sub intrare, la exact 98.80 (candela 12).
WICKS = (
[(0.15, 0.10)] * 8 # 0-7
+ [(0.10, 0.15), (0.10, 0.20), (0.10, 0.15), # 8-10
(0.10, 0.15), (0.10, 0.10), (0.10, 0.05), (0.10, 0.10)] # 11-14
+ [(0.15, 0.10), (0.15, 0.10)] # 15-16
+ [(0.15, 0.10)] * 7 # 17-23
+ [(0.20, 0.10), (0.10, 0.15), (0.10, 0.15), (0.12, 0.15), # 24-27
(0.10, 0.15), (0.10, 0.15), (0.10, 0.15), (0.10, 0.20)] # 28-31
)
assert len(WICKS) == 32, len(WICKS)
VOLS = (
[62, 66, 60, 68, 64, 70, 66, 74] # 0-7 trend
+ [44, 40, 38, 36, 34, 33, 35] # 8-14 pullback mic
+ [82, 88] # 15-16 trigger
+ [72, 70, 74, 68, 72, 66, 70] # 17-23 trend
+ [76, 64, 60, 58, 62, 56, 54, 66] # 24-31 vârf + ieșire
)
assert len(VOLS) == 32, len(VOLS)
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=96.4)
# ---------------------------------------------------------------- assert-urile din spec
entry = cds[15].c # 1. intrarea = close-ul trigger-ului
SL = min(c.l for c in cds[11:15]) # 2. minimul ultimelor 4 dinainte
TP1 = entry + (entry - SL) # 4. TP1 exact 1:1
assert entry == round(cds[15].c, 2), (entry, cds[15].c)
assert SL == min(c.l for c in cds[11:15]), SL
assert SL < entry, (SL, entry)
assert abs(TP1 - (entry + (entry - SL))) < 1e-6
for a, b in zip(cds, cds[1:]): # 5. serie continuă, fără goluri
assert a.c == b.o, (a.c, b.o)
assert cds[15].c > cds[14].c # 6. trigger în sus după pullback
assert any(c.c < c.o for c in cds[8:15]) # 7. pullback = Sell
assert any(c.c > c.o for c in cds[0:8]) # context în sus
exit_price = cds[31].c
assert exit_price > entry # 8. ieșirea e peste intrare
# bull trail = low-ul ultimei candele care a închis peste high-ul precedentei
qual = [i for i in range(1, 31) if cds[i].c > cds[i - 1].h]
assert qual and qual[-1] == 24, qual
bull = cds[24].l
assert cds[31].c < bull, (cds[31].c, bull) # finala închide sub linie = ieșire aici
r_mult = (exit_price - entry) / (entry - SL) # 9. R-ul afișat
assert round(r_mult, 1) == 2.2, r_mult
# ---------------------------------------------------------------- niveluri, etichete, marks
levels = [
Level(entry, f"intrare {entry:.2f}", GREEN, label_at="left"),
Level(SL, f"SL {SL:.2f} (4 candele)", RED, label_at="right"),
Level(TP1, f"TP1 {TP1:.2f} = 1:1", BLUE, label_at="right"),
]
assert [round(lv.price, 2) for lv in levels] == [99.80, 98.80, 100.80]
exit_text = f"ieșire {exit_price:.2f} pe bull trail ({r_mult:.1f}R)"
assert f"{r_mult:.1f}R" in exit_text
marks = [
Mark(0, "TF mare (15 min) în Buy", BLUE, "above", dy=-227),
Mark(11, "M: Sell Momentum (pullback)", RED, "below"),
Mark(15, "A: primul Buy = trigger", GREEN, "above", dy=-122),
# „vârf" stă sub candela 24: deasupra ei, orice text de la y-ul vârfului se
# calcă pe titlul din stânga-sus (ambele pornesc din `top`).
Mark(24, "vârf 103.30", GRAY, "below", dy=25),
Mark(31, "close sub bull trail = ieși", RED, "below", dy=55),
Mark(31, exit_text, RED, "below", dy=73),
]
caption = (f"Serie construită (32 bare, 3 min). R = ({exit_price:.2f} − {entry:.2f}) / "
f"{entry - SL:.2f} = {r_mult:.1f}R.")
assert f"{r_mult:.1f}R" in caption
ch = Chart(
candles=cds, levels=levels, marks=marks,
mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")],
scale=52.0, gap=16, body_w=9, left=44, width=720, vol_h=70,
vol_label="volum",
title="Simbol construit — 3 min (TF mare 15 min în Buy)",
caption=caption,
)
svg = ch.render()
# bull trail: nu e un `Level` (spec-ul cere exact trei niveluri), deci se
# injectează ca linie separată, la aceeași scară (`ch._y`).
yb = ch._y(bull)
bull_layer = (
f'<line x1="24" y1="{yb:.1f}" x2="{ch.width - 14}" y2="{yb:.1f}" '
f'stroke="{ORANGE}" stroke-width="1.5" stroke-dasharray="2 3"/>'
f'<text x="{ch.width - 28}" y="{yb - 6:.1f}" fill="{ORANGE}" '
f'text-anchor="end">bull trail {bull:.2f}</text>'
)
i = svg.index(">") + 1
svg = svg[:i] + "\n " + bull_layer + svg[i:]
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
OUT.write_text(svg)
print(f"scris {OUT.name}: {len(cds)} candele, intrare {entry:.2f}, SL {SL:.2f}, "
f"TP1 {TP1:.2f}, ieșire {exit_price:.2f} = {r_mult:.1f}R, bull trail {bull:.2f}")