07_mdsrt: capitol reconstruit dupa sablon

Nivelurile 1-2-3, cei 6 pasi cu diagrama fiecare, mini-glosar de 33 de
termeni, oglinda pe short cu diagrama ei, checklist. Sursele in
fise/07_mdsrt_surse.md.

Capitolul avea TREI reguli numerice diferite in acelasi loc: stop fix pe
low in ansamblu, 0.10 sub low in aplus si exemplu, plus doua reguli de
intrare. Acum mdsrt_ansamblu.py tine seria si regulile, iar picior,
trigger, stop, iesire si short le importa — nu mai pot diverge.

Noi: zona (daily -> 60 min ca descompunere reala, 2 candele daily = 14
bare, cu caseta, ghidaje si pivotul pe ambele panouri), picior, trigger,
stop, iesire, short (seria long rasturnata, deci fiecare cifra are
perechea ei).

Verificare numerica text-diagrama: trei nepotriviri reparate, inclusiv o
cifra din proza care nu exista pe niciun desen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
This commit is contained in:
Claude Agent
2026-09-20 12:50:01 +00:00
parent 7e5c7093fa
commit f8dd2f7bd0
19 changed files with 3840 additions and 1629 deletions

View File

@@ -161,7 +161,7 @@ if __name__ == "__main__":
]
arrows = [
Arrow(TRIG, "Buy Hammer: coadă lungă în jos\nvânzătorii au dus prețul sub nivel\nși nu l-au putut ține acolo",
Arrow(TRIG, f"Buy Hammer: close {cds[TRIG].c:.2f}, coadă lungă în jos\nvânzătorii au dus prețul sub nivel\nși nu l-au putut ține acolo",
where="below", price=leg2_l, color=RED, dx=210, dy=58),
Arrow(CONF, f"intrare {ENTRY:.2f}: intri abia aici,\nla close peste high-ul candelei-trigger",
where="above", color=GREEN, dx=40, dy=-44),

View File

@@ -116,7 +116,7 @@
<text x="314.0" y="445.6" fill="#c00" text-anchor="middle" style="font-weight:bold">piciorul 2: low 589.50</text>
<line x1="524.0" y1="514.8" x2="321.4" y2="433.8" stroke="#c00" stroke-width="1.8"/>
<polygon points="314.0,430.8 322.9,430.1 319.9,437.5" fill="#c00"/>
<text x="524.0" y="528.8" fill="#c00" text-anchor="middle" style="font-weight:bold">Buy Hammer: coadă lungă în jos</text>
<text x="524.0" y="528.8" fill="#c00" text-anchor="middle" style="font-weight:bold">Buy Hammer: close 589.80, coadă lungă în jos</text>
<text x="524.0" y="542.8" fill="#c00" text-anchor="middle" style="font-weight:bold">vânzătorii au dus prețul sub nivel</text>
<text x="524.0" y="556.8" fill="#c00" text-anchor="middle" style="font-weight:bold">și nu l-au putut ține acolo</text>
<line x1="369.0" y1="190.0" x2="330.6" y2="376.6" stroke="#188a3e" stroke-width="1.8"/>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -149,7 +149,7 @@ marks = [
Mark(10, "picior 1 = 622.40", BLUE, "below", dy=15),
Mark(18, "picior 2 = 621.40 (mai jos) · Buy Hammer", RED, "below"),
Mark(18, "hammer închide 622.60, înapoi peste pivot", GREEN, "above", dy=-42),
Mark(19, "intri aici: close peste high-ul hammer-ului", GREEN, "above", dy=-62),
Mark(19, "intri aici: close peste high-ul hammer-ului (622.70)", GREEN, "above", dy=-62),
]
ch = Chart(
@@ -185,6 +185,7 @@ assert "sunt reale, din curs" in svg and "construcția mea" in svg, \
for eticheta in ("pivot daily 622.00", "picior 1 = 622.40",
"picior 2 = 621.40 (mai jos) · Buy Hammer",
"hammer închide 622.60, înapoi peste pivot",
"intri aici: close peste high-ul hammer-ului (622.70)",
"intrare 623.00 (construit)", "SL 621.30 (construit)",
"TP1 624.70 = 1:1 (construit)"):
assert eticheta in svg, f"lipsește eticheta: {eticheta!r}"

View File

@@ -105,7 +105,7 @@
<text x="194.0" y="386.7" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">picior 1 = 622.40</text>
<text x="314.0" y="430.6" fill="#c00" text-anchor="middle" style="font-weight:bold">picior 2 = 621.40 (mai jos) · Buy Hammer</text>
<text x="314.0" y="278.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">hammer închide 622.60, înapoi peste pivot</text>
<text x="329.0" y="232.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intri aici: close peste high-ul hammer-ului</text>
<text x="329.0" y="232.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intri aici: close peste high-ul hammer-ului (622.70)</text>
<text x="24" y="555.0" fill="#555">Legendă: nivelurile 622.40 / 622.00 / 621.40 / 622.60 și cele 8 candele dintre</text>
<text x="24" y="572.0" fill="#555">picioare sunt reale, din curs. Intrarea 623.00, SL-ul 621.30 și TP-ul 624.70</text>
<text x="24" y="589.0" fill="#555">sunt construcția mea — materialul nu le dă.</text>

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,96 @@
"""MDSRT, Pasul 6 — ieșirea: TP1 la 1:1, breakeven, restul pe trail.
Aceeași serie canonică (importată din `mdsrt_ansamblu`), de la intrare
încolo: prima parte a poziției iese la ținta simetrică 1:1 (591.00), stopul
se mută la breakeven după ce prețul a trecut de jumătatea drumului până la
țintă, iar restul merge pe linia de trail — ultimul swing low — până la prima
închidere sub ea.
Din curs: ținta stabilită împreună cu stopul, simetric; trail pe Bull Trail
sau pe swing low, la alegerea traderului; trail-ul se evaluează pe candelă
închisă, nu în timpul barei. Numărul de tranșe NU e dat de curs —
transcrierea se întrerupe exact acolo.
Rulează: python3 gen/strategii/mdsrt_iesire.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
sys.path.insert(0, str(Path(__file__).parent))
from svg_candles import Chart, Level, Mark, Arrow, Zone # noqa: E402
from mdsrt_ansamblu import (serie, CONF, ENTRY, SL, R, TP1, EXIT_I, TRAIL, # noqa: E402
bull_trail)
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, BLUE, GRAY, ORANGE = "#188a3e", "#c00", "#1a5fb4", "#555", "#b06000"
W = 720
S = 14
cds = serie()[S:EXIT_I + 1]
cf, tp_i, ex_i = CONF - S, 22 - S, EXIT_I - S
BE = ENTRY # stopul mutat la prețul de intrare
half = round(ENTRY + R / 2, 2) # jumătatea drumului până la țintă
be_i = next(i for i in range(cf + 1, len(cds)) if cds[i].c > half)
# ----------------------------------------------------------------- assert-uri
assert cds[cf].c == ENTRY and round(ENTRY - SL, 2) == R
assert round(TP1 - ENTRY, 2) == R, "ținta nu e simetrică cu stopul"
assert cds[tp_i].h >= TP1 and max(c.h for c in cds[cf + 1:tp_i]) < TP1, "TP1 atins altundeva"
assert cds[be_i].c > half and all(c.c <= half for c in cds[cf + 1:be_i]), "breakeven greșit"
assert be_i < tp_i, "breakeven-ul ar veni după țintă"
# trail: ultimul swing low înaintea întoarcerii; ieșirea e prima închidere sub el
assert cds[ex_i].c < TRAIL and all(c.c > TRAIL for c in cds[ex_i - 2:ex_i])
r_exit = round((cds[ex_i].c - ENTRY) / R, 2)
assert r_exit == 2.75, r_exit
# scara: distanța intrare→TP1 e egală cu distanța intrare→SL, și în preț, și în px
print(f"ieșire: TP1 {TP1:.2f} la bara {tp_i + S}, breakeven de la bara {be_i + S} "
f"(peste {half:.2f}), trail {TRAIL:.2f}, ieșire {cds[ex_i].c:.2f} = {r_exit:.2f}R")
ch = Chart(
candles=cds,
levels=[
Level(TP1, f"TP1 {TP1:.2f} = 1:1", GREEN, "2 2",
label_at="left"),
Level(ENTRY, f"intrare {ENTRY:.2f} = breakeven", GRAY, "4 3",
label_at="left"),
Level(SL, f"SL inițial {SL:.2f} (risc {R:.2f} = 1R)", RED, "4 3", label_at="left"),
],
marks=[Mark(ex_i, f"ieșire {cds[ex_i].c:.2f} = {r_exit:.2f}R", GREEN, "above")],
zones=[Zone(be_i, tp_i, "de aici stopul stă la breakeven", ORANGE)],
arrows=[
Arrow(be_i, f"prețul a trecut de jumătatea drumului ({half:.2f}):\n"
f"muți stopul la {BE:.2f} — trade-ul nu mai poate pierde",
where="below", price=SL, color=ORANGE, dx=-40, dy=44),
Arrow(tp_i, f"ținta 1:1 atinsă: {TP1:.2f}\nrestul poziției rămâne pe trail",
where="above", color=GREEN, dx=90, dy=-30),
Arrow(ex_i, f"prima închidere sub linia de trail ({TRAIL:.2f}):\n"
f"ieși cu restul — se citește pe candelă închisă,\nnu în timpul barei",
where="below", price=cds[ex_i].c, color=BLUE, dx=-30, dy=64),
],
scale=56.0, gap=24, body_w=12, left=200, width=W, top=44,
title="Pasul 6 — ieșirea: ținta 1:1, breakeven, restul pe trail (SPY 5 min, construit)",
caption="Câte tranșe faci nu e dat de curs: acolo se întrerupe transcrierea.",
)
svg = ch.render()
# linia de trail: ultimul swing low de la fiecare pas, desenată ca scară
trail_pts = bull_trail(cds, cf)
assert trail_pts[-1][1] == TRAIL, (trail_pts[-1], TRAIL)
assert len(trail_pts) > 5, trail_pts
d = []
for k, (i, p) in enumerate(trail_pts):
x0, x1 = ch._cx(i) - ch.gap / 2, ch._cx(i) + ch.gap / 2
d.append(("M" if k == 0 else "L") + f"{x0:.1f},{ch._y(p):.1f}")
d.append(f"L{x1:.1f},{ch._y(p):.1f}")
trail_svg = (f'<path d="{" ".join(d)}" fill="none" stroke="{BLUE}" stroke-width="1.6" '
f'stroke-dasharray="5 3"/>')
i = svg.rindex("</svg>")
svg = svg[:i] + " " + trail_svg + "\n" + svg[i:]
assert "\n\n" not in svg, "linie goală în SVG"
H = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1))
for x, y in re.findall(r'<text x="([-\d.]+)" y="([-\d.]+)"', svg):
assert 0 <= float(x) <= W and 0 <= float(y) <= H, f"text în afara pânzei la ({x}, {y})"
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name} ({W}x{H:.0f})")

View File

@@ -0,0 +1,65 @@
<svg viewBox="0 0 755 478" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
<text x="24" y="96" fill="#333" style="font-weight:bold">Pasul 6 — ieșirea: ținta 1:1, breakeven, restul pe trail (SPY 5 min, construit)</text>
<rect x="357.0" y="213.6" width="46.0" height="60.8" fill="#b06000" fill-opacity="0.08" stroke="#b06000" stroke-width="1"/>
<text x="357.0" y="148.2" fill="#b06000" style="font-weight:bold">de aici stopul stă la breakeven</text>
<line x1="24" y1="235.6" x2="741" y2="235.6" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 2"/>
<text x="30" y="229.6" fill="#188a3e">TP1 591.00 = 1:1</text>
<line x1="24" y1="280.4" x2="741" y2="280.4" stroke="#555" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="274.4" fill="#555">intrare 590.20 = breakeven</text>
<line x1="24" y1="325.2" x2="741" y2="325.2" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="319.2" fill="#c00">SL inițial 589.40 (risc 0.80 = 1R)</text>
<line x1="200.0" y1="204.8" x2="200.0" y2="246.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="194.0" y="218.8" width="12" height="11.2" fill="#188a3e"/>
<line x1="224.0" y1="204.8" x2="224.0" y2="258.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="218.0" y="218.8" width="12" height="22.4" fill="#c00"/>
<line x1="248.0" y1="230.0" x2="248.0" y2="272.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="242.0" y="241.2" width="12" height="22.4" fill="#c00"/>
<line x1="272.0" y1="255.2" x2="272.0" y2="294.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="266.0" y="263.6" width="12" height="25.2" fill="#c00"/>
<line x1="296.0" y1="283.2" x2="296.0" y2="319.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="290.0" y="288.8" width="12" height="14.0" fill="#c00"/>
<line x1="320.0" y1="274.8" x2="320.0" y2="305.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="314.0" y="280.4" width="12" height="22.4" fill="#188a3e"/>
<line x1="344.0" y1="244.0" x2="344.0" y2="288.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="338.0" y="258.0" width="12" height="22.4" fill="#188a3e"/>
<line x1="368.0" y1="238.4" x2="368.0" y2="266.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="362.0" y="241.2" width="12" height="16.8" fill="#188a3e"/>
<line x1="392.0" y1="221.6" x2="392.0" y2="258.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="386.0" y="235.6" width="12" height="5.6" fill="#188a3e"/>
<line x1="416.0" y1="204.8" x2="416.0" y2="252.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="410.0" y="218.8" width="12" height="16.8" fill="#188a3e"/>
<line x1="440.0" y1="204.8" x2="440.0" y2="246.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="434.0" y="218.8" width="12" height="11.2" fill="#c00"/>
<line x1="464.0" y1="196.4" x2="464.0" y2="246.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="458.0" y="210.4" width="12" height="19.6" fill="#188a3e"/>
<line x1="488.0" y1="182.4" x2="488.0" y2="227.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="482.0" y="196.4" width="12" height="14.0" fill="#188a3e"/>
<line x1="512.0" y1="168.4" x2="512.0" y2="213.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="506.0" y="182.4" width="12" height="14.0" fill="#188a3e"/>
<line x1="536.0" y1="157.2" x2="536.0" y2="199.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="530.0" y="171.2" width="12" height="11.2" fill="#188a3e"/>
<line x1="560.0" y1="120.8" x2="560.0" y2="188.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="554.0" y="134.8" width="12" height="36.4" fill="#188a3e"/>
<line x1="584.0" y1="104.0" x2="584.0" y2="151.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="578.0" y="118.0" width="12" height="16.8" fill="#188a3e"/>
<line x1="608.0" y1="104.0" x2="608.0" y2="146.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="602.0" y="118.0" width="12" height="11.2" fill="#c00"/>
<line x1="632.0" y1="115.2" x2="632.0" y2="174.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="626.0" y="129.2" width="12" height="28.0" fill="#c00"/>
<text x="632.0" y="105.2" fill="#188a3e" text-anchor="middle" style="font-weight:bold">ieșire 592.40 = 2.75R</text>
<line x1="328.0" y1="399.2" x2="364.0" y2="336.1" stroke="#b06000" stroke-width="1.8"/>
<polygon points="368.0,329.2 367.5,338.1 360.6,334.2" fill="#b06000"/>
<text x="328.0" y="413.2" fill="#b06000" text-anchor="middle" style="font-weight:bold">prețul a trecut de jumătatea drumului (590.60):</text>
<text x="328.0" y="427.2" fill="#b06000" text-anchor="middle" style="font-weight:bold">muți stopul la 590.20 — trade-ul nu mai poate pierde</text>
<line x1="482.0" y1="66.0" x2="396.1" y2="210.7" stroke="#188a3e" stroke-width="1.8"/>
<polygon points="392.0,217.6 392.6,208.7 399.5,212.8" fill="#188a3e"/>
<text x="482.0" y="47.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">ținta 1:1 atinsă: 591.00</text>
<text x="482.0" y="61.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">restul poziției rămâne pe trail</text>
<line x1="602.0" y1="318.8" x2="630.5" y2="169.1" stroke="#1a5fb4" stroke-width="1.8"/>
<polygon points="632.0,161.2 634.4,169.8 626.6,168.3" fill="#1a5fb4"/>
<text x="602.0" y="332.8" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">prima închidere sub linia de trail (592.50):</text>
<text x="602.0" y="346.8" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">ieși cu restul — se citește pe candelă închisă,</text>
<text x="602.0" y="360.8" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">nu în timpul barei</text>
<text x="24" y="466" fill="#555">Câte tranșe faci nu e dat de curs: acolo se întrerupe transcrierea.</text>
<path d="M308.0,245.6 L332.0,245.6 L332.0,228.8 L356.0,228.8 L356.0,206.4 L380.0,206.4 L380.0,198.0 L404.0,198.0 L404.0,192.4 L428.0,192.4 L428.0,192.4 L452.0,192.4 L452.0,192.4 L476.0,192.4 L476.0,192.4 L500.0,192.4 L500.0,192.4 L524.0,192.4 L524.0,192.4 L548.0,192.4 L548.0,128.0 L572.0,128.0 L572.0,91.6 L596.0,91.6 L596.0,91.6 L620.0,91.6 L620.0,91.6 L644.0,91.6" fill="none" stroke="#1a5fb4" stroke-width="1.6" stroke-dasharray="5 3"/>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,76 @@
"""MDSRT, Pasul 2 — al doilea picior: cine vinde acolo și cine cumpără înapoi.
Aceeași serie ca diagrama de ansamblu (importată din `mdsrt_ansamblu`), tăiată
în jurul celor două picioare. Diagrama arată unde stau stopurile celor care au
cumpărat la piciorul 1, de ce ruperea nivelului atrage vânzare, și de ce
întoarcerea e bruscă: shorturile intrate pe rupere ies cu buy to cover.
Mecanica short squeeze-ului e a cursului (`09_QA/19_2025-09-08.md:158-196`);
nivelurile sunt cele construite în `mdsrt_ansamblu`.
Rulează: python3 gen/strategii/mdsrt_picior.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
sys.path.insert(0, str(Path(__file__).parent))
from svg_candles import Chart, Level, Zone, Arrow # noqa: E402
from mdsrt_ansamblu import (serie, SUPORT, LEG1, TRIG, CONF, ENTRY, SL, # noqa: E402
R, TP1, MARJA)
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, BLUE, GRAY, ORANGE = "#188a3e", "#c00", "#1a5fb4", "#555", "#b06000"
S, E = 4, 23 # fereastra arătată din serie
W = 700
cds = serie()[S:E]
l1, l2, cf = LEG1 - S, TRIG - S, CONF - S
leg1_l, leg2_l = cds[l1].l, cds[l2].l
STOPURI = round(leg1_l - 0.05, 2) # unde stau, tipic, stopurile de sub piciorul 1
# ----------------------------------------------------------------- assert-uri
assert leg1_l == 590.10 and leg2_l == 589.50, (leg1_l, leg2_l)
assert leg2_l < SUPORT < leg1_l, "povestea diagramei cere picior 1 peste nivel, picior 2 sub"
assert leg2_l < STOPURI, "piciorul 2 nu ajunge la stopurile de sub piciorul 1"
assert cds[l2].c > leg2_l and cds[l2].c < SUPORT, cds[l2].c
assert cds[cf].c == ENTRY > cds[l2].h, (ENTRY, cds[l2].h)
assert round(leg2_l - MARJA, 2) == SL
# ruperea ține o singură candelă: nicio bară de după intrare nu mai coboară sub nivel
assert all(c.l > SUPORT for c in cds[cf + 1:]), "prețul recade sub nivel după intrare"
assert min(c.l for c in cds[cf + 1:]) > SL, "stopul ar fi fost atins după intrare"
# distanța dintre picioare, peste minimul de 5 candele cerut de curs
assert l2 - l1 == 8
print(f"picior: 1 = {leg1_l:.2f} (peste {SUPORT:.2f}), 2 = {leg2_l:.2f}, "
f"stopuri la {STOPURI:.2f}, intrare {ENTRY:.2f}, SL {SL:.2f}")
ch = Chart(
candles=cds,
levels=[
Level(SUPORT, f"pivot daily {SUPORT:.2f}", BLUE, label_at="left"),
Level(SL, f"SL {SL:.2f}", RED, label_at="left"),
],
zones=[Zone(l2, cf, "aici se schimbă mâna", RED)],
arrows=[
Arrow(l1, f"piciorul 1: low {leg1_l:.2f} — nivelul ține\n"
f"cine cumpără aici își pune stopul chiar sub {leg1_l:.2f}",
where="below", price=leg1_l, color=GRAY, dx=-30, dy=34),
Arrow(l2, f"piciorul 2: low {leg2_l:.2f}\n1) mătură stopurile de sus\n"
f"2) cei care shortează ruperea intră aici",
where="below", price=leg2_l, color=RED, dx=150, dy=44),
Arrow(cf, "close înapoi peste nivel: shorturile\nprinse cumpără ca să iasă (buy to cover)\n"
"de aici urcarea e bruscă, nu treptată",
where="above", color=GREEN, dx=40, dy=-30),
],
scale=62.0, gap=22, body_w=12, left=64, width=W, top=54,
title="Pasul 2 — al doilea picior, sub nivel (SPY 5 min, exemplu construit)",
caption="Vânzarea de sub nivel se epuizează într-o singură candelă: nu mai are cine vinde.",
)
svg = ch.render()
assert "\n\n" not in svg, "linie goală în SVG"
for x, y in re.findall(r'<text x="([-\d.]+)" y="([-\d.]+)"', svg):
h = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1))
assert 0 <= float(x) <= W and 0 <= float(y) <= h, f"text în afara pânzei la ({x}, {y})"
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name}")

View File

@@ -0,0 +1,62 @@
<svg viewBox="0 0 700 587" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
<text x="24" y="68" fill="#333" style="font-weight:bold">Pasul 2 — al doilea picior, sub nivel (SPY 5 min, exemplu construit)</text>
<rect x="361.0" y="362.5" width="44.0" height="65.6" fill="#c00" fill-opacity="0.08" stroke="#c00" stroke-width="1"/>
<text x="361.0" y="302.6" fill="#c00" style="font-weight:bold">aici se schimbă mâna</text>
<line x1="24" y1="389.1" x2="686" y2="389.1" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="383.1" fill="#1a5fb4">pivot daily 590.00</text>
<line x1="24" y1="426.3" x2="686" y2="426.3" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="420.3" fill="#c00">SL 589.40</text>
<line x1="64.0" y1="76.0" x2="64.0" y2="153.5" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="58.0" y="91.5" width="12" height="43.4" fill="#c00"/>
<line x1="86.0" y1="119.4" x2="86.0" y2="196.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="80.0" y="134.9" width="12" height="43.4" fill="#c00"/>
<line x1="108.0" y1="144.2" x2="108.0" y2="196.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="102.0" y="159.7" width="12" height="18.6" fill="#188a3e"/>
<line x1="130.0" y1="144.2" x2="130.0" y2="227.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="124.0" y="159.7" width="12" height="49.6" fill="#c00"/>
<line x1="152.0" y1="193.8" x2="152.0" y2="271.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="146.0" y="209.3" width="12" height="43.4" fill="#c00"/>
<line x1="174.0" y1="237.2" x2="174.0" y2="308.5" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="168.0" y="252.7" width="12" height="37.2" fill="#c00"/>
<line x1="196.0" y1="274.4" x2="196.0" y2="382.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="190.0" y="289.9" width="12" height="74.4" fill="#c00"/>
<line x1="218.0" y1="317.8" x2="218.0" y2="382.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="212.0" y="333.3" width="12" height="31.0" fill="#188a3e"/>
<line x1="240.0" y1="286.8" x2="240.0" y2="351.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="234.0" y="302.3" width="12" height="31.0" fill="#188a3e"/>
<line x1="262.0" y1="286.8" x2="262.0" y2="339.5" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="256.0" y="302.3" width="12" height="18.6" fill="#c00"/>
<line x1="284.0" y1="293.0" x2="284.0" y2="339.5" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="278.0" y="308.5" width="12" height="12.4" fill="#188a3e"/>
<line x1="306.0" y1="293.0" x2="306.0" y2="351.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="300.0" y="308.5" width="12" height="24.8" fill="#c00"/>
<line x1="328.0" y1="320.9" x2="328.0" y2="367.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="322.0" y="333.3" width="12" height="24.8" fill="#c00"/>
<line x1="350.0" y1="348.8" x2="350.0" y2="392.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="344.0" y="358.1" width="12" height="27.9" fill="#c00"/>
<line x1="372.0" y1="379.8" x2="372.0" y2="420.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="366.0" y="386.0" width="12" height="15.5" fill="#c00"/>
<line x1="394.0" y1="370.5" x2="394.0" y2="404.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="388.0" y="376.7" width="12" height="24.8" fill="#188a3e"/>
<line x1="416.0" y1="336.4" x2="416.0" y2="386.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="410.0" y="351.9" width="12" height="24.8" fill="#188a3e"/>
<line x1="438.0" y1="330.2" x2="438.0" y2="361.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="432.0" y="333.3" width="12" height="18.6" fill="#188a3e"/>
<line x1="460.0" y1="311.6" x2="460.0" y2="351.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="454.0" y="327.1" width="12" height="6.2" fill="#188a3e"/>
<line x1="166.0" y1="446.9" x2="192.4" y2="394.1" stroke="#555" stroke-width="1.8"/>
<polygon points="196.0,386.9 196.0,395.8 188.8,392.3" fill="#555"/>
<text x="166.0" y="460.9" fill="#555" text-anchor="middle" style="font-weight:bold">piciorul 1: low 590.10 — nivelul ține</text>
<text x="166.0" y="474.9" fill="#555" text-anchor="middle" style="font-weight:bold">cine cumpără aici își pune stopul chiar sub 590.10</text>
<line x1="522.0" y1="494.1" x2="379.2" y2="427.5" stroke="#c00" stroke-width="1.8"/>
<polygon points="372.0,424.1 380.9,423.9 377.6,431.1" fill="#c00"/>
<text x="522.0" y="508.1" fill="#c00" text-anchor="middle" style="font-weight:bold">piciorul 2: low 589.50</text>
<text x="522.0" y="522.1" fill="#c00" text-anchor="middle" style="font-weight:bold">1) mătură stopurile de sus</text>
<text x="522.0" y="536.1" fill="#c00" text-anchor="middle" style="font-weight:bold">2) cei care shortează ruperea intră aici</text>
<line x1="434.0" y1="255.0" x2="396.7" y2="359.0" stroke="#188a3e" stroke-width="1.8"/>
<polygon points="394.0,366.5 392.9,357.6 400.5,360.3" fill="#188a3e"/>
<text x="434.0" y="222.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">close înapoi peste nivel: shorturile</text>
<text x="434.0" y="236.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">prinse cumpără ca să iasă (buy to cover)</text>
<text x="434.0" y="250.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">de aici urcarea e bruscă, nu treptată</text>
<text x="24" y="575" fill="#555">Vânzarea de sub nivel se epuizează într-o singură candelă: nu mai are cine vinde.</text>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,102 @@
"""MDSRT pe short — oglinda completă a trade-ului long.
Seria e exact seria canonică a capitolului, răsturnată în jurul unui preț
(p' = 690 − p): fiecare candelă verde devine roșie, fiecare low devine high.
Așa, orice regulă verificată pe long e verificată și aici, cu semnul schimbat
— și se vede că nu e „doar invers", ci un trade întreg.
ATENȚIE: varianta short a MDSRT NU e explicată în curs — transcrierea se
întrerupe înainte. Regulile de aici sunt reconstruite prin oglindire de
autorul sumarului, marcate ca atare, și se tratează ca ipoteză de testat.
Rulează: python3 gen/strategii/mdsrt_short.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
sys.path.insert(0, str(Path(__file__).parent))
from svg_candles import Candle, Chart, Level, Mark, Zone, Arrow # noqa: E402
from mdsrt_ansamblu import (serie, SUPORT, LEG1, TRIG, CONF, EXIT_I, # noqa: E402
ENTRY, SL, R, TP1, MARJA, STOP_BARS)
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, BLUE, GRAY, ORANGE = "#188a3e", "#c00", "#1a5fb4", "#555", "#b06000"
W, K = 700, 690.0 # K = prețul în jurul căruia se răstoarnă seria
S = 6
def m(p):
return round(K - p, 2)
cds = [Candle(m(c.o), m(c.l), m(c.h), m(c.c), vol=c.vol) for c in serie()]
REZ = m(SUPORT) # rezistența / pivotul, sus
l1, l2, cf, ex = LEG1, TRIG, CONF, EXIT_I
leg1_h, leg2_h = cds[l1].h, cds[l2].h
entry = cds[cf].c
sl = round(leg2_h + MARJA, 2)
risk = round(sl - entry, 2)
tp1 = round(entry - risk, 2)
# ----------------------------------------------------------------- assert-uri
for i, c in enumerate(cds):
assert c.h >= max(c.o, c.c) and c.l <= min(c.o, c.c), i
if i:
assert abs(c.o - cds[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]"
# 1. oglinda e exactă: fiecare cifră a trade-ului long are perechea ei
assert (REZ, leg1_h, leg2_h, entry, sl, tp1) == (100.00, 99.90, 100.50, 99.80, 100.60, 99.00), \
(REZ, leg1_h, leg2_h, entry, sl, tp1)
assert risk == R == 0.80
# 2. povestea: piciorul 1 nu trece de nivel, piciorul 2 îl rupe în sus și recade
assert leg1_h < REZ < leg2_h
assert cds[l2].c > REZ, "candela-trigger ar trebui să se închidă tot deasupra nivelului"
assert cds[cf].c < REZ, "candela de intrare nu se închide înapoi sub nivel"
# 3. intrarea rupe low-ul candelei-trigger, stopul e cu marja peste maximul ei
assert entry < cds[l2].l, (entry, cds[l2].l)
high4 = max(c.h for c in cds[cf - STOP_BARS + 1:cf + 1])
assert high4 == leg2_h and round(high4 + MARJA, 2) == sl
# 4. ținta e simetrică, atinsă la aceeași bară ca pe long
assert abs((sl - entry) - (entry - tp1)) < 1e-9
assert next(i for i in range(cf + 1, len(cds)) if cds[i].l <= tp1 + 1e-9) == 22
# 5. ieșirea pe bear trail, tot la 2.75R
r_exit = round((entry - cds[ex].c) / risk, 2)
assert r_exit == 2.75, r_exit
# 6. divergența, în oglindă: higher high pe preț, lower high pe indicator/volum
assert cds[l2].h > cds[l1].h and cds[l2].vol > cds[l1].vol
print(f"short: rezistență {REZ:.2f}, picior 1 {leg1_h:.2f}, picior 2 {leg2_h:.2f}, "
f"intrare {entry:.2f}, SL {sl:.2f} (risc {risk:.2f}), TP1 {tp1:.2f}, ieșire {r_exit:.2f}R")
ch = Chart(
candles=cds[S:ex + 1],
levels=[
Level(REZ, f"pivot daily {REZ:.2f} — zona, sus", BLUE, label_at="left"),
Level(sl, f"SL {sl:.2f} = {MARJA:.2f} peste piciorul 2", RED, "4 3"),
Level(entry, f"intrare short {entry:.2f}", GREEN, "6 3"),
Level(tp1, f"TP1 {tp1:.2f} = 1:1", GREEN, "2 2", label_at="left"),
],
marks=[Mark(ex - S, f"ieșire pe trail {cds[ex].c:.2f} = {r_exit:.2f}R", GREEN, "below")],
zones=[Zone(l2 - S, cf - S, "", RED)],
arrows=[
Arrow(l1 - S, f"piciorul 1: high {leg1_h:.2f}\nrezistența ține",
where="above", price=leg1_h, color=GRAY, dx=-30, dy=-30),
Arrow(l2 - S, f"piciorul 2: high {leg2_h:.2f} — rupe nivelul în sus\n"
f"și mătură stopurile celor intrați pe short,\ndar nu poate ține acolo",
where="above", price=leg2_h, color=RED, dx=60, dy=-16),
Arrow(cf - S, f"intri short la {entry:.2f}: close sub nivel și sub low-ul candelei-trigger\n"
f"cei prinși long vând ca să iasă — coborârea e bruscă",
where="below", color=GREEN, dx=20, dy=30),
],
scale=58.0, gap=18, body_w=10, left=176, width=W, top=64, vol_h=60,
vol_label="volum: la piciorul 2 volumul crește, prețul nu mai are cumpărători = divergență",
title="Oglinda pe short (serie construită prin răsturnarea exemplului long)",
caption="Varianta short nu e explicată în curs: e reconstruită prin oglindire.",
)
svg = ch.render()
assert "\n\n" not in svg, "linie goală în SVG"
H = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1))
for x, y in re.findall(r'<text x="([-\d.]+)" y="([-\d.]+)"', svg):
assert 0 <= float(x) <= W and 0 <= float(y) <= H, f"text în afara pânzei la ({x}, {y})"
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name} ({W}x{H:.0f})")

View File

@@ -0,0 +1,110 @@
<svg viewBox="0 0 700 552" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
<text x="24" y="112" fill="#333" style="font-weight:bold">Oglinda pe short (serie construită prin răsturnarea exemplului long)</text>
<rect x="382.0" y="118.0" width="38.0" height="62.4" fill="#c00" fill-opacity="0.08" stroke="#c00" stroke-width="1"/>
<line x1="24" y1="155.0" x2="686" y2="155.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="149.0" fill="#1a5fb4">pivot daily 100.00 — zona, sus</text>
<line x1="24" y1="120.2" x2="686" y2="120.2" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="672" y="114.2" fill="#c00" text-anchor="end">SL 100.60 = 0.10 peste piciorul 2</text>
<line x1="24" y1="166.6" x2="686" y2="166.6" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="6 3"/>
<text x="672" y="160.6" fill="#188a3e" text-anchor="end">intrare short 99.80</text>
<line x1="24" y1="213.0" x2="686" y2="213.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 2"/>
<text x="30" y="207.0" fill="#188a3e">TP1 99.00 = 1:1</text>
<line x1="176.0" y1="334.8" x2="176.0" y2="384.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="171.0" y="352.2" width="10" height="17.4" fill="#c00"/>
<line x1="194.0" y1="305.8" x2="194.0" y2="384.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="189.0" y="323.2" width="10" height="46.4" fill="#188a3e"/>
<line x1="212.0" y1="265.2" x2="212.0" y2="337.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="207.0" y="282.6" width="10" height="40.6" fill="#188a3e"/>
<line x1="230.0" y1="230.4" x2="230.0" y2="297.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="225.0" y="247.8" width="10" height="34.8" fill="#188a3e"/>
<line x1="248.0" y1="160.8" x2="248.0" y2="262.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="243.0" y="178.2" width="10" height="69.6" fill="#188a3e"/>
<line x1="266.0" y1="160.8" x2="266.0" y2="221.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="261.0" y="178.2" width="10" height="29.0" fill="#c00"/>
<line x1="284.0" y1="189.8" x2="284.0" y2="250.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="279.0" y="207.2" width="10" height="29.0" fill="#c00"/>
<line x1="302.0" y1="201.4" x2="302.0" y2="250.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="297.0" y="218.8" width="10" height="17.4" fill="#188a3e"/>
<line x1="320.0" y1="201.4" x2="320.0" y2="244.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="315.0" y="218.8" width="10" height="11.6" fill="#c00"/>
<line x1="338.0" y1="189.8" x2="338.0" y2="244.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="333.0" y="207.2" width="10" height="23.2" fill="#188a3e"/>
<line x1="356.0" y1="175.3" x2="356.0" y2="218.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="351.0" y="184.0" width="10" height="23.2" fill="#188a3e"/>
<line x1="374.0" y1="152.1" x2="374.0" y2="192.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="369.0" y="157.9" width="10" height="26.1" fill="#188a3e"/>
<line x1="392.0" y1="126.0" x2="392.0" y2="163.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="387.0" y="143.4" width="10" height="14.5" fill="#188a3e"/>
<line x1="410.0" y1="140.5" x2="410.0" y2="172.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="405.0" y="143.4" width="10" height="23.2" fill="#c00"/>
<line x1="428.0" y1="157.9" x2="428.0" y2="204.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="423.0" y="166.6" width="10" height="23.2" fill="#c00"/>
<line x1="446.0" y1="181.1" x2="446.0" y2="210.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="441.0" y="189.8" width="10" height="17.4" fill="#c00"/>
<line x1="464.0" y1="189.8" x2="464.0" y2="227.5" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="459.0" y="207.2" width="10" height="5.8" fill="#c00"/>
<line x1="482.0" y1="195.6" x2="482.0" y2="244.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="477.0" y="213.0" width="10" height="17.4" fill="#c00"/>
<line x1="500.0" y1="201.4" x2="500.0" y2="244.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="495.0" y="218.8" width="10" height="11.6" fill="#188a3e"/>
<line x1="518.0" y1="201.4" x2="518.0" y2="253.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="513.0" y="218.8" width="10" height="20.3" fill="#c00"/>
<line x1="536.0" y1="221.7" x2="536.0" y2="268.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="531.0" y="239.1" width="10" height="14.5" fill="#c00"/>
<line x1="554.0" y1="236.2" x2="554.0" y2="282.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="549.0" y="253.6" width="10" height="14.5" fill="#c00"/>
<line x1="572.0" y1="250.7" x2="572.0" y2="294.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="567.0" y="268.1" width="10" height="11.6" fill="#c00"/>
<line x1="590.0" y1="262.3" x2="590.0" y2="331.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="585.0" y="279.7" width="10" height="37.7" fill="#c00"/>
<line x1="608.0" y1="300.0" x2="608.0" y2="349.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="603.0" y="317.4" width="10" height="17.4" fill="#c00"/>
<line x1="626.0" y1="305.8" x2="626.0" y2="349.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="621.0" y="323.2" width="10" height="11.6" fill="#188a3e"/>
<line x1="644.0" y1="276.8" x2="644.0" y2="337.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="639.0" y="294.2" width="10" height="29.0" fill="#188a3e"/>
<line x1="24" y1="510.3" x2="686" y2="510.3" stroke="#888"/>
<rect x="171.0" y="483.0" width="10" height="27.3" fill="#c00" opacity="0.55"/>
<rect x="189.0" y="481.7" width="10" height="28.6" fill="#188a3e" opacity="0.55"/>
<rect x="207.0" y="480.3" width="10" height="30.0" fill="#188a3e" opacity="0.55"/>
<rect x="225.0" y="478.9" width="10" height="31.4" fill="#188a3e" opacity="0.55"/>
<rect x="243.0" y="474.8" width="10" height="35.5" fill="#188a3e" opacity="0.55"/>
<rect x="261.0" y="484.4" width="10" height="25.9" fill="#c00" opacity="0.55"/>
<rect x="279.0" y="483.0" width="10" height="27.3" fill="#c00" opacity="0.55"/>
<rect x="297.0" y="481.7" width="10" height="28.6" fill="#188a3e" opacity="0.55"/>
<rect x="315.0" y="483.7" width="10" height="26.6" fill="#c00" opacity="0.55"/>
<rect x="333.0" y="480.3" width="10" height="30.0" fill="#188a3e" opacity="0.55"/>
<rect x="351.0" y="476.2" width="10" height="34.1" fill="#188a3e" opacity="0.55"/>
<rect x="369.0" y="470.8" width="10" height="39.5" fill="#188a3e" opacity="0.55"/>
<rect x="387.0" y="450.3" width="10" height="60.0" fill="#188a3e" opacity="0.55"/>
<rect x="405.0" y="455.8" width="10" height="54.5" fill="#c00" opacity="0.55"/>
<rect x="423.0" y="462.6" width="10" height="47.7" fill="#c00" opacity="0.55"/>
<rect x="441.0" y="466.7" width="10" height="43.6" fill="#c00" opacity="0.55"/>
<rect x="459.0" y="470.8" width="10" height="39.5" fill="#c00" opacity="0.55"/>
<rect x="477.0" y="469.4" width="10" height="40.9" fill="#c00" opacity="0.55"/>
<rect x="495.0" y="472.8" width="10" height="37.5" fill="#188a3e" opacity="0.55"/>
<rect x="513.0" y="474.8" width="10" height="35.5" fill="#c00" opacity="0.55"/>
<rect x="531.0" y="476.2" width="10" height="34.1" fill="#c00" opacity="0.55"/>
<rect x="549.0" y="477.6" width="10" height="32.7" fill="#c00" opacity="0.55"/>
<rect x="567.0" y="478.9" width="10" height="31.4" fill="#c00" opacity="0.55"/>
<rect x="585.0" y="474.8" width="10" height="35.5" fill="#c00" opacity="0.55"/>
<rect x="603.0" y="477.6" width="10" height="32.7" fill="#c00" opacity="0.55"/>
<rect x="621.0" y="481.7" width="10" height="28.6" fill="#188a3e" opacity="0.55"/>
<rect x="639.0" y="478.9" width="10" height="31.4" fill="#188a3e" opacity="0.55"/>
<text x="24" y="444.3" fill="#555">volum: la piciorul 2 volumul crește, prețul nu mai are cumpărători = divergență</text>
<text x="656.0" y="357.7" fill="#188a3e" text-anchor="end" style="font-weight:bold">ieșire pe trail 97.60 = 2.75R</text>
<line x1="218.0" y1="100.8" x2="244.2" y2="149.7" stroke="#555" stroke-width="1.8"/>
<polygon points="248.0,156.8 240.7,151.6 247.7,147.9" fill="#555"/>
<text x="218.0" y="81.8" fill="#555" text-anchor="middle" style="font-weight:bold">piciorul 1: high 99.90</text>
<text x="218.0" y="95.8" fill="#555" text-anchor="middle" style="font-weight:bold">rezistența ține</text>
<line x1="452.0" y1="80.0" x2="398.6" y2="117.4" stroke="#c00" stroke-width="1.8"/>
<polygon points="392.0,122.0 396.3,114.1 400.8,120.7" fill="#c00"/>
<text x="452.0" y="47.0" fill="#c00" text-anchor="middle" style="font-weight:bold">piciorul 2: high 100.50 — rupe nivelul în sus</text>
<text x="452.0" y="61.0" fill="#c00" text-anchor="middle" style="font-weight:bold">și mătură stopurile celor intrați pe short,</text>
<text x="452.0" y="75.0" fill="#c00" text-anchor="middle" style="font-weight:bold">dar nu poate ține acolo</text>
<line x1="430.0" y1="387.3" x2="410.8" y2="184.4" stroke="#188a3e" stroke-width="1.8"/>
<polygon points="410.0,176.4 414.7,184.0 406.8,184.7" fill="#188a3e"/>
<text x="430.0" y="401.3" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intri short la 99.80: close sub nivel și sub low-ul candelei-trigger</text>
<text x="430.0" y="415.3" fill="#188a3e" text-anchor="middle" style="font-weight:bold">cei prinși long vând ca să iasă — coborârea e bruscă</text>
<text x="24" y="540" fill="#555">Varianta short nu e explicată în curs: e reconstruită prin oglindire.</text>
</svg>

After

Width:  |  Height:  |  Size: 9.3 KiB

138
gen/strategii/mdsrt_stop.py Normal file
View File

@@ -0,0 +1,138 @@
"""MDSRT, Pasul 5 — unde pui stopul și de ce nu mai aproape.
Sus: cum numeri cele 4 candele înapoi de la candela de intrare, care e cel mai
jos low dintre ele și unde ajunge stopul (0.10 sub el).
Jos: același trade, cu o continuare construită în care prețul retestează
nivelul. Stopul „la ochi", pus sub candela de intrare, e măturat de retest;
stopul din regulă ține, iar trade-ul ajunge la țintă.
Cursul lasă două variante de stop (extrema ultimelor N candele sau ATR) și nu
dă valoarea lui N; N = 4 e valoarea folosită la celelalte strategii mecanice
ale cursului, iar marja de 0.10 e regula acestui capitol.
Rulează: python3 gen/strategii/mdsrt_stop.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
sys.path.insert(0, str(Path(__file__).parent))
from svg_candles import Candle, Chart, Level, Zone, Arrow # noqa: E402
from mdsrt_ansamblu import (serie, SUPORT, TRIG, CONF, ENTRY, SL, R, TP1, # noqa: E402
MARJA, STOP_BARS)
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, BLUE, GRAY, ORANGE = "#188a3e", "#c00", "#1a5fb4", "#555", "#b06000"
W, VGAP = 720, 16
S = 9 # prima bară arătată
base = serie()[S:CONF + 1]
t, cf = TRIG - S, CONF - S
low4 = min(c.l for c in base[cf - STOP_BARS + 1:cf + 1])
OCHI = round(base[cf].l - MARJA, 2) # stopul „la ochi", sub candela de intrare
# continuare construită: un retest care coboară sub candela de intrare
after = [Candle(*b) for b in [
(590.20, 590.35, 589.60, 589.85), # +1: retestul; mătură stopul „la ochi"
(589.85, 590.45, 589.80, 590.35),
(590.35, 590.75, 590.25, 590.65),
(590.65, 591.10, 590.55, 591.05), # +4: atinge ținta
]]
jos = base + after
# ----------------------------------------------------------------- assert-uri
for i, c in enumerate(jos):
assert c.h >= max(c.o, c.c) and c.l <= min(c.o, c.c), i
if i:
assert abs(c.o - jos[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]"
# 1. cel mai jos low din cele 4 candele e al piciorului 2, iar stopul e sub el
assert low4 == base[t].l == 589.50, (low4, base[t].l)
assert round(low4 - MARJA, 2) == SL == 589.40
assert round(ENTRY - SL, 2) == R == 0.80
# 2. cu 5 candele în loc de 4 iese același stop (minimul e tot al piciorului 2)
assert min(c.l for c in base[cf - 4:cf + 1]) == low4
# 3. stopul „la ochi" e mai aproape de intrare decât cel din regulă
assert SL < OCHI < ENTRY and OCHI == 589.65, OCHI
# 4. retestul mătură stopul „la ochi", dar nu-l atinge pe cel din regulă
assert min(c.l for c in after) == 589.60
assert SL < 589.60 < OCHI
assert next(k for k, c in enumerate(after) if c.l <= OCHI) == 0
assert all(c.l > SL for c in after), "stopul din regulă ar fi fost atins"
# 5. ținta e atinsă abia după retest, la a 4-a bară de după intrare
assert next(k for k, c in enumerate(after, 1) if c.h >= TP1) == 4
print(f"stop: low4 {low4:.2f} → SL {SL:.2f} (marjă {MARJA:.2f}); la ochi {OCHI:.2f} "
f"măturat de {min(c.l for c in after):.2f}; țintă {TP1:.2f} la a 4-a bară")
common: dict = dict(scale=52.0, gap=22, body_w=11, left=180, width=W)
sus = Chart(
candles=base,
levels=[Level(SUPORT, f"pivot daily {SUPORT:.2f}", BLUE, label_at="left"),
Level(SL, f"stopul din regulă: {SL:.2f}", RED, "4 3", label_at="left")],
zones=[Zone(cf - STOP_BARS + 1, cf, f"numeri {STOP_BARS} candele înapoi, de la intrare", BLUE)],
arrows=[
Arrow(t, f"cel mai jos low din cele {STOP_BARS}: {low4:.2f}\n"
f"stopul merge cu {MARJA:.2f} sub el",
where="below", price=low4, color=GRAY, dx=170, dy=34),
Arrow(cf, f"intrare {ENTRY:.2f}\nde aici numeri înapoi",
where="above", color=GREEN, dx=60, dy=-44),
],
title=f"Pasul 5 — stopul: {STOP_BARS} candele înapoi de la intrare, minus {MARJA:.2f}",
**common,
)
gata = Chart(
candles=jos,
levels=[Level(TP1, f"ținta 1:1: {TP1:.2f}", GREEN, "2 2", label_at="left"),
Level(ENTRY, f"intrarea: {ENTRY:.2f}", GRAY, "4 3", label_at="left"),
Level(OCHI, f"stop pus la ochi: {OCHI:.2f}", ORANGE, "4 3"),
Level(SL, f"stopul din regulă: {SL:.2f}", RED, "4 3", label_at="left")],
zones=[Zone(cf + 1, cf + 1, "", RED, p_top=OCHI, p_bot=SL)],
arrows=[
Arrow(cf + 1, f"retestul coboară la {min(c.l for c in after):.2f}:\n"
f"mătură stopul de {OCHI:.2f},\nnu-l atinge pe cel de {SL:.2f}",
where="below", price=589.60, color=ORANGE, dx=150, dy=26),
Arrow(len(jos) - 1, "cu stopul din regulă mai erai în trade\ncând a venit ținta",
where="above", price=TP1, color=GREEN, dx=40, dy=-14),
],
title="Același trade, cu un retest: stopul „la ochi” e măturat, cel din regulă nu",
caption="Barele de după intrare sunt construite, ca să arate retestul.",
**common,
)
def panou(ch, sus_de=0.0):
ch.top += sus_de
svg = ch.render()
h = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1))
off = float(re.search(r'<line x1="24" y1="([\d.]+)"', svg).group(1)) - ch._y(ch.levels[0].price)
return svg[svg.index(">") + 1:svg.rindex("</svg>")].strip(), h, off
body_sus, h_sus, off_sus = panou(sus)
# numerele 4 3 2 1 sub barele numărate
yb = sus._y(SL) + off_sus + 20
nums = [f'<text x="{sus._cx(k):.1f}" y="{yb:.1f}" fill="{BLUE}" text-anchor="middle" '
f'style="font-weight:bold">{n}</text>'
for k, n in zip(range(cf - STOP_BARS + 1, cf + 1), "4321")]
nums.append(f'<text x="{sus._cx(cf - STOP_BARS):.1f}" y="{yb:.1f}" fill="#999" '
f'text-anchor="middle">(5)</text>')
y_sub = max([yb] + [float(m) for m in re.findall(r'<text[^>]*\sy="([\d.]+)"', body_sus)]) + 24
nums.append(f'<text x="24" y="{y_sub:.1f}" fill="#555">cu 4 sau cu 5 candele iese același stop: '
f'după o coborâre, low-ul piciorului 2 e oricum cel mai jos</text>')
h_sus = max(h_sus, y_sub + 12)
body_jos, h_jos, _ = panou(gata, h_sus + VGAP)
H = h_jos + 8
svg = "\n ".join([
f'<svg viewBox="0 0 {W} {H:.0f}" xmlns="http://www.w3.org/2000/svg" '
f'style="width:100%;height:auto;font:12px sans-serif">',
body_sus, *nums,
f'<line x1="24" y1="{h_sus + VGAP / 2:.0f}" x2="{W - 14}" y2="{h_sus + VGAP / 2:.0f}" stroke="#ddd"/>',
body_jos,
]) + "\n</svg>"
assert "\n\n" not in svg, "linie goală în SVG"
for x, y in re.findall(r'<text x="([-\d.]+)" y="([-\d.]+)"', svg):
assert 0 <= float(x) <= W and 0 <= float(y) <= H, f"text în afara pânzei la ({x}, {y})"
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name} ({W}x{H:.0f})")

View File

@@ -0,0 +1,96 @@
<svg viewBox="0 0 720 715" 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">Pasul 5 — stopul: 4 candele înapoi de la intrare, minus 0.10</text>
<rect x="323.5" y="118.2" width="87.0" height="99.2" fill="#1a5fb4" fill-opacity="0.08" stroke="#1a5fb4" stroke-width="1"/>
<text x="323.5" y="113.2" fill="#1a5fb4" style="font-weight:bold">numeri 4 candele înapoi, de la intrare</text>
<line x1="24" y1="183.4" x2="706" y2="183.4" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="177.4" fill="#1a5fb4">pivot daily 590.00</text>
<line x1="24" y1="214.6" x2="706" y2="214.6" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="208.6" fill="#c00">stopul din regulă: 589.40</text>
<line x1="180.0" y1="56.0" x2="180.0" y2="115.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="174.5" y="69.0" width="11" height="31.2" fill="#c00"/>
<line x1="202.0" y1="87.2" x2="202.0" y2="178.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="196.5" y="100.2" width="11" height="62.4" fill="#c00"/>
<line x1="224.0" y1="123.6" x2="224.0" y2="178.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="218.5" y="136.6" width="11" height="26.0" fill="#188a3e"/>
<line x1="246.0" y1="97.6" x2="246.0" y2="152.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="240.5" y="110.6" width="11" height="26.0" fill="#188a3e"/>
<line x1="268.0" y1="97.6" x2="268.0" y2="141.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="262.5" y="110.6" width="11" height="15.6" fill="#c00"/>
<line x1="290.0" y1="102.8" x2="290.0" y2="141.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="284.5" y="115.8" width="11" height="10.4" fill="#188a3e"/>
<line x1="312.0" y1="102.8" x2="312.0" y2="152.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="306.5" y="115.8" width="11" height="20.8" fill="#c00"/>
<line x1="334.0" y1="126.2" x2="334.0" y2="165.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="328.5" y="136.6" width="11" height="20.8" fill="#c00"/>
<line x1="356.0" y1="149.6" x2="356.0" y2="186.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="350.5" y="157.4" width="11" height="23.4" fill="#c00"/>
<line x1="378.0" y1="175.6" x2="378.0" y2="209.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="372.5" y="180.8" width="11" height="13.0" fill="#c00"/>
<line x1="400.0" y1="167.8" x2="400.0" y2="196.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="394.5" y="173.0" width="11" height="20.8" fill="#188a3e"/>
<line x1="548.0" y1="273.4" x2="385.5" y2="216.1" stroke="#555" stroke-width="1.8"/>
<polygon points="378.0,213.4 386.9,212.3 384.2,219.8" fill="#555"/>
<text x="548.0" y="287.4" fill="#555" text-anchor="middle" style="font-weight:bold">cel mai jos low din cele 4: 589.50</text>
<text x="548.0" y="301.4" fill="#555" text-anchor="middle" style="font-weight:bold">stopul merge cu 0.10 sub el</text>
<line x1="460.0" y1="93.8" x2="405.2" y2="157.7" stroke="#188a3e" stroke-width="1.8"/>
<polygon points="400.0,163.8 402.2,155.1 408.2,160.3" fill="#188a3e"/>
<text x="460.0" y="74.8" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intrare 590.20</text>
<text x="460.0" y="88.8" fill="#188a3e" text-anchor="middle" style="font-weight:bold">de aici numeri înapoi</text>
<text x="334.0" y="234.6" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">4</text>
<text x="356.0" y="234.6" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">3</text>
<text x="378.0" y="234.6" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">2</text>
<text x="400.0" y="234.6" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">1</text>
<text x="312.0" y="234.6" fill="#999" text-anchor="middle">(5)</text>
<text x="24" y="325.4" fill="#555">cu 4 sau cu 5 candele iese același stop: după o coborâre, low-ul piciorului 2 e oricum cel mai jos</text>
<line x1="24" y1="345" x2="706" y2="345" stroke="#ddd"/>
<text x="24" y="401" fill="#333" style="font-weight:bold">Același trade, cu un retest: stopul „la ochi” e măturat, cel din regulă nu</text>
<rect x="411.5" y="555.0" width="21.0" height="13.0" fill="#c00" fill-opacity="0.08" stroke="#c00" stroke-width="1"/>
<line x1="24" y1="484.8" x2="706" y2="484.8" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 2"/>
<text x="30" y="478.8" fill="#188a3e">ținta 1:1: 591.00</text>
<line x1="24" y1="526.4" x2="706" y2="526.4" stroke="#555" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="520.4" fill="#555">intrarea: 590.20</text>
<line x1="24" y1="555.0" x2="706" y2="555.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="692" y="549.0" fill="#b06000" text-anchor="end">stop pus la ochi: 589.65</text>
<line x1="24" y1="568.0" x2="706" y2="568.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="562.0" fill="#c00">stopul din regulă: 589.40</text>
<line x1="180.0" y1="409.4" x2="180.0" y2="469.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="174.5" y="422.4" width="11" height="31.2" fill="#c00"/>
<line x1="202.0" y1="440.6" x2="202.0" y2="531.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="196.5" y="453.6" width="11" height="62.4" fill="#c00"/>
<line x1="224.0" y1="477.0" x2="224.0" y2="531.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="218.5" y="490.0" width="11" height="26.0" fill="#188a3e"/>
<line x1="246.0" y1="451.0" x2="246.0" y2="505.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="240.5" y="464.0" width="11" height="26.0" fill="#188a3e"/>
<line x1="268.0" y1="451.0" x2="268.0" y2="495.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="262.5" y="464.0" width="11" height="15.6" fill="#c00"/>
<line x1="290.0" y1="456.2" x2="290.0" y2="495.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="284.5" y="469.2" width="11" height="10.4" fill="#188a3e"/>
<line x1="312.0" y1="456.2" x2="312.0" y2="505.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="306.5" y="469.2" width="11" height="20.8" fill="#c00"/>
<line x1="334.0" y1="479.6" x2="334.0" y2="518.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="328.5" y="490.0" width="11" height="20.8" fill="#c00"/>
<line x1="356.0" y1="503.0" x2="356.0" y2="539.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="350.5" y="510.8" width="11" height="23.4" fill="#c00"/>
<line x1="378.0" y1="529.0" x2="378.0" y2="562.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="372.5" y="534.2" width="11" height="13.0" fill="#c00"/>
<line x1="400.0" y1="521.2" x2="400.0" y2="549.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="394.5" y="526.4" width="11" height="20.8" fill="#188a3e"/>
<line x1="422.0" y1="518.6" x2="422.0" y2="557.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="416.5" y="526.4" width="11" height="18.2" fill="#c00"/>
<line x1="444.0" y1="513.4" x2="444.0" y2="547.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="438.5" y="518.6" width="11" height="26.0" fill="#188a3e"/>
<line x1="466.0" y1="497.8" x2="466.0" y2="523.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="460.5" y="503.0" width="11" height="15.6" fill="#188a3e"/>
<line x1="488.0" y1="479.6" x2="488.0" y2="508.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="482.5" y="482.2" width="11" height="20.8" fill="#188a3e"/>
<line x1="572.0" y1="613.6" x2="429.6" y2="564.2" stroke="#b06000" stroke-width="1.8"/>
<polygon points="422.0,561.6 430.9,560.4 428.2,568.0" fill="#b06000"/>
<text x="572.0" y="627.6" fill="#b06000" text-anchor="middle" style="font-weight:bold">retestul coboară la 589.60:</text>
<text x="572.0" y="641.6" fill="#b06000" text-anchor="middle" style="font-weight:bold">mătură stopul de 589.65,</text>
<text x="572.0" y="655.6" fill="#b06000" text-anchor="middle" style="font-weight:bold">nu-l atinge pe cel de 589.40</text>
<line x1="528.0" y1="440.8" x2="493.7" y2="475.1" stroke="#188a3e" stroke-width="1.8"/>
<polygon points="488.0,480.8 490.8,472.3 496.5,478.0" fill="#188a3e"/>
<text x="528.0" y="421.8" fill="#188a3e" text-anchor="middle" style="font-weight:bold">cu stopul din regulă mai erai în trade</text>
<text x="528.0" y="435.8" fill="#188a3e" text-anchor="middle" style="font-weight:bold">când a venit ținta</text>
<text x="24" y="695" fill="#555">Barele de după intrare sunt construite, ca să arate retestul.</text>
</svg>

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -0,0 +1,114 @@
"""MDSRT, Pasul 4 — trigger valid (DA) vs. trigger prematur (NU).
Aceeași formă de candelă, două locuri diferite. Sus: candela-trigger apare
după ce M e complet (prețul e în zona pivotului și divergența s-a format
acolo) — seria e cea canonică a capitolului, importată din `mdsrt_ansamblu`.
Jos: o candelă la fel de frumoasă, dar la 2.50 deasupra zonei; M nu e complet,
iar stopul pus sub ea e lovit de candela următoare.
Regula „forma candelei nu e trigger dacă M nu e complet" e a cursului
(`05_MODUL_4/02_strategia_5_mdsrt.md:249-311`); prețurile sunt construite, pe
scara exemplului din capitol.
Rulează: python3 gen/strategii/mdsrt_trigger.py
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
sys.path.insert(0, str(Path(__file__).parent))
from svg_candles import Chart, Level, Zone, Arrow, from_closes # noqa: E402
from mdsrt_ansamblu import serie, SUPORT, TRIG, CONF, ENTRY, SL, MARJA # noqa: E402
OUT = Path(__file__).with_suffix(".svg")
GREEN, RED, BLUE, GRAY, ORANGE = "#188a3e", "#c00", "#1a5fb4", "#555", "#b06000"
W, VGAP = 700, 16
S = 8 # prima bară arătată din seria canonică
# ---------------------------------------------------------------- panoul DA
da = serie()[S:CONF + 4]
t, cf = TRIG - S, CONF - S
assert da[t].l == 589.50 < SUPORT, da[t].l
assert da[cf].c == ENTRY > da[t].h, (ENTRY, da[t].h)
# ---------------------------------------------------------------- panoul NU
NU_CLOSES = [595.80, 595.20, 594.60, 594.00, 593.40, 592.90, 592.55,
592.50, # 7 = doji frumos, dar sus
592.10, 591.60, 591.20, 590.80, 590.50, 590.20, 589.90, 589.60]
NU_WICKS = [(0.25, 0.30)] * len(NU_CLOSES)
NU_WICKS[7] = (0.35, 0.35)
nu = from_closes(NU_CLOSES, wicks=NU_WICKS, start=596.20)
D = 7
NU_SL = round(nu[D].l - MARJA, 2)
# doji: corp de cel mult 10 % din înălțimea candelei, fitile în ambele părți
body = abs(nu[D].o - nu[D].c)
rng = nu[D].h - nu[D].l
assert body <= 0.10 * rng, (body, rng)
assert nu[D].h > max(nu[D].o, nu[D].c) and nu[D].l < min(nu[D].o, nu[D].c)
# e la 2.50 deasupra zonei — asta e tot ce-l deosebește de cel de sus
assert round(nu[D].c - SUPORT, 2) == 2.50, nu[D].c - SUPORT
# stopul pus sub el e lovit chiar de candela următoare
lovit = next(i for i in range(D + 1, len(nu)) if nu[i].l <= NU_SL)
assert lovit == D + 1, lovit
# prețul chiar ajunge, mai târziu, în zona în care trigger-ul ar fi contat
assert min(c.l for c in nu) < SUPORT
print(f"trigger: DA la {da[t].l:.2f} (sub {SUPORT:.2f}), intrare {ENTRY:.2f}, SL {SL:.2f}; "
f"NU la {nu[D].c:.2f} (+2.50), stop {NU_SL:.2f} lovit la bara {lovit}")
common: dict = dict(scale=40.0, gap=20, body_w=10, left=170, width=W)
sus = Chart(
candles=da,
levels=[Level(SUPORT, f"pivot daily {SUPORT:.2f}", BLUE, label_at="left"),
Level(ENTRY, f"intrare {ENTRY:.2f}", GREEN, "6 3"),
Level(SL, f"SL {SL:.2f}", RED, "4 3")],
zones=[Zone(t, cf, "M complet: preț în zonă + divergență acolo", GREEN)],
arrows=[Arrow(t, "DA — aceeași formă, dar în zonă:\ntrigger-ul contează",
where="below", price=da[t].l, color=GREEN, dx=140, dy=30)],
title="DA — trigger valid: candela apare după ce M e complet",
**common,
)
jos = Chart(
candles=nu,
levels=[Level(SUPORT, f"pivot daily {SUPORT:.2f} — zona e abia aici", BLUE, label_at="left"),
Level(NU_SL, f"stopul pus sub doji: {NU_SL:.2f}", RED, "4 3")],
zones=[Zone(D, D, "doji corect ca formă", ORANGE)],
arrows=[
Arrow(D, f"NU — doji la {nu[D].c:.2f}, cu 2.50 deasupra zonei:\n"
f"M nu e complet, deci nu e trigger",
where="above", color=RED, dx=60, dy=-18),
Arrow(D + 1, f"stopul de sub el, {NU_SL:.2f}, e lovit\nchiar de candela următoare",
where="below", price=NU_SL, color=RED, dx=170, dy=30),
],
title="NU — trigger prematur: aceeași candelă, dar deasupra zonei",
caption="Ce deosebește cele două candele nu e forma, ci unde stă prețul când apar.",
**common,
)
def panou(ch, sus_de=0.0):
ch.top += sus_de
svg = ch.render()
h = float(re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg).group(1))
off = float(re.search(r'<line x1="24" y1="([\d.]+)"', svg).group(1)) - ch._y(ch.levels[0].price)
assert abs(off) < 40, off
return svg[svg.index(">") + 1:svg.rindex("</svg>")].strip(), h
body_sus, h_sus = panou(sus)
body_jos, h_jos = panou(jos, h_sus + VGAP)
H = h_jos + 8
svg = "\n ".join([
f'<svg viewBox="0 0 {W} {H:.0f}" xmlns="http://www.w3.org/2000/svg" '
f'style="width:100%;height:auto;font:12px sans-serif">',
body_sus,
f'<line x1="24" y1="{h_sus + VGAP / 2:.0f}" x2="{W - 14}" y2="{h_sus + VGAP / 2:.0f}" stroke="#ddd"/>',
body_jos,
]) + "\n</svg>"
assert "\n\n" not in svg, "linie goală în SVG"
for x, y in re.findall(r'<text x="([-\d.]+)" y="([-\d.]+)"', svg):
assert 0 <= float(x) <= W and 0 <= float(y) <= H, f"text în afara pânzei la ({x}, {y})"
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name} ({W}x{H:.0f})")

View File

@@ -0,0 +1,94 @@
<svg viewBox="0 0 700 808" 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">DA — trigger valid: candela apare după ce M e complet</text>
<rect x="360.0" y="162.0" width="40.0" height="48.0" fill="#188a3e" fill-opacity="0.08" stroke="#188a3e" stroke-width="1"/>
<text x="360.0" y="123.0" fill="#188a3e" style="font-weight:bold">M complet: preț în zonă + divergență acolo</text>
<line x1="24" y1="182.0" x2="686" y2="182.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="176.0" fill="#1a5fb4">pivot daily 590.00</text>
<line x1="24" y1="174.0" x2="686" y2="174.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="6 3"/>
<text x="672" y="168.0" fill="#188a3e" text-anchor="end">intrare 590.20</text>
<line x1="24" y1="206.0" x2="686" y2="206.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="672" y="200.0" fill="#c00" text-anchor="end">SL 589.40</text>
<line x1="170.0" y1="56.0" x2="170.0" y2="106.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="165.0" y="66.0" width="10" height="28.0" fill="#c00"/>
<line x1="190.0" y1="84.0" x2="190.0" y2="130.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="185.0" y="94.0" width="10" height="24.0" fill="#c00"/>
<line x1="210.0" y1="108.0" x2="210.0" y2="178.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="205.0" y="118.0" width="10" height="48.0" fill="#c00"/>
<line x1="230.0" y1="136.0" x2="230.0" y2="178.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="225.0" y="146.0" width="10" height="20.0" fill="#188a3e"/>
<line x1="250.0" y1="116.0" x2="250.0" y2="158.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="245.0" y="126.0" width="10" height="20.0" fill="#188a3e"/>
<line x1="270.0" y1="116.0" x2="270.0" y2="150.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="265.0" y="126.0" width="10" height="12.0" fill="#c00"/>
<line x1="290.0" y1="120.0" x2="290.0" y2="150.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="285.0" y="130.0" width="10" height="8.0" fill="#188a3e"/>
<line x1="310.0" y1="120.0" x2="310.0" y2="158.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="305.0" y="130.0" width="10" height="16.0" fill="#c00"/>
<line x1="330.0" y1="138.0" x2="330.0" y2="168.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="325.0" y="146.0" width="10" height="16.0" fill="#c00"/>
<line x1="350.0" y1="156.0" x2="350.0" y2="184.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="345.0" y="162.0" width="10" height="18.0" fill="#c00"/>
<line x1="370.0" y1="176.0" x2="370.0" y2="202.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="365.0" y="180.0" width="10" height="10.0" fill="#c00"/>
<line x1="390.0" y1="170.0" x2="390.0" y2="192.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="385.0" y="174.0" width="10" height="16.0" fill="#188a3e"/>
<line x1="410.0" y1="148.0" x2="410.0" y2="180.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="405.0" y="158.0" width="10" height="16.0" fill="#188a3e"/>
<line x1="430.0" y1="144.0" x2="430.0" y2="164.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="425.0" y="146.0" width="10" height="12.0" fill="#188a3e"/>
<line x1="450.0" y1="132.0" x2="450.0" y2="158.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="445.0" y="142.0" width="10" height="4.0" fill="#188a3e"/>
<line x1="510.0" y1="262.0" x2="377.4" y2="209.0" stroke="#188a3e" stroke-width="1.8"/>
<polygon points="370.0,206.0 378.9,205.3 375.9,212.7" fill="#188a3e"/>
<text x="510.0" y="276.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">DA — aceeași formă, dar în zonă:</text>
<text x="510.0" y="290.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">trigger-ul contează</text>
<line x1="24" y1="333" x2="686" y2="333" stroke="#ddd"/>
<text x="24" y="389" fill="#333" style="font-weight:bold">NU — trigger prematur: aceeași candelă, dar deasupra zonei</text>
<rect x="300.0" y="531.0" width="20.0" height="46.0" fill="#b06000" fill-opacity="0.08" stroke="#b06000" stroke-width="1"/>
<text x="300.0" y="526.0" fill="#b06000" style="font-weight:bold">doji corect ca formă</text>
<line x1="24" y1="655.0" x2="686" y2="655.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="649.0" fill="#1a5fb4">pivot daily 590.00 — zona e abia aici</text>
<line x1="24" y1="573.0" x2="686" y2="573.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="672" y="567.0" fill="#c00" text-anchor="end">stopul pus sub doji: 592.05</text>
<line x1="170.0" y1="397.0" x2="170.0" y2="435.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="165.0" y="407.0" width="10" height="16.0" fill="#c00"/>
<line x1="190.0" y1="413.0" x2="190.0" y2="459.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="185.0" y="423.0" width="10" height="24.0" fill="#c00"/>
<line x1="210.0" y1="437.0" x2="210.0" y2="483.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="205.0" y="447.0" width="10" height="24.0" fill="#c00"/>
<line x1="230.0" y1="461.0" x2="230.0" y2="507.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="225.0" y="471.0" width="10" height="24.0" fill="#c00"/>
<line x1="250.0" y1="485.0" x2="250.0" y2="531.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="245.0" y="495.0" width="10" height="24.0" fill="#c00"/>
<line x1="270.0" y1="509.0" x2="270.0" y2="551.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="265.0" y="519.0" width="10" height="20.0" fill="#c00"/>
<line x1="290.0" y1="529.0" x2="290.0" y2="565.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="285.0" y="539.0" width="10" height="14.0" fill="#c00"/>
<line x1="310.0" y1="539.0" x2="310.0" y2="569.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="305.0" y="553.0" width="10" height="2.0" fill="#c00"/>
<line x1="330.0" y1="545.0" x2="330.0" y2="583.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="325.0" y="555.0" width="10" height="16.0" fill="#c00"/>
<line x1="350.0" y1="561.0" x2="350.0" y2="603.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="345.0" y="571.0" width="10" height="20.0" fill="#c00"/>
<line x1="370.0" y1="581.0" x2="370.0" y2="619.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="365.0" y="591.0" width="10" height="16.0" fill="#c00"/>
<line x1="390.0" y1="597.0" x2="390.0" y2="635.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="385.0" y="607.0" width="10" height="16.0" fill="#c00"/>
<line x1="410.0" y1="613.0" x2="410.0" y2="647.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="405.0" y="623.0" width="10" height="12.0" fill="#c00"/>
<line x1="430.0" y1="625.0" x2="430.0" y2="659.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="425.0" y="635.0" width="10" height="12.0" fill="#c00"/>
<line x1="450.0" y1="637.0" x2="450.0" y2="671.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="445.0" y="647.0" width="10" height="12.0" fill="#c00"/>
<line x1="470.0" y1="649.0" x2="470.0" y2="683.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="465.0" y="659.0" width="10" height="12.0" fill="#c00"/>
<line x1="370.0" y1="435.0" x2="314.1" y2="528.1" stroke="#c00" stroke-width="1.8"/>
<polygon points="310.0,535.0 310.7,526.1 317.5,530.2" fill="#c00"/>
<text x="370.0" y="416.0" fill="#c00" text-anchor="middle" style="font-weight:bold">NU — doji la 592.50, cu 2.50 deasupra zonei:</text>
<text x="370.0" y="430.0" fill="#c00" text-anchor="middle" style="font-weight:bold">M nu e complet, deci nu e trigger</text>
<line x1="500.0" y1="721.0" x2="336.1" y2="582.2" stroke="#c00" stroke-width="1.8"/>
<polygon points="330.0,577.0 338.7,579.1 333.5,585.2" fill="#c00"/>
<text x="500.0" y="735.0" fill="#c00" text-anchor="middle" style="font-weight:bold">stopul de sub el, 592.05, e lovit</text>
<text x="500.0" y="749.0" fill="#c00" text-anchor="middle" style="font-weight:bold">chiar de candela următoare</text>
<text x="24" y="788" fill="#555">Ce deosebește cele două candele nu e forma, ci unde stă prețul când apar.</text>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

313
gen/strategii/mdsrt_zona.py Normal file
View File

@@ -0,0 +1,313 @@
"""MDSRT, Pasul 1 — nivelul trăiește pe TF-ul mare, trigger-ul pe cel mic.
Un singur rând: daily (stânga) → 60 min (dreapta). Graficul din dreapta NU e
o serie care vine după cel din stânga: e **descompunerea reală** a ultimelor
două candele daily. O zi de bursă are 7 bare de 60 de minute (ședința
9:30-16:00 are 6 ore și jumătate — ultima bară a zilei e de 30 de minute),
deci 2 candele daily = 14 bare de 60 de minute, cu open, close, max(high) și
min(low) care coincid — verificat bară cu bară prin assert.
Sursa de adevăr e seria fină; candelele daily se calculează din ea prin
agregare. Pivotul monthly (76.00) e desenat pe amândouă panourile, cu același
număr de ambele părți, și legat printr-un conector — scările au pante
diferite, deci același preț cade la înălțimi diferite: e o lupă, nu o eroare.
Prețurile sunt ale altui simbol decât exemplul din ceilalți pași (care e pe
SPY, 5 min); regulile sunt aceleași. Toate cifrele din desen ies din serii.
Rulează: python3 gen/strategii/mdsrt_zona.py
"""
from pathlib import Path
OUT = Path(__file__).with_suffix(".svg")
W = 720
MARJA = 0.10 # aceeași marjă de stop ca în tot capitolul
PIVOT = 76.00 # pivot monthly — nivelul de pe TF-ul mare
K = 7 # 7 bare de 60 min pe zi
NZ = 2 # 2 candele daily mărite
# --- seria fină (60 min): (open, high, low, close) --------------------------
# 0-6 = ziua 1: coborârea spre pivot; bara 4 = piciorul 1 (low 76.05, ține nivelul)
# 7-13 = ziua 2: piciorul 2 rupe pivotul (bara 10, hammer), apoi intrarea
FINE = [
(77.40, 77.55, 77.05, 77.15), (77.15, 77.30, 76.80, 76.90),
(76.90, 77.00, 76.50, 76.60), (76.60, 76.75, 76.30, 76.45),
(76.45, 76.55, 76.05, 76.20), # 4: piciorul 1, low 76.05
(76.20, 76.60, 76.15, 76.50), (76.50, 76.65, 76.25, 76.35),
(76.35, 76.45, 76.05, 76.15), (76.15, 76.25, 75.90, 75.95),
(75.95, 76.05, 75.85, 75.95),
(75.95, 76.20, 75.70, 76.10), # 10: piciorul 2, hammer (low 75.70)
(76.10, 76.40, 76.05, 76.30), # 11: intrarea (close peste high-ul 76.20)
(76.30, 76.75, 76.20, 76.60), (76.60, 77.15, 76.55, 77.05),
]
# --- candelele daily dinaintea casetei de lupă ------------------------------
BASE = [
(86.00, 86.60, 84.20, 84.60), (84.60, 85.20, 83.00, 83.40),
(83.40, 84.60, 82.80, 84.00), (84.00, 84.40, 81.50, 81.90),
(81.90, 82.40, 80.20, 80.60), (80.60, 81.20, 79.00, 79.40),
(79.40, 80.40, 78.20, 79.60), (79.60, 80.00, 77.20, 77.40),
]
LEG1, LEG2, ENTRY_I = 4, 10, 11
def aggregate(fine, k, n):
out_ = []
for j in range(n):
part = fine[j * k:(j + 1) * k]
out_.append((part[0][0], max(b[1] for b in part),
min(b[2] for b in part), part[-1][3]))
return out_
def eq(a, b):
return abs(a - b) < 0.005
zoom = aggregate(FINE, K, NZ)
big = BASE + zoom
# ------------------------------------------------------------- assert-urile
for name, bars in (("daily", big), ("60 min", FINE)):
for i, (o, h, l, c) in enumerate(bars):
assert h >= max(o, c) and l <= min(o, c), f"{name}[{i}] h/l incoerent"
if i:
assert eq(o, bars[i - 1][3]), f"{name}[{i}] open != close precedent"
assert len(FINE) == K * NZ
assert eq(FINE[0][0], BASE[-1][3]), "lupa nu se leagă de ultima candelă daily"
# 1. proprietăți pe care textul le pretinde (nu recalculul agregării)
assert LEG1 // K == 0 and LEG2 // K == 1, "cele două picioare nu cad în zile diferite"
assert LEG2 - LEG1 >= 5, "sub minimul de 5 bare între punctele divergenței"
leg1_l, leg2_l = FINE[LEG1][2], FINE[LEG2][2]
assert leg1_l == 76.05 > PIVOT, leg1_l
assert leg2_l == 75.70 < PIVOT, leg2_l
assert leg2_l == min(b[2] for b in FINE), "piciorul 2 nu e minimul zonei mărite"
assert leg1_l == min(b[2] for b in FINE[:K]), "piciorul 1 nu e minimul zilei 1"
# 2. ziua a doua, ca o singură candelă daily, NU arată piciorul 2 ca minim propriu
assert eq(zoom[1][2], leg2_l), "low-ul zilei 2 nu e low-ul piciorului 2"
assert zoom[1][3] > PIVOT, "ziua 2 nu se închide peste pivot"
# 3. hammer-ul: corp mic sus, coadă lungă în jos, close înapoi peste pivot
o, h, l, c = FINE[LEG2]
assert abs(o - c) <= 0.5 * (h - l) and min(o, c) - l > abs(o - c), (o, h, l, c)
assert c > PIVOT, c
# 4. trade-ul, cu regulile capitolului
low4 = min(b[2] for b in FINE[ENTRY_I - 3:ENTRY_I + 1])
assert eq(low4, leg2_l), low4
sl = round(low4 - MARJA, 2)
entry = FINE[ENTRY_I][3]
assert entry > FINE[LEG2][1], "intrarea nu sparge high-ul candelei-trigger"
risk = round(entry - sl, 2)
tp = round(entry + risk, 2)
first = next(i for i in range(ENTRY_I + 1, len(FINE)) if FINE[i][1] >= tp - 1e-9)
assert (sl, entry, risk, tp, first) == (75.60, 76.30, 0.70, 77.00, 13), \
(sl, entry, risk, tp, first)
# 5. stopul măsurat pe daily (sub low-ul zilei 2) e de câteva ori mai larg
b_sl = round(zoom[1][2] - MARJA, 2)
b_entry = zoom[1][3]
b_risk = round(b_entry - b_sl, 2)
ratio = (b_risk / b_entry) / (risk / entry)
assert 1.8 <= ratio <= 2.5, ratio
# --- desen ------------------------------------------------------------------
GREEN, RED, GRAY, INK, BLUE = "#188a3e", "#c00", "#888", "#333", "#1a5fb4"
LINK = "#aaa"
PLOT_H = 190
BX0, BGAP = 105, 15
SX0, SGAP = 372, 15
BODY_W = 9
FRAME_L = SX0 - 9
COL_X = 600
MIDX = FRAME_L - 20
PAD = 0.06
out, LABELS = [], []
def text(x, y, s, fill=INK, anchor="start", bold=False, size=12):
st = [f"font-size:{size}px"] + (["font-weight:bold"] if bold else [])
a = f' text-anchor="{anchor}"' if anchor != "start" else ""
w = len(s) * (5.7 if size >= 12 else size * 0.5)
x0 = x if anchor == "start" else (x - w / 2 if anchor == "middle" else x - w)
assert 4 <= x0 and x0 + w <= W - 4, f"text iese din viewBox: {s!r} ({x0:.0f}..{x0 + w:.0f})"
LABELS.append((x0, x0 + w, y, s))
out.append(f'<text x="{x:.1f}" y="{y:.1f}" fill="{fill}"{a} style="{";".join(st)}">{s}</text>')
def hline(x1, x2, y, color, dash, width=1.5):
out.append(f'<line x1="{x1:.1f}" y1="{y:.1f}" x2="{x2:.1f}" y2="{y:.1f}" '
f'stroke="{color}" stroke-width="{width}" stroke-dasharray="{dash}"/>')
def line(x1, y1, x2, y2, color, dash="", width=1.2, marker=""):
d = f' stroke-dasharray="{dash}"' if dash else ""
m = f' marker-end="url(#{marker})"' if marker else ""
out.append(f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" '
f'stroke="{color}" stroke-width="{width}"{d}{m}/>')
def rect(x0_, y0_, x1, y1, color, fill_op=0.10, dash=""):
d = f' stroke-dasharray="{dash}"' if dash else ""
out.append(f'<rect x="{x0_:.1f}" y="{y0_:.1f}" width="{x1 - x0_:.1f}" '
f'height="{y1 - y0_:.1f}" fill="{color}" fill-opacity="{fill_op}" '
f'stroke="{color}" stroke-width="1.2"{d}/>')
def candles(bars, x0, gap, yf):
for i, (o_, h_, l_, c_) in enumerate(bars):
cx = x0 + i * gap
line(cx, yf(h_), cx, yf(l_), "#1a1a1a", width=1.2)
top_ = yf(max(o_, c_))
hh = max(yf(min(o_, c_)) - top_, 1.6)
out.append(f'<rect x="{cx - BODY_W / 2:.1f}" y="{top_:.1f}" width="{BODY_W}" '
f'height="{hh:.1f}" fill="{GREEN if c_ >= o_ else RED}"/>')
def seg_dist(p, a, b):
(px, py), (ax, ay), (bx, by) = p, a, b
dx, dy = bx - ax, by - ay
t = 0.0 if dx == dy == 0 else max(0.0, min(1.0, ((px - ax) * dx + (py - ay) * dy)
/ (dx * dx + dy * dy)))
return ((px - ax - t * dx) ** 2 + (py - ay - t * dy) ** 2) ** 0.5
out.append('<defs><marker id="pt-red" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" '
'markerHeight="7" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#c00"/></marker>'
'</defs>')
text(20, 22, "Pasul 1 — nivelul se citește pe daily, trigger-ul se ia pe 60 min",
bold=True, size=13)
text(20, 40, "Graficul din dreapta e aceeași bucată de piață, mărită: 2 candele daily = 14 bare "
"de 60 min", fill="#555", size=11)
top = 76
text(20, top - 8, "daily: unde e nivelul", fill="#555", size=11)
text(FRAME_L, top - 8, "60 min: descompunerea ultimelor 2 candele daily", fill="#555", size=11)
zlo_, zhi_ = sl, max(b[1] for b in FINE)
pad = PAD * (zhi_ - zlo_)
lo, hi = zlo_ - pad, zhi_ + pad
bmax = max(b[1] for b in big)
bmin = min(min(b[2] for b in big), lo)
bs, ss = PLOT_H / (bmax - bmin), PLOT_H / (hi - lo)
yb = lambda p: top + (bmax - p) * bs # noqa: E731
ys = lambda p: top + (hi - p) * ss # noqa: E731
# ---- panoul daily ----------------------------------------------------------
lx0, lx1 = BX0 + len(BASE) * BGAP - 6, BX0 + (len(big) - 1) * BGAP + 9
ly0, ly1 = yb(hi), yb(lo)
rect(lx0, ly0, lx1, ly1, BLUE, fill_op=0.06, dash="4 3")
hline(BX0 - 15, lx1, yb(PIVOT), BLUE, "4 3")
text(30, yb(PIVOT) + 12, "pivot monthly", fill=BLUE, size=11)
text(30, yb(PIVOT) + 26, f"{PIVOT:.2f}", fill=BLUE, size=11, bold=True)
candles(big, BX0, BGAP, yb)
# zona de evidențiere: ultimele candele daily, acolo unde prețul ajunge la nivel
zx0, zx1 = BX0 + (len(BASE) - 2) * BGAP - 8, lx0 - 3
rect(zx0, yb(big[-3][1]) - 4, zx1, yb(PIVOT) + 10, GREEN, fill_op=0.10)
text(BX0 - 15, top + PLOT_H + 20, "downtrend care se oprește la un nivel:", fill=GREEN,
size=11, bold=True)
text(BX0 - 15, top + PLOT_H + 34, "fără nivelul de dedesubt e doar downtrend", fill=GREEN,
size=11, bold=True)
text(BX0 - 15, top + PLOT_H + 48, "care continuă — nu ai unde pune stopul", fill=GREEN,
size=11, bold=True)
# pe daily nu ai unde să intri mai devreme de close-ul zilei: stopul e același
# (low-ul zilei = low-ul piciorului 2), dar intrarea e cu o zi întreagă mai sus
line(BX0 - 19, yb(b_entry), lx1, yb(b_entry), GREEN, dash="6 3", width=1.3)
text(30, yb(b_entry) - 20, f"intrare pe daily {b_entry:.2f} = close-ul zilei", fill=GREEN, size=10)
text(30, yb(b_entry) - 6, "(mai devreme n-ai ce citi pe daily)", fill=GREEN, size=10)
# ---- ghidajele de lupă -----------------------------------------------------
guides = [((lx1, ly0), (FRAME_L, top)), ((lx1, ly1), (FRAME_L, top + PLOT_H))]
for a_, b_ in guides:
line(a_[0], a_[1], b_[0], b_[1], BLUE, dash="5 3", width=1.1)
# ---- conectorul nivelului comun -------------------------------------------
ycb, ycs = yb(PIVOT), ys(PIVOT)
def clearance(mx):
conn = [((lx1, ycb), (mx, ycb)), ((mx, ycb), (mx, ycs)), ((mx, ycs), (FRAME_L, ycs))]
pts = [(sx + (ex - sx) * t / 24, sy + (ey - sy) * t / 24)
for (sx, sy), (ex, ey) in conn for t in range(25)]
return min(seg_dist(q, ga, gb) for q in pts if q[0] >= lx1 + 10 for ga, gb in guides)
mid = max(range(int(lx1) + 14, int(FRAME_L) - 13), key=clearance)
# pivotul e aproape de marginea de jos a lupei, deci conectorul are de mers
# printr-un coridor îngust între cele două ghidaje; 8 px e minimul la care
# linia lui punctată se citește încă separat de ghidaj.
assert clearance(mid) > 8, f"conectorul trece la {clearance(mid):.1f} px de un ghidaj"
out.append(f'<path d="M{lx1:.1f},{ycb:.1f} H{mid:.1f} V{ycs:.1f} H{FRAME_L:.1f}" '
f'fill="none" stroke={LINK!r} stroke-width="1.0" stroke-dasharray="3 3"/>')
text(min(mid + 5, FRAME_L - 58), (ycb + ycs) / 2 + 4, "același nivel", fill="#888", size=10)
# ---- panoul 60 min ---------------------------------------------------------
y_e, y_sl, y_tp, y_p = ys(entry), ys(sl), ys(tp), ys(PIVOT)
assert abs((y_sl - y_e) / (y_e - y_tp) - 1) < 1e-6, "SL-intrare != intrare-TP1 în px"
assert top + PLOT_H - y_sl > 6, "linia de stop e lipită de marginea cadrului"
frame_r = SX0 + (len(FINE) - 1) * SGAP + 9
rect(FRAME_L, top, frame_r, top + PLOT_H, BLUE, fill_op=0.03)
# zona verde peste piciorul 2 + intrare
sx0, sx1 = SX0 + LEG2 * SGAP - 9, SX0 + ENTRY_I * SGAP + 9
rect(sx0, y_e - 6, sx1, ys(leg2_l) + 4, GREEN)
hline(FRAME_L, frame_r, y_p, BLUE, "4 3")
hline(FRAME_L, frame_r, y_e, GREEN, "6 3")
hline(FRAME_L, frame_r, y_sl, RED, "6 3")
hline(FRAME_L, frame_r, y_tp, GREEN, "2 3")
candles(FINE, SX0, SGAP, ys)
# linia care desparte cele două zile
xd = SX0 + (K - 0.5) * SGAP
line(xd, top + 2, xd, top + PLOT_H - 2, "#bbb", dash="2 4", width=1)
text(SX0 + 3 * SGAP, top + 12, "ziua 1", fill="#999", anchor="middle", size=10)
text(SX0 + 10 * SGAP, top + 12, "ziua 2", fill="#999", anchor="middle", size=10)
# etichetele coloanei din dreapta, fiecare legată de linia ei
for y, lines_, fill in [(y_tp + 4, [f"TP1 {tp:.2f} = 1:1"], GREEN),
(y_e + 4, [f"intrare {entry:.2f}"], GREEN),
(y_p + 4, [f"pivot monthly {PIVOT:.2f}"], BLUE),
(y_sl + 4, [f"stop {sl:.2f}", f"= {risk / entry * 100:.2f} %".replace(".", ","),
"0.10 sub piciorul 2"], RED)]:
line(frame_r + 2, y - 4, COL_X - 4, y - 4, fill, width=0.8)
for j, s in enumerate(lines_):
text(COL_X, y + j * 14, s, fill=fill, size=10)
# marcaje pe cele două picioare
line(SX0 + LEG1 * SGAP, ys(leg1_l) + 4, SX0 + LEG1 * SGAP, top + PLOT_H + 8, GRAY, width=1)
text(SX0 + LEG1 * SGAP, top + PLOT_H + 20, f"piciorul 1: {leg1_l:.2f}", fill="#666",
anchor="middle", size=10)
text(SX0 + LEG1 * SGAP, top + PLOT_H + 34, "ține pivotul", fill="#666", anchor="middle", size=10)
line(SX0 + LEG2 * SGAP, ys(leg2_l) + 4, SX0 + LEG2 * SGAP, top + PLOT_H + 46, RED, width=1)
text(frame_r, top + PLOT_H + 58, f"piciorul 2: {leg2_l:.2f} — rupe pivotul și se închide", fill=RED,
anchor="end", size=10)
text(frame_r, top + PLOT_H + 72, "înapoi peste el: acolo sunt măturate stopurile", fill=RED,
anchor="end", size=10)
# săgeata de comparație a stopurilor
ya = top + PLOT_H + 92
line(BX0 - 19, ya, SX0 + ENTRY_I * SGAP, ya, RED, width=1.6, marker="pt-red")
text(20, ya + 18, f"același stop, două intrări: pe daily riști {b_risk / b_entry * 100:.2f} %, "
f"pe 60 min {risk / entry * 100:.2f} % — de {ratio:.1f} ori mai puțin,".replace(".", ","),
fill=RED, size=11, bold=True)
text(20, ya + 32, "pentru că pe TF-ul mic intri lângă minim, nu la close-ul zilei", fill=RED,
size=11, bold=True)
H = ya + 44
for a in range(len(LABELS)):
for b in range(a + 1, len(LABELS)):
xa0, xa1, ya_, sa = LABELS[a]
xb0, xb1, yb_, sb = LABELS[b]
assert not (abs(ya_ - yb_) < 13.5 and xa0 < xb1 - 4 and xb0 < xa1 - 4), \
f"etichete suprapuse: {sa!r} / {sb!r}"
assert 0 < LABELS[a][2] < H, f"etichetă în afara înălțimii: {LABELS[a][3]!r}"
# nivelul comun apare cu același număr pe ambele panouri
v = f"{PIVOT:.2f}"
assert any(v in s and x0 < MIDX for x0, _x1, _y, s in LABELS), "pivotul lipsește de pe panoul mare"
assert any(v in s and x0 >= MIDX for x0, _x1, _y, s in LABELS), "pivotul lipsește de pe panoul mic"
# niciun preț al panoului mic pe panoul mare și invers (în afara nivelului comun)
for x0, _x1, _y, s in LABELS:
rau = [b_entry] if x0 >= MIDX else [entry, sl, tp, leg1_l, leg2_l]
for val in rau:
assert f"{val:.2f}" not in s, f"eticheta {s!r} poartă prețul {val:.2f} al celuilalt panou"
svg = (f'<svg viewBox="0 0 {W} {H:.0f}" xmlns="http://www.w3.org/2000/svg" '
f'style="width:100%;height:auto;font:12px sans-serif">\n ' + "\n ".join(out) + "\n</svg>")
assert "\n\n" not in svg
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name}: pivot {PIVOT:.2f}, picior 1 {leg1_l:.2f} / picior 2 {leg2_l:.2f}, "
f"intrare {entry:.2f}, SL {sl:.2f} (risc {risk:.2f}), TP1 {tp:.2f} la bara {first}; "
f"stop daily {b_sl:.2f} = x{ratio:.2f}")

View File

@@ -0,0 +1,98 @@
<svg viewBox="0 0 720 402" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
<defs><marker id="pt-red" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#c00"/></marker></defs>
<text x="20.0" y="22.0" fill="#333" style="font-size:13px;font-weight:bold">Pasul 1 — nivelul se citește pe daily, trigger-ul se ia pe 60 min</text>
<text x="20.0" y="40.0" fill="#555" style="font-size:11px">Graficul din dreapta e aceeași bucată de piață, mărită: 2 candele daily = 14 bare de 60 min</text>
<text x="20.0" y="68.0" fill="#555" style="font-size:11px">daily: unde e nivelul</text>
<text x="363.0" y="68.0" fill="#555" style="font-size:11px">60 min: descompunerea ultimelor 2 candele daily</text>
<rect x="219.0" y="228.7" width="30.0" height="37.3" fill="#1a5fb4" fill-opacity="0.06" stroke="#1a5fb4" stroke-width="1.2" stroke-dasharray="4 3"/>
<line x1="90.0" y1="257.2" x2="249.0" y2="257.2" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30.0" y="269.2" fill="#1a5fb4" style="font-size:11px">pivot monthly</text>
<text x="30.0" y="283.2" fill="#1a5fb4" style="font-size:11px;font-weight:bold">76.00</text>
<line x1="105.0" y1="76.0" x2="105.0" y2="117.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="100.5" y="86.3" width="9" height="23.9" fill="#c00"/>
<line x1="120.0" y1="99.9" x2="120.0" y2="137.5" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="115.5" y="110.2" width="9" height="20.5" fill="#c00"/>
<line x1="135.0" y1="110.2" x2="135.0" y2="140.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="130.5" y="120.4" width="9" height="10.3" fill="#188a3e"/>
<line x1="150.0" y1="113.6" x2="150.0" y2="163.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="145.5" y="120.4" width="9" height="35.9" fill="#c00"/>
<line x1="165.0" y1="147.8" x2="165.0" y2="185.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="160.5" y="156.3" width="9" height="22.2" fill="#c00"/>
<line x1="180.0" y1="168.3" x2="180.0" y2="205.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="175.5" y="178.5" width="9" height="20.5" fill="#c00"/>
<line x1="195.0" y1="182.0" x2="195.0" y2="219.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="190.5" y="195.6" width="9" height="3.4" fill="#188a3e"/>
<line x1="210.0" y1="188.8" x2="210.0" y2="236.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="205.5" y="195.6" width="9" height="37.6" fill="#c00"/>
<line x1="225.0" y1="230.7" x2="225.0" y2="256.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="220.5" y="233.2" width="9" height="17.9" fill="#c00"/>
<line x1="240.0" y1="237.5" x2="240.0" y2="262.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="235.5" y="239.2" width="9" height="12.0" fill="#188a3e"/>
<rect x="187.0" y="184.8" width="29.0" height="82.4" fill="#188a3e" fill-opacity="0.1" stroke="#188a3e" stroke-width="1.2"/>
<text x="90.0" y="286.0" fill="#188a3e" style="font-size:11px;font-weight:bold">downtrend care se oprește la un nivel:</text>
<text x="90.0" y="300.0" fill="#188a3e" style="font-size:11px;font-weight:bold">fără nivelul de dedesubt e doar downtrend</text>
<text x="90.0" y="314.0" fill="#188a3e" style="font-size:11px;font-weight:bold">care continuă — nu ai unde pune stopul</text>
<line x1="86.0" y1="239.2" x2="249.0" y2="239.2" stroke="#188a3e" stroke-width="1.3" stroke-dasharray="6 3"/>
<text x="30.0" y="219.2" fill="#188a3e" style="font-size:10px">intrare pe daily 77.05 = close-ul zilei</text>
<text x="30.0" y="233.2" fill="#188a3e" style="font-size:10px">(mai devreme n-ai ce citi pe daily)</text>
<line x1="249.0" y1="228.7" x2="363.0" y2="76.0" stroke="#1a5fb4" stroke-width="1.1" stroke-dasharray="5 3"/>
<line x1="249.0" y1="266.0" x2="363.0" y2="266.0" stroke="#1a5fb4" stroke-width="1.1" stroke-dasharray="5 3"/>
<path d="M249.0,257.2 H266.0 V221.0 H363.0" fill="none" stroke='#aaa' stroke-width="1.0" stroke-dasharray="3 3"/>
<text x="271.0" y="243.1" fill="#888" style="font-size:10px">același nivel</text>
<rect x="363.0" y="76.0" width="213.0" height="190.0" fill="#1a5fb4" fill-opacity="0.03" stroke="#1a5fb4" stroke-width="1.2"/>
<rect x="513.0" y="188.9" width="33.0" height="62.2" fill="#188a3e" fill-opacity="0.1" stroke="#188a3e" stroke-width="1.2"/>
<line x1="363.0" y1="221.0" x2="576.0" y2="221.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<line x1="363.0" y1="194.9" x2="576.0" y2="194.9" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="6 3"/>
<line x1="363.0" y1="255.8" x2="576.0" y2="255.8" stroke="#c00" stroke-width="1.5" stroke-dasharray="6 3"/>
<line x1="363.0" y1="134.0" x2="576.0" y2="134.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 3"/>
<line x1="372.0" y1="86.2" x2="372.0" y2="129.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="367.5" y="99.2" width="9" height="21.7" fill="#c00"/>
<line x1="387.0" y1="107.9" x2="387.0" y2="151.4" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="382.5" y="121.0" width="9" height="21.7" fill="#c00"/>
<line x1="402.0" y1="134.0" x2="402.0" y2="177.5" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="397.5" y="142.7" width="9" height="26.1" fill="#c00"/>
<line x1="417.0" y1="155.8" x2="417.0" y2="194.9" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="412.5" y="168.8" width="9" height="13.0" fill="#c00"/>
<line x1="432.0" y1="173.2" x2="432.0" y2="216.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="427.5" y="181.9" width="9" height="21.7" fill="#c00"/>
<line x1="447.0" y1="168.8" x2="447.0" y2="208.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="442.5" y="177.5" width="9" height="26.1" fill="#188a3e"/>
<line x1="462.0" y1="164.5" x2="462.0" y2="199.3" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="457.5" y="177.5" width="9" height="13.0" fill="#c00"/>
<line x1="477.0" y1="181.9" x2="477.0" y2="216.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="472.5" y="190.6" width="9" height="17.4" fill="#c00"/>
<line x1="492.0" y1="199.3" x2="492.0" y2="229.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="487.5" y="208.0" width="9" height="17.4" fill="#c00"/>
<line x1="507.0" y1="216.7" x2="507.0" y2="234.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="502.5" y="225.4" width="9" height="1.6" fill="#188a3e"/>
<line x1="522.0" y1="203.6" x2="522.0" y2="247.1" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="517.5" y="212.3" width="9" height="13.0" fill="#188a3e"/>
<line x1="537.0" y1="186.2" x2="537.0" y2="216.7" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="532.5" y="194.9" width="9" height="17.4" fill="#188a3e"/>
<line x1="552.0" y1="155.8" x2="552.0" y2="203.6" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="547.5" y="168.8" width="9" height="26.1" fill="#188a3e"/>
<line x1="567.0" y1="121.0" x2="567.0" y2="173.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="562.5" y="129.7" width="9" height="39.1" fill="#188a3e"/>
<line x1="469.5" y1="78.0" x2="469.5" y2="264.0" stroke="#bbb" stroke-width="1" stroke-dasharray="2 4"/>
<text x="417.0" y="88.0" fill="#999" text-anchor="middle" style="font-size:10px">ziua 1</text>
<text x="522.0" y="88.0" fill="#999" text-anchor="middle" style="font-size:10px">ziua 2</text>
<line x1="578.0" y1="134.0" x2="596.0" y2="134.0" stroke="#188a3e" stroke-width="0.8"/>
<text x="600.0" y="138.0" fill="#188a3e" style="font-size:10px">TP1 77.00 = 1:1</text>
<line x1="578.0" y1="194.9" x2="596.0" y2="194.9" stroke="#188a3e" stroke-width="0.8"/>
<text x="600.0" y="198.9" fill="#188a3e" style="font-size:10px">intrare 76.30</text>
<line x1="578.0" y1="221.0" x2="596.0" y2="221.0" stroke="#1a5fb4" stroke-width="0.8"/>
<text x="600.0" y="225.0" fill="#1a5fb4" style="font-size:10px">pivot monthly 76.00</text>
<line x1="578.0" y1="255.8" x2="596.0" y2="255.8" stroke="#c00" stroke-width="0.8"/>
<text x="600.0" y="259.8" fill="#c00" style="font-size:10px">stop 75.60</text>
<text x="600.0" y="273.8" fill="#c00" style="font-size:10px">= 0,92 %</text>
<text x="600.0" y="287.8" fill="#c00" style="font-size:10px">0.10 sub piciorul 2</text>
<line x1="432.0" y1="220.7" x2="432.0" y2="274.0" stroke="#888" stroke-width="1"/>
<text x="432.0" y="286.0" fill="#666" text-anchor="middle" style="font-size:10px">piciorul 1: 76.05</text>
<text x="432.0" y="300.0" fill="#666" text-anchor="middle" style="font-size:10px">ține pivotul</text>
<line x1="522.0" y1="251.1" x2="522.0" y2="312.0" stroke="#c00" stroke-width="1"/>
<text x="576.0" y="324.0" fill="#c00" text-anchor="end" style="font-size:10px">piciorul 2: 75.70 — rupe pivotul și se închide</text>
<text x="576.0" y="338.0" fill="#c00" text-anchor="end" style="font-size:10px">înapoi peste el: acolo sunt măturate stopurile</text>
<line x1="86.0" y1="358.0" x2="537.0" y2="358.0" stroke="#c00" stroke-width="1.6" marker-end="url(#pt-red)"/>
<text x="20.0" y="376.0" fill="#c00" style="font-size:11px;font-weight:bold">același stop, două intrări: pe daily riști 1,88 %, pe 60 min 0,92 % — de 2,1 ori mai puțin,</text>
<text x="20.0" y="390.0" fill="#c00" style="font-size:11px;font-weight:bold">pentru că pe TF-ul mic intri lângă minim, nu la close-ul zilei</text>
</svg>

After

Width:  |  Height:  |  Size: 9.1 KiB