Mostra de format ceruta de user pe 19.09.2026 — de aprobat inainte sa aplicam formatul la celelalte 8 capitole. Specificatia e in STYLE.md, sectiunea FORMAT TUTORIAL. Structura: pasii 1-9 in ordinea in care faci lucrurile la ecran, fiecare terminat cu "Treci mai departe doar daca...". Cele trei perechi de TF (weekly-daily, monthly-weekly, daily-60min) una langa alta in pasul 2; perechea monthly-weekly e acoperita doar partial de material si scrie asta. Nivelurile 0/1/2 desfiintate ca structura. Niciun tabel de 2 coloane. Sursele mutate la final. 14 diagrame, de la 5: cele 5 vechi raman neatinse, 9 noi. Un agent per diagrama, fiecare cu audit + PNG privit. Toate 9 ies curate. 10 reguli din material care lipseau din versiunea veche, fiecare cu citat si fisier:linie — printre ele stopul se numara pe TF-ul deciziei (20_2025-08-25:9-15) si nu muti ordinul dupa pret (03_locul_crimei:296-311). Contradictie noua, a 7-a: pe weekly regula de pivoti spune Quarterly, dar MICI citeste pivotul monthly. Completarile proprii stau in blocuri "Completare proprie:", conform deciziei userului de azi. Cunoscut, nereparat: audit_diagrams da 9 fals-pozitive pe buy_doji_aplus si buy_doji_fereastra, care folosesc <g transform> (auditul nu-l intelege). A zecea, in buy_doji_sell (titlu peste eticheta "doji 99.20 / 100.10"), e reala si preexistenta. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
131 lines
6.1 KiB
Python
131 lines
6.1 KiB
Python
"""Buy Doji — unde pui stopul: 4 candele înapoi, sub structură (exemplu construit).
|
||
|
||
Citește specificația `gen/strategii/buy_doji_stop.spec.txt`. Două panouri
|
||
stivuite, aceeași serie 0-14 ca buy_doji_transe (downtrend, doji 12, +1 13,
|
||
confirmare 14). Sus: numărătoarea celor 4 candele (1 = confirmarea). Jos:
|
||
același trade, stop „la ochi" 52.90 față de stopul din regulă 51.55.
|
||
"""
|
||
import re
|
||
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
||
from svg_candles import Candle, Chart, Level, Mark
|
||
|
||
OUT = "/workspace/atm-curs-dl/gen/strategii/buy_doji_stop.svg"
|
||
|
||
# --- seria: barele 0-14 exact ca în buy_doji_transe.spec.txt -----------------
|
||
closes = [60.40, 59.60, 59.90, 58.80, 58.10, 58.40, 57.20, 56.30, 56.60,
|
||
55.30, 54.20, 52.40]
|
||
bars, o = [], 61.00
|
||
for c in closes: # fitile implicite 0.15 peste/sub corp
|
||
bars.append((o, max(o, c) + 0.15, min(o, c) - 0.15, c))
|
||
o = c
|
||
bars += [(52.40, 53.00, 51.60, 52.30), # 12 doji
|
||
(52.30, 52.85, 52.10, 52.60), # 13 +1
|
||
(52.60, 53.85, 52.45, 53.70)] # 14 confirmare
|
||
after = [(53.70, 53.75, 52.70, 52.90), # 15 lovește stopul la ochi
|
||
(52.90, 53.05, 52.00, 52.20), # 16 al doilea picior, higher low
|
||
(52.20, 53.40, 52.10, 53.30),
|
||
(53.30, 54.30, 53.20, 54.20),
|
||
(54.20, 55.10, 54.10, 55.00),
|
||
(55.00, 56.00, 54.90, 55.95)] # 20 TP1
|
||
full = bars + after
|
||
o_, h_, l_, c_ = (list(x) for x in zip(*full))
|
||
|
||
ENTRY, SL, OCHI, TP1, LOW4 = 53.70, 51.55, 52.90, 55.85, 51.60
|
||
|
||
# --- assert-urile din spec ---------------------------------------------------
|
||
for i in range(1, len(full)): # 1
|
||
assert abs(o_[i] - c_[i - 1]) < 1e-9, f"open[{i}] != close[{i-1}]"
|
||
for i in range(len(full)):
|
||
assert l_[i] <= min(o_[i], c_[i]) and h_[i] >= max(o_[i], c_[i]), f"bara {i}"
|
||
assert min(l_[11:15]) == min(l_[10:15]) == LOW4 # 2
|
||
assert l_.index(LOW4) == 12, "cel mai jos low nu e dojiul"
|
||
assert round(LOW4 - 0.05, 2) == SL
|
||
assert min(l_[15:21]) == 52.00 and SL < 52.00 < OCHI # 3
|
||
assert next(i for i in range(15, 21) if l_[i] <= OCHI) == 15 # 4
|
||
assert all(l_[i] > SL for i in range(15, 21))
|
||
assert next(i for i in range(15, 21) if h_[i] >= TP1) == 20 # 5
|
||
assert 52.00 > LOW4 # 6
|
||
assert round(ENTRY - OCHI, 2) == 0.80 and c_[14] == ENTRY
|
||
assert round(ENTRY + (ENTRY - SL), 2) == TP1
|
||
|
||
GREY, RED, ORANGE, GREEN, BLUE = "#555", "#c00", "#b06000", "#188a3e", "#1a5fb4"
|
||
W = 640
|
||
common: dict = dict(scale=34.0, gap=18, body_w=10, left=44, width=W)
|
||
|
||
|
||
def mk(n):
|
||
return [Candle(*b) for b in full[:n]]
|
||
|
||
|
||
# --- panoul de sus: numărătoarea ---------------------------------------------
|
||
top = Chart(
|
||
candles=mk(15),
|
||
levels=[Level(LOW4, "cel mai jos low: 51.60 (doji)", GREY, "2 2", "left"),
|
||
Level(SL, "SL 51.55", RED, "4 3")],
|
||
# fără Mark pe doji: la gap 18 eticheta calcă pe bara 11; îl numește
|
||
# paranteza de dedesubt și cercul de pe low
|
||
marks=[Mark(14, "confirmare 53.70", GREEN, "above")],
|
||
title="Unde pui stopul: 4 candele înapoi, sub structură — exemplu construit",
|
||
**common,
|
||
)
|
||
svg_top = top.render()
|
||
# numerele 4 3 2 1 + (5) sub bare, bandă de evidențiere, paranteză
|
||
yb = top._y(SL) + 18 # rândul cu numere, sub linia de SL
|
||
x = top._cx
|
||
extra_top = [
|
||
f'<rect x="{x(11) - 8:.1f}" y="{top._y(max(h_[11:15])) - 4:.1f}" '
|
||
f'width="{x(14) - x(11) + 16:.1f}" height="{yb + 6 - top._y(max(h_[11:15])):.1f}" '
|
||
f'fill="{BLUE}" opacity="0.08"/>',
|
||
f'<circle cx="{x(12):.1f}" cy="{top._y(LOW4):.1f}" r="4" fill="none" '
|
||
f'stroke="{RED}" stroke-width="1.5"/>',
|
||
]
|
||
for i, n in zip(range(11, 15), "4321"):
|
||
extra_top.append(f'<text x="{x(i):.1f}" y="{yb:.1f}" fill="{BLUE}" '
|
||
f'text-anchor="middle" style="font-weight:bold">{n}</text>')
|
||
extra_top.append(f'<text x="{x(10):.1f}" y="{yb:.1f}" fill="#999" '
|
||
f'text-anchor="middle">(5)</text>')
|
||
yp = yb + 8
|
||
extra_top.append(f'<polyline points="{x(11) - 5:.1f},{yp:.1f} {x(11) - 5:.1f},{yp + 5:.1f} '
|
||
f'{x(14) + 5:.1f},{yp + 5:.1f} {x(14) + 5:.1f},{yp:.1f}" '
|
||
f'fill="none" stroke="{BLUE}" stroke-width="1.2"/>')
|
||
extra_top.append(f'<text x="{x(14) + 14:.1f}" y="{yp + 9:.1f}" fill="{BLUE}">'
|
||
f'ultimele 4 candele (3 = doji, 1 = confirmarea)</text>')
|
||
extra_top.append(f'<text x="24" y="{yp + 28:.1f}" fill="#555">'
|
||
f'Cu 4 sau cu 5 candele iese la fel: în downtrend low-ul dojiului '
|
||
f'e oricum cel mai jos. SL = 51.60 − 0.05.</text>')
|
||
h_top = yp + 40
|
||
|
||
# --- panoul de jos: același trade, două stopuri -------------------------------
|
||
bot = Chart(
|
||
candles=mk(21),
|
||
levels=[Level(TP1, "TP1 55.85", GREEN, "2 2"),
|
||
Level(ENTRY, "intrare 53.70", GREY, "4 3"),
|
||
Level(OCHI, "stop la ochi 52.90 — lovit", ORANGE, "4 3"),
|
||
Level(SL, "SL din regulă 51.55 — ținut", RED, "4 3")],
|
||
marks=[Mark(16, "al doilea picior: 52.00, higher low", BLUE, "below", dy=10),
|
||
Mark(20, "TP1", GREEN, "above")],
|
||
title="Același trade: stop la ochi (0.80 sub intrare) vs stop din regulă",
|
||
caption="Barele 15-20 și stopul la ochi sunt construite, pentru contrast.",
|
||
top=h_top + 10 + 34, # panoul de jos începe sub cel de sus, fără <g transform>
|
||
**common,
|
||
)
|
||
svg_bot = bot.render()
|
||
extra_bot = [
|
||
f'<circle cx="{bot._cx(15):.1f}" cy="{bot._y(OCHI):.1f}" r="5" fill="none" '
|
||
f'stroke="{ORANGE}" stroke-width="2"/>',
|
||
]
|
||
|
||
|
||
# header-ul (viewBox) e al panoului de jos: el conține și offset-ul celui de sus
|
||
svg = "\n ".join([
|
||
svg_bot.split(">", 1)[0] + ">",
|
||
svg_top.split(">", 1)[1].rsplit("</svg>", 1)[0].strip(), *extra_top,
|
||
f'<line x1="24" y1="{h_top + 2:.0f}" x2="{W - 14}" y2="{h_top + 2:.0f}" stroke="#ccc"/>',
|
||
svg_bot.split(">", 1)[1].rsplit("</svg>", 1)[0].strip(), *extra_bot,
|
||
]) + "\n</svg>"
|
||
H = float(re.search(r'viewBox="0 0 \d+ ([\d.]+)"', svg).group(1))
|
||
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
|
||
with open(OUT, "w") as f:
|
||
f.write(svg)
|
||
print(f"scris {OUT} ({W}x{H:.0f})")
|