Userul a respins mostra de format tutorial: prea lunga, prea multe trimiteri in text, termeni tehnici nedefiniti, prea multe cifre fara desen. Cerintele lui sunt acum contract in STYLE.md (FORMAT TUTORIAL v2). Textul: 8430 -> 4190 de cuvinte. Nivel 1 (din avion, cu mini-glosar) -> nivel 2 (6 pasi mecanici, o diagrama fiecare) -> oglinda short -> nivel 3 (rafinari). Trimiterile fisier:linie ies din corp si se aduna la final, cu legenda de prescurtari. Doua tabele in tot documentul. Toate cele 14 diagrame refacute: zone dreptunghiulare de evidentiere si sageti care spun si de ce, nu doar ce. svg_candles primeste Zone si Arrow; render() nu mai muta obiectul la iesire. Trei corecturi de continut: - un singur trade prin toata procedura (doji 50.10/49.25, intrare 50.40, stop 49.20, 1R 1.20, TP1 51.60); seria e acum o functie comuna, importata de stop/transe/iesire - short: stopul urca la 100.15, ca sa fie deasupra maximului cum cere regula oglindita; cifra cursului ramane in text ca observatie - LABU: trei minime dar doua atingeri ale pivotului 86.00, ceea ce impaca "triple bottom" cu "al doilea test", ambele din transcriere Reparat si bug-ul vechi din buy_doji_sell.svg: titlul peste eticheta dojiului. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
147 lines
7.4 KiB
Python
147 lines
7.4 KiB
Python
"""Buy Doji — unde pui stopul: 4 candele înapoi, sub structură.
|
|
|
|
Citește `gen/strategii/buy_doji_stop.spec.txt`. Barele vin din
|
|
`buy_doji_ansamblu.serie()` — e același trade ca în toate celelalte
|
|
diagrame ale capitolului (doji 50.10/49.25, intrare 50.40, stop 49.20).
|
|
|
|
Panoul de sus: cum numeri cele 4 candele, de la confirmare înapoi.
|
|
Panoul de jos: același trade, cu un stop pus „la ochi" (49.70, sub candela
|
|
de confirmare) față de stopul din regulă (49.20). Al doilea picior al
|
|
fundului coboară la 49.50: mătură stopul la ochi, nu-l atinge pe cel din
|
|
regulă. Barele de după intrare sunt CONSTRUITE.
|
|
"""
|
|
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 buy_doji_ansamblu import serie, DOJI, CONF, DOJI_L, ENTRY, SL, TP1, R # noqa: E402
|
|
|
|
OUT = Path(__file__).with_suffix(".svg")
|
|
S = 6 # prima bară arătată din serie
|
|
OCHI = 49.70 # stopul „la ochi", sub candela de confirmare
|
|
GREEN, RED, ORANGE, BLUE, GREY = "#188a3e", "#c00", "#b06000", "#1a5fb4", "#555"
|
|
W, VGAP = 720, 14
|
|
|
|
base = serie()[S:CONF + 1] # până la confirmare, inclusiv
|
|
after = [Candle(*b) for b in [
|
|
(50.40, 50.50, 49.85, 49.95), # +1: corecție
|
|
(49.95, 50.05, 49.50, 49.60), # +2: al doilea picior, low 49.50 — mătură 49.70
|
|
(49.60, 50.35, 49.55, 50.25),
|
|
(50.25, 50.90, 50.15, 50.80),
|
|
(50.80, 51.35, 50.70, 51.25),
|
|
(51.25, 51.80, 51.15, 51.70), # +6: atinge ținta 51.60
|
|
]]
|
|
jos = base + after
|
|
d, cf = DOJI - S, CONF - S # indici locali: doji, confirmare
|
|
|
|
# --- aserțiuni -----------------------------------------------------------------
|
|
for i, c in enumerate(jos):
|
|
assert c.l <= min(c.o, c.c) and c.h >= max(c.o, c.c), f"bara {i}: h/l nu cuprind o/c"
|
|
if i:
|
|
assert abs(c.o - jos[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]"
|
|
# 1. cel mai jos low e al doji-ului, și cu 4 și cu 5 candele numărate
|
|
assert min(c.l for c in jos[cf - 3:cf + 1]) == min(c.l for c in jos[cf - 4:cf + 1]) == DOJI_L
|
|
assert jos[d].l == DOJI_L and round(DOJI_L - 0.05, 2) == SL
|
|
# 2. al doilea picior: mătură stopul la ochi, nu-l atinge pe cel din regulă
|
|
dip = [c.l for c in after]
|
|
assert min(dip) == 49.50 and SL < 49.50 < OCHI, dip
|
|
assert next(k for k, c in enumerate(after) if c.l <= OCHI) == 1, "stopul la ochi nu e lovit de al doilea picior"
|
|
assert all(c.l > SL for c in after), "stopul din regulă ar fi fost lovit"
|
|
# 3. al doilea picior e higher low față de low-ul doji-ului
|
|
assert min(dip) > DOJI_L
|
|
# 4. ținta e atinsă abia după corecție, la a 6-a bară de după intrare
|
|
assert next(k for k, c in enumerate(after, 1) if c.h >= TP1) == 6
|
|
# 5. stopul la ochi e mai aproape de intrare decât cel din regulă, și peste low-ul doji-ului
|
|
assert round(ENTRY - OCHI, 2) == 0.70 < R and DOJI_L < OCHI < jos[cf].l # sub candela de confirmare, peste low-ul doji-ului
|
|
assert round(ENTRY - SL, 2) == R
|
|
print(f"stop: low4 {DOJI_L:.2f} -> SL {SL:.2f}; la ochi {OCHI:.2f} lovit de low {min(dip):.2f}; "
|
|
f"țintă {TP1:.2f} la a 6-a bară")
|
|
|
|
common: dict = dict(scale=36.0, gap=20, body_w=10, left=48, width=W)
|
|
|
|
# --- panoul de sus: numărătoarea celor 4 candele --------------------------------
|
|
sus = Chart(
|
|
candles=base,
|
|
levels=[Level(SL, "stopul: 49.20", RED, "4 3", "left")],
|
|
zones=[Zone(cf - 3, cf, "numeri 4 candele, începând cu confirmarea", BLUE)],
|
|
arrows=[
|
|
Arrow(d, "cel mai jos low din cele 4: 49.25\n(e al doji-ului)",
|
|
where="below", price=DOJI_L, color=GREY, dx=250, dy=34),
|
|
Arrow(cf - 1, "stopul: 49.20, cu 5 cenți mai jos\nsub el, ideea că vânzătorii\nau obosit e moartă",
|
|
where="below", price=SL, color=RED, dx=250, dy=76),
|
|
Arrow(cf, "confirmarea: close 50.40\nde aici numeri înapoi",
|
|
where="above", color=GREEN, dx=250),
|
|
],
|
|
title="Pasul 5 — stopul: numeri 4 candele înapoi de la confirmare",
|
|
**common,
|
|
)
|
|
|
|
# --- panoul de jos: stop la ochi vs stop din regulă -----------------------------
|
|
gata = Chart(
|
|
candles=jos,
|
|
levels=[Level(TP1, "ținta 1: 51.60", GREEN, "2 2", "left"),
|
|
Level(ENTRY, "intrarea: 50.40", GREY, "4 3", "left"),
|
|
Level(OCHI, "stop pus la ochi: 49.70", ORANGE, "4 3", "left"),
|
|
Level(SL, "stopul din regulă: 49.20", RED, "4 3", "left")],
|
|
# eticheta zonei o punem manual: Zone o ridică deasupra candelelor din
|
|
# dreptul textului, adică departe de banda pe care o marchează
|
|
zones=[Zone(cf + 1, cf + 2, "", RED, p_top=OCHI, p_bot=SL)],
|
|
arrows=[
|
|
Arrow(cf + 2, "stopul pus la ochi (49.70) e măturat\nera prea aproape de intrare",
|
|
where="below", price=OCHI, color=ORANGE, dx=230, dy=6),
|
|
Arrow(cf + 2, "al doilea picior al fundului: 49.50\nfundurile fac des asta",
|
|
where="below", price=49.50, color=BLUE, dx=230, dy=56),
|
|
Arrow(cf + 1, "stopul din regulă (49.20) ține\nal doilea picior rămâne peste el",
|
|
where="below", price=SL, color=RED, dx=230, dy=106),
|
|
Arrow(len(jos) - 1, "prețul ajunge la țintă\ndar numai cu stopul corect\nmai erai în trade",
|
|
where="above", price=TP1, color=GREEN, dx=150),
|
|
],
|
|
title="Același trade: stop „la ochi” (sub candela de confirmare) vs stopul din regulă",
|
|
caption="Barele de după intrare și stopul la ochi sunt construite, pentru contrast.",
|
|
**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))
|
|
# render() mută `top` intern și îl restaurează; offsetul se citește din
|
|
# prima linie de Level, altfel tot ce desenăm pe urmă iese mai sus cu ~22px
|
|
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, plus (5) — arată că iese la fel
|
|
yb = sus._y(SL) + off_sus + 22
|
|
nums = []
|
|
for k, n in zip(range(cf - 3, cf + 1), "4321"):
|
|
nums.append(f'<text x="{sus._cx(k):.1f}" y="{yb:.1f}" fill="{BLUE}" text-anchor="middle" '
|
|
f'style="font-weight:bold">{n}</text>')
|
|
nums.append(f'<text x="{sus._cx(cf - 4):.1f}" y="{yb:.1f}" fill="#999" text-anchor="middle">(5)</text>')
|
|
nums.append(f'<text x="24" y="{yb + 20:.1f}" fill="#555">'
|
|
f'cu 4 sau cu 5 candele iese la fel: în scădere, low-ul doji-ului e oricum cel mai jos</text>')
|
|
h_sus = max(h_sus, yb + 32)
|
|
|
|
body_jos, h_jos, off_jos = panou(gata, h_sus + VGAP)
|
|
zx = gata._cx(cf + 3) + 10
|
|
banda = (f'<text x="{zx:.1f}" y="{gata._y(OCHI) + off_jos - 6:.1f}" fill="{RED}" '
|
|
f'style="font-weight:bold">pe aici trece al doilea picior</text>')
|
|
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, banda,
|
|
]) + "\n</svg>"
|
|
assert "\n\n" not in svg and "<svg" 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} ({W}x{H:.0f})")
|