Cea mai grava, si cea care a cerut refacerea seriei: exemplul central de MBT-10 era luat de pe 60 de minute si bagat in procedura de daily, cu barele numite "zile", desi sursa spune explicit de ce nu merge pe daily. Pe langa asta, suportul fusese schimbat din 88.00 (testat de trei ori, cu compresie, cum e in sursa) in 87.50 cu un double bottom si o divergenta de volum imprumutate din alt exemplu. Seria e refacuta pe exemplul materialului, cu assert pe cele trei atingeri ale lui 88.00 si pe compresia varfurilor. Adaosurile au fost scoase, nu doar marcate: cu ele in desen, suportul din sursa n-ar fi avut sens. Aritmetica a supravietuit neatinsa. Restul: stopul de 60.40 sta acum sub o structura reala; pivotul daily nu mai e o linie fixa peste tot graficul, ci un segment peste ziua intrarii; SL-ul de pe LABU respecta regula proprie de 5 centi; citatul de la A2 nu mai e dat ca al autorului; pragul de 1-2% isi spune originea (cursul da 0,5%, pe 60 min); o singura conventie de distanta, impartirea la nivel; swing low definit la fel in pas si in glosar. eticheta_libera a prins 7 suprapuneri reale. La bh_aplus a trebuit rulat pe fiecare panou separat: wrapper-ul le muta cu <g transform>, pe care unealta nu-l citeste - de notat pentru data viitoare. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
104 lines
5.0 KiB
Python
104 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""MBT-10 — singura bucată reconstituibilă: close peste pivotul daily.
|
|
|
|
Aceleași bare ca la pasul „unde" (importate din `hammer_mbt10_mbt_confluente`),
|
|
tăiate în jurul trigger-ului. Exemplul e pe 60 de minute, ca în material: bara
|
|
18 atinge pivotul 89.60 în timpul zilei, dar se închide sub el (nu e semnal),
|
|
iar bara 19 se închide la 89.90, peste pivot — aici intri.
|
|
|
|
Pivotul daily NU e un nivel fix pe grafic: se recalculează în fiecare zi. Pe
|
|
60 de minute el e același pentru toate barele unei zile, așa că aici e desenat
|
|
doar peste barele zilei de intrare, între două linii de separare de zi. În
|
|
ziua dinainte era alt număr, pe care materialul nu-l dă — de aceea nu-l
|
|
inventăm, doar spunem că era altul.
|
|
|
|
Rulare: python3 gen/strategii/hammer_mbt10_mbt_ppd.py
|
|
"""
|
|
import re
|
|
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
|
sys.path.insert(0, "/workspace/atm-curs-dl/gen")
|
|
sys.path.insert(0, "/workspace/atm-curs-dl/gen/strategii")
|
|
from pathlib import Path
|
|
from eticheta_libera import eticheta_libera
|
|
from hammer_mbt10_mbt_confluente import (CLOSE_I, INTRARE, PPD, SUP_W,
|
|
ZI_INTRARE, serie)
|
|
from svg_candles import Arrow, Chart, Level, Zone
|
|
|
|
OUT = Path(__file__).with_suffix(".svg")
|
|
GREEN, RED, BLUE, ORANGE, GREY = "#188a3e", "#c00", "#1a5fb4", "#b06000", "#555"
|
|
|
|
ET_PIVOT = ("pivotul daily al zilei de", "intrare: 89.60 (ieri, altul) →")
|
|
|
|
cds = serie()
|
|
I0, I1 = 9, 22
|
|
w = cds[I0:I1 + 1]
|
|
sh = lambda i: i - I0 # noqa: E731
|
|
NU, DA = CLOSE_I - 1, CLOSE_I
|
|
|
|
assert cds[NU].h > PPD > cds[NU].c, "bara 18 nu mai e contraexemplul așteptat"
|
|
assert cds[DA].c > PPD and round(cds[DA].c, 2) == INTRARE
|
|
assert 0 <= sh(NU) < len(w) and sh(DA) < len(w), "fereastra nu prinde cele două bare"
|
|
assert max(c.c for c in cds[I0:DA]) < PPD, "o bară din fereastră închide deja peste pivot"
|
|
assert I0 < ZI_INTRARE[0] and ZI_INTRARE[1] <= I1, \
|
|
"ziua intrării trebuie să se vadă întreagă, cu bare de altă zi de o parte și de alta"
|
|
|
|
ch = Chart(
|
|
candles=w,
|
|
levels=[Level(SUP_W, "suport weekly 88.00", GREY, label_at="right")],
|
|
zones=[Zone(sh(NU), sh(DA), "", ORANGE, p_top=90.4)],
|
|
arrows=[
|
|
Arrow(sh(NU), "a atins 89.70 în timpul barei,\ndar s-a închis la 89.40, sub pivot:\nnu e semnal",
|
|
where="below", color=RED, dx=-190, dy=40),
|
|
Arrow(sh(DA), "s-a închis la 89.90, peste pivot:\naici intri, la close-ul barei",
|
|
where="above", color=GREEN, dx=-70, dy=-20),
|
|
],
|
|
scale=44.0, gap=26, body_w=13, left=64, width=680, top=58, vol_h=60, vol_label="volum",
|
|
title="MBT-10 — condiția de preț: close peste pivotul daily (60 de minute)",
|
|
)
|
|
svg = ch.render()
|
|
|
|
# --- pivotul daily: un segment peste ziua de intrare, nu o linie fixă -------
|
|
# aceeași corecție de offset ca în `_mbt_stop`: `_render` mută `top` ca să facă
|
|
# loc titlului și săgeților, deci `_y` singur nu nimerește coordonatele finale.
|
|
OFF = float(re.search(r'<line x1="24\.0" y1="([\d.]+)"', svg).group(1)) - ch._y(SUP_W)
|
|
OFFX = float(re.search(r'<line x1="([\d.]+)" y1="[\d.]+" x2="\1" [^/]*stroke="#1a1a1a"',
|
|
svg).group(1)) - ch._cx(0)
|
|
y_ppd = ch._y(PPD) + OFF
|
|
x_zi0 = ch._cx(sh(ZI_INTRARE[0])) + OFFX - ch.gap / 2
|
|
x_zi1 = ch._cx(sh(ZI_INTRARE[1])) + OFFX + ch.gap / 2
|
|
y_sus, y_jos = ch._y(ch._pmax) + OFF, ch._y(SUP_W) + OFF + 10
|
|
pivot = (
|
|
f'<line x1="{x_zi0:.1f}" y1="{y_sus:.1f}" x2="{x_zi0:.1f}" y2="{y_jos:.1f}" '
|
|
f'stroke="#bbb" stroke-width="1"/>'
|
|
f'<line x1="{x_zi1:.1f}" y1="{y_sus:.1f}" x2="{x_zi1:.1f}" y2="{y_jos:.1f}" '
|
|
f'stroke="#bbb" stroke-width="1"/>'
|
|
f'<line x1="{x_zi0:.1f}" y1="{y_ppd:.1f}" x2="{x_zi1:.1f}" y2="{y_ppd:.1f}" '
|
|
f'stroke="{BLUE}" stroke-width="1.5" stroke-dasharray="4 3"/>'
|
|
+ "".join(
|
|
f'<text x="{x_zi0 - 10:.1f}" y="{y_ppd - 20 + 14 * i:.1f}" fill="{BLUE}" '
|
|
f'text-anchor="end">{t}</text>' for i, t in enumerate(ET_PIVOT))
|
|
)
|
|
svg = svg.replace("</svg>", pivot + "</svg>")
|
|
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
|
W, H = int(m.group(1)), int(m.group(2))
|
|
linii = [
|
|
("Pivotul daily se recalculează în fiecare zi din maximul, minimul și închiderea zilei "
|
|
"precedente.", "#333"),
|
|
("Pe 60 de minute e același nivel pentru toate barele unei zile, așa că e desenat doar "
|
|
"între cele două", "#333"),
|
|
("linii subțiri care taie ziua intrării — nu peste tot graficul.", "#333"),
|
|
("Asta e tot ce se poate verifica din MBT-10 fără indicatorul proprietar — „prima "
|
|
"componentă\" din el,", GREY),
|
|
("cum îi spune materialul; restul formulei nu e publicat nicăieri.", GREY),
|
|
]
|
|
add = 16 * len(linii) + 10
|
|
svg = svg.replace(f'viewBox="0 0 {W} {H}"', f'viewBox="0 0 {W} {H + add}"')
|
|
svg = svg.replace("</svg>", "".join(
|
|
f'<text x="24" y="{H + 10 + 16 * i}" fill="{c}">{t}</text>'
|
|
for i, (t, c) in enumerate(linii)) + "</svg>")
|
|
eticheta_libera(svg, "mbt_ppd",
|
|
{ET_PIVOT}, rects=True)
|
|
assert "\n\n" not in svg, "linie goală în SVG"
|
|
OUT.write_text(svg, encoding="utf-8")
|
|
print(f"scris {OUT.name} ({W}x{H + add})")
|