strategii: cele trei diagrame ale anexei A3

Un agent per diagramă, din `.spec.txt`-urile scrise la sesiunea trecută.
`audit_diagrams` 0 pe toate trei; fiecare randată și privită ca PNG.

Două retușuri făcute de sesiunea principală, peste ce au raportat agenții:
- `intrarea_retest_ratat` folosea alte fitiluri la candelele 12 și 14 decât
  `intrarea_doua_variante`, deși cele două împart aceeași serie 0-15.
  Aliniate după `doua_variante`, care e scris direct din spec.
- `intrarea_cazul_lui` avea un `Mark` „suport weekly" sub linia de SL, care
  dubla eticheta nivelului și părea să eticheteze SL-ul. Scos.

Abaterile de la spec pe care le-au raportat agenții și le-am acceptat:
vârful din `cazul_lui` cade pe bara 32, nu 31 (în lista de close-uri din
spec acolo e 105.20), iar bara 33 deschide chiar la 105.20, deci cerința
„toate celelalte high-uri sub 105.20" nu se poate îndeplini strict.

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-19 17:29:50 +00:00
parent 9348b1ec1b
commit edc270417d
9 changed files with 1798 additions and 4 deletions

View File

@@ -0,0 +1,155 @@
"""Diagrama din `gen/strategii/intrarea_cazul_lui.spec.txt` (TIP: PRET).
Trade-ul userului din A3 §2.1, reconstruit din descriere: downtrend pe 15 min
până la suportul weekly 100.00, buy momentum pe o verde mare, intrare la
close 102.10, imediat o roșie mare care acoperă 83% din corpul verzii, apoi
trendul. Ieșirea userului la 102.60, înainte de TP1; vârful 105.20 după.
Pe același grafic, nivelul 1/2 = 101.20 unde s-ar fi umplut varianta B.
Toate cifrele sunt CONSTRUITE (fără ticker); mediile SMA 5 / SMA 10 sunt
ilustrative — lecția 10 nu dă lungimile reale.
Rulează:
python3 gen/strategii/intrarea_cazul_lui.py
Scrie lângă el `intrarea_cazul_lui.svg`.
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
OUT = Path(__file__).resolve().parent / "intrarea_cazul_lui.svg"
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
MA5_C, MA10_C = "#8a3ea8", "#2a8a8a"
SUPORT, SL, ENTRY, HALF, TP1 = 100.00, 99.90, 102.10, 101.20, 104.30
CLOSES = [
103.80, 103.50, 103.60, 103.20, 102.90, 103.00, 102.60, 102.30, 101.90, 101.40, 101.00, # 0-10
100.70, 100.45, 100.30, # 11-13 la suport
102.10, # 14 buy momentum
100.60, # 15 roșie mare
101.10, 101.60, 101.90, # 16-18
102.60, # 19 ieșirea userului
102.90, 102.70, 103.20, 103.60, 103.40, 103.90, 104.20, 104.10, 104.60,
104.90, 104.70, 105.00, 105.20, 105.00, # 20-33
]
assert len(CLOSES) == 34, len(CLOSES)
# (sus, jos); excepțiile fixează exact low-urile/high-urile din specificație
WICKS = [(0.12, 0.10)] * 34
WICKS[10] = (0.10, 0.20) # low 100.80
WICKS[11] = (0.10, 0.30) # low 100.40
WICKS[12] = (0.10, 0.55) # low 99.90, wick sub 100.00
WICKS[13] = (0.10, 0.20) # low 100.10
WICKS[14] = (0.15, 0.10) # h 102.25, l 100.20
WICKS[15] = (0.10, 0.10) # h 102.20, l 100.50
# spec zice „candela 31 (close 105.20)", dar în lista lui 105.20 e la indexul 32;
# vârful e pus pe bara 32, unde e de fapt close-ul 105.20.
WICKS[31] = (0.10, 0.10) # h 105.10 < 105.20
WICKS[32] = (0.00, 0.10) # h 105.20 = maximul
WICKS[33] = (0.00, 0.10) # open 105.20 → high nu poate fi sub 105.20
cds = from_closes(CLOSES, wicks=WICKS, start=104.00)
R = lambda x: round(x, 2) # noqa: E731
# ── assert-urile din specificație ──────────────────────────────────────────
# 1. open == close anterior
for a, b in zip(cds, cds[1:]):
assert a.c == b.o, (a.c, b.o)
g, r = cds[14], cds[15]
assert (R(g.o), R(g.c), R(g.h), R(g.l)) == (100.30, 102.10, 102.25, 100.20), g
assert (R(r.o), R(r.c), R(r.h), R(r.l)) == (102.10, 100.60, 102.20, 100.50), r
assert (R(cds[10].l), R(cds[11].l), R(cds[12].l), R(cds[13].l)) == (100.80, 100.40, 99.90, 100.10)
# 2. SL = low 4 candele; nici verdea, nici roșia nu îl ating
assert R(min(c.l for c in cds[10:14])) == SL
assert g.l > SL and r.l > SL
# 3. culori
assert g.c > g.o and r.c < r.o
# 4. roșia acoperă 83% din corpul verzii
acop = (r.o - r.c) / (g.c - g.o)
assert abs(acop - 0.83) <= 0.01, acop
# 5. 1/2 din corp, atins de roșie
half = (g.o + g.c) / 2
assert R(half) == HALF and r.l <= half, (half, r.l)
# 6. R-urile
risc = ENTRY - SL
assert R(risc) == 2.20 and R(ENTRY + risc) == TP1
flot = (r.c - ENTRY) / risc
iesire = (cds[19].c - ENTRY) / risc
varf = (105.20 - ENTRY) / risc
assert (R(flot), R(iesire), R(varf)) == (-0.68, 0.23, 1.41), (flot, iesire, varf)
# 7. mediile mobile
sma = lambda n, i: sum(CLOSES[i + 1 - n:i + 1]) / n # noqa: E731
assert g.c > sma(5, 14) and g.c > sma(10, 14)
assert r.c < sma(5, 15) and r.c < sma(10, 15)
assert (R(sma(5, 15)), round(sma(10, 15), 3)) == (100.83, 101.335)
# 8. ieșirea înainte de TP1; vârful
assert cds[19].c < TP1 and all(c.h < TP1 for c in cds[15:20])
assert R(max(c.h for c in cds)) == 105.20 and R(cds[32].h) == 105.20 == cds[32].c
assert all(c.h < 105.20 - 1e-9 for c in cds[:32])
# varianta B (doar în legendă)
assert R(g.c - (g.c - g.o) / 3) == 101.50
assert R(HALF - SL) == 1.30 and R(HALF + (HALF - SL)) == 102.50
levels = [
Level(TP1, "TP1 104.30 (1:1)", BLUE, label_at="left"),
Level(ENTRY, "intrare la close 102.10", GREEN),
Level(HALF, "1/2 = 101.20 (B s-ar fi umplut aici)", ORANGE, dash="2 3"),
Level(SUPORT, "suport weekly 100.00", GRAY, label_at="left"),
Level(SL, "", RED), # eticheta SL sub linie, mai jos (0.10 sub suport)
Level(99.50, "", "none", dash=""), # rezervă loc sub SL pentru etichete
]
marks = [
Mark(14, "▲ buy momentum", GREEN, "above", dy=-20), # peste SMA 10
Mark(32, "vârf 105.20 = +1.41R", GRAY, "above"),
]
ch = Chart(
candles=cds, levels=levels, marks=marks,
mas=[MA(5, MA5_C), MA(10, MA10_C, dash="5 3")],
scale=60, gap=16, body_w=10, left=44, width=640,
title="Exemplu construit (fără ticker) — 15 min, suport weekly",
)
svg = ch.render()
# barele 15 și 19 au candele lipite în stânga/dreapta: un Mark centrat le-ar
# tăia, așa că eticheta lor stă în golul de lângă candelă (roșia: ▲ chiar sub
# low-urile barelor 16+, peste suport; ieșirea: deasupra barelor 14-19,
# terminată în bara 19, cu linie de legătură).
y19 = ch._y(103.30)
extra = [
f'<text x="{ch._cx(15) - 5:.0f}" y="{ch._y(100.20):.1f}" fill="{RED}" '
f'style="font-weight:bold">▲ roșie mare: −0.68R flotant</text>',
f'<text x="{ch._cx(19) + 4:.0f}" y="{y19:.1f}" fill="{ORANGE}" text-anchor="end" '
f'style="font-weight:bold">ieși aici: 102.60 = +0.23R</text>',
f'<line x1="{ch._cx(19):.1f}" y1="{y19 + 4:.1f}" x2="{ch._cx(19):.1f}" '
f'y2="{ch._y(cds[19].h) - 3:.1f}" stroke="{ORANGE}" stroke-width="1"/>',
f'<text x="30" y="{ch._y(SL) + 15:.1f}" fill="{RED}">SL 99.90 (low 4 candele)</text>',
f'<text x="24" y="{ch.top + 12:.1f}" fill="#555">'
f'<tspan fill="{MA5_C}">── SMA 5</tspan> / <tspan fill="{MA10_C}">- - SMA 10</tspan> ilustrative</text>',
]
legend = [
"Exemplu construit, fără ticker. Prețurile, mediile mobile și ieșirea sunt",
"ilustrative; secvența (verde mare → roșie mare → trend) e cea descrisă de user.",
"Varianta B (ordin la 1/2 = 101.20, risc 1.30, TP1 102.50) e aritmetică pe",
"aceleași cifre, nu un trade din curs. 1/3 din corp = 101.50.",
"Close-ul roșiei (100.60): sub ambele MA (SMA 5 = 100.83, SMA 10 = 101.34).",
]
H0 = float(re.findall(r'viewBox="0 0 \d+ ([0-9.]+)"', svg)[0])
extra += [f'<text x="24" y="{H0 + 10 + 17 * i:.1f}" fill="#555">{t}</text>'
for i, t in enumerate(legend)]
new_h = H0 + 10 + 17 * (len(legend) - 1) + 12
svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")',
lambda m: m.group(1) + f"{new_h:.0f}" + m.group(2), svg, count=1)
svg = svg.replace("\n</svg>", "\n " + "\n ".join(extra) + "\n</svg>")
svg = re.sub(r"\n <text [^>]*></text>", "", svg) # etichete goale (SL, rezervă)
svg = re.sub(r"\n <line [^>]*stroke=\"none\"[^>]*/>", "", svg)
assert "\n\n" not in svg
OUT.write_text(svg, encoding="utf-8")
print(f"scris {OUT.name}: acoperire {acop:.3f}, flotant {flot:+.2f}R, "
f"ieșire {iesire:+.2f}R, vârf {varf:+.2f}R, SMA5/10@15 = "
f"{sma(5, 15):.2f}/{sma(10, 15):.3f}")

View File

@@ -0,0 +1,94 @@
<svg viewBox="0 0 640 514" 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">Exemplu construit (fără ticker) — 15 min, suport weekly</text>
<line x1="24" y1="110.0" x2="626" y2="110.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="104.0" fill="#1a5fb4">TP1 104.30 (1:1)</text>
<line x1="24" y1="242.0" x2="626" y2="242.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="612" y="236.0" fill="#188a3e" text-anchor="end">intrare la close 102.10</text>
<line x1="24" y1="296.0" x2="626" y2="296.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="2 3"/>
<text x="612" y="290.0" fill="#b06000" text-anchor="end">1/2 = 101.20 (B s-ar fi umplut aici)</text>
<line x1="24" y1="368.0" x2="626" y2="368.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="30" y="362.0" fill="#777">suport weekly 100.00</text>
<line x1="24" y1="374.0" x2="626" y2="374.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<polyline points="108.0,164.0 124.0,173.6 140.0,184.4 156.0,200.0 172.0,215.6 188.0,233.6 204.0,257.6 220.0,280.4 236.0,302.6 252.0,321.8 268.0,313.4 284.0,318.2 300.0,313.4 316.0,299.6 332.0,280.4 348.0,274.4 364.0,246.8 380.0,227.6 396.0,208.4 412.0,188.0 428.0,178.4 444.0,166.4 460.0,148.4 476.0,137.6 492.0,125.6 508.0,107.6 524.0,98.0 540.0,88.4 556.0,75.2 572.0,70.4" fill="none" stroke="#8a3ea8" stroke-width="1.5"/>
<polyline points="188.0,198.8 204.0,215.6 220.0,232.4 236.0,251.3 252.0,268.7 268.0,273.5 284.0,287.9 300.0,296.9 316.0,301.1 332.0,301.1 348.0,293.9 364.0,282.5 380.0,270.5 396.0,254.0 412.0,234.2 428.0,226.4 444.0,206.6 460.0,188.0 476.0,173.0 492.0,156.8 508.0,143.0 524.0,132.2 540.0,118.4 556.0,106.4 572.0,98.0" fill="none" stroke="#2a8a8a" stroke-width="1.5" stroke-dasharray="5 3"/>
<line x1="44.0" y1="120.8" x2="44.0" y2="146.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="39.0" y="128.0" width="10" height="12.0" fill="#c00"/>
<line x1="60.0" y1="132.8" x2="60.0" y2="164.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="55.0" y="140.0" width="10" height="18.0" fill="#c00"/>
<line x1="76.0" y1="144.8" x2="76.0" y2="164.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="71.0" y="152.0" width="10" height="6.0" fill="#188a3e"/>
<line x1="92.0" y1="144.8" x2="92.0" y2="182.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="87.0" y="152.0" width="10" height="24.0" fill="#c00"/>
<line x1="108.0" y1="168.8" x2="108.0" y2="200.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="103.0" y="176.0" width="10" height="18.0" fill="#c00"/>
<line x1="124.0" y1="180.8" x2="124.0" y2="200.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="119.0" y="188.0" width="10" height="6.0" fill="#188a3e"/>
<line x1="140.0" y1="180.8" x2="140.0" y2="218.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="135.0" y="188.0" width="10" height="24.0" fill="#c00"/>
<line x1="156.0" y1="204.8" x2="156.0" y2="236.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="151.0" y="212.0" width="10" height="18.0" fill="#c00"/>
<line x1="172.0" y1="222.8" x2="172.0" y2="260.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="167.0" y="230.0" width="10" height="24.0" fill="#c00"/>
<line x1="188.0" y1="246.8" x2="188.0" y2="290.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="183.0" y="254.0" width="10" height="30.0" fill="#c00"/>
<line x1="204.0" y1="278.0" x2="204.0" y2="320.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="199.0" y="284.0" width="10" height="24.0" fill="#c00"/>
<line x1="220.0" y1="302.0" x2="220.0" y2="344.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="215.0" y="308.0" width="10" height="18.0" fill="#c00"/>
<line x1="236.0" y1="320.0" x2="236.0" y2="374.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="231.0" y="326.0" width="10" height="15.0" fill="#c00"/>
<line x1="252.0" y1="335.0" x2="252.0" y2="362.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="247.0" y="341.0" width="10" height="9.0" fill="#c00"/>
<line x1="268.0" y1="233.0" x2="268.0" y2="356.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="263.0" y="242.0" width="10" height="108.0" fill="#188a3e"/>
<line x1="284.0" y1="236.0" x2="284.0" y2="338.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="279.0" y="242.0" width="10" height="90.0" fill="#c00"/>
<line x1="300.0" y1="294.8" x2="300.0" y2="338.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="295.0" y="302.0" width="10" height="30.0" fill="#188a3e"/>
<line x1="316.0" y1="264.8" x2="316.0" y2="308.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="311.0" y="272.0" width="10" height="30.0" fill="#188a3e"/>
<line x1="332.0" y1="246.8" x2="332.0" y2="278.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="327.0" y="254.0" width="10" height="18.0" fill="#188a3e"/>
<line x1="348.0" y1="204.8" x2="348.0" y2="260.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="343.0" y="212.0" width="10" height="42.0" fill="#188a3e"/>
<line x1="364.0" y1="186.8" x2="364.0" y2="218.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="359.0" y="194.0" width="10" height="18.0" fill="#188a3e"/>
<line x1="380.0" y1="186.8" x2="380.0" y2="212.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="375.0" y="194.0" width="10" height="12.0" fill="#c00"/>
<line x1="396.0" y1="168.8" x2="396.0" y2="212.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="391.0" y="176.0" width="10" height="30.0" fill="#188a3e"/>
<line x1="412.0" y1="144.8" x2="412.0" y2="182.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="407.0" y="152.0" width="10" height="24.0" fill="#188a3e"/>
<line x1="428.0" y1="144.8" x2="428.0" y2="170.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="423.0" y="152.0" width="10" height="12.0" fill="#c00"/>
<line x1="444.0" y1="126.8" x2="444.0" y2="170.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="439.0" y="134.0" width="10" height="30.0" fill="#188a3e"/>
<line x1="460.0" y1="108.8" x2="460.0" y2="140.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="455.0" y="116.0" width="10" height="18.0" fill="#188a3e"/>
<line x1="476.0" y1="108.8" x2="476.0" y2="128.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="471.0" y="116.0" width="10" height="6.0" fill="#c00"/>
<line x1="492.0" y1="84.8" x2="492.0" y2="128.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="487.0" y="92.0" width="10" height="30.0" fill="#188a3e"/>
<line x1="508.0" y1="66.8" x2="508.0" y2="98.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="503.0" y="74.0" width="10" height="18.0" fill="#188a3e"/>
<line x1="524.0" y1="66.8" x2="524.0" y2="92.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="519.0" y="74.0" width="10" height="12.0" fill="#c00"/>
<line x1="540.0" y1="62.0" x2="540.0" y2="92.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="535.0" y="68.0" width="10" height="18.0" fill="#188a3e"/>
<line x1="556.0" y1="56.0" x2="556.0" y2="74.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="551.0" y="56.0" width="10" height="12.0" fill="#188a3e"/>
<line x1="572.0" y1="56.0" x2="572.0" y2="74.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="567.0" y="56.0" width="10" height="12.0" fill="#c00"/>
<text x="268.0" y="203.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">▲ buy momentum</text>
<text x="568.0" y="46.0" fill="#777" text-anchor="end" style="font-weight:bold">vârf 105.20 = +1.41R</text>
<text x="279" y="356.0" fill="#c00" style="font-weight:bold">▲ roșie mare: −0.68R flotant</text>
<text x="352" y="170.0" fill="#b06000" text-anchor="end" style="font-weight:bold">ieși aici: 102.60 = +0.23R</text>
<line x1="348.0" y1="174.0" x2="348.0" y2="201.8" stroke="#b06000" stroke-width="1"/>
<text x="30" y="389.0" fill="#c00">SL 99.90 (low 4 candele)</text>
<text x="24" y="68.0" fill="#555"><tspan fill="#8a3ea8">── SMA 5</tspan> / <tspan fill="#2a8a8a">- - SMA 10</tspan> ilustrative</text>
<text x="24" y="434.0" fill="#555">Exemplu construit, fără ticker. Prețurile, mediile mobile și ieșirea sunt</text>
<text x="24" y="451.0" fill="#555">ilustrative; secvența (verde mare → roșie mare → trend) e cea descrisă de user.</text>
<text x="24" y="468.0" fill="#555">Varianta B (ordin la 1/2 = 101.20, risc 1.30, TP1 102.50) e aritmetică pe</text>
<text x="24" y="485.0" fill="#555">aceleași cifre, nu un trade din curs. 1/3 din corp = 101.50.</text>
<text x="24" y="502.0" fill="#555">Close-ul roșiei (100.60): sub ambele MA (SMA 5 = 100.83, SMA 10 = 101.34).</text>
</svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -0,0 +1,155 @@
"""Diagrama din `gen/strategii/intrarea_doua_variante.spec.txt` (TIP: PRET).
Același setup cu cele două intrări pe același grafic: A la close-ul candelei
de semnal, B la retestul de 1/3 și 1/2 din corpul ei. Un singur stop, comun
(sub structură), și trei paranteze de risc în dreapta, la aceeași scară cu
prețul — diferența de risc vine din intrare, nu din stop.
Seria e CONSTRUITĂ (fără ticker), 32 de candele de 15 min, fără volum.
Etichetele de sub candele (suport atins / atinge 1/3 / atinge 1/2) sunt
desenate de mână sub linia de SL, cu linii de legătură: ca Mark-uri „below"
ar cădea peste liniile de 1/2 și 1/3 și peste wick-ul candelei vecine.
Rulează: .venv/bin/python gen/strategii/intrarea_doua_variante.py
-> gen/strategii/intrarea_doua_variante.svg
"""
import math
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
OUT = Path(__file__).resolve().parent / "intrarea_doua_variante.svg"
GRI, ROSU, VERDE, PORT, ALB = "#777", "#c00", "#188a3e", "#b06000", "#1a5fb4"
closes = [103.60, 103.35, 103.45, 103.10, 102.80, 102.90, 102.50, 102.20, 101.85, 101.95, 101.50, 101.10, # 0-11
100.60, 100.40, 100.35, # 12-14 suport
102.15, # 15 semnal
101.70, 101.40, 101.60, # 16-18 retest
102.00, 102.40, 102.30, 102.80, 103.20, 103.00, 103.50, 103.90, 104.20, 104.10, 104.50, 104.80, 105.00]
wicks = [(0.15, 0.15)] * 12 \
+ [(0.10, 0.15), (0.10, 0.50), (0.10, 0.15)] \
+ [(0.15, 0.10)] \
+ [(0.10, 0.20), (0.10, 0.20), (0.10, 0.10)] \
+ [(0.10, 0.03)] + [(0.15, 0.12)] * 12
assert len(closes) == len(wicks) == 32
cds = from_closes(closes, wicks=wicks, start=103.80)
# ── assert-urile din spec ───────────────────────────────────────────────────
S = cds[15]
for a, b in zip(cds, cds[1:]): # 1
assert abs(a.c - b.o) < 1e-9
assert abs(S.o - 100.35) < 1e-9 and abs(S.c - 102.15) < 1e-9 # 2
assert abs(S.h - 102.30) < 1e-9 and abs(S.l - 100.25) < 1e-9
SL = min(c.l for c in cds[11:15]) # 3
assert abs(SL - 99.90) < 1e-9 and abs(cds[13].l - SL) < 1e-9 and S.l > SL
assert all(c.l > SL for i, c in enumerate(cds[11:15], 11) if i != 13)
half = (S.o + S.c) / 2 # 4
third = S.c - (S.c - S.o) / 3
assert abs(half - 101.25) < 1e-9 and abs(third - 101.55) < 1e-9
assert min(c.l for c in cds[16:19]) <= half and cds[16].l <= third # 5
assert cds[16].l > half # 16 atinge doar 1/3, 17 atinge 1/2
assert all(c.l > third for c in cds[19:]) # după retest nu mai revine la 1/3
RISK_PER_TRADE = 50.0
intrari = {"A": S.c, "1/3": third, "1/2": half}
riscuri = {k: e - SL for k, e in intrari.items()} # 6
for k, r in zip(intrari, (2.25, 1.65, 1.35)):
assert abs(riscuri[k] - r) < 1e-6, (k, riscuri[k])
actiuni = {k: math.floor(RISK_PER_TRADE / r + 1e-9) for k, r in riscuri.items()}
assert list(actiuni.values()) == [22, 30, 37], actiuni
tp = {k: e + (e - SL) for k, e in intrari.items()} # 7
for k, v in zip(intrari, (104.40, 103.20, 102.60)):
assert abs(tp[k] - v) < 1e-9, (k, tp[k])
assert max(c.h for c in cds) >= tp["A"] # 9
# ── desenul ─────────────────────────────────────────────────────────────────
W = 880
levels = [
Level(100.00, "suport 100.00", GRI),
Level(SL, "", ROSU), # eticheta SL e scrisă sub linie, mai jos
Level(S.c, f"A: intrare la close {S.c:.2f}", VERDE),
Level(third, f"B: 1/3 = {third:.2f}", PORT),
Level(half, f"B: 1/2 = {half:.2f}", PORT),
Level(tp["A"], f"TP1 A {tp['A']:.2f}", ALB),
Level(tp["1/2"], f"TP1 B (1/2) {tp['1/2']:.2f}", ALB, dash="2 3"),
Level(tp["1/3"], f"TP1 B (1/3) {tp['1/3']:.2f}", ALB, dash="2 3"),
]
ch = Chart(candles=cds, levels=levels,
marks=[Mark(15, "semnal: candela verde mare", VERDE, "above", dy=-14)],
scale=60, gap=15, body_w=9, left=44, width=W,
title="Exemplu construit (fără ticker) — 15 min")
svg = ch.render()
svg = re.sub(r'\s*<text [^>]*></text>', "", svg) # eticheta goală a SL-ului
y, cx = ch._y, ch._cx
ySL = y(SL)
extra = []
def txt(x, yy, s, fill, anchor="start", bold=False):
st = ' style="font-weight:bold"' if bold else ""
extra.append(f'<text x="{x:.1f}" y="{yy:.1f}" fill="{fill}" text-anchor="{anchor}"{st}>{s}</text>')
def ln(x1, y1, x2, y2, stroke, w=1.2, dash=""):
d = f' stroke-dasharray="{dash}"' if dash else ""
extra.append(f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" stroke="{stroke}" stroke-width="{w}"{d}/>')
# eticheta SL, sub linia lui, în dreapta
txt(W - 28, ySL + 16, f"SL {SL:.2f} = low-ul ultimelor 4 candele (comun A și B)", ROSU, "end")
# etichetele de sub candele, sub linia de SL, cu linii de legătură spre wick
R1, R2 = ySL + 16, ySL + 31
txt(cx(13), R1, "suport atins", GRI, "middle", True)
ln(cx(16), y(cds[16].l) + 2, cx(16), R2 - 11, PORT, 1, "2 2")
txt(cx(16) + 4, R2, "atinge 1/3", PORT, "end", True)
ln(cx(17), y(cds[17].l) + 2, cx(17), R1 - 11, PORT, 1, "2 2")
txt(cx(17) - 4, R1, "atinge 1/2", PORT, "start", True)
# parantezele de risc: intrare → SL, la aceeași scară cu prețul
BX = {"A": 560, "1/3": 605, "1/2": 650}
col = {"A": VERDE, "1/3": PORT, "1/2": PORT}
eticheta = {"A": "A", "1/3": "B 1/3", "1/2": "B 1/2"}
px = {}
for k, e in intrari.items():
x, y0, y1 = BX[k], y(e), ySL
px[k] = y1 - y0
ln(x, y0, x, y1, col[k], 2.2)
ln(x - 6, y0, x + 6, y0, col[k], 2.2)
ln(x - 6, y1, x + 6, y1, ROSU, 2.2)
txt(x, y0 - 6, eticheta[k], col[k], "middle", True)
txt(x + 6, (y0 + y1) / 2 + 4, f"{riscuri[k]:.2f}", col[k], "start")
# 8. scara: raportul pixelilor == raportul riscurilor, toleranță 1%
for k in intrari:
assert abs(px[k] / px["A"] - riscuri[k] / riscuri["A"]) < 0.01 * riscuri[k] / riscuri["A"], k
# blocul cu riscurile, sub SL, sub paranteze
bloc = [(f"risc A {riscuri['A']:.2f} → {actiuni['A']} acțiuni la 50 $", VERDE),
(f"risc B 1/3 {riscuri['1/3']:.2f} → {actiuni['1/3']} acțiuni", PORT),
(f"risc B 1/2 {riscuri['1/2']:.2f} → {actiuni['1/2']} acțiuni", PORT)]
for i, (s, c) in enumerate(bloc):
txt(530, ySL + 34 + 15 * i, s, c)
ylegenda = ySL + 34 + 15 * len(bloc) + 12
txt(24, ylegenda, "Exemplu construit, fără ticker. Cifrele nu vin din curs. "
"Risc permis 50 $ (0,5% din 10.000 $).", "#555")
H = ylegenda + 12
svg = re.sub(r'viewBox="0 0 \d+ \d+"', f'viewBox="0 0 {W} {H:.0f}"', svg)
svg = svg.replace("\n</svg>", "\n " + "\n ".join(extra) + "\n</svg>")
assert "\n\n" not in svg, "linie goală în SVG"
# ── verificare: etichete în pânză, fără suprapuneri între ele ─────────────
cutii = []
for xs, ys, attrs, s in re.findall(r'<text x="([\d.-]+)" y="([\d.-]+)"([^>]*)>([^<]*)</text>', svg):
x, yy = float(xs), float(ys)
w = len(s) * (7.0 if "bold" in attrs else 6.3)
x0 = x - w if 'anchor="end"' in attrs else (x - w / 2 if 'anchor="middle"' in attrs else x)
assert -2 <= x0 and x0 + w <= W + 2 and 0 <= yy <= H, f"în afara pânzei: {s!r}"
cutii.append((x0, x0 + w, yy, s))
for i, a in enumerate(cutii):
for b in cutii[i + 1:]:
if abs(a[2] - b[2]) < 13 and a[0] < b[1] - 2 and b[0] < a[1] - 2:
raise AssertionError(f"texte suprapuse: {a[3]!r} / {b[3]!r}")
OUT.write_text(svg + "\n", encoding="utf-8")
print(f"SL {SL:.2f} | 1/3 {third:.2f} | 1/2 {half:.2f} | riscuri "
+ ", ".join(f"{k} {r:.2f}→{actiuni[k]}" for k, r in riscuri.items())
+ " | px " + ", ".join(f"{v:.1f}" for v in px.values()))

View File

@@ -0,0 +1,108 @@
<svg viewBox="0 0 880 474" 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">Exemplu construit (fără ticker) — 15 min</text>
<line x1="24" y1="365.0" x2="866" y2="365.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="852" y="359.0" fill="#777" text-anchor="end">suport 100.00</text>
<line x1="24" y1="371.0" x2="866" y2="371.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
<line x1="24" y1="236.0" x2="866" y2="236.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="852" y="230.0" fill="#188a3e" text-anchor="end">A: intrare la close 102.15</text>
<line x1="24" y1="272.0" x2="866" y2="272.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="852" y="266.0" fill="#b06000" text-anchor="end">B: 1/3 = 101.55</text>
<line x1="24" y1="290.0" x2="866" y2="290.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="852" y="284.0" fill="#b06000" text-anchor="end">B: 1/2 = 101.25</text>
<line x1="24" y1="101.0" x2="866" y2="101.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="852" y="95.0" fill="#1a5fb4" text-anchor="end">TP1 A 104.40</text>
<line x1="24" y1="209.0" x2="866" y2="209.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="2 3"/>
<text x="852" y="203.0" fill="#1a5fb4" text-anchor="end">TP1 B (1/2) 102.60</text>
<line x1="24" y1="173.0" x2="866" y2="173.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="2 3"/>
<text x="852" y="167.0" fill="#1a5fb4" text-anchor="end">TP1 B (1/3) 103.20</text>
<line x1="44.0" y1="128.0" x2="44.0" y2="158.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="39.5" y="137.0" width="9" height="12.0" fill="#c00"/>
<line x1="59.0" y1="140.0" x2="59.0" y2="173.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="54.5" y="149.0" width="9" height="15.0" fill="#c00"/>
<line x1="74.0" y1="149.0" x2="74.0" y2="173.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="69.5" y="158.0" width="9" height="6.0" fill="#188a3e"/>
<line x1="89.0" y1="149.0" x2="89.0" y2="188.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="84.5" y="158.0" width="9" height="21.0" fill="#c00"/>
<line x1="104.0" y1="170.0" x2="104.0" y2="206.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="99.5" y="179.0" width="9" height="18.0" fill="#c00"/>
<line x1="119.0" y1="182.0" x2="119.0" y2="206.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="114.5" y="191.0" width="9" height="6.0" fill="#188a3e"/>
<line x1="134.0" y1="182.0" x2="134.0" y2="224.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="129.5" y="191.0" width="9" height="24.0" fill="#c00"/>
<line x1="149.0" y1="206.0" x2="149.0" y2="242.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="144.5" y="215.0" width="9" height="18.0" fill="#c00"/>
<line x1="164.0" y1="224.0" x2="164.0" y2="263.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="159.5" y="233.0" width="9" height="21.0" fill="#c00"/>
<line x1="179.0" y1="239.0" x2="179.0" y2="263.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="174.5" y="248.0" width="9" height="6.0" fill="#188a3e"/>
<line x1="194.0" y1="239.0" x2="194.0" y2="284.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="189.5" y="248.0" width="9" height="27.0" fill="#c00"/>
<line x1="209.0" y1="266.0" x2="209.0" y2="308.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="204.5" y="275.0" width="9" height="24.0" fill="#c00"/>
<line x1="224.0" y1="293.0" x2="224.0" y2="338.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="219.5" y="299.0" width="9" height="30.0" fill="#c00"/>
<line x1="239.0" y1="323.0" x2="239.0" y2="371.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="234.5" y="329.0" width="9" height="12.0" fill="#c00"/>
<line x1="254.0" y1="335.0" x2="254.0" y2="353.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="249.5" y="341.0" width="9" height="3.0" fill="#c00"/>
<line x1="269.0" y1="227.0" x2="269.0" y2="350.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="264.5" y="236.0" width="9" height="108.0" fill="#188a3e"/>
<line x1="284.0" y1="230.0" x2="284.0" y2="275.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="279.5" y="236.0" width="9" height="27.0" fill="#c00"/>
<line x1="299.0" y1="257.0" x2="299.0" y2="293.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="294.5" y="263.0" width="9" height="18.0" fill="#c00"/>
<line x1="314.0" y1="263.0" x2="314.0" y2="287.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="309.5" y="269.0" width="9" height="12.0" fill="#188a3e"/>
<line x1="329.0" y1="239.0" x2="329.0" y2="270.8" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="324.5" y="245.0" width="9" height="24.0" fill="#188a3e"/>
<line x1="344.0" y1="212.0" x2="344.0" y2="252.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="339.5" y="221.0" width="9" height="24.0" fill="#188a3e"/>
<line x1="359.0" y1="212.0" x2="359.0" y2="234.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="354.5" y="221.0" width="9" height="6.0" fill="#c00"/>
<line x1="374.0" y1="188.0" x2="374.0" y2="234.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="369.5" y="197.0" width="9" height="30.0" fill="#188a3e"/>
<line x1="389.0" y1="164.0" x2="389.0" y2="204.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="384.5" y="173.0" width="9" height="24.0" fill="#188a3e"/>
<line x1="404.0" y1="164.0" x2="404.0" y2="192.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="399.5" y="173.0" width="9" height="12.0" fill="#c00"/>
<line x1="419.0" y1="146.0" x2="419.0" y2="192.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="414.5" y="155.0" width="9" height="30.0" fill="#188a3e"/>
<line x1="434.0" y1="122.0" x2="434.0" y2="162.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="429.5" y="131.0" width="9" height="24.0" fill="#188a3e"/>
<line x1="449.0" y1="104.0" x2="449.0" y2="138.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="444.5" y="113.0" width="9" height="18.0" fill="#188a3e"/>
<line x1="464.0" y1="104.0" x2="464.0" y2="126.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="459.5" y="113.0" width="9" height="6.0" fill="#c00"/>
<line x1="479.0" y1="86.0" x2="479.0" y2="126.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="474.5" y="95.0" width="9" height="24.0" fill="#188a3e"/>
<line x1="494.0" y1="68.0" x2="494.0" y2="102.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="489.5" y="77.0" width="9" height="18.0" fill="#188a3e"/>
<line x1="509.0" y1="56.0" x2="509.0" y2="84.2" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="504.5" y="65.0" width="9" height="12.0" fill="#188a3e"/>
<text x="269.0" y="203.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">semnal: candela verde mare</text>
<text x="852.0" y="387.0" fill="#c00" text-anchor="end">SL 99.90 = low-ul ultimelor 4 candele (comun A și B)</text>
<text x="239.0" y="387.0" fill="#777" text-anchor="middle" style="font-weight:bold">suport atins</text>
<line x1="284.0" y1="277.0" x2="284.0" y2="391.0" stroke="#b06000" stroke-width="1" stroke-dasharray="2 2"/>
<text x="288.0" y="402.0" fill="#b06000" text-anchor="end" style="font-weight:bold">atinge 1/3</text>
<line x1="299.0" y1="295.0" x2="299.0" y2="376.0" stroke="#b06000" stroke-width="1" stroke-dasharray="2 2"/>
<text x="295.0" y="387.0" fill="#b06000" text-anchor="start" style="font-weight:bold">atinge 1/2</text>
<line x1="560.0" y1="236.0" x2="560.0" y2="371.0" stroke="#188a3e" stroke-width="2.2"/>
<line x1="554.0" y1="236.0" x2="566.0" y2="236.0" stroke="#188a3e" stroke-width="2.2"/>
<line x1="554.0" y1="371.0" x2="566.0" y2="371.0" stroke="#c00" stroke-width="2.2"/>
<text x="560.0" y="230.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">A</text>
<text x="566.0" y="307.5" fill="#188a3e" text-anchor="start">2.25</text>
<line x1="605.0" y1="272.0" x2="605.0" y2="371.0" stroke="#b06000" stroke-width="2.2"/>
<line x1="599.0" y1="272.0" x2="611.0" y2="272.0" stroke="#b06000" stroke-width="2.2"/>
<line x1="599.0" y1="371.0" x2="611.0" y2="371.0" stroke="#c00" stroke-width="2.2"/>
<text x="605.0" y="266.0" fill="#b06000" text-anchor="middle" style="font-weight:bold">B 1/3</text>
<text x="611.0" y="325.5" fill="#b06000" text-anchor="start">1.65</text>
<line x1="650.0" y1="290.0" x2="650.0" y2="371.0" stroke="#b06000" stroke-width="2.2"/>
<line x1="644.0" y1="290.0" x2="656.0" y2="290.0" stroke="#b06000" stroke-width="2.2"/>
<line x1="644.0" y1="371.0" x2="656.0" y2="371.0" stroke="#c00" stroke-width="2.2"/>
<text x="650.0" y="284.0" fill="#b06000" text-anchor="middle" style="font-weight:bold">B 1/2</text>
<text x="656.0" y="334.5" fill="#b06000" text-anchor="start">1.35</text>
<text x="530.0" y="405.0" fill="#188a3e" text-anchor="start">risc A 2.25 → 22 acțiuni la 50 $</text>
<text x="530.0" y="420.0" fill="#b06000" text-anchor="start">risc B 1/3 1.65 → 30 acțiuni</text>
<text x="530.0" y="435.0" fill="#b06000" text-anchor="start">risc B 1/2 1.35 → 37 acțiuni</text>
<text x="24.0" y="462.0" fill="#555" text-anchor="start">Exemplu construit, fără ticker. Cifrele nu vin din curs. Risc permis 50 $ (0,5% din 10.000 $).</text>
</svg>

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -0,0 +1,123 @@
"""Diagrama din `gen/strategii/intrarea_retest_ratat.spec.txt` (TIP: PRET).
Pereche pentru `intrarea_doua_variante`: aceeași serie până la candela de
semnal inclusiv (0-15), apoi prețul nu mai coboară în corpul ei. Ordinele de
la 1/2 și 1/3 rămân neumplute, varianta A merge la +1.7R, varianta B n-are
poziție. Seria e construită (fără ticker), nu un trade din curs.
"""
import re
import sys
from pathlib import Path
sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
OUT = Path(__file__).resolve().parent / "intrarea_retest_ratat.svg"
GREEN, RED, ORANGE, GREY = "#188a3e", "#c00", "#b06000", "#777"
# ── 0-15: copiate din intrarea_doua_variante.spec.txt (aceeași serie) ───────
CLOSES_0_15 = [103.60, 103.35, 103.45, 103.10, 102.80, 102.90, 102.50, 102.20,
101.85, 101.95, 101.50, 101.10, # downtrend spre suport
100.60, 100.40, 100.35, # la suport
102.15] # candela de semnal
WICKS_0_15 = [(0.15, 0.15)] * 16
# 12-14 la suport: aceleași fitiluri ca în intrarea_doua_variante.py
WICKS_0_15[12] = (0.10, 0.15)
WICKS_0_15[13] = (0.10, 0.50) # low 99.90 (wick sub suportul de 100.00)
WICKS_0_15[14] = (0.10, 0.15)
WICKS_0_15[15] = (0.15, 0.10) # semnal: high 102.30, low 100.25
START = 103.80 # open-ul candelei 0
# ── 16-31: plecare fără pullback adânc ──────────────────────────────────────
CLOSES_16_31 = [102.40, 102.70, 102.55, 102.95, 103.30, 103.20, 103.60, 104.00,
103.85, 104.30, 104.70, 104.60, 105.10, 105.40, 105.30, 106.00]
WICKS_16_31 = [(0.15, 0.15)] * 16
WICKS_16_31[0] = (0.15, 0.25) # low-ul candelei 16 = 101.90
cds = from_closes(CLOSES_0_15 + CLOSES_16_31, wicks=WICKS_0_15 + WICKS_16_31,
start=START)
SL, SIG = 99.90, 15
sig = cds[SIG]
half = (sig.o + sig.c) / 2
third = sig.c - (sig.c - sig.o) / 3
entry_a = sig.c
low_after = min(c.l for c in cds[SIG + 1:])
r_a = (cds[31].c - entry_a) / (entry_a - SL)
# ── assert-urile din specificație ───────────────────────────────────────────
assert len(cds) == 32
# 1. open == close-ul barei anterioare
assert all(abs(cds[i].o - cds[i - 1].c) < 1e-9 for i in range(1, len(cds)))
# 2. 0-15 identice cu intrarea_doua_variante (lista e copiată din spec-ul ei)
assert abs(sig.o - 100.35) < 1e-9 and abs(sig.c - 102.15) < 1e-9
assert abs(sig.h - 102.30) < 1e-9 and abs(sig.l - 100.25) < 1e-9
assert abs(cds[13].l - 99.90) < 1e-9
assert abs(min(c.l for c in cds[11:15]) - SL) < 1e-9 and sig.l > SL
assert all(c.l > SL for i, c in enumerate(cds[11:15], 11) if i != 13)
assert abs(half - 101.25) < 1e-9 and abs(third - 101.55) < 1e-9
# 3. niciun ordin nu se umple
assert low_after > 101.55, low_after
assert abs(cds[16].l - 101.90) < 1e-9 and low_after >= 101.80
assert abs(low_after - 101.90) < 1e-9 # „cel mai jos după semnal" e chiar candela 16
# 4. finalul și R-ul
assert abs(cds[31].c - 106.00) < 1e-9
assert round(r_a, 2) == 1.71, r_a
R_TEXT = f"+{r_a:.1f}R"
assert R_TEXT == "+1.7R"
# 5. candela 23 și niciun „suport" inventat în 17-23
assert abs(cds[23].c - 104.00) < 1e-9
MARKED = [SL, half, third, entry_a]
assert not any(abs(c.l - p) < 1e-6 for c in cds[17:24] for p in MARKED)
levels = [
Level(SL, "SL 99.90", RED, dash=""),
Level(third, f"ordin 1/3 {third:.2f} — neumplut", ORANGE),
Level(half, f"ordin 1/2 {half:.2f} — neumplut", ORANGE),
Level(entry_a, f"A: close {entry_a:.2f}", GREEN),
]
marks = [
Mark(SIG, "semnal", GREEN, "above"),
# dy=-16: textul stă chiar la înălțimea low-ului, între eticheta 1/3 și linia A
Mark(16, f"cel mai jos după semnal: {low_after:.2f}", GREY, "below", dy=-16),
Mark(23, "NU muți ordinul aici: sub 104.00 nu e niciun nivel", RED, "above"),
Mark(31, f"A: {R_TEXT} · B: 0 (fără poziție)", GREY, "above"),
]
CAPTION = ["Exemplu construit, fără ticker. «Poate să nu mai facă, poate nu mai face niciodată.»",
"Ai ratat trade-ul; aștepți următoarea structură."]
ch = Chart(
candles=cds, levels=levels, marks=marks,
scale=80.0, width=660, gap=16, body_w=9, left=40,
title="Exemplu construit (fără ticker) — 15 min", caption=CAPTION[0],
)
svg = ch.render()
def reanchor(svg, text, anchor, dx):
"""Mark-urile ies centrate; unele trebuie să curgă într-o parte, unde e loc
liber (stânga lui 15: candelele 13-14 sunt jos; dreapta lui 16: toate low-urile
>= 102.25; stânga lui 23: toate high-urile < 104.00)."""
pat = re.compile(r'<text x="([\d.]+)"( y="[\d.]+" fill="[^"]+") text-anchor="middle"'
r'( style="font-weight:bold">' + re.escape(text) + '</text>)')
svg, n = pat.subn(lambda m: f'<text x="{float(m[1]) + dx:.1f}"{m[2]} '
f'text-anchor="{anchor}"{m[3]}', svg)
assert n == 1, text
return svg
svg = reanchor(svg, marks[0].text, "end", 5)
svg = reanchor(svg, marks[1].text, "start", 8)
svg = reanchor(svg, marks[2].text, "end", 6)
# legenda pe două rânduri: pe unul singur trece de lățimea de 660
m = re.search(r'<text x="24" y="([\d.]+)" fill="#555">' + re.escape(CAPTION[0]) + '</text>', svg)
assert m
y2 = float(m[1]) + 17
svg = svg.replace(m[0], m[0] + f'\n <text x="24" y="{y2:.0f}" fill="#555">{CAPTION[1]}</text>')
vb = re.search(r'viewBox="0 0 660 ([\d.]+)"', svg)
assert vb
svg = svg.replace(vb[0], f'viewBox="0 0 660 {float(vb[1]) + 17:.0f}"')
assert svg.count("\n\n") == 0, "linie goală în SVG — rupe markdown-ul"
OUT.write_text(svg)
print(f"low după semnal {low_after:.2f} | A {R_TEXT} ({r_a:.4f}) | scris {OUT}")

View File

@@ -0,0 +1,81 @@
<svg viewBox="0 0 660 615" 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">Exemplu construit (fără ticker) — 15 min</text>
<line x1="24" y1="556.0" x2="646" y2="556.0" stroke="#c00" stroke-width="1.5" stroke-dasharray=""/>
<text x="632" y="550.0" fill="#c00" text-anchor="end">SL 99.90</text>
<line x1="24" y1="424.0" x2="646" y2="424.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="632" y="418.0" fill="#b06000" text-anchor="end">ordin 1/3 101.55 — neumplut</text>
<line x1="24" y1="448.0" x2="646" y2="448.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="632" y="442.0" fill="#b06000" text-anchor="end">ordin 1/2 101.25 — neumplut</text>
<line x1="24" y1="376.0" x2="646" y2="376.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
<text x="632" y="370.0" fill="#188a3e" text-anchor="end">A: close 102.15</text>
<line x1="40.0" y1="232.0" x2="40.0" y2="272.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="35.5" y="244.0" width="9" height="16.0" fill="#c00"/>
<line x1="56.0" y1="248.0" x2="56.0" y2="292.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="51.5" y="260.0" width="9" height="20.0" fill="#c00"/>
<line x1="72.0" y1="260.0" x2="72.0" y2="292.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="67.5" y="272.0" width="9" height="8.0" fill="#188a3e"/>
<line x1="88.0" y1="260.0" x2="88.0" y2="312.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="83.5" y="272.0" width="9" height="28.0" fill="#c00"/>
<line x1="104.0" y1="288.0" x2="104.0" y2="336.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="99.5" y="300.0" width="9" height="24.0" fill="#c00"/>
<line x1="120.0" y1="304.0" x2="120.0" y2="336.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="115.5" y="316.0" width="9" height="8.0" fill="#188a3e"/>
<line x1="136.0" y1="304.0" x2="136.0" y2="360.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="131.5" y="316.0" width="9" height="32.0" fill="#c00"/>
<line x1="152.0" y1="336.0" x2="152.0" y2="384.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="147.5" y="348.0" width="9" height="24.0" fill="#c00"/>
<line x1="168.0" y1="360.0" x2="168.0" y2="412.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="163.5" y="372.0" width="9" height="28.0" fill="#c00"/>
<line x1="184.0" y1="380.0" x2="184.0" y2="412.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="179.5" y="392.0" width="9" height="8.0" fill="#188a3e"/>
<line x1="200.0" y1="380.0" x2="200.0" y2="440.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="195.5" y="392.0" width="9" height="36.0" fill="#c00"/>
<line x1="216.0" y1="416.0" x2="216.0" y2="472.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="211.5" y="428.0" width="9" height="32.0" fill="#c00"/>
<line x1="232.0" y1="452.0" x2="232.0" y2="512.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="227.5" y="460.0" width="9" height="40.0" fill="#c00"/>
<line x1="248.0" y1="492.0" x2="248.0" y2="556.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="243.5" y="500.0" width="9" height="16.0" fill="#c00"/>
<line x1="264.0" y1="508.0" x2="264.0" y2="532.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="259.5" y="516.0" width="9" height="4.0" fill="#c00"/>
<line x1="280.0" y1="364.0" x2="280.0" y2="528.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="275.5" y="376.0" width="9" height="144.0" fill="#188a3e"/>
<line x1="296.0" y1="344.0" x2="296.0" y2="396.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="291.5" y="356.0" width="9" height="20.0" fill="#188a3e"/>
<line x1="312.0" y1="320.0" x2="312.0" y2="368.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="307.5" y="332.0" width="9" height="24.0" fill="#188a3e"/>
<line x1="328.0" y1="320.0" x2="328.0" y2="356.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="323.5" y="332.0" width="9" height="12.0" fill="#c00"/>
<line x1="344.0" y1="300.0" x2="344.0" y2="356.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="339.5" y="312.0" width="9" height="32.0" fill="#188a3e"/>
<line x1="360.0" y1="272.0" x2="360.0" y2="324.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="355.5" y="284.0" width="9" height="28.0" fill="#188a3e"/>
<line x1="376.0" y1="272.0" x2="376.0" y2="304.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="371.5" y="284.0" width="9" height="8.0" fill="#c00"/>
<line x1="392.0" y1="248.0" x2="392.0" y2="304.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="387.5" y="260.0" width="9" height="32.0" fill="#188a3e"/>
<line x1="408.0" y1="216.0" x2="408.0" y2="272.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="403.5" y="228.0" width="9" height="32.0" fill="#188a3e"/>
<line x1="424.0" y1="216.0" x2="424.0" y2="252.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="419.5" y="228.0" width="9" height="12.0" fill="#c00"/>
<line x1="440.0" y1="192.0" x2="440.0" y2="252.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="435.5" y="204.0" width="9" height="36.0" fill="#188a3e"/>
<line x1="456.0" y1="160.0" x2="456.0" y2="216.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="451.5" y="172.0" width="9" height="32.0" fill="#188a3e"/>
<line x1="472.0" y1="160.0" x2="472.0" y2="192.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="467.5" y="172.0" width="9" height="8.0" fill="#c00"/>
<line x1="488.0" y1="128.0" x2="488.0" y2="192.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="483.5" y="140.0" width="9" height="40.0" fill="#188a3e"/>
<line x1="504.0" y1="104.0" x2="504.0" y2="152.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="499.5" y="116.0" width="9" height="24.0" fill="#188a3e"/>
<line x1="520.0" y1="104.0" x2="520.0" y2="136.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="515.5" y="116.0" width="9" height="8.0" fill="#c00"/>
<line x1="536.0" y1="56.0" x2="536.0" y2="136.0" stroke="#1a1a1a" stroke-width="1.2"/>
<rect x="531.5" y="68.0" width="9" height="56.0" fill="#188a3e"/>
<text x="285.0" y="354.0" fill="#188a3e" text-anchor="end" style="font-weight:bold">semnal</text>
<text x="304.0" y="400.0" fill="#777" text-anchor="start" style="font-weight:bold">cel mai jos după semnal: 101.90</text>
<text x="414.0" y="206.0" fill="#c00" text-anchor="end" style="font-weight:bold">NU muți ordinul aici: sub 104.00 nu e niciun nivel</text>
<text x="536.0" y="46.0" fill="#777" text-anchor="middle" style="font-weight:bold">A: +1.7R · B: 0 (fără poziție)</text>
<text x="24" y="586" fill="#555">Exemplu construit, fără ticker. «Poate să nu mai facă, poate nu mai face niciodată.»</text>
<text x="24" y="603" fill="#555">Ai ratat trade-ul; aștepți următoarea structură.</text>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB