Files
atm-curs-dl/gen/strategii/buy_doji_trend.py
Claude Agent 9c1936e73a buy_doji: capitolul rescris ca tutorial in 9 pasi, cu 9 diagrame noi
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
2026-09-19 18:27:13 +00:00

118 lines
5.4 KiB
Python

"""Buy Doji — trendul dinaintea dojiului: 9 candele vs. 3 candele (exemplu construit).
Citește specificația `gen/strategii/buy_doji_trend.spec.txt`. Două panouri
stivuite, aceeași scară și dojiul pe aceeași verticală, ca să se compare
direct: sus downtrend de 9 candele (ideal), jos range + 3 candele (acceptat,
semnal mai slab, scannerul cu Since Bear ≥ 8 nu-l găsește). Candelele
trendului sunt numerotate sub bară, ca numărătoarea să se vadă.
"""
import re
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
from svg_candles import Candle, Chart, Mark
OUT = "/workspace/atm-curs-dl/gen/strategii/buy_doji_trend.svg"
DOJI = (49.65, 50.10, 49.20, 49.60)
CONF = (49.60, 50.55, 49.45, 50.40)
def series(start, closes):
"""Fitile implicite 0.15 peste/sub corp; open = close anterior."""
bars, o = [], start
for c in closes:
bars.append((o, max(o, c) + 0.15, min(o, c) - 0.15, c))
o = c
return bars + [DOJI, CONF]
L = series(55.50, [55.00, 54.30, 54.60, 53.60, 52.90, 53.10, 52.00, 51.20, 50.60, 49.65])
R = series(51.40, [51.70, 51.20, 51.60, 51.10, 51.50, 51.30, 51.70, 51.40, 50.90, 50.30, 49.65])
# --- assert-urile din spec ---------------------------------------------------
for bars in (L, R): # 1
for i, (o, h, l, c) in enumerate(bars):
assert l <= min(o, c) and h >= max(o, c), f"bara {i}"
if i:
assert abs(o - bars[i - 1][3]) < 1e-9, f"open[{i}] != close[{i-1}]"
assert L[10] == R[11] == DOJI # 2
assert abs(DOJI[0] - DOJI[3]) <= 0.10 * (DOJI[1] - DOJI[2])
cL = [b[3] for b in L]; cR = [b[3] for b in R]
assert cL[9] < cL[0] - 5 and cL[9] == min(cL[0:10]) # 3
assert sum(cL[i] < cL[i - 1] for i in range(1, 10)) >= 6
assert max(cR[0:8]) - min(cR[0:8]) <= 0.60 + 1e-9 # 4
assert cR[8] > cR[9] > cR[10]
assert L[11][3] > DOJI[1] and R[12][3] > DOJI[1] and L[11][3] == 50.40 # 5
GREY, GREEN, BLUE, ORANGE, RED = "#555", "#188a3e", "#1a5fb4", "#b06000", "#c00"
W, GAP = 540, 18
common: dict = dict(scale=30.0, gap=GAP, body_w=10, width=W)
def numbers(ch, idx, color):
"""Numără candelele trendului: 1, 2, 3... sub fiecare bară."""
return [f'<text x="{ch._cx(i):.1f}" y="{ch._y(ch.candles[i].l) + 14:.1f}" fill="{color}" '
f'text-anchor="middle" style="font-weight:bold;font-size:11px">{n}</text>'
for n, i in enumerate(idx, 1)]
def bracket(ch, a, b, y, text, color):
xa, xb = ch._cx(a) - 6, ch._cx(b) + 6
return [f'<polyline points="{xa:.1f},{y + 6:.1f} {xa:.1f},{y:.1f} {xb:.1f},{y:.1f} '
f'{xb:.1f},{y + 6:.1f}" fill="none" stroke="{color}" stroke-width="1.3"/>',
f'<text x="{xb + 8:.1f}" y="{y + 4:.1f}" fill="{color}" '
f'style="font-weight:bold">{text}</text>']
def body(svg):
return svg.split(">", 1)[1].rsplit("</svg>", 1)[0].strip()
# --- panoul de sus: 9 candele ------------------------------------------------
top = Chart(candles=[Candle(*b) for b in L], left=44 + GAP, # dojiul pe aceeași verticală
marks=[Mark(10, "doji", ORANGE, "below", dy=8),
Mark(11, "confirmare 50.40", GREEN, "above")],
title="Trendul dinaintea dojiului: 9 candele vs. 3 candele — exemplu construit",
**common)
svg_top = top.render()
y_br = top._y(max(b[1] for b in L[1:10])) - 10
ext_top = numbers(top, range(1, 10), RED) + bracket(top, 1, 9, y_br, "downtrend 9 candele", RED)
y_cap1 = top._y(DOJI[2]) + 50
ext_top.append(f'<text x="24" y="{y_cap1:.1f}" fill="{GREEN}" style="font-weight:bold">'
f'DA — ideal (8-10 candele)</text>')
# --- panoul de jos: range + 3 candele ----------------------------------------
y_sep = y_cap1 + 16
bot = Chart(candles=[Candle(*b) for b in R], left=44,
marks=[Mark(11, "doji", ORANGE, "below", dy=8),
Mark(12, "confirmare 50.40", GREEN, "above")],
top=int(y_sep + 62), **common) # titlul și paranteza range-ului, desenate mai jos
svg_bot = bot.render()
y_rg = bot._y(max(b[1] for b in R[0:8])) - 10
ext_bot = (numbers(bot, range(8, 11), RED)
+ bracket(bot, 8, 10, bot._y(R[8][1]) - 10, "doar 3 candele", RED)
+ bracket(bot, 0, 7, y_rg, "", GREY)
+ [f'<text x="24" y="{y_sep + 24:.1f}" fill="#333" style="font-weight:bold">'
f'Același doji, după un range</text>',
f'<text x="{bot._cx(0) - 6:.1f}" y="{y_rg - 5:.1f}" fill="{GREY}">'
f'range: nu se numără</text>'])
y_cap2 = bot._y(DOJI[2]) + 50
ext_bot += [f'<text x="24" y="{y_cap2:.1f}" fill="{ORANGE}" style="font-weight:bold">'
f'ACCEPTAT, semnal mai slab</text>',
f'<text x="24" y="{y_cap2 + 17:.1f}" fill="{GREY}">'
f'scannerul cu Since Bear ≥ 8 nu-l vede</text>']
H = y_cap2 + 30
# dojiul e aceeași bară, la aceeași scară, în același loc pe orizontală
assert abs(top._cx(10) - bot._cx(11)) < 1e-9 and top.scale == bot.scale
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(svg_top), *ext_top,
f'<line x1="24" y1="{y_sep:.0f}" x2="{W - 14}" y2="{y_sep:.0f}" stroke="#ccc"/>',
body(svg_bot), *ext_bot,
]) + "\n</svg>"
assert "\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})")