mbp: patru diagrame noi, pentru pasii care nu aveau
Context (DA/NU: cadere in suport major vs. consolidare stransa, cu benzile Bollinger calculate din close-uri), trigger (aceeasi candela M in ambele panouri), stop (fereastra de 4 candele, 1R si sizing derivate) si iesire (TP1 -> break even -> trail, fata de capcana iesirii la 50 %, unde prețul urca apoi la +2R). Aceeasi regula de stop in toate cinci diagramele ale capitolului, si banda inferioara calibrata explicit ca sa nu existe doua valori pentru acelasi nivel. Textul capitolului e neatins — agentul a atins pragul de context. SVG-urile sunt deocamdata fisiere in plus, fara marcaje care sa le ceara. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
This commit is contained in:
248
gen/strategii/mbp_context.py
Normal file
248
gen/strategii/mbp_context.py
Normal file
@@ -0,0 +1,248 @@
|
||||
"""Diagrama de context MBP (marcajul `context`), Pasul 1.
|
||||
|
||||
Două panouri de 15 minute, cu benzile Bollinger calculate din close-uri
|
||||
(SMA20 ± 2 deviații standard), nu desenate de mână:
|
||||
|
||||
- DA: mișcare rapidă în jos, opt bare, care ajunge într-un nivel major
|
||||
(minimul zilei precedente) și împinge o candelă să închidă SUB banda
|
||||
inferioară. Ăsta e terenul strategiei.
|
||||
- NU: consolidare strânsă, aceeași durată; benzile se strâng odată cu
|
||||
prețul, niciun close nu iese din ele, deci condiția de bandă nu se poate
|
||||
îndeplini niciodată.
|
||||
|
||||
Cifrele desenate ies din serie prin calcul (assert-uri mai jos). Nivelul
|
||||
„suport major" e construit de mine: exemplul din curs nu dă un preț pentru
|
||||
el, spune doar că semnalul are sens la capătul unei mișcări rapide care
|
||||
ajunge într-un nivel major.
|
||||
|
||||
Rulează: python3 gen/strategii/mbp_context.py
|
||||
"""
|
||||
import re
|
||||
import statistics
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
RED, GREEN, BLUE, GRAY = "#c00", "#188a3e", "#1a5fb4", "#777"
|
||||
N, K = 20, 2.0
|
||||
|
||||
|
||||
def bb(closes: list[float]) -> list[tuple[float, float, float] | None]:
|
||||
"""SMA20 ± 2 deviații standard, per bară; None până se adună 20 de close-uri."""
|
||||
out: list[tuple[float, float, float] | None] = []
|
||||
for i in range(len(closes)):
|
||||
if i + 1 < N:
|
||||
out.append(None)
|
||||
continue
|
||||
w = closes[i + 1 - N:i + 1]
|
||||
mid = sum(w) / N
|
||||
sd = statistics.pstdev(w)
|
||||
out.append((mid - K * sd, mid, mid + K * sd))
|
||||
return out
|
||||
|
||||
|
||||
# Benzile au nevoie de 20 de close-uri înainte să existe. Ca să se vadă pe tot
|
||||
# graficul, seriile au un prefix de 20 de bare care se calculează, dar nu se
|
||||
# desenează; se afișează doar cele 28 de bare de după el.
|
||||
PRE_DA = [624.55, 624.40, 624.52, 624.35, 624.20, 624.32, 624.15, 624.28,
|
||||
624.10, 624.22, 624.05, 624.18, 624.00, 624.12, 623.95, 624.08,
|
||||
623.90, 624.02, 623.85, 623.80]
|
||||
# ── panoul DA: dip în suport, urcare, apoi 8 bare de cădere rapidă înapoi în el
|
||||
CTX_DA = [623.72, 623.65, 623.88, 624.10, 624.35, 624.60, 624.80, 625.00,
|
||||
625.15, 625.30, 625.20, 625.35, 625.25, 625.42, 625.30, 625.48,
|
||||
625.36, 625.52, 625.44, 625.58]
|
||||
FALL = [625.20, 624.85, 624.60, 624.40, 624.25, 624.10, 624.02, 623.87]
|
||||
SHOW_DA = CTX_DA + FALL
|
||||
# Calibrare: banda inferioară se calculează din close-uri, iar în exemplul din
|
||||
# curs ea e 623.90. Ridic cu 1,2 cenți close-urile din fereastra de 20 de bare
|
||||
# (fără candela M, al cărei close 623.87 e cifra cursului), ca banda calculată
|
||||
# să iasă exact 623.90 și capitolul să nu aibă două valori pentru același nivel.
|
||||
CAL = 0.012
|
||||
CLOSES_DA = [c + (CAL if 28 <= i <= 46 else 0.0)
|
||||
for i, c in enumerate(PRE_DA + SHOW_DA)]
|
||||
W_DA = ([(0.08, 0.08)] * 20
|
||||
+ [(0.06, 0.06), (0.06, 0.05), (0.10, 0.05)] + [(0.10, 0.10)] * 17
|
||||
+ [(0.06, 0.14)] * 7 + [(0.06, 0.27)])
|
||||
assert len(CLOSES_DA) == len(W_DA) == 48
|
||||
|
||||
# ── panoul NU: aceeași lungime afișată, dar consolidare strânsă până la capăt
|
||||
PRE_NU = [625.30, 625.52, 625.60, 625.44, 625.20, 625.05, 625.12, 625.31,
|
||||
625.53, 625.62, 625.45, 625.22, 625.07, 625.14, 625.34, 625.55,
|
||||
625.59, 625.47, 625.26, 625.08]
|
||||
SHOW_NU = [625.35, 625.57, 625.62, 625.45, 625.23, 625.08, 625.15, 625.33,
|
||||
625.55, 625.64, 625.47, 625.21, 625.10, 625.13, 625.37, 625.56,
|
||||
625.60, 625.49, 625.25, 625.06, 625.14, 625.36, 625.58, 625.61,
|
||||
625.46, 625.24, 625.09, 625.16]
|
||||
CLOSES_NU = PRE_NU + SHOW_NU
|
||||
W_NU = [(0.07, 0.07)] * 48
|
||||
assert len(CLOSES_NU) == len(W_NU) == 48
|
||||
assert len(SHOW_DA) == len(SHOW_NU) == 28
|
||||
|
||||
SKIP = 20 # bare de încălzire, nedesenate
|
||||
|
||||
da_full = from_closes(CLOSES_DA, wicks=W_DA, start=624.70)
|
||||
nu_full = from_closes(CLOSES_NU, wicks=W_NU, start=625.20)
|
||||
da, nu = da_full[SKIP:], nu_full[SKIP:]
|
||||
|
||||
bb_da, bb_nu = bb(CLOSES_DA)[SKIP:], bb(CLOSES_NU)[SKIP:]
|
||||
M = len(SHOW_DA) - 1 # ultima bară a panoului DA = candela M
|
||||
|
||||
# ── ce afirmă textul, verificat pe serie ───────────────────────────────────
|
||||
b_m = bb_da[M]
|
||||
assert b_m is not None
|
||||
lo_da, mid_da, up_da = b_m
|
||||
assert abs(lo_da - 623.90) < 0.005, lo_da # aceeași bandă ca în exemplul din curs
|
||||
assert da[M].c < lo_da, (da[M].c, lo_da) # DA: close sub banda inferioară
|
||||
assert da[M].c < da[M].o # candela M e roșie
|
||||
# într-o cădere rapidă ies din bandă mai multe bare; le numărăm, ca eticheta
|
||||
# să spună o cifră care chiar iese din serie
|
||||
sub = [i for i, v in enumerate(bb_da) if v and da[i].c < v[0]]
|
||||
assert M in sub and len(sub) >= 2, sub
|
||||
n_sub = len(sub)
|
||||
|
||||
nivel = min(c.l for c in da[:6]) # suport major = minimul din stânga graficului
|
||||
assert abs(nivel - 623.60) < 1e-9, nivel
|
||||
assert abs(da[M].l - nivel) < 1e-9, da[M].l # căderea se oprește exact în el
|
||||
assert min(c.l for c in da[6:M]) > nivel + 0.2 # între timp prețul n-a mai fost acolo
|
||||
|
||||
cadere = da[len(CTX_DA) - 1].c - da[M].c
|
||||
assert cadere > 1.50, cadere # mișcare rapidă, nu derivă
|
||||
n_fall = len(FALL)
|
||||
assert n_fall == 8
|
||||
|
||||
lat_da = up_da - lo_da
|
||||
b_nu = bb_nu[-1]
|
||||
assert b_nu is not None
|
||||
lat_nu = b_nu[2] - b_nu[0]
|
||||
assert lat_nu < lat_da / 2.5, (lat_nu, lat_da) # benzile se strâng în consolidare
|
||||
for i in range(len(SHOW_NU)):
|
||||
v = bb_nu[i]
|
||||
assert v is not None and v[0] < nu[i].c < v[2], (i, nu[i].c, v)
|
||||
|
||||
SCALE, GAP, BODY, LEFT, PW = 78.0, 12, 8, 40, 372
|
||||
|
||||
|
||||
def bands_svg(ch: Chart, vals, color=BLUE) -> str:
|
||||
"""Cele trei linii Bollinger, desenate din valorile calculate mai sus."""
|
||||
parts = []
|
||||
for j, dash in ((0, "4 3"), (1, ""), (2, "4 3")):
|
||||
pts = [f"{ch._cx(i):.1f},{ch._y(v[j]):.1f}" for i, v in enumerate(vals) if v]
|
||||
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||
parts.append(f'<polyline points="{" ".join(pts)}" fill="none" stroke="{color}" '
|
||||
f'stroke-width="1.2" opacity="0.85"{d}/>')
|
||||
return "\n ".join(parts)
|
||||
|
||||
|
||||
# aceeași fereastră de preț în ambele panouri (altfel lățimile de bandă nu se
|
||||
# pot compara cu ochiul) și 6 % aer sus și jos, ca nicio linie să nu cadă pe
|
||||
# marginea cadrului
|
||||
pts_all = [p for c in (da + nu) for p in (c.h, c.l)]
|
||||
pts_all += [v[j] for vv in (bb_da, bb_nu) for v in vv if v for j in (0, 2)]
|
||||
P_LO, P_HI = min(pts_all), max(pts_all)
|
||||
AER = 0.06 * (P_HI - P_LO)
|
||||
SPACER = [Level(P_HI + AER, "", "none", dash=""), Level(P_LO - AER, "", "none", dash="")]
|
||||
|
||||
|
||||
def panel(cds, vals, levels):
|
||||
ch = Chart(candles=cds, levels=levels + SPACER, scale=SCALE, gap=GAP, body_w=BODY,
|
||||
left=LEFT, width=PW)
|
||||
raw = ch.render()
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', raw)
|
||||
w, h = int(m.group(1)), float(m.group(2))
|
||||
inner = raw[raw.index(">") + 1:raw.rindex("</svg>")].strip("\n")
|
||||
return ch, w, h, bands_svg(ch, vals) + "\n " + inner
|
||||
|
||||
|
||||
ch_da, w_da, h_da, in_da = panel(
|
||||
da, bb_da,
|
||||
[Level(nivel, "", GRAY, dash="6 3", label_at="left")])
|
||||
ch_nu, w_nu, h_nu, in_nu = panel(nu, bb_nu, [])
|
||||
|
||||
# liniile nu au voie să cadă pe marginea cadrului (6 % aer sus și jos)
|
||||
for ch, cds, vals in ((ch_da, da, bb_da), (ch_nu, nu, bb_nu)):
|
||||
ys = [ch._y(p) for c in cds for p in (c.h, c.l)]
|
||||
ys += [ch._y(v[j]) for v in vals if v for j in (0, 2)]
|
||||
assert min(ys) > 8.0, min(ys) # aer față de marginea de sus
|
||||
assert max(ys) < min(h_da, h_nu) - 8, (max(ys), h_da)
|
||||
assert max(ys) - min(ys) > 40
|
||||
|
||||
PH = max(h_da, h_nu)
|
||||
TOP = 52
|
||||
add_da = [
|
||||
# eticheta nivelului, legată de linia lui, pusă în zona liberă din mijloc
|
||||
f'<text x="{ch_da._cx(13):.1f}" y="{ch_da._y(nivel) - 6:.1f}" fill="{GRAY}" '
|
||||
f'text-anchor="middle">suport major {nivel:.2f}</text>',
|
||||
f'<text x="{ch_da._cx(M) - 4:.1f}" y="{ch_da._y(da[M].l) + 20:.1f}" fill="{RED}" '
|
||||
f'text-anchor="end" style="font-weight:bold">candela M: close {da[M].c:.2f},</text>',
|
||||
f'<text x="{ch_da._cx(M) - 4:.1f}" y="{ch_da._y(da[M].l) + 34:.1f}" fill="{RED}" '
|
||||
f'text-anchor="end" style="font-weight:bold">sub banda inferioară {lo_da:.2f}</text>',
|
||||
f'<text x="{ch_da._cx(len(CTX_DA) - 1) - 6:.1f}" '
|
||||
f'y="{ch_da._y(da[len(CTX_DA) - 1].h) - 14:.1f}" fill="{RED}" text-anchor="end" '
|
||||
f'style="font-weight:bold">cădere de {cadere:.2f} $ în {n_fall} bare</text>',
|
||||
f'<line x1="{ch_da._cx(len(CTX_DA) - 1):.1f}" y1="{ch_da._y(da[len(CTX_DA) - 1].h) - 6:.1f}" '
|
||||
f'x2="{ch_da._cx(M):.1f}" y2="{ch_da._y(da[M].h) - 6:.1f}" stroke="{RED}" '
|
||||
f'stroke-width="1.5" stroke-dasharray="3 3"/>',
|
||||
]
|
||||
add_nu = [
|
||||
f'<text x="{ch_nu._cx(14):.1f}" y="{ch_nu._y(max(c.h for c in nu)) - 16:.1f}" '
|
||||
f'fill="{GRAY}" text-anchor="middle" style="font-weight:bold">'
|
||||
f'benzile se strâng la {lat_nu:.2f} $ lățime</text>',
|
||||
f'<text x="{ch_nu._cx(14):.1f}" y="{ch_nu._y(min(c.l for c in nu)) + 22:.1f}" '
|
||||
f'fill="{GRAY}" text-anchor="middle">niciun close în afara lor — semnalul nu apare</text>',
|
||||
]
|
||||
|
||||
FOOTER = [
|
||||
f"DA: opt bare duc prețul cu {cadere:.2f} $ în jos, în suportul major {nivel:.2f} "
|
||||
f"(minimul din stânga graficului),",
|
||||
f"iar {n_sub} bare închid sub banda inferioară; M e ultima dintre ele, la {da[M].c:.2f} "
|
||||
f"față de bandă {lo_da:.2f}.",
|
||||
"Care dintre ele e candela M o alege TICUS, la pasul 2 — banda singură nu decide.",
|
||||
f"NU: aceeași durată, dar prețul stă pe loc; banda are {lat_nu:.2f} $ lățime față de "
|
||||
f"{lat_da:.2f} $ în stânga,",
|
||||
"iar niciun close nu iese din ea. Fără atingerea benzii nu există condiția M, deci nu există trade.",
|
||||
"Benzile sunt calculate din close-uri (SMA20 ± 2 deviații standard), nu desenate de mână.",
|
||||
"Nivelul de suport major e construit de mine: exemplul din curs nu dă un preț pentru el.",
|
||||
]
|
||||
root_h = TOP + PH + 24 + 17 * len(FOOTER) + 8
|
||||
out = [f'<svg viewBox="0 0 780 {root_h:.0f}" xmlns="http://www.w3.org/2000/svg" '
|
||||
f'style="width:100%;height:auto;font:12px sans-serif">',
|
||||
f'<text x="8" y="20" fill="#333" style="font-weight:bold">Pasul 1 — unde are voie '
|
||||
f'strategia să caute: mișcare rapidă într-un nivel major</text>',
|
||||
f'<text x="8" y="40" fill="{GREEN}" style="font-weight:bold">DA — cădere rapidă în '
|
||||
f'suport, banda atinsă</text>',
|
||||
f'<text x="398" y="40" fill="{RED}" style="font-weight:bold">NU — consolidare strânsă, '
|
||||
f'banda nu se atinge</text>',
|
||||
f'<svg x="8" y="{TOP}" width="{w_da}" height="{PH:.0f}" viewBox="0 0 {w_da} {PH:.0f}">'
|
||||
f'{in_da}\n {chr(10).join(add_da)}</svg>',
|
||||
f'<svg x="398" y="{TOP}" width="{w_nu}" height="{PH:.0f}" viewBox="0 0 {w_nu} {PH:.0f}">'
|
||||
f'{in_nu}\n {chr(10).join(add_nu)}</svg>']
|
||||
fy = TOP + PH + 24
|
||||
out += [f'<text x="8" y="{fy + 17 * i:.0f}" fill="#555">{t}</text>' for i, t in enumerate(FOOTER)]
|
||||
out.append("</svg>")
|
||||
svg = "\n ".join(out)
|
||||
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
|
||||
|
||||
def _fits(body: str, w: float, h: float, unde: str) -> None:
|
||||
"""Nicio etichetă nu are voie să iasă din cadrul panoului (text tăiat)."""
|
||||
for mm in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', body):
|
||||
x, y, attrs, txt = float(mm.group(1)), float(mm.group(2)), mm.group(3), mm.group(4)
|
||||
if not txt.strip():
|
||||
continue
|
||||
tw = len(re.sub(r"<[^>]+>", "", txt)) * (6.4 if "bold" in attrs else 5.9)
|
||||
x0 = x - tw if 'anchor="end"' in attrs else (x - tw / 2 if 'anchor="middle"' in attrs else x)
|
||||
assert x0 >= -2 and x0 + tw <= w + 2, f"{unde}: text tăiat: {txt!r} ({x0:.0f}..{x0 + tw:.0f} / {w})"
|
||||
assert 0 <= y <= h + 2, f"{unde}: text în afara înălțimii: {txt!r}"
|
||||
|
||||
|
||||
_fits(in_da + "\n" + "\n".join(add_da), w_da, PH, "panoul DA")
|
||||
_fits(in_nu + "\n" + "\n".join(add_nu), w_nu, PH, "panoul NU")
|
||||
assert svg.count("<svg") == 3 and svg.count("</svg>") == 3
|
||||
assert f"{n_fall} bare" in svg and len(FALL) == n_fall
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: DA close {da[M].c:.2f} < bandă {lo_da:.2f}, cădere {cadere:.2f} $ "
|
||||
f"în {n_fall} bare, suport {nivel:.2f}; NU lățime bandă {lat_nu:.2f} vs {lat_da:.2f}")
|
||||
147
gen/strategii/mbp_context.svg
Normal file
147
gen/strategii/mbp_context.svg
Normal file
@@ -0,0 +1,147 @@
|
||||
<svg viewBox="0 0 780 524" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="8" y="20" fill="#333" style="font-weight:bold">Pasul 1 — unde are voie strategia să caute: mișcare rapidă într-un nivel major</text>
|
||||
<text x="8" y="40" fill="#188a3e" style="font-weight:bold">DA — cădere rapidă în suport, banda atinsă</text>
|
||||
<text x="398" y="40" fill="#c00" style="font-weight:bold">NU — consolidare strânsă, banda nu se atinge</text>
|
||||
<svg x="8" y="52" width="372" height="321" viewBox="0 0 372 321"><polyline points="40.0,241.6 52.0,246.4 64.0,245.4 76.0,244.4 88.0,245.3 100.0,248.7 112.0,254.6 124.0,261.8 136.0,268.5 148.0,275.2 160.0,277.5 172.0,280.7 184.0,280.1 196.0,280.5 208.0,277.3 220.0,275.3 232.0,269.4 244.0,264.4 256.0,255.4 268.0,244.3 280.0,228.7 292.0,209.9 304.0,196.2 316.0,189.7 328.0,191.5 340.0,200.4 352.0,212.0 364.0,226.4" fill="none" stroke="#1a5fb4" stroke-width="1.2" opacity="0.85" stroke-dasharray="4 3"/>
|
||||
<polyline points="40.0,210.0 52.0,212.9 64.0,215.4 76.0,216.4 88.0,215.8 100.0,214.7 112.0,212.2 124.0,209.4 136.0,205.3 148.0,201.0 160.0,196.5 172.0,191.9 184.0,186.9 196.0,181.8 208.0,176.5 220.0,171.0 232.0,165.3 244.0,159.4 256.0,153.1 268.0,146.1 280.0,140.3 292.0,135.6 304.0,132.7 316.0,131.5 328.0,131.9 340.0,133.8 352.0,136.8 364.0,141.2" fill="none" stroke="#1a5fb4" stroke-width="1.2" opacity="0.85"/>
|
||||
<polyline points="40.0,178.4 52.0,179.5 64.0,185.5 76.0,188.5 88.0,186.3 100.0,180.8 112.0,169.8 124.0,157.0 136.0,142.0 148.0,126.8 160.0,115.4 172.0,103.1 184.0,93.8 196.0,83.1 208.0,75.7 220.0,66.7 232.0,61.1 244.0,54.3 256.0,50.8 268.0,48.0 280.0,51.9 292.0,61.3 304.0,69.2 316.0,73.4 328.0,72.2 340.0,67.1 352.0,61.5 364.0,55.9" fill="none" stroke="#1a5fb4" stroke-width="1.2" opacity="0.85" stroke-dasharray="4 3"/>
|
||||
<line x1="24" y1="249.8" x2="358" y2="249.8" stroke="#777" stroke-width="1.5" stroke-dasharray="6 3"/>
|
||||
<text x="30" y="243.8" fill="#777"></text>
|
||||
<line x1="24" y1="34.0" x2="358" y2="34.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="344" y="28.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="24" y1="294.6" x2="358" y2="294.6" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="344" y="288.6" fill="none" text-anchor="end"></text>
|
||||
<line x1="40.0" y1="229.6" x2="40.0" y2="245.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="36.0" y="234.2" width="8" height="6.2" fill="#c00"/>
|
||||
<line x1="52.0" y1="235.8" x2="52.0" y2="249.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="48.0" y="240.5" width="8" height="5.5" fill="#c00"/>
|
||||
<line x1="64.0" y1="220.2" x2="64.0" y2="249.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="60.0" y="228.0" width="8" height="17.9" fill="#188a3e"/>
|
||||
<line x1="76.0" y1="203.0" x2="76.0" y2="235.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="72.0" y="210.8" width="8" height="17.2" fill="#188a3e"/>
|
||||
<line x1="88.0" y1="183.5" x2="88.0" y2="218.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.0" y="191.3" width="8" height="19.5" fill="#188a3e"/>
|
||||
<line x1="100.0" y1="164.0" x2="100.0" y2="199.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="96.0" y="171.8" width="8" height="19.5" fill="#188a3e"/>
|
||||
<line x1="112.0" y1="148.4" x2="112.0" y2="179.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="108.0" y="156.2" width="8" height="15.6" fill="#188a3e"/>
|
||||
<line x1="124.0" y1="132.8" x2="124.0" y2="164.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="120.0" y="140.6" width="8" height="15.6" fill="#188a3e"/>
|
||||
<line x1="136.0" y1="120.2" x2="136.0" y2="148.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="132.0" y="128.0" width="8" height="12.6" fill="#188a3e"/>
|
||||
<line x1="148.0" y1="108.5" x2="148.0" y2="135.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.0" y="116.3" width="8" height="11.7" fill="#188a3e"/>
|
||||
<line x1="160.0" y1="108.5" x2="160.0" y2="131.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="156.0" y="116.3" width="8" height="7.8" fill="#c00"/>
|
||||
<line x1="172.0" y1="104.6" x2="172.0" y2="131.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="168.0" y="112.4" width="8" height="11.7" fill="#188a3e"/>
|
||||
<line x1="184.0" y1="104.6" x2="184.0" y2="128.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="180.0" y="112.4" width="8" height="7.8" fill="#c00"/>
|
||||
<line x1="196.0" y1="99.1" x2="196.0" y2="128.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="192.0" y="106.9" width="8" height="13.3" fill="#188a3e"/>
|
||||
<line x1="208.0" y1="99.1" x2="208.0" y2="124.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.0" y="106.9" width="8" height="9.4" fill="#c00"/>
|
||||
<line x1="220.0" y1="94.5" x2="220.0" y2="124.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="216.0" y="102.3" width="8" height="14.0" fill="#188a3e"/>
|
||||
<line x1="232.0" y1="94.5" x2="232.0" y2="119.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="228.0" y="102.3" width="8" height="9.4" fill="#c00"/>
|
||||
<line x1="244.0" y1="91.3" x2="244.0" y2="119.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="240.0" y="99.1" width="8" height="12.5" fill="#188a3e"/>
|
||||
<line x1="256.0" y1="91.3" x2="256.0" y2="113.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="252.0" y="99.1" width="8" height="6.2" fill="#c00"/>
|
||||
<line x1="268.0" y1="86.7" x2="268.0" y2="113.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.0" y="94.5" width="8" height="10.9" fill="#188a3e"/>
|
||||
<line x1="280.0" y1="89.8" x2="280.0" y2="135.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="276.0" y="94.5" width="8" height="29.6" fill="#c00"/>
|
||||
<line x1="292.0" y1="119.4" x2="292.0" y2="162.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="288.0" y="124.1" width="8" height="27.3" fill="#c00"/>
|
||||
<line x1="304.0" y1="146.7" x2="304.0" y2="181.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="300.0" y="151.4" width="8" height="19.5" fill="#c00"/>
|
||||
<line x1="316.0" y1="166.2" x2="316.0" y2="197.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="312.0" y="170.9" width="8" height="15.6" fill="#c00"/>
|
||||
<line x1="328.0" y1="181.8" x2="328.0" y2="209.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.0" y="186.5" width="8" height="11.7" fill="#c00"/>
|
||||
<line x1="340.0" y1="193.5" x2="340.0" y2="220.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="336.0" y="198.2" width="8" height="11.7" fill="#c00"/>
|
||||
<line x1="352.0" y1="205.2" x2="352.0" y2="227.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="348.0" y="209.9" width="8" height="6.2" fill="#c00"/>
|
||||
<line x1="364.0" y1="211.5" x2="364.0" y2="249.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="360.0" y="216.1" width="8" height="12.6" fill="#c00"/>
|
||||
<text x="196.0" y="243.8" fill="#777" text-anchor="middle">suport major 623.60</text>
|
||||
<text x="360.0" y="269.8" fill="#c00" text-anchor="end" style="font-weight:bold">candela M: close 623.87,</text>
|
||||
<text x="360.0" y="283.8" fill="#c00" text-anchor="end" style="font-weight:bold">sub banda inferioară 623.90</text>
|
||||
<text x="262.0" y="72.7" fill="#c00" text-anchor="end" style="font-weight:bold">cădere de 1.72 $ în 8 bare</text>
|
||||
<line x1="268.0" y1="80.7" x2="364.0" y2="205.5" stroke="#c00" stroke-width="1.5" stroke-dasharray="3 3"/></svg>
|
||||
<svg x="398" y="52" width="372" height="321" viewBox="0 0 372 321"><polyline points="40.0,143.2 52.0,143.4 64.0,143.5 76.0,143.5 88.0,143.2 100.0,142.8 112.0,142.4 124.0,142.3 136.0,142.3 148.0,142.5 160.0,142.5 172.0,142.6 184.0,142.1 196.0,142.3 208.0,142.1 220.0,142.2 232.0,142.2 244.0,142.3 256.0,142.3 268.0,142.7 280.0,144.4 292.0,144.2 304.0,143.9 316.0,144.5 328.0,143.6 340.0,141.7 352.0,142.5 364.0,143.9" fill="none" stroke="#1a5fb4" stroke-width="1.2" opacity="0.85" stroke-dasharray="4 3"/>
|
||||
<polyline points="40.0,113.7 52.0,113.5 64.0,113.4 76.0,113.4 88.0,113.3 100.0,113.1 112.0,113.0 124.0,113.0 136.0,112.9 148.0,112.8 160.0,112.7 172.0,112.8 184.0,112.6 196.0,112.7 208.0,112.6 220.0,112.5 232.0,112.5 244.0,112.4 256.0,112.4 268.0,112.5 280.0,113.3 292.0,114.2 304.0,114.3 316.0,113.7 328.0,112.8 340.0,112.2 352.0,112.4 364.0,113.1" fill="none" stroke="#1a5fb4" stroke-width="1.2" opacity="0.85"/>
|
||||
<polyline points="40.0,84.2 52.0,83.6 64.0,83.3 76.0,83.3 88.0,83.3 100.0,83.5 112.0,83.7 124.0,83.6 136.0,83.4 148.0,83.1 160.0,82.9 172.0,82.9 184.0,83.1 196.0,83.1 208.0,83.0 220.0,82.9 232.0,82.7 244.0,82.5 256.0,82.5 268.0,82.4 280.0,82.3 292.0,84.1 304.0,84.7 316.0,82.9 328.0,82.0 340.0,82.7 352.0,82.3 364.0,82.2" fill="none" stroke="#1a5fb4" stroke-width="1.2" opacity="0.85" stroke-dasharray="4 3"/>
|
||||
<line x1="24" y1="34.0" x2="358" y2="34.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="344" y="28.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="24" y1="294.6" x2="358" y2="294.6" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="344" y="288.6" fill="none" text-anchor="end"></text>
|
||||
<line x1="40.0" y1="107.9" x2="40.0" y2="139.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="36.0" y="113.3" width="8" height="21.1" fill="#188a3e"/>
|
||||
<line x1="52.0" y1="90.7" x2="52.0" y2="118.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="48.0" y="96.2" width="8" height="17.2" fill="#188a3e"/>
|
||||
<line x1="64.0" y1="86.8" x2="64.0" y2="101.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="60.0" y="92.3" width="8" height="3.9" fill="#188a3e"/>
|
||||
<line x1="76.0" y1="86.8" x2="76.0" y2="111.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="72.0" y="92.3" width="8" height="13.3" fill="#c00"/>
|
||||
<line x1="88.0" y1="100.1" x2="88.0" y2="128.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.0" y="105.5" width="8" height="17.2" fill="#c00"/>
|
||||
<line x1="100.0" y1="117.2" x2="100.0" y2="139.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="96.0" y="122.7" width="8" height="11.7" fill="#c00"/>
|
||||
<line x1="112.0" y1="123.5" x2="112.0" y2="139.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="108.0" y="128.9" width="8" height="5.5" fill="#188a3e"/>
|
||||
<line x1="124.0" y1="109.4" x2="124.0" y2="134.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="120.0" y="114.9" width="8" height="14.0" fill="#188a3e"/>
|
||||
<line x1="136.0" y1="92.3" x2="136.0" y2="120.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="132.0" y="97.7" width="8" height="17.2" fill="#188a3e"/>
|
||||
<line x1="148.0" y1="85.3" x2="148.0" y2="103.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.0" y="90.7" width="8" height="7.0" fill="#188a3e"/>
|
||||
<line x1="160.0" y1="85.3" x2="160.0" y2="109.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="156.0" y="90.7" width="8" height="13.3" fill="#c00"/>
|
||||
<line x1="172.0" y1="98.5" x2="172.0" y2="129.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="168.0" y="104.0" width="8" height="20.3" fill="#c00"/>
|
||||
<line x1="184.0" y1="118.8" x2="184.0" y2="138.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="180.0" y="124.3" width="8" height="8.6" fill="#c00"/>
|
||||
<line x1="196.0" y1="125.0" x2="196.0" y2="138.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="192.0" y="130.5" width="8" height="2.3" fill="#188a3e"/>
|
||||
<line x1="208.0" y1="106.3" x2="208.0" y2="136.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.0" y="111.8" width="8" height="18.7" fill="#188a3e"/>
|
||||
<line x1="220.0" y1="91.5" x2="220.0" y2="117.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="216.0" y="97.0" width="8" height="14.8" fill="#188a3e"/>
|
||||
<line x1="232.0" y1="88.4" x2="232.0" y2="102.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="228.0" y="93.8" width="8" height="3.1" fill="#188a3e"/>
|
||||
<line x1="244.0" y1="88.4" x2="244.0" y2="107.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="240.0" y="93.8" width="8" height="8.6" fill="#c00"/>
|
||||
<line x1="256.0" y1="97.0" x2="256.0" y2="126.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="252.0" y="102.4" width="8" height="18.7" fill="#c00"/>
|
||||
<line x1="268.0" y1="115.7" x2="268.0" y2="141.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.0" y="121.1" width="8" height="14.8" fill="#c00"/>
|
||||
<line x1="280.0" y1="124.3" x2="280.0" y2="141.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="276.0" y="129.7" width="8" height="6.2" fill="#188a3e"/>
|
||||
<line x1="292.0" y1="107.1" x2="292.0" y2="135.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="288.0" y="112.6" width="8" height="17.2" fill="#188a3e"/>
|
||||
<line x1="304.0" y1="89.9" x2="304.0" y2="118.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="300.0" y="95.4" width="8" height="17.2" fill="#188a3e"/>
|
||||
<line x1="316.0" y1="87.6" x2="316.0" y2="100.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="312.0" y="93.1" width="8" height="2.3" fill="#188a3e"/>
|
||||
<line x1="328.0" y1="87.6" x2="328.0" y2="110.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.0" y="93.1" width="8" height="11.7" fill="#c00"/>
|
||||
<line x1="340.0" y1="99.3" x2="340.0" y2="127.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="336.0" y="104.8" width="8" height="17.2" fill="#c00"/>
|
||||
<line x1="352.0" y1="116.5" x2="352.0" y2="139.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="348.0" y="121.9" width="8" height="11.7" fill="#c00"/>
|
||||
<line x1="364.0" y1="122.7" x2="364.0" y2="139.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="360.0" y="128.2" width="8" height="5.5" fill="#188a3e"/>
|
||||
<text x="208.0" y="69.3" fill="#777" text-anchor="middle" style="font-weight:bold">benzile se strâng la 0.79 $ lățime</text>
|
||||
<text x="208.0" y="163.4" fill="#777" text-anchor="middle">niciun close în afara lor — semnalul nu apare</text></svg>
|
||||
<text x="8" y="397" fill="#555">DA: opt bare duc prețul cu 1.72 $ în jos, în suportul major 623.60 (minimul din stânga graficului),</text>
|
||||
<text x="8" y="414" fill="#555">iar 4 bare închid sub banda inferioară; M e ultima dintre ele, la 623.87 față de bandă 623.90.</text>
|
||||
<text x="8" y="431" fill="#555">Care dintre ele e candela M o alege TICUS, la pasul 2 — banda singură nu decide.</text>
|
||||
<text x="8" y="448" fill="#555">NU: aceeași durată, dar prețul stă pe loc; banda are 0.79 $ lățime față de 2.19 $ în stânga,</text>
|
||||
<text x="8" y="465" fill="#555">iar niciun close nu iese din ea. Fără atingerea benzii nu există condiția M, deci nu există trade.</text>
|
||||
<text x="8" y="482" fill="#555">Benzile sunt calculate din close-uri (SMA20 ± 2 deviații standard), nu desenate de mână.</text>
|
||||
<text x="8" y="499" fill="#555">Nivelul de suport major e construit de mine: exemplul din curs nu dă un preț pentru el.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
190
gen/strategii/mbp_iesire.py
Normal file
190
gen/strategii/mbp_iesire.py
Normal file
@@ -0,0 +1,190 @@
|
||||
"""Diagrama de ieșire MBP (marcajul `iesire`), Pasul 5.
|
||||
|
||||
Două panouri de 15 minute care pornesc din aceeași intrare (623.95, stop
|
||||
623.60, deci 1R = 0.35 — cifrele exemplului din curs) și se deosebesc prin ce
|
||||
faci cu stopul după ce prima tranșă a ieșit la 1:1:
|
||||
|
||||
- DA: prima tranșă iese la TP1 624.30 (1:1), stopul rămas se mută la break
|
||||
even (623.95, prețul de intrare), restul merge pe trail — stopul urcă sub
|
||||
minimul fiecărei candele închise — și iese la prima închidere sub el.
|
||||
- NU: stopul e mutat la jumătatea drumului („50 %", 624.125). Fitilul unei
|
||||
bare lungi coboară sub el și te scoate, deși prețul urcă apoi mai sus
|
||||
decât în panoul din stânga.
|
||||
|
||||
Toate nivelurile ies din serie prin calcul: TP1 = intrare + (intrare − stop),
|
||||
break even = intrarea, nivelul „50 %" = intrare + 1R/2, iar ieșirea pe trail e
|
||||
prima închidere sub minimul candelei precedente, căutată în serie.
|
||||
|
||||
Rulează: python3 gen/strategii/mbp_iesire.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Arrow, Chart, Level, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
RED, GREEN, BLUE, GRAY, ORANGE = "#c00", "#188a3e", "#1a5fb4", "#777", "#b06000"
|
||||
|
||||
ENTRY, SL = 623.95, 623.60
|
||||
RISC = ENTRY - SL
|
||||
TP1 = ENTRY + RISC
|
||||
assert abs(RISC - 0.35) < 1e-9 and abs(TP1 - 624.30) < 1e-9
|
||||
|
||||
E = 3 # indexul candelei de trigger (intrarea = close-ul ei)
|
||||
PRE = [624.15, 623.98, 623.87, ENTRY] # ...M la index 2, trigger la 3
|
||||
W_PRE = [(0.06, 0.10), (0.05, 0.16), (0.06, 0.27), (0.05, 0.12)]
|
||||
|
||||
# DA: urcare la TP1, încă două bare, apoi întoarcere care rupe trail-ul
|
||||
DA = PRE + [624.12, 624.24, 624.34, 624.46, 624.52, 624.40, 624.28]
|
||||
W_DA = W_PRE + [(0.05, 0.06), (0.06, 0.05), (0.04, 0.06), (0.05, 0.06),
|
||||
(0.07, 0.05), (0.04, 0.08), (0.03, 0.06)]
|
||||
# NU: aceeași urcare, dar a treia bară are un fitil lung care taie nivelul 50 %
|
||||
NU = PRE + [624.12, 624.24, 624.20, 624.38, 624.52, 624.60, 624.55]
|
||||
W_NU = W_PRE + [(0.05, 0.06), (0.06, 0.05), (0.04, 0.22), (0.05, 0.06),
|
||||
(0.06, 0.05), (0.06, 0.05), (0.04, 0.07)]
|
||||
|
||||
da = from_closes(DA, wicks=W_DA, start=624.30)
|
||||
nu = from_closes(NU, wicks=W_NU, start=624.30)
|
||||
assert len(da) == len(nu) == 11
|
||||
|
||||
# ── ce afirmă textul, verificat pe serie ───────────────────────────────────
|
||||
assert abs(da[E].c - ENTRY) < 1e-9 and abs(nu[E].c - ENTRY) < 1e-9
|
||||
assert da[E].c > da[E].o and nu[E].c > nu[E].o # candela de trigger e verde
|
||||
assert min(c.l for c in da[E - 3:E + 1]) == SL, "stopul: cel mai jos minim din 4 candele"
|
||||
assert min(c.l for c in nu[E - 3:E + 1]) == SL
|
||||
assert all(abs(a.c - b.c) < 1e-9 for a, b in zip(da[:E + 2], nu[:E + 2]))
|
||||
|
||||
tp_i = next(i for i in range(E + 1, len(da)) if da[i].h >= TP1)
|
||||
assert abs(da[tp_i].h - 624.34) < 0.05, da[tp_i].h # TP1 atins de fitilul barei
|
||||
assert tp_i == E + 3, tp_i
|
||||
|
||||
# trail: stopul urcă sub minimul fiecărei candele închise; ieși la prima
|
||||
# închidere sub el. Nivelul și bara de ieșire se caută în serie, nu se scriu.
|
||||
iesire_i = next(i for i in range(tp_i + 1, len(da)) if da[i].c < da[i - 1].l)
|
||||
trail = da[iesire_i - 1].l
|
||||
iesire = da[iesire_i].c
|
||||
assert iesire > ENTRY, iesire
|
||||
r_iesire = (iesire - ENTRY) / RISC
|
||||
assert 0.5 < r_iesire < 2.5, r_iesire
|
||||
assert tp_i < iesire_i < len(da), iesire_i # ieșirea vine după TP1, în serie
|
||||
|
||||
# panoul NU: nivelul „50 %" și fitilul care îl taie
|
||||
half = ENTRY + RISC / 2
|
||||
assert abs(half - 624.125) < 1e-9, half
|
||||
sc_i = E + 3
|
||||
assert nu[sc_i].l < half, (nu[sc_i].l, half) # fitilul coboară sub 50 %
|
||||
assert nu[sc_i].c > half, nu[sc_i].c # dar candela închide peste el
|
||||
assert nu[sc_i].h < TP1, "în panoul NU ieșirea vine înainte de TP1"
|
||||
assert max(c.h for c in nu[sc_i + 1:]) > max(c.h for c in da), \
|
||||
"NU: prețul urcă după aceea mai sus decât în panoul DA"
|
||||
ratat = max(c.h for c in nu[sc_i + 1:])
|
||||
r_scos = (half - ENTRY) / RISC
|
||||
r_ratat = (ratat - ENTRY) / RISC
|
||||
|
||||
SCALE, GAP, BODY, LEFT, PW = 300.0, 30, 18, 52, 372
|
||||
pts = [p for c in (da + nu) for p in (c.h, c.l)] + [TP1, SL, half]
|
||||
AER = 0.07 * (max(pts) - min(pts))
|
||||
SPACER = [Level(max(pts) + AER, "", "none", dash=""), Level(min(pts) - AER, "", "none", dash="")]
|
||||
|
||||
|
||||
def panel(cds, levels, arrows):
|
||||
ch = Chart(candles=cds, levels=levels + SPACER, arrows=arrows, scale=SCALE,
|
||||
gap=GAP, body_w=BODY, left=LEFT, width=PW)
|
||||
raw = ch.render()
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', raw)
|
||||
assert m
|
||||
return ch, int(m.group(1)), float(m.group(2)), raw[raw.index(">") + 1:raw.rindex("</svg>")].strip("\n")
|
||||
|
||||
|
||||
ch_da, w_da, h_da, in_da = panel(
|
||||
da,
|
||||
[Level(TP1, f"TP1 {TP1:.2f} (1:1)", BLUE, dash="4 3", label_at="left"),
|
||||
Level(ENTRY, f"intrare {ENTRY:.2f} = break even", GREEN, dash="2 3", label_at="left"),
|
||||
Level(SL, f"SL inițial {SL:.2f}", RED, dash="4 3", label_at="left"),
|
||||
Level(trail, f"trail {trail:.2f}", ORANGE, dash="1 3", label_at="right")],
|
||||
[Arrow(tp_i, f"prima tranșă iese la {TP1:.2f}", where="above", color=BLUE, dy=-6),
|
||||
Arrow(iesire_i, f"închidere sub trail:\nieși cu +{r_iesire:.2f}R", where="below", color=ORANGE)])
|
||||
ch_nu, w_nu, h_nu, in_nu = panel(
|
||||
nu,
|
||||
[Level(TP1, f"TP1 {TP1:.2f} (1:1) — nu mai ajungi la el", BLUE, dash="4 3", label_at="left"),
|
||||
Level(half, f"stop mutat la 50 %: {half:.3f}", RED, dash="4 3", label_at="right"),
|
||||
Level(ENTRY, f"intrare {ENTRY:.2f}", GREEN, dash="2 3", label_at="left"),
|
||||
Level(SL, f"SL inițial {SL:.2f}", RED, dash="4 3", label_at="left")],
|
||||
[Arrow(sc_i, f"fitilul taie 50 %:\nieși cu +{r_scos:.1f}R", where="below", color=RED),
|
||||
Arrow(len(nu) - 2, f"prețul urcă la {ratat:.2f}\n(+{r_ratat:.1f}R) fără tine",
|
||||
where="above", color=GRAY)])
|
||||
|
||||
PH = max(h_da, h_nu)
|
||||
TOP = 52
|
||||
X_DA, X_NU = 8, 8 + w_da + 16
|
||||
RW = max(780, X_NU + w_nu + 8)
|
||||
FOOTER = [
|
||||
f"Ambele panouri pornesc din aceeași intrare {ENTRY:.2f}, cu stopul la {SL:.2f}, "
|
||||
f"deci 1R = {RISC:.2f} $ și TP1 = {TP1:.2f}.",
|
||||
f"DA: prima tranșă iese la TP1. Stopul rămas se duce la break even {ENTRY:.2f} — de acolo "
|
||||
f"trade-ul nu mai poate ieși pe minus —",
|
||||
f"iar restul merge pe trail: stopul urcă sub minimul fiecărei candele închise "
|
||||
f"({trail:.2f} aici)",
|
||||
f"și ieși la prima închidere sub el, {iesire:.2f} = +{r_iesire:.2f}R.",
|
||||
f"NU: stopul mutat la jumătatea drumului, {half:.3f}. Fitilul unei singure bare coboară sub "
|
||||
f"el și te scoate cu +{r_scos:.1f}R,",
|
||||
f"deși bara închide peste nivel, iar prețul urcă după aceea la {ratat:.2f} "
|
||||
f"(+{r_ratat:.1f}R).",
|
||||
"Break even nu are cum să te scoată în pierdere: stă fix la prețul de intrare.",
|
||||
"Ce nu a ieșit nici la țintă, nici pe trail se închide oricum la finalul sesiunii.",
|
||||
"Cifrele intrării, stopului și ale TP1 sunt din curs; barele de după intrare sunt "
|
||||
"construite de mine.",
|
||||
]
|
||||
root_h = TOP + PH + 24 + 17 * len(FOOTER) + 8
|
||||
out = [f'<svg viewBox="0 0 {RW:.0f} {root_h:.0f}" xmlns="http://www.w3.org/2000/svg" '
|
||||
f'style="width:100%;height:auto;font:12px sans-serif">',
|
||||
'<text x="8" y="20" fill="#333" style="font-weight:bold">Pasul 5 — unde muți stopul '
|
||||
'după ce prima tranșă a ieșit</text>',
|
||||
f'<text x="8" y="40" fill="{GREEN}" style="font-weight:bold">DA — break even, apoi '
|
||||
f'trail</text>',
|
||||
f'<text x="{X_NU}" y="40" fill="{RED}" style="font-weight:bold">NU — stopul mutat la '
|
||||
f'„50 %"</text>',
|
||||
f'<svg x="{X_DA}" y="{TOP}" width="{w_da}" height="{PH:.0f}" viewBox="0 0 {w_da} {PH:.0f}">'
|
||||
f'{in_da}</svg>',
|
||||
f'<svg x="{X_NU}" y="{TOP}" width="{w_nu}" height="{PH:.0f}" viewBox="0 0 {w_nu} {PH:.0f}">'
|
||||
f'{in_nu}</svg>']
|
||||
fy = TOP + PH + 24
|
||||
out += [f'<text x="8" y="{fy + 17 * i:.0f}" fill="#555">{t}</text>' for i, t in enumerate(FOOTER)]
|
||||
out.append("</svg>")
|
||||
svg = "\n ".join(out)
|
||||
|
||||
|
||||
def fits(body: str, w: float, h: float, unde: str) -> None:
|
||||
box: list[tuple[float, float, float, str]] = []
|
||||
for mm in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', body):
|
||||
x, y, attrs, txt = float(mm.group(1)), float(mm.group(2)), mm.group(3), mm.group(4)
|
||||
if not txt.strip():
|
||||
continue
|
||||
tw = len(txt) * (6.4 if "bold" in attrs else 5.9)
|
||||
x0 = x - tw if 'anchor="end"' in attrs else (x - tw / 2 if 'anchor="middle"' in attrs else x)
|
||||
assert x0 >= -2 and x0 + tw <= w + 2, f"{unde}: text tăiat: {txt!r}"
|
||||
assert 0 <= y <= h + 2, f"{unde}: text în afara înălțimii: {txt!r}"
|
||||
box.append((x0, x0 + tw, y, txt))
|
||||
for i in range(len(box)):
|
||||
for j in range(i + 1, len(box)):
|
||||
(a0, a1, ay, at), (b0, b1, by, bt) = box[i], box[j]
|
||||
assert not (abs(ay - by) < 13 and a0 < b1 - 4 and b0 < a1 - 4), \
|
||||
f"{unde}: texte suprapuse: {at!r} / {bt!r}"
|
||||
|
||||
|
||||
fits(in_da, w_da, PH, "panoul DA")
|
||||
fits(in_nu, w_nu, PH, "panoul NU")
|
||||
fits("\n".join(out[-len(FOOTER) - 1:]), RW, root_h, "subsolul")
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
assert svg.count("<svg") == 3 and svg.count("</svg>") == 3
|
||||
# aceeași scară în ambele panouri: 1R desenat identic
|
||||
assert abs((ch_da._y(ENTRY) - ch_da._y(TP1)) - (ch_nu._y(ENTRY) - ch_nu._y(TP1))) < 1e-9
|
||||
# 1:1 și vizual: distanța intrare-TP1 == distanța SL-intrare
|
||||
assert abs((ch_da._y(ENTRY) - ch_da._y(TP1)) - (ch_da._y(SL) - ch_da._y(ENTRY))) < 1e-9
|
||||
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: TP1 {TP1:.2f} la bara {tp_i}, trail {trail:.2f}, ieșire {iesire:.2f} "
|
||||
f"(+{r_iesire:.2f}R); NU: 50 % la {half:.3f}, scos cu +{r_scos:.1f}R, ratat {ratat:.2f} "
|
||||
f"(+{r_ratat:.1f}R)")
|
||||
97
gen/strategii/mbp_iesire.svg
Normal file
97
gen/strategii/mbp_iesire.svg
Normal file
@@ -0,0 +1,97 @@
|
||||
<svg viewBox="0 0 819 677" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="8" y="20" fill="#333" style="font-weight:bold">Pasul 5 — unde muți stopul după ce prima tranșă a ieșit</text>
|
||||
<text x="8" y="40" fill="#188a3e" style="font-weight:bold">DA — break even, apoi trail</text>
|
||||
<text x="416" y="40" fill="#c00" style="font-weight:bold">NU — stopul mutat la „50 %"</text>
|
||||
<svg x="8" y="52" width="392" height="440" viewBox="0 0 392 440"> <line x1="24" y1="164.3" x2="378" y2="164.3" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="158.3" fill="#1a5fb4">TP1 624.30 (1:1)</text>
|
||||
<line x1="24" y1="269.3" x2="378" y2="269.3" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 3"/>
|
||||
<text x="30" y="263.3" fill="#188a3e">intrare 623.95 = break even</text>
|
||||
<line x1="24" y1="374.3" x2="378" y2="374.3" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="368.3" fill="#c00">SL inițial 623.60</text>
|
||||
<line x1="24" y1="131.3" x2="378" y2="131.3" stroke="#b06000" stroke-width="1.5" stroke-dasharray="1 3"/>
|
||||
<text x="364" y="125.3" fill="#b06000" text-anchor="end">trail 624.41</text>
|
||||
<line x1="24" y1="34.0" x2="378" y2="34.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="364" y="28.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="24" y1="396.5" x2="378" y2="396.5" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="364" y="390.5" fill="none" text-anchor="end"></text>
|
||||
<line x1="52.0" y1="146.3" x2="52.0" y2="239.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="43.0" y="164.3" width="18" height="45.0" fill="#c00"/>
|
||||
<line x1="82.0" y1="194.3" x2="82.0" y2="308.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="73.0" y="209.3" width="18" height="51.0" fill="#c00"/>
|
||||
<line x1="112.0" y1="242.3" x2="112.0" y2="374.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.0" y="260.3" width="18" height="33.0" fill="#c00"/>
|
||||
<line x1="142.0" y1="254.3" x2="142.0" y2="329.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="133.0" y="269.3" width="18" height="24.0" fill="#188a3e"/>
|
||||
<line x1="172.0" y1="203.3" x2="172.0" y2="287.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="163.0" y="218.3" width="18" height="51.0" fill="#188a3e"/>
|
||||
<line x1="202.0" y1="164.3" x2="202.0" y2="233.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="193.0" y="182.3" width="18" height="36.0" fill="#188a3e"/>
|
||||
<line x1="232.0" y1="140.3" x2="232.0" y2="200.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="223.0" y="152.3" width="18" height="30.0" fill="#188a3e"/>
|
||||
<line x1="262.0" y1="101.3" x2="262.0" y2="170.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="253.0" y="116.3" width="18" height="36.0" fill="#188a3e"/>
|
||||
<line x1="292.0" y1="77.3" x2="292.0" y2="131.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="283.0" y="98.3" width="18" height="18.0" fill="#188a3e"/>
|
||||
<line x1="322.0" y1="86.3" x2="322.0" y2="158.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="313.0" y="98.3" width="18" height="36.0" fill="#c00"/>
|
||||
<line x1="352.0" y1="125.3" x2="352.0" y2="188.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="343.0" y="134.3" width="18" height="36.0" fill="#c00"/>
|
||||
<line x1="232.0" y1="63.3" x2="232.0" y2="128.3" stroke="#1a5fb4" stroke-width="1.8"/>
|
||||
<polygon points="232.0,136.3 228.0,128.3 236.0,128.3" fill="#1a5fb4"/>
|
||||
<text x="232.0" y="58.3" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">prima tranșă iese la 624.30</text>
|
||||
<line x1="322.0" y1="196.3" x2="322.0" y2="170.3" stroke="#b06000" stroke-width="1.8"/>
|
||||
<polygon points="322.0,162.3 326.0,170.3 318.0,170.3" fill="#b06000"/>
|
||||
<text x="322.0" y="210.3" fill="#b06000" text-anchor="middle" style="font-weight:bold">închidere sub trail:</text>
|
||||
<text x="322.0" y="224.3" fill="#b06000" text-anchor="middle" style="font-weight:bold">ieși cu +1.29R</text></svg>
|
||||
<svg x="416" y="52" width="395" height="440" viewBox="0 0 395 440"> <line x1="24" y1="182.0" x2="381" y2="182.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="176.0" fill="#1a5fb4">TP1 624.30 (1:1) — nu mai ajungi la el</text>
|
||||
<line x1="24" y1="234.5" x2="381" y2="234.5" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="367" y="228.5" fill="#c00" text-anchor="end">stop mutat la 50 %: 624.125</text>
|
||||
<line x1="24" y1="287.0" x2="381" y2="287.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 3"/>
|
||||
<text x="30" y="281.0" fill="#188a3e">intrare 623.95</text>
|
||||
<line x1="24" y1="392.0" x2="381" y2="392.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="386.0" fill="#c00">SL inițial 623.60</text>
|
||||
<line x1="24" y1="51.7" x2="381" y2="51.7" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="367" y="45.7" fill="none" text-anchor="end"></text>
|
||||
<line x1="24" y1="414.3" x2="381" y2="414.3" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="367" y="408.3" fill="none" text-anchor="end"></text>
|
||||
<line x1="52.0" y1="164.0" x2="52.0" y2="257.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="43.0" y="182.0" width="18" height="45.0" fill="#c00"/>
|
||||
<line x1="82.0" y1="212.0" x2="82.0" y2="326.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="73.0" y="227.0" width="18" height="51.0" fill="#c00"/>
|
||||
<line x1="112.0" y1="260.0" x2="112.0" y2="392.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.0" y="278.0" width="18" height="33.0" fill="#c00"/>
|
||||
<line x1="142.0" y1="272.0" x2="142.0" y2="347.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="133.0" y="287.0" width="18" height="24.0" fill="#188a3e"/>
|
||||
<line x1="172.0" y1="221.0" x2="172.0" y2="305.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="163.0" y="236.0" width="18" height="51.0" fill="#188a3e"/>
|
||||
<line x1="202.0" y1="182.0" x2="202.0" y2="251.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="193.0" y="200.0" width="18" height="36.0" fill="#188a3e"/>
|
||||
<line x1="232.0" y1="188.0" x2="232.0" y2="278.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="223.0" y="200.0" width="18" height="12.0" fill="#c00"/>
|
||||
<line x1="262.0" y1="143.0" x2="262.0" y2="230.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="253.0" y="158.0" width="18" height="54.0" fill="#188a3e"/>
|
||||
<line x1="292.0" y1="98.0" x2="292.0" y2="173.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="283.0" y="116.0" width="18" height="42.0" fill="#188a3e"/>
|
||||
<line x1="322.0" y1="74.0" x2="322.0" y2="131.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="313.0" y="92.0" width="18" height="24.0" fill="#188a3e"/>
|
||||
<line x1="352.0" y1="80.0" x2="352.0" y2="128.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="343.0" y="92.0" width="18" height="15.0" fill="#c00"/>
|
||||
<line x1="232.0" y1="313.0" x2="232.0" y2="290.0" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="232.0,282.0 236.0,290.0 228.0,290.0" fill="#c00"/>
|
||||
<text x="232.0" y="327.0" fill="#c00" text-anchor="middle" style="font-weight:bold">fitilul taie 50 %:</text>
|
||||
<text x="232.0" y="341.0" fill="#c00" text-anchor="middle" style="font-weight:bold">ieși cu +0.5R</text>
|
||||
<line x1="322.0" y1="44.0" x2="322.0" y2="62.0" stroke="#777" stroke-width="1.8"/>
|
||||
<polygon points="322.0,70.0 318.0,62.0 326.0,62.0" fill="#777"/>
|
||||
<text x="322.0" y="25.0" fill="#777" text-anchor="middle" style="font-weight:bold">prețul urcă la 624.66</text>
|
||||
<text x="322.0" y="39.0" fill="#777" text-anchor="middle" style="font-weight:bold">(+2.0R) fără tine</text></svg>
|
||||
<text x="8" y="516" fill="#555">Ambele panouri pornesc din aceeași intrare 623.95, cu stopul la 623.60, deci 1R = 0.35 $ și TP1 = 624.30.</text>
|
||||
<text x="8" y="533" fill="#555">DA: prima tranșă iese la TP1. Stopul rămas se duce la break even 623.95 — de acolo trade-ul nu mai poate ieși pe minus —</text>
|
||||
<text x="8" y="550" fill="#555">iar restul merge pe trail: stopul urcă sub minimul fiecărei candele închise (624.41 aici)</text>
|
||||
<text x="8" y="567" fill="#555">și ieși la prima închidere sub el, 624.40 = +1.29R.</text>
|
||||
<text x="8" y="584" fill="#555">NU: stopul mutat la jumătatea drumului, 624.125. Fitilul unei singure bare coboară sub el și te scoate cu +0.5R,</text>
|
||||
<text x="8" y="601" fill="#555">deși bara închide peste nivel, iar prețul urcă după aceea la 624.66 (+2.0R).</text>
|
||||
<text x="8" y="618" fill="#555">Break even nu are cum să te scoată în pierdere: stă fix la prețul de intrare.</text>
|
||||
<text x="8" y="635" fill="#555">Ce nu a ieșit nici la țintă, nici pe trail se închide oricum la finalul sesiunii.</text>
|
||||
<text x="8" y="652" fill="#555">Cifrele intrării, stopului și ale TP1 sunt din curs; barele de după intrare sunt construite de mine.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
154
gen/strategii/mbp_stop.py
Normal file
154
gen/strategii/mbp_stop.py
Normal file
@@ -0,0 +1,154 @@
|
||||
"""Diagrama de stop MBP (marcajul `stop`), Pasul 4.
|
||||
|
||||
Fereastra de 4 candele din care iese stopul, mărită: candela de trigger și
|
||||
cele 3 dinaintea ei (deci candela M intră în fereastră). Cel mai jos minim
|
||||
din ele e 623.60 — low-ul lui M — iar stopul stă fix acolo, ca în exemplul
|
||||
din curs (fără marjă adăugată; e aceeași regulă în toate diagramele
|
||||
capitolului).
|
||||
|
||||
Pe desen: zona punctată peste exact cele 4 candele (numărul din etichetă e
|
||||
numărat, nu scris de mână), linia de intrare la 623.95, linia de stop la
|
||||
623.60 și măsura 1R = 0.35 $ între ele, cu care se calculează mărimea
|
||||
poziției.
|
||||
|
||||
Cifrele din curs (`05_MODUL_4/03_strategia_6_mbp_mbappe.md`): M close 623.87
|
||||
sub banda 623.90, trigger → intrare 623.95, SL 623.60 = lowest low din 4
|
||||
candele, trigger inclus. Structura barelor e construită de mine.
|
||||
|
||||
Rulează: python3 gen/strategii/mbp_stop.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Arrow, Chart, Level, Zone, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
RED, GREEN, BLUE, GRAY = "#c00", "#188a3e", "#1a5fb4", "#777"
|
||||
|
||||
BAND = 623.90
|
||||
M = 11
|
||||
CLOSES = [624.90, 624.75, 624.60, 624.68, 624.50, 624.38, 624.22, 624.30,
|
||||
624.12, 624.05, 623.98, 623.87, 623.95, 624.08, 624.02, 624.15]
|
||||
WICKS = ([(0.06, 0.10)] * 9
|
||||
+ [(0.05, 0.20), # 9: low 623.85
|
||||
(0.05, 0.16), # 10: low 623.82
|
||||
(0.06, 0.27), # 11 = M: low 623.60
|
||||
(0.05, 0.12), # 12 = trigger: low 623.75
|
||||
(0.06, 0.08), (0.06, 0.08), (0.06, 0.08)])
|
||||
assert len(CLOSES) == len(WICKS) == 16
|
||||
|
||||
cds = from_closes(CLOSES, wicks=WICKS, start=625.02)
|
||||
TRIG = M + 1
|
||||
I0 = TRIG - 3 # prima candelă din fereastră
|
||||
|
||||
# ── ce afirmă textul, verificat pe serie ───────────────────────────────────
|
||||
entry = cds[TRIG].c
|
||||
assert abs(entry - 623.95) < 1e-9, entry
|
||||
assert cds[TRIG].c > cds[TRIG].o # trigger verde
|
||||
assert abs(cds[M].c - 623.87) < 1e-9 and cds[M].c < BAND
|
||||
assert cds[M].c < cds[M].o # M roșie
|
||||
|
||||
fereastra = cds[I0:TRIG + 1]
|
||||
N_F = len(fereastra)
|
||||
assert N_F == 4, N_F
|
||||
sl = min(c.l for c in fereastra)
|
||||
assert abs(sl - 623.60) < 1e-9, sl
|
||||
assert sl == cds[M].l, "cel mai jos minim din fereastră e low-ul lui M"
|
||||
assert all(c.l > sl for c in fereastra if c is not cds[M]), \
|
||||
"nicio altă candelă din fereastră nu coboară sub low-ul lui M"
|
||||
|
||||
risc = entry - sl
|
||||
assert abs(risc - 0.35) < 1e-9, risc
|
||||
# stopul nu are marjă adăugată: e fix pe minim, ca în cifra cursului
|
||||
assert sl == min(c.l for c in fereastra)
|
||||
# candela dinaintea ferestrei are minim peste stop — altfel fereastra de 4 n-ar conta
|
||||
assert cds[I0 - 1].l > sl, cds[I0 - 1].l
|
||||
|
||||
# mărimea poziției, calculată din risc (cifrele contului sunt alegerea mea)
|
||||
CONT, RISC_PCT = 50_000.0, 0.5
|
||||
risc_bani = CONT * RISC_PCT / 100
|
||||
unitati = int(risc_bani / risc)
|
||||
notional = unitati * entry
|
||||
assert unitati == 714, unitati
|
||||
assert abs(notional / CONT - 8.9) < 0.1, notional / CONT
|
||||
|
||||
SCALE, GAP, BODY, LEFT, W = 300.0, 30, 18, 64, 756
|
||||
AER = 0.07 * (max(c.h for c in cds) - min(c.l for c in cds))
|
||||
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[Level(BAND, f"banda inferioară {BAND:.2f}", BLUE, dash="4 3", label_at="left"),
|
||||
Level(entry, f"intrare {entry:.2f} = close-ul trigger-ului", GREEN, dash="2 3",
|
||||
label_at="right"),
|
||||
Level(sl, f"SL {sl:.2f}", RED, dash="4 3", label_at="left"),
|
||||
Level(max(c.h for c in cds) + AER, "", "none", dash=""),
|
||||
Level(sl - AER, "", "none", dash="")],
|
||||
zones=[Zone(I0, TRIG, f"{N_F} candele: trigger-ul și cele 3 dinaintea lui", RED)],
|
||||
arrows=[Arrow(M, f"cel mai jos minim din fereastră: {sl:.2f}\nsub el, ideea de epuizare "
|
||||
f"e moartă — acolo stă stopul", where="below", color=RED),
|
||||
Arrow(TRIG, "trigger: aici intri, la close", where="above", color=GREEN, dx=52, dy=-24)],
|
||||
scale=SCALE, gap=GAP, body_w=BODY, left=LEFT, width=W,
|
||||
title="SPY — 15 min: de unde iese stopul (fereastra de 4 candele)")
|
||||
svg = ch.render()
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
|
||||
assert m
|
||||
W2, H = int(m.group(1)), float(m.group(2))
|
||||
|
||||
# ── măsura 1R între intrare și stop, lipită de liniile ei ──────────────────
|
||||
xr = ch._cx(len(cds) - 1) + 26
|
||||
y_e, y_s = ch._y(entry), ch._y(sl)
|
||||
assert y_s > y_e
|
||||
masura = [
|
||||
f'<line x1="{xr:.1f}" y1="{y_e:.1f}" x2="{xr:.1f}" y2="{y_s:.1f}" stroke="{GRAY}" '
|
||||
f'stroke-width="1.2"/>',
|
||||
f'<line x1="{xr - 5:.1f}" y1="{y_e:.1f}" x2="{xr + 5:.1f}" y2="{y_e:.1f}" stroke="{GRAY}"/>',
|
||||
f'<line x1="{xr - 5:.1f}" y1="{y_s:.1f}" x2="{xr + 5:.1f}" y2="{y_s:.1f}" stroke="{GRAY}"/>',
|
||||
f'<text x="{xr + 8:.1f}" y="{(y_e + y_s) / 2 + 4:.1f}" fill="{GRAY}" '
|
||||
f'style="font-weight:bold">1R = {risc:.2f} $</text>',
|
||||
]
|
||||
W3 = max(W2, xr + 8 + len(f"1R = {risc:.2f} $") * 6.4 + 8)
|
||||
|
||||
LEG = [
|
||||
f"Numeri {N_F} candele înapoi începând cu cea de trigger (trigger, M și cele două "
|
||||
f"dinaintea lui M) și iei cel mai jos minim:",
|
||||
f"{sl:.2f}. Acolo pui stopul, fix pe minim, fără marjă adăugată — asta e cifra cursului.",
|
||||
f"Riscul pe unitate iese din desen: {entry:.2f} − {sl:.2f} = {risc:.2f} $ = 1R. Din el "
|
||||
f"calculezi câte unități cumperi.",
|
||||
f"Completare proprie (cifrele contului sunt ale mele): cont {CONT:,.0f} $, risc "
|
||||
f"{RISC_PCT} % = {risc_bani:.0f} $ → {risc_bani:.0f} / {risc:.2f} = {unitati} unități,".replace(",", "."),
|
||||
f"adică un notional de {notional:,.0f} $ ≈ {notional / CONT:.1f}× contul. "
|
||||
f"Un stop strâns nu înseamnă un trade mic.".replace(",", "."),
|
||||
]
|
||||
W3 = max(W3, max(len(t) for t in LEG) * 5.9 + 32)
|
||||
ly = H + 6
|
||||
svg = svg.replace("\n</svg>", "\n " + "\n ".join(
|
||||
masura + [f'<text x="24" y="{ly + 17 * i:.0f}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(LEG)]) + "\n</svg>")
|
||||
H2 = ly + 17 * len(LEG) + 6
|
||||
svg = re.sub(r'(viewBox="0 0 )\d+ [0-9.]+(")', lambda mm: mm.group(1) + f"{W3:.0f} {H2:.0f}" + mm.group(2),
|
||||
svg, count=1)
|
||||
|
||||
# ── verificări pe SVG-ul final ─────────────────────────────────────────────
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
assert f"{N_F} candele" in svg and N_F == TRIG - I0 + 1
|
||||
# liniile nu cad pe marginea cadrului
|
||||
assert ch._y(sl) < H2 - 20 and ch._y(max(c.h for c in cds)) > 14
|
||||
box = []
|
||||
for mm in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', svg):
|
||||
x, y, attrs, txt = float(mm.group(1)), float(mm.group(2)), mm.group(3), mm.group(4)
|
||||
tw = len(txt) * (6.4 if "bold" in attrs else 5.9)
|
||||
x0 = x - tw if 'anchor="end"' in attrs else (x - tw / 2 if 'anchor="middle"' in attrs else x)
|
||||
assert x0 >= -2 and x0 + tw <= W3 + 2, f"text tăiat: {txt!r}"
|
||||
assert 0 <= y <= H2 + 2, f"text în afara înălțimii: {txt!r}"
|
||||
box.append((x0, x0 + tw, y, txt))
|
||||
for i in range(len(box)):
|
||||
for j in range(i + 1, len(box)):
|
||||
(a0, a1, ay, at), (b0, b1, by, bt) = box[i], box[j]
|
||||
assert not (abs(ay - by) < 13 and a0 < b1 - 4 and b0 < a1 - 4), \
|
||||
f"texte suprapuse: {at!r} / {bt!r}"
|
||||
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: fereastră {N_F} candele [{I0}..{TRIG}], SL {sl:.2f} (= low M), "
|
||||
f"intrare {entry:.2f}, 1R {risc:.2f}, {unitati} unități, notional {notional:,.0f}")
|
||||
63
gen/strategii/mbp_stop.svg
Normal file
63
gen/strategii/mbp_stop.svg
Normal file
@@ -0,0 +1,63 @@
|
||||
<svg viewBox="0 0 756 721" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">SPY — 15 min: de unde iese stopul (fereastra de 4 candele)</text>
|
||||
<rect x="320.0" y="352.1" width="118.0" height="187.0" fill="#c00" fill-opacity="0.08" stroke="#c00" stroke-width="1"/>
|
||||
<text x="320.0" y="339.1" fill="#c00" style="font-weight:bold">4 candele: trigger-ul și cele 3 dinaintea lui</text>
|
||||
<line x1="24" y1="441.1" x2="742" y2="441.1" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="435.1" fill="#1a5fb4">banda inferioară 623.90</text>
|
||||
<line x1="24" y1="426.1" x2="742" y2="426.1" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 3"/>
|
||||
<text x="728" y="420.1" fill="#188a3e" text-anchor="end">intrare 623.95 = close-ul trigger-ului</text>
|
||||
<line x1="24" y1="531.1" x2="742" y2="531.1" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="525.1" fill="#c00">SL 623.60</text>
|
||||
<line x1="24" y1="56.0" x2="742" y2="56.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="728" y="50.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="24" y1="562.2" x2="742" y2="562.2" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="728" y="556.2" fill="none" text-anchor="end"></text>
|
||||
<line x1="64.0" y1="87.1" x2="64.0" y2="171.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="55.0" y="105.1" width="18" height="36.0" fill="#c00"/>
|
||||
<line x1="94.0" y1="123.1" x2="94.0" y2="216.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="85.0" y="141.1" width="18" height="45.0" fill="#c00"/>
|
||||
<line x1="124.0" y1="168.1" x2="124.0" y2="261.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="115.0" y="186.1" width="18" height="45.0" fill="#c00"/>
|
||||
<line x1="154.0" y1="189.1" x2="154.0" y2="261.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="145.0" y="207.1" width="18" height="24.0" fill="#188a3e"/>
|
||||
<line x1="184.0" y1="189.1" x2="184.0" y2="291.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="175.0" y="207.1" width="18" height="54.0" fill="#c00"/>
|
||||
<line x1="214.0" y1="243.1" x2="214.0" y2="327.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="205.0" y="261.1" width="18" height="36.0" fill="#c00"/>
|
||||
<line x1="244.0" y1="279.1" x2="244.0" y2="375.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="235.0" y="297.1" width="18" height="48.0" fill="#c00"/>
|
||||
<line x1="274.0" y1="303.1" x2="274.0" y2="375.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="265.0" y="321.1" width="18" height="24.0" fill="#188a3e"/>
|
||||
<line x1="304.0" y1="303.1" x2="304.0" y2="405.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="295.0" y="321.1" width="18" height="54.0" fill="#c00"/>
|
||||
<line x1="334.0" y1="360.1" x2="334.0" y2="456.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="325.0" y="375.1" width="18" height="21.0" fill="#c00"/>
|
||||
<line x1="364.0" y1="381.1" x2="364.0" y2="465.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="355.0" y="396.1" width="18" height="21.0" fill="#c00"/>
|
||||
<line x1="394.0" y1="399.1" x2="394.0" y2="531.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="385.0" y="417.1" width="18" height="33.0" fill="#c00"/>
|
||||
<line x1="424.0" y1="411.1" x2="424.0" y2="486.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="415.0" y="426.1" width="18" height="24.0" fill="#188a3e"/>
|
||||
<line x1="454.0" y1="369.1" x2="454.0" y2="450.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="445.0" y="387.1" width="18" height="39.0" fill="#188a3e"/>
|
||||
<line x1="484.0" y1="369.1" x2="484.0" y2="429.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="475.0" y="387.1" width="18" height="18.0" fill="#c00"/>
|
||||
<line x1="514.0" y1="348.1" x2="514.0" y2="429.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="505.0" y="366.1" width="18" height="39.0" fill="#188a3e"/>
|
||||
<line x1="394.0" y1="561.1" x2="394.0" y2="543.1" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="394.0,535.1 398.0,543.1 390.0,543.1" fill="#c00"/>
|
||||
<text x="394.0" y="575.1" fill="#c00" text-anchor="middle" style="font-weight:bold">cel mai jos minim din fereastră: 623.60</text>
|
||||
<text x="394.0" y="589.1" fill="#c00" text-anchor="middle" style="font-weight:bold">sub el, ideea de epuizare e moartă — acolo stă stopul</text>
|
||||
<line x1="476.0" y1="316.1" x2="428.0" y2="400.1" stroke="#188a3e" stroke-width="1.8"/>
|
||||
<polygon points="424.0,407.1 424.5,398.1 431.4,402.1" fill="#188a3e"/>
|
||||
<text x="476.0" y="311.1" fill="#188a3e" text-anchor="middle" style="font-weight:bold">trigger: aici intri, la close</text>
|
||||
<line x1="540.0" y1="404.1" x2="540.0" y2="509.1" stroke="#777" stroke-width="1.2"/>
|
||||
<line x1="535.0" y1="404.1" x2="545.0" y2="404.1" stroke="#777"/>
|
||||
<line x1="535.0" y1="509.1" x2="545.0" y2="509.1" stroke="#777"/>
|
||||
<text x="548.0" y="460.6" fill="#777" style="font-weight:bold">1R = 0.35 $</text>
|
||||
<text x="24" y="630" fill="#555">Numeri 4 candele înapoi începând cu cea de trigger (trigger, M și cele două dinaintea lui M) și iei cel mai jos minim:</text>
|
||||
<text x="24" y="647" fill="#555">623.60. Acolo pui stopul, fix pe minim, fără marjă adăugată — asta e cifra cursului.</text>
|
||||
<text x="24" y="664" fill="#555">Riscul pe unitate iese din desen: 623.95 − 623.60 = 0.35 $ = 1R. Din el calculezi câte unități cumperi.</text>
|
||||
<text x="24" y="681" fill="#555">Completare proprie (cifrele contului sunt ale mele): cont 50.000 $. risc 0.5 % = 250 $ → 250 / 0.35 = 714 unități.</text>
|
||||
<text x="24" y="698" fill="#555">adică un notional de 445.500 $ ≈ 8.9× contul. Un stop strâns nu înseamnă un trade mic.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
165
gen/strategii/mbp_trigger.py
Normal file
165
gen/strategii/mbp_trigger.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""Diagrama de trigger MBP (marcajul `trigger`), Pasul 3.
|
||||
|
||||
Două panouri de 15 minute care pornesc din exact aceeași candelă M (close
|
||||
623.87 sub banda 623.90, low 623.60 — cifrele exemplului din curs), și se
|
||||
despart după ea:
|
||||
|
||||
- DA: prima candelă de după M e verde și închide la 623.95 (Buy Momentum
|
||||
din curs). Acolo intri, la close-ul ei.
|
||||
- NU: cele trei candele de după M rămân roșii, fără nicio candelă de
|
||||
moment. Nu intri. Cine ar fi cumpărat „că oricum era oversold" ar fi
|
||||
avut stopul la 623.60 și l-ar fi văzut rupt două bare mai târziu.
|
||||
|
||||
Zona punctată acoperă exact ferestrele în care aștepți trigger-ul, iar
|
||||
numărul din eticheta ei e numărul de bare pe care le acoperă, calculat.
|
||||
|
||||
Rulează: python3 gen/strategii/mbp_trigger.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Arrow, Chart, Level, Zone, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
RED, GREEN, BLUE, GRAY = "#c00", "#188a3e", "#1a5fb4", "#777"
|
||||
|
||||
BAND = 623.90 # banda inferioară din exemplul cursului
|
||||
M = 10 # indexul candelei M, în ambele panouri
|
||||
COMUN = [624.95, 624.80, 624.62, 624.70, 624.50, 624.36, 624.20, 624.28,
|
||||
624.10, 624.02, 623.87] # 0..10, ultima = M
|
||||
W_COMUN = [(0.06, 0.10)] * 10 + [(0.06, 0.27)] # M: low 623.60
|
||||
|
||||
DA = COMUN + [623.95, 624.12, 624.24] # 11 = trigger verde
|
||||
W_DA = W_COMUN + [(0.05, 0.12), (0.06, 0.08), (0.06, 0.08)]
|
||||
NU = COMUN + [623.80, 623.62, 623.45] # trei bare roșii, fără trigger
|
||||
W_NU = W_COMUN + [(0.04, 0.10), (0.04, 0.10), (0.04, 0.10)]
|
||||
|
||||
da = from_closes(DA, wicks=W_DA, start=625.10)
|
||||
nu = from_closes(NU, wicks=W_NU, start=625.10)
|
||||
|
||||
# ── ce afirmă textul, verificat pe serie ───────────────────────────────────
|
||||
assert len(da) == len(nu) == 14
|
||||
assert all(abs(a.c - b.c) < 1e-9 for a, b in zip(da[:M + 1], nu[:M + 1])), \
|
||||
"panourile trebuie să pornească din aceeași candelă M"
|
||||
assert abs(da[M].c - 623.87) < 1e-9 and da[M].c < BAND # M închide sub bandă
|
||||
assert da[M].c < da[M].o # M e roșie
|
||||
assert abs(da[M].l - 623.60) < 1e-9, da[M].l # low-ul lui M = viitorul stop
|
||||
|
||||
TRIG = M + 1
|
||||
entry = da[TRIG].c
|
||||
assert abs(entry - 623.95) < 1e-9, entry
|
||||
assert da[TRIG].c > da[TRIG].o, "candela de trigger trebuie să fie verde"
|
||||
assert entry > da[M].c, "trigger-ul închide peste close-ul lui M"
|
||||
|
||||
sl = min(c.l for c in da[TRIG - 3:TRIG + 1]) # 4 candele, trigger inclus
|
||||
assert abs(sl - 623.60) < 1e-9, sl
|
||||
assert sl == da[M].l
|
||||
|
||||
# panoul NU: nicio candelă verde după M, iar prețul rupe nivelul stopului
|
||||
fereastra_nu = nu[M + 1:]
|
||||
assert all(c.c < c.o for c in fereastra_nu), "în panoul NU nu are voie să apară o candelă verde"
|
||||
assert min(c.l for c in fereastra_nu) < sl, "NU: prețul trebuie să rupă 623.60"
|
||||
rupere = min(c.l for c in fereastra_nu)
|
||||
n_nu = len(fereastra_nu)
|
||||
assert n_nu == 3
|
||||
pierdere = sl - rupere
|
||||
|
||||
SCALE, GAP, BODY, LEFT, PW = 190.0, 22, 14, 44, 372
|
||||
|
||||
pts_all = [p for c in (da + nu) for p in (c.h, c.l)] + [BAND]
|
||||
P_LO, P_HI = min(pts_all), max(pts_all)
|
||||
AER = 0.07 * (P_HI - P_LO)
|
||||
SPACER = [Level(P_HI + AER, "", "none", dash=""), Level(P_LO - AER, "", "none", dash="")]
|
||||
|
||||
|
||||
def panel(cds, levels, zones, arrows):
|
||||
ch = Chart(candles=cds, levels=levels + SPACER, zones=zones, arrows=arrows,
|
||||
scale=SCALE, gap=GAP, body_w=BODY, left=LEFT, width=PW)
|
||||
raw = ch.render()
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', raw)
|
||||
assert m
|
||||
w, h = int(m.group(1)), float(m.group(2))
|
||||
return ch, w, h, raw[raw.index(">") + 1:raw.rindex("</svg>")].strip("\n")
|
||||
|
||||
|
||||
ch_da, w_da, h_da, in_da = panel(
|
||||
da,
|
||||
[Level(BAND, f"bandă inferioară {BAND:.2f}", BLUE, dash="4 3", label_at="left"),
|
||||
Level(sl, f"SL {sl:.2f}", RED, dash="4 3", label_at="left")],
|
||||
[Zone(TRIG, TRIG, "", GREEN)],
|
||||
[Arrow(TRIG, f"trigger: candelă verde,\nintri la close {entry:.2f}", where="above", color=GREEN),
|
||||
Arrow(M, f"M: close {da[M].c:.2f},\nsub bandă", where="below", color=RED)])
|
||||
ch_nu, w_nu, h_nu, in_nu = panel(
|
||||
nu,
|
||||
[Level(BAND, f"bandă inferioară {BAND:.2f}", BLUE, dash="4 3", label_at="left"),
|
||||
Level(sl, f"stopul ar fi fost {sl:.2f}", RED, dash="4 3", label_at="left")],
|
||||
[Zone(M + 1, M + n_nu, f"{n_nu} candele fără trigger", RED)],
|
||||
[Arrow(len(nu) - 1, f"aici s-ar fi rupt stopul,\ncu {pierdere:.2f} $ sub {sl:.2f}",
|
||||
where="below", color=RED)])
|
||||
|
||||
PH = max(h_da, h_nu)
|
||||
add_da: list[str] = []
|
||||
add_nu: list[str] = []
|
||||
|
||||
FOOTER = [
|
||||
f"Aceeași candelă M în ambele panouri: close {da[M].c:.2f} sub banda {BAND:.2f}, "
|
||||
f"low {sl:.2f}. Ce urmează după ea decide.",
|
||||
f"DA: candela {TRIG - M} de după M e verde și închide la {entry:.2f} — ăsta e trigger-ul, "
|
||||
f"iar close-ul ei e prețul de intrare.",
|
||||
f"NU: {n_nu} candele roșii la rând, niciun trigger. Nu intri. Prețul coboară până la "
|
||||
f"{rupere:.2f}, cu {pierdere:.2f} $ sub {sl:.2f},",
|
||||
"adică exact sub stopul pe care l-ai fi avut dacă intrai „pentru că oricum era oversold\".",
|
||||
"Condiția M (bandă + TICUS) nu e un ordin de cumpărare: e doar permisiunea de a aștepta trigger-ul.",
|
||||
"Cifrele lui M și ale trigger-ului (623.87 / 623.90 / 623.60 / 623.95) sunt din curs; "
|
||||
"restul barelor sunt construite de mine.",
|
||||
]
|
||||
TOP = 52
|
||||
X_DA, X_NU = 8, 8 + w_da + 16
|
||||
RW = max(780, X_NU + w_nu + 8)
|
||||
root_h = TOP + PH + 24 + 17 * len(FOOTER) + 8
|
||||
out = [f'<svg viewBox="0 0 {RW:.0f} {root_h:.0f}" xmlns="http://www.w3.org/2000/svg" '
|
||||
f'style="width:100%;height:auto;font:12px sans-serif">',
|
||||
'<text x="8" y="20" fill="#333" style="font-weight:bold">Pasul 3 — condiția M deschide '
|
||||
'fereastra; trigger-ul e cel care te bagă în trade</text>',
|
||||
f'<text x="8" y="40" fill="{GREEN}" style="font-weight:bold">DA — candelă de moment '
|
||||
f'după M</text>',
|
||||
f'<text x="{X_NU}" y="40" fill="{RED}" style="font-weight:bold">NU — M valid, dar fără '
|
||||
f'trigger</text>',
|
||||
f'<svg x="{X_DA}" y="{TOP}" width="{w_da}" height="{PH:.0f}" viewBox="0 0 {w_da} {PH:.0f}">'
|
||||
f'{in_da}\n {chr(10).join(add_da)}</svg>',
|
||||
f'<svg x="{X_NU}" y="{TOP}" width="{w_nu}" height="{PH:.0f}" viewBox="0 0 {w_nu} {PH:.0f}">'
|
||||
f'{in_nu}\n {chr(10).join(add_nu)}</svg>']
|
||||
fy = TOP + PH + 24
|
||||
out += [f'<text x="8" y="{fy + 17 * i:.0f}" fill="#555">{t}</text>' for i, t in enumerate(FOOTER)]
|
||||
out.append("</svg>")
|
||||
svg = "\n ".join(out)
|
||||
|
||||
|
||||
def fits(body: str, w: float, h: float, unde: str) -> None:
|
||||
box: list[tuple[float, float, float, str]] = []
|
||||
for mm in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', body):
|
||||
x, y, attrs, txt = float(mm.group(1)), float(mm.group(2)), mm.group(3), mm.group(4)
|
||||
if not txt.strip():
|
||||
continue
|
||||
tw = len(re.sub(r"<[^>]+>", "", txt)) * (6.4 if "bold" in attrs else 5.9)
|
||||
x0 = x - tw if 'anchor="end"' in attrs else (x - tw / 2 if 'anchor="middle"' in attrs else x)
|
||||
assert x0 >= -2 and x0 + tw <= w + 2, f"{unde}: text tăiat: {txt!r}"
|
||||
assert 0 <= y <= h + 2, f"{unde}: text în afara înălțimii: {txt!r}"
|
||||
box.append((x0, x0 + tw, y, txt))
|
||||
for i in range(len(box)):
|
||||
for j in range(i + 1, len(box)):
|
||||
(a0, a1, ay, at), (b0, b1, by, bt) = box[i], box[j]
|
||||
assert not (abs(ay - by) < 13 and a0 < b1 - 4 and b0 < a1 - 4), \
|
||||
f"{unde}: texte suprapuse: {at!r} / {bt!r}"
|
||||
|
||||
|
||||
fits(in_da + "\n" + "\n".join(add_da), w_da, PH, "panoul DA")
|
||||
fits(in_nu + "\n" + "\n".join(add_nu), w_nu, PH, "panoul NU")
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
assert svg.count("<svg") == 3 and svg.count("</svg>") == 3
|
||||
assert f"{n_nu} candele fără trigger" in svg and n_nu == len(fereastra_nu)
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: M {da[M].c:.2f} (low {sl:.2f}), trigger {entry:.2f}; "
|
||||
f"NU {n_nu} bare roșii, rupere {rupere:.2f} ({pierdere:.2f} $ sub stop)")
|
||||
100
gen/strategii/mbp_trigger.svg
Normal file
100
gen/strategii/mbp_trigger.svg
Normal file
@@ -0,0 +1,100 @@
|
||||
<svg viewBox="0 0 822 681" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="8" y="20" fill="#333" style="font-weight:bold">Pasul 3 — condiția M deschide fereastra; trigger-ul e cel care te bagă în trade</text>
|
||||
<text x="8" y="40" fill="#188a3e" style="font-weight:bold">DA — candelă de moment după M</text>
|
||||
<text x="396" y="40" fill="#c00" style="font-weight:bold">NU — M valid, dar fără trigger</text>
|
||||
<svg x="8" y="52" width="372" height="495" viewBox="0 0 372 495"> <rect x="274.0" y="270.5" width="24.0" height="63.5" fill="#188a3e" fill-opacity="0.08" stroke="#188a3e" stroke-width="1"/>
|
||||
<line x1="24" y1="297.5" x2="358" y2="297.5" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="291.5" fill="#1a5fb4">bandă inferioară 623.90</text>
|
||||
<line x1="24" y1="354.5" x2="358" y2="354.5" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="348.5" fill="#c00">SL 623.60</text>
|
||||
<line x1="24" y1="34.0" x2="358" y2="34.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="344" y="28.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="24" y1="426.0" x2="358" y2="426.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="344" y="420.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="44.0" y1="58.1" x2="44.0" y2="117.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="37.0" y="69.5" width="14" height="28.5" fill="#c00"/>
|
||||
<line x1="66.0" y1="86.6" x2="66.0" y2="145.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="59.0" y="98.0" width="14" height="28.5" fill="#c00"/>
|
||||
<line x1="88.0" y1="115.1" x2="88.0" y2="179.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="81.0" y="126.5" width="14" height="34.2" fill="#c00"/>
|
||||
<line x1="110.0" y1="134.1" x2="110.0" y2="179.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.0" y="145.5" width="14" height="15.2" fill="#188a3e"/>
|
||||
<line x1="132.0" y1="134.1" x2="132.0" y2="202.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="125.0" y="145.5" width="14" height="38.0" fill="#c00"/>
|
||||
<line x1="154.0" y1="172.1" x2="154.0" y2="229.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="147.0" y="183.5" width="14" height="26.6" fill="#c00"/>
|
||||
<line x1="176.0" y1="198.7" x2="176.0" y2="259.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="169.0" y="210.1" width="14" height="30.4" fill="#c00"/>
|
||||
<line x1="198.0" y1="213.9" x2="198.0" y2="259.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="191.0" y="225.3" width="14" height="15.2" fill="#188a3e"/>
|
||||
<line x1="220.0" y1="213.9" x2="220.0" y2="278.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="213.0" y="225.3" width="14" height="34.2" fill="#c00"/>
|
||||
<line x1="242.0" y1="248.1" x2="242.0" y2="293.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="235.0" y="259.5" width="14" height="15.2" fill="#c00"/>
|
||||
<line x1="264.0" y1="263.3" x2="264.0" y2="354.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="257.0" y="274.7" width="14" height="28.5" fill="#c00"/>
|
||||
<line x1="286.0" y1="278.5" x2="286.0" y2="326.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.0" y="288.0" width="14" height="15.2" fill="#188a3e"/>
|
||||
<line x1="308.0" y1="244.3" x2="308.0" y2="303.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="301.0" y="255.7" width="14" height="32.3" fill="#188a3e"/>
|
||||
<line x1="330.0" y1="221.5" x2="330.0" y2="270.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="323.0" y="232.9" width="14" height="22.8" fill="#188a3e"/>
|
||||
<line x1="286.0" y1="205.9" x2="286.0" y2="266.5" stroke="#188a3e" stroke-width="1.8"/>
|
||||
<polygon points="286.0,274.5 282.0,266.5 290.0,266.5" fill="#188a3e"/>
|
||||
<text x="286.0" y="186.9" fill="#188a3e" text-anchor="middle" style="font-weight:bold">trigger: candelă verde,</text>
|
||||
<text x="286.0" y="200.9" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intri la close 623.95</text>
|
||||
<line x1="264.0" y1="384.5" x2="264.0" y2="366.5" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="264.0,358.5 268.0,366.5 260.0,366.5" fill="#c00"/>
|
||||
<text x="264.0" y="398.5" fill="#c00" text-anchor="middle" style="font-weight:bold">M: close 623.87,</text>
|
||||
<text x="264.0" y="412.5" fill="#c00" text-anchor="middle" style="font-weight:bold">sub bandă</text>
|
||||
</svg>
|
||||
<svg x="396" y="52" width="418" height="495" viewBox="0 0 418 495"> <rect x="274.0" y="287.6" width="68.0" height="122.4" fill="#c00" fill-opacity="0.08" stroke="#c00" stroke-width="1"/>
|
||||
<text x="274.0" y="282.6" fill="#c00" style="font-weight:bold">3 candele fără trigger</text>
|
||||
<line x1="24" y1="297.5" x2="404" y2="297.5" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="291.5" fill="#1a5fb4">bandă inferioară 623.90</text>
|
||||
<line x1="24" y1="354.5" x2="404" y2="354.5" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="348.5" fill="#c00">stopul ar fi fost 623.60</text>
|
||||
<line x1="24" y1="34.0" x2="404" y2="34.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="390" y="28.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="24" y1="426.0" x2="404" y2="426.0" stroke="none" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="390" y="420.0" fill="none" text-anchor="end"></text>
|
||||
<line x1="44.0" y1="58.1" x2="44.0" y2="117.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="37.0" y="69.5" width="14" height="28.5" fill="#c00"/>
|
||||
<line x1="66.0" y1="86.6" x2="66.0" y2="145.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="59.0" y="98.0" width="14" height="28.5" fill="#c00"/>
|
||||
<line x1="88.0" y1="115.1" x2="88.0" y2="179.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="81.0" y="126.5" width="14" height="34.2" fill="#c00"/>
|
||||
<line x1="110.0" y1="134.1" x2="110.0" y2="179.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.0" y="145.5" width="14" height="15.2" fill="#188a3e"/>
|
||||
<line x1="132.0" y1="134.1" x2="132.0" y2="202.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="125.0" y="145.5" width="14" height="38.0" fill="#c00"/>
|
||||
<line x1="154.0" y1="172.1" x2="154.0" y2="229.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="147.0" y="183.5" width="14" height="26.6" fill="#c00"/>
|
||||
<line x1="176.0" y1="198.7" x2="176.0" y2="259.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="169.0" y="210.1" width="14" height="30.4" fill="#c00"/>
|
||||
<line x1="198.0" y1="213.9" x2="198.0" y2="259.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="191.0" y="225.3" width="14" height="15.2" fill="#188a3e"/>
|
||||
<line x1="220.0" y1="213.9" x2="220.0" y2="278.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="213.0" y="225.3" width="14" height="34.2" fill="#c00"/>
|
||||
<line x1="242.0" y1="248.1" x2="242.0" y2="293.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="235.0" y="259.5" width="14" height="15.2" fill="#c00"/>
|
||||
<line x1="264.0" y1="263.3" x2="264.0" y2="354.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="257.0" y="274.7" width="14" height="28.5" fill="#c00"/>
|
||||
<line x1="286.0" y1="295.6" x2="286.0" y2="335.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.0" y="303.2" width="14" height="13.3" fill="#c00"/>
|
||||
<line x1="308.0" y1="308.9" x2="308.0" y2="369.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="301.0" y="316.5" width="14" height="34.2" fill="#c00"/>
|
||||
<line x1="330.0" y1="343.1" x2="330.0" y2="402.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="323.0" y="350.7" width="14" height="32.3" fill="#c00"/>
|
||||
<line x1="330.0" y1="432.0" x2="330.0" y2="414.0" stroke="#c00" stroke-width="1.8"/>
|
||||
<polygon points="330.0,406.0 334.0,414.0 326.0,414.0" fill="#c00"/>
|
||||
<text x="330.0" y="446.0" fill="#c00" text-anchor="middle" style="font-weight:bold">aici s-ar fi rupt stopul,</text>
|
||||
<text x="330.0" y="460.0" fill="#c00" text-anchor="middle" style="font-weight:bold">cu 0.25 $ sub 623.60</text>
|
||||
</svg>
|
||||
<text x="8" y="571" fill="#555">Aceeași candelă M în ambele panouri: close 623.87 sub banda 623.90, low 623.60. Ce urmează după ea decide.</text>
|
||||
<text x="8" y="588" fill="#555">DA: candela 1 de după M e verde și închide la 623.95 — ăsta e trigger-ul, iar close-ul ei e prețul de intrare.</text>
|
||||
<text x="8" y="605" fill="#555">NU: 3 candele roșii la rând, niciun trigger. Nu intri. Prețul coboară până la 623.35, cu 0.25 $ sub 623.60,</text>
|
||||
<text x="8" y="622" fill="#555">adică exact sub stopul pe care l-ai fi avut dacă intrai „pentru că oricum era oversold".</text>
|
||||
<text x="8" y="639" fill="#555">Condiția M (bandă + TICUS) nu e un ordin de cumpărare: e doar permisiunea de a aștepta trigger-ul.</text>
|
||||
<text x="8" y="656" fill="#555">Cifrele lui M și ale trigger-ului (623.87 / 623.90 / 623.60 / 623.95) sunt din curs; restul barelor sunt construite de mine.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
Reference in New Issue
Block a user