Ansamblul arata acum 15 min si 3 min ca descompunere reala (k=5, cu assert), cu caseta punctata, ghidaje si nivelul comun legat prin conector — sablonul din buy_doji_perechi_tf. Noi, pentru pasii care nu aveau diagrama: directie (15 min ramane in Buy vs. se inchide Sell), trigger (bara inchisa peste capat vs. atinsa doar cu fitilul, plus al doilea semnal marcat "nu mai intri"), stop (cele 4 bare si alternativa 2xATR, cu cifrele SPY din curs), iesire (TP1 plus bull trail, long si short, oglinda calculata nu desenata din ochi). Secventa era din polilinii desenate de mana — rescrisa pe serie. Stopul e fix pe extrema celor 4 bare, fara marja: asa spune cursul aici, spre deosebire de 01_buy_doji. Diferenta trebuie spusa in text. Textul capitolului 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
126 lines
6.0 KiB
Python
126 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""M2D, pasul 2 — pullback-ul (M) pe TF mic: DA (există) vs. NU (lipsește).
|
|
|
|
Pentru `summaries/17_STRATEGII/03_m2d.md`, marcajul `<!-- DIAGRAMA: secventa -->`.
|
|
|
|
Panoul de sus e chiar graficul de 3 minute din diagrama de ansamblu, primele
|
|
13 bare — aceleași cifre în tot capitolul. Panoul de jos pornește din același
|
|
preț, dar urcă fără să se oprească: nu există pullback, deci nu există setup
|
|
M2D, oricât de frumoasă ar fi mișcarea.
|
|
|
|
Definiția pe care o susține desenul: pullback = revenire scurtă contra
|
|
direcției mari care NU rupe minimul din care a pornit mișcarea. Minimul acela
|
|
e low-ul candelei de 15 min care a dat semnalul (99.80) și e desenat pe
|
|
ambele panouri.
|
|
|
|
Rulare: python3 gen/strategii/m2d_secventa.py
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, "/workspace/atm-curs-dl")
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
from svg_candles import Arrow, Candle, Chart # noqa: E402
|
|
import m2d_duo as duo # noqa: E402
|
|
|
|
OUT = Path(__file__).with_suffix(".svg")
|
|
W, SCALE, GAP, LEFT = 680, 108.0, 32, 66
|
|
GREEN, RED, BLUE, CYAN, GREY = "#188a3e", "#c00", "#1a5fb4", "#0891b2", "#555"
|
|
|
|
REF_LOW = 99.80 # low-ul candelei de 15 min de semnal (vezi diagrama „directie")
|
|
SEMNAL = 100.60 # close-ul ei = open-ul primei bare de 3 min
|
|
|
|
# --- DA: primele 13 bare din seria de 3 min a diagramei de ansamblu --------
|
|
DA = [(100.60, 100.70, 100.40, 100.45), (100.45, 100.50, 100.25, 100.30),
|
|
(100.30, 100.35, 100.15, 100.20), (100.20, 100.25, 100.05, 100.10),
|
|
(100.10, 100.15, 100.00, 100.05), (100.05, 100.10, 99.95, 100.00),
|
|
(100.00, 100.05, 99.92, 99.95), (99.95, 100.00, 99.90, 99.98),
|
|
(99.98, 100.08, 99.94, 100.02), # 8: capătul pullback-ului
|
|
(100.02, 100.45, 100.00, 100.40), # 9: primul Buy = trigger
|
|
(100.40, 100.60, 100.35, 100.55), (100.55, 100.75, 100.50, 100.70),
|
|
(100.70, 101.00, 100.65, 100.95)]
|
|
PULL_END = 8
|
|
|
|
# --- NU: aceeași plecare, dar urcare continuă, fără pullback --------------
|
|
NU = [(100.60, 100.80, 100.55, 100.75), (100.75, 100.95, 100.70, 100.90),
|
|
(100.90, 101.10, 100.85, 101.05), (101.05, 101.20, 101.00, 101.15),
|
|
(101.15, 101.35, 101.10, 101.30), (101.30, 101.45, 101.25, 101.40),
|
|
(101.40, 101.60, 101.35, 101.55), (101.55, 101.75, 101.50, 101.70),
|
|
(101.70, 101.90, 101.65, 101.85), (101.85, 102.00, 101.80, 101.95),
|
|
(101.95, 102.15, 101.90, 102.10), (102.10, 102.25, 102.05, 102.20),
|
|
(102.20, 102.40, 102.15, 102.35)]
|
|
|
|
# ---------------------------------------------------------------- verificări
|
|
for nume, bars in (("DA", DA), ("NU", NU)):
|
|
for i, (o, h, l, c) in enumerate(bars):
|
|
assert h >= max(o, c) and l <= min(o, c), f"{nume}[{i}]: high/low incoerent"
|
|
if i:
|
|
assert abs(o - bars[i - 1][3]) < 1e-9, f"{nume}[{i}]: open != close precedent"
|
|
assert abs(bars[0][0] - SEMNAL) < 1e-9, f"{nume}: nu pornește din close-ul semnalului"
|
|
|
|
|
|
def run_in_jos(bars):
|
|
"""Cel mai lung șir de close-uri strict descrescătoare."""
|
|
best = cur = 1
|
|
for a, b in zip(bars, bars[1:]):
|
|
cur = cur + 1 if b[3] < a[3] else 1
|
|
best = max(best, cur)
|
|
return best
|
|
|
|
|
|
assert run_in_jos(DA) >= 3, "DA nu are pullback (niciun șir de 3 close-uri în jos)"
|
|
assert run_in_jos(NU) < 3, "NU are totuși un pullback"
|
|
|
|
PULL_LOW = min(b[2] for b in DA[:PULL_END + 1])
|
|
assert abs(PULL_LOW - 99.90) < 1e-9, PULL_LOW
|
|
assert PULL_LOW > REF_LOW, "pullback-ul rupe minimul din care a pornit mișcarea"
|
|
assert DA[0][3] > DA[PULL_END - 1][3], "pullback-ul nu coboară"
|
|
assert DA[9][3] > max(b[1] for b in DA[6:9]), "bara 9 nu rupe în sus capătul pullback-ului"
|
|
assert min(b[3] for b in NU) > SEMNAL, "NU: o bară se închide sub prețul de plecare"
|
|
|
|
|
|
def chart(bars, arrows):
|
|
return Chart(candles=[Candle(*b) for b in bars], arrows=arrows,
|
|
scale=SCALE, gap=GAP, body_w=11, left=LEFT, width=W, top=64)
|
|
|
|
|
|
ch_da = chart(DA, [
|
|
Arrow(PULL_END - 1, "M: pullback-ul obligatoriu — prețul merge\n"
|
|
"contra direcției mari, dar se oprește la 99.90",
|
|
where="below", color=RED, dx=-24, dy=46),
|
|
Arrow(9, "prima bară care urcă peste capătul pullback-ului:\n"
|
|
"aici începe pasul 3 (trigger-ul)",
|
|
where="above", color=GREEN, dx=-40, dy=6)])
|
|
ch_nu = chart(NU, [
|
|
Arrow(6, "urcare continuă, fără nicio pauză în jos:\n"
|
|
"asta e breakout, nu M2D — nu ai unde pune stopul",
|
|
where="below", color=RED, dx=-70, dy=40)])
|
|
|
|
body_da, h_da = duo.panel(ch_da, W)
|
|
body_nu, h_nu = duo.panel(ch_nu, W)
|
|
|
|
extra_da = (duo.titlu("DA — pullback pe 3 min, care nu rupe minimul de referință", color=GREEN)
|
|
+ duo.zone(ch_da, 0, PULL_END, "M: pullback", RED, pad=12, lift=26)
|
|
+ duo.level(ch_da, REF_LOW,
|
|
["minimul din care a pornit mișcarea 99.80 — sub el nu mai e pullback"],
|
|
GREY, W, below=True, dash="2 3")
|
|
+ duo.level(ch_da, PULL_LOW, ["cel mai jos punct al pullback-ului 99.90"],
|
|
RED, W, below=False, at_left=False))
|
|
extra_nu = (duo.titlu("NU — niciun pullback: nu ai setup, oricât de frumoasă e urcarea",
|
|
color=RED)
|
|
+ duo.level(ch_nu, SEMNAL,
|
|
["close-ul candelei de 15 min de semnal 100.60"], CYAN, W,
|
|
below=True, at_left=False))
|
|
|
|
svg, H, n = duo.build(
|
|
[("DA", body_da, h_da, extra_da), ("NU", body_nu, h_nu, extra_nu)],
|
|
["Ambele panouri: 3 minute, serie construită, aceleași cifre ca în diagrama de ansamblu.",
|
|
'Pullback nu înseamnă „nu mai face minim nou": înseamnă că nu rupe minimul '
|
|
'din care a pornit mișcarea.'],
|
|
W)
|
|
duo.fara_preturi_straine(svg, [REF_LOW, PULL_LOW, SEMNAL])
|
|
OUT.write_text(svg, encoding="utf-8")
|
|
print(f"scris {OUT} ({W}x{H:.0f}), {n} etichete | DA: șir în jos de {run_in_jos(DA)} bare, "
|
|
f"minim {PULL_LOW:.2f} peste referința {REF_LOW:.2f} | NU: șir în jos de "
|
|
f"{run_in_jos(NU)} bare")
|