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
170 lines
8.0 KiB
Python
170 lines
8.0 KiB
Python
#!/usr/bin/env python3
|
||
"""M2D, pasul 5 — ieșirea: TP1 la 1:1 și restul pe trail, long și short.
|
||
|
||
Pentru `summaries/17_STRATEGII/03_m2d.md`, marcajul `<!-- DIAGRAMA: iesire -->`.
|
||
|
||
Sus, LONG: barele de după trigger din seria de 3 minute a diagramei de
|
||
ansamblu. Prima tranșă iese la TP1 = 1:1. Restul merge pe bull trail: linia
|
||
sare pe low-ul fiecărei bare care se ÎNCHIDE peste high-ul precedentei și nu
|
||
coboară niciodată. Ieșirea vine la prima bară care se închide sub linie —
|
||
fitilul de dedesubt al barei dinainte nu scoate pe nimeni.
|
||
|
||
Jos, SHORT: exact aceeași serie oglindită în jurul lui 100.00 (p' = 200 − p),
|
||
deci bear trail-ul e construit de același cod, doar cu semnele întoarse.
|
||
Oglinda e calculată, nu desenată din ochi.
|
||
|
||
Rulare: python3 gen/strategii/m2d_iesire.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, 150.0, 40, 66
|
||
GREEN, RED, ORANGE, GREY = "#188a3e", "#c00", "#b06000", "#555"
|
||
OGLINDA = 200.0 # axa de oglindire: p' = OGLINDA − p
|
||
|
||
LONG = [(99.98, 100.08, 99.94, 100.02),
|
||
(100.02, 100.45, 100.00, 100.40), # 1: trigger, intrare
|
||
(100.40, 100.60, 100.35, 100.55), (100.55, 100.75, 100.50, 100.70),
|
||
(100.70, 101.00, 100.65, 100.95), # 4: TP1 atins
|
||
(100.95, 101.10, 100.88, 101.05), (101.05, 101.15, 100.95, 101.00),
|
||
(101.00, 101.40, 100.95, 101.35), (101.35, 101.55, 101.25, 101.45),
|
||
(101.45, 101.50, 101.20, 101.25), # 9: fitil sub linie, close peste
|
||
(101.25, 101.30, 101.05, 101.10), # 10: close sub linie = ieși
|
||
(101.10, 101.15, 100.90, 100.95)]
|
||
TRIG, SL_LONG = 1, 99.90 # stopul vine din pasul 4
|
||
|
||
SHORT = [(OGLINDA - o, OGLINDA - l, OGLINDA - h, OGLINDA - c) for o, h, l, c in LONG]
|
||
SL_SHORT = OGLINDA - SL_LONG
|
||
|
||
|
||
def trepte(bars, trig, sus):
|
||
"""Linia de trail, bară cu bară, de la trigger încolo.
|
||
|
||
sus=True: bull trail — sare pe low-ul barei care se ÎNCHIDE peste high-ul
|
||
precedentei. sus=False: bear trail — pe high-ul barei care se închide sub
|
||
low-ul precedentei. `None` cât timp linia nu există încă.
|
||
"""
|
||
linie, out, iesire = None, [], None
|
||
for i, (o, h, l, c) in enumerate(bars):
|
||
if i > trig and linie is not None and ((c < linie) if sus else (c > linie)):
|
||
iesire = i
|
||
out.append(linie)
|
||
break
|
||
if i > trig:
|
||
p = bars[i - 1]
|
||
if (c > p[1]) if sus else (c < p[2]):
|
||
linie = l if sus else h
|
||
out.append(linie)
|
||
return out + [linie] * (len(bars) - len(out)), iesire
|
||
|
||
|
||
# ---------------------------------------------------------------- verificări
|
||
for nume, bars in (("LONG", LONG), ("SHORT", SHORT)):
|
||
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"
|
||
|
||
E_L, E_S = LONG[TRIG][3], SHORT[TRIG][3]
|
||
R_L, R_S = round(E_L - SL_LONG, 2), round(SL_SHORT - E_S, 2)
|
||
TP_L, TP_S = round(E_L + R_L, 2), round(E_S - R_S, 2)
|
||
assert (E_L, R_L, TP_L) == (100.40, 0.50, 100.90), (E_L, R_L, TP_L)
|
||
assert (E_S, R_S, TP_S) == (99.60, 0.50, 99.10), (E_S, R_S, TP_S)
|
||
assert abs((OGLINDA - E_L) - E_S) < 1e-9 and abs((OGLINDA - TP_L) - TP_S) < 1e-9, \
|
||
"oglinda nu e exactă"
|
||
|
||
TP_BAR_L = next(i for i in range(TRIG + 1, len(LONG)) if LONG[i][1] >= TP_L - 1e-9)
|
||
TP_BAR_S = next(i for i in range(TRIG + 1, len(SHORT)) if SHORT[i][2] <= TP_S + 1e-9)
|
||
assert TP_BAR_L == TP_BAR_S == 4, (TP_BAR_L, TP_BAR_S)
|
||
|
||
LIN_L, EX_L = trepte(LONG, TRIG, True)
|
||
LIN_S, EX_S = trepte(SHORT, TRIG, False)
|
||
assert EX_L == EX_S == 10, (EX_L, EX_S)
|
||
TRAIL_L, TRAIL_S = LIN_L[EX_L], LIN_S[EX_S]
|
||
assert (TRAIL_L, TRAIL_S) == (101.25, 98.75), (TRAIL_L, TRAIL_S)
|
||
assert abs((OGLINDA - TRAIL_L) - TRAIL_S) < 1e-9, "trail-ul oglindit nu se potrivește"
|
||
# linia urcă (long) / coboară (short) și nu se întoarce niciodată
|
||
pas_l = [v for v in LIN_L[:EX_L + 1] if v is not None]
|
||
pas_s = [v for v in LIN_S[:EX_S + 1] if v is not None]
|
||
assert all(b >= a for a, b in zip(pas_l, pas_l[1:])), "bull trail-ul coboară"
|
||
assert all(b <= a for a, b in zip(pas_s, pas_s[1:])), "bear trail-ul urcă"
|
||
# bara 9: fitilul trece de linie, close-ul nu — deci nu scoate
|
||
assert LONG[9][2] < TRAIL_L <= LONG[9][3], "bara 9 nu mai ilustrează fitilul sub linie"
|
||
assert SHORT[9][1] > TRAIL_S >= SHORT[9][3], "oglinda barei 9 nu ține"
|
||
EXIT_L, EXIT_S = LONG[EX_L][3], SHORT[EX_S][3]
|
||
RR_L = round((EXIT_L - E_L) / R_L, 1)
|
||
RR_S = round((E_S - EXIT_S) / R_S, 1)
|
||
assert RR_L == RR_S == 1.4, (RR_L, RR_S)
|
||
|
||
|
||
def chart(bars, arrows, top=70):
|
||
return Chart(candles=[Candle(*b) for b in bars], arrows=arrows, scale=SCALE,
|
||
gap=GAP, body_w=14, left=LEFT, width=W, top=top)
|
||
|
||
|
||
def scara(ch, linie, pana_la, color):
|
||
"""Linia de trail desenată ca scară: un palier pe fiecare bară."""
|
||
seg, prev = [], None
|
||
for i, v in enumerate(linie[:pana_la + 1]):
|
||
if v is None:
|
||
continue
|
||
y, x0, x1 = ch._y(v), ch._cx(i) - GAP / 2, ch._cx(i) + GAP / 2
|
||
seg.append(f'<line x1="{x0:.1f}" y1="{y:.1f}" x2="{x1:.1f}" y2="{y:.1f}" '
|
||
f'stroke="{color}" stroke-width="2"/>')
|
||
if prev is not None and abs(prev - v) > 1e-9:
|
||
seg.append(f'<line x1="{x0:.1f}" y1="{ch._y(prev):.1f}" x2="{x0:.1f}" '
|
||
f'y2="{y:.1f}" stroke="{color}" stroke-width="1.2" '
|
||
f'stroke-dasharray="2 2"/>')
|
||
prev = v
|
||
return seg
|
||
|
||
|
||
ch_l = chart(LONG, [
|
||
Arrow(TP_BAR_L, "prima tranșă iese aici, la TP1",
|
||
where="above", color=GREEN, dx=-60, dy=-8),
|
||
Arrow(9, "fitilul trece sub linie, close-ul nu:\nnu ieși pe fitil",
|
||
where="below", color=GREY, dx=-30, dy=86),
|
||
Arrow(EX_L, "prima bară ÎNCHISĂ sub linie:\niese restul poziției, 1.4R",
|
||
where="below", color=ORANGE, dx=-20, dy=18)])
|
||
ch_s = chart(SHORT, [
|
||
Arrow(TP_BAR_S, "prima tranșă iese aici, la TP1",
|
||
where="below", color=GREEN, dx=-60, dy=20),
|
||
Arrow(EX_S, "prima bară ÎNCHISĂ peste linie:\niese restul poziției, 1.4R",
|
||
where="above", color=ORANGE, dx=-20, dy=-8)])
|
||
|
||
body_l, h_l = duo.panel(ch_l, W)
|
||
body_s, h_s = duo.panel(ch_s, W)
|
||
|
||
extra_l = (duo.titlu("LONG — bull trail: linia urcă, nu coboară", color=GREEN)
|
||
+ scara(ch_l, LIN_L, EX_L, ORANGE)
|
||
+ duo.level(ch_l, TP_L, ["TP1 100.90 = 1:1"], GREEN, W, at_left=False)
|
||
+ duo.level(ch_l, E_L, ["intrare 100.40"], GREEN, W, below=True, at_left=False)
|
||
+ duo.level(ch_l, SL_LONG, ["stop 99.90 (pasul 4)"], RED, W, below=True))
|
||
extra_s = (duo.titlu("SHORT — bear trail: exact aceeași serie, oglindită", color=RED)
|
||
+ scara(ch_s, LIN_S, EX_S, ORANGE)
|
||
+ duo.level(ch_s, SL_SHORT, ["stop 100.10 (pasul 4)"], RED, W, at_left=False)
|
||
+ duo.level(ch_s, E_S, ["intrare 99.60"], GREEN, W, below=False)
|
||
+ duo.level(ch_s, TP_S, ["TP1 99.10 = 1:1"], GREEN, W, below=True,
|
||
at_left=False))
|
||
|
||
svg, H, n = duo.build(
|
||
[("LONG", body_l, h_l, extra_l), ("SHORT", body_s, h_s, extra_s)],
|
||
["Serii construite, aceleași cifre ca în restul capitolului.",
|
||
"Scara portocalie e linia de trail, palier cu palier.",
|
||
"Panoul de jos e panoul de sus oglindit în jurul lui 100.00, calculat, "
|
||
"nu desenat din ochi."],
|
||
W)
|
||
duo.fara_preturi_straine(svg, [E_L, TP_L, SL_LONG, TRAIL_L, EXIT_L,
|
||
E_S, TP_S, SL_SHORT, TRAIL_S, EXIT_S, OGLINDA, 100.00])
|
||
OUT.write_text(svg, encoding="utf-8")
|
||
print(f"scris {OUT} ({W}x{H:.0f}), {n} etichete | LONG: intrare {E_L:.2f} SL {SL_LONG:.2f} "
|
||
f"TP1 {TP_L:.2f} la bara {TP_BAR_L}, trail {TRAIL_L:.2f}, ieșire bara {EX_L} la "
|
||
f"{EXIT_L:.2f} = {RR_L}R | SHORT (oglindit): intrare {E_S:.2f} SL {SL_SHORT:.2f} "
|
||
f"TP1 {TP_S:.2f}, trail {TRAIL_S:.2f}, ieșire {EXIT_S:.2f} = {RR_S}R")
|