"""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'',
f'',
]
for i, n in zip(range(11, 15), "4321"):
extra_top.append(f'{n}')
extra_top.append(f'(5)')
yp = yb + 8
extra_top.append(f'')
extra_top.append(f''
f'ultimele 4 candele (3 = doji, 1 = confirmarea)')
extra_top.append(f''
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.')
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ă
**common,
)
svg_bot = bot.render()
extra_bot = [
f'',
]
# 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("", 1)[0].strip(), *extra_top,
f'',
svg_bot.split(">", 1)[1].rsplit("", 1)[0].strip(), *extra_bot,
]) + "\n"
H = float(re.search(r'viewBox="0 0 \d+ ([\d.]+)"', svg).group(1))
assert "