Files
atm-curs-dl/gen/strategii/m2d_ansamblu.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

137 lines
6.1 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.

"""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}")