strategii: primele 30 de diagrame, cu generatoarele lor
Cate un agent grafician per diagrama, fiecare cu generatorul si SVG-ul lui. Graficele de pret trec prin svg_candles, cu assert-urile din specificatie scrise in generator — verifica structura, nu doar ca ruleaza: ca al doilea picior e mai jos, ca intrarea e close-ul barei de confirmare, ca 1:1 e exact. Verificare prin sondaj pe buy_doji_ansamblu, cu screenshot citit: serie continua de 32 de candele, panou de volum, MA(20), doji-ul si ieșirea pe trail etichetate, cifrele identice cu specificatia. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
123
gen/strategii/atmi_ansamblu.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""Diagrama de ansamblu ATMI (strategii/05_atmi.md, marcajul `ansamblu`).
|
||||
|
||||
„SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 %".
|
||||
|
||||
Structura seriei și nivelurile vin din spec-ul
|
||||
`gen/strategii/atmi_ansamblu.spec.txt`. Toate cifrele desenate (100.00 swing
|
||||
high, 96.00 intrare, 105.60 = +10 %) sunt din exemplul „DA" al cursului
|
||||
(`summaries/04_MODUL_3/03_strategia_2_atmi.md:185-247`). Benzile PPQ/SQ sunt
|
||||
schematice: cursul spune explicit că nu dă cifrele lor
|
||||
(`...:143`), deci nu poartă prețuri.
|
||||
|
||||
Rulează:
|
||||
python3 gen/strategii/atmi_ansamblu.py
|
||||
Scrie lângă el `atmi_ansamblu.svg`.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "atmi_ansamblu.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
PURPLE = "#7b2fbf" # folosit în materialul de curs pentru PPQ
|
||||
|
||||
# ---------------------------------------------------------------- seria (33 bare)
|
||||
# 15 bare de urcare ~88 -> 100, 8 de corecție 100 -> 96 (ultima = intrarea),
|
||||
# 10 de urcare 96 -> 105.60. open-ul fiecărei bare = close-ul precedentei.
|
||||
UP_A = [88.2, 88.8, 89.5, 90.1, 90.8, 91.6, 92.5, 93.3, 94.2, 95.1,
|
||||
96.0, 96.9, 97.8, 98.9, 100.00] # 0-14
|
||||
CORR = [99.3, 98.5, 97.8, 97.2, 96.7, 96.3, 96.00, 96.00] # 15-22
|
||||
UP_B = [96.6, 97.2, 97.9, 98.6, 99.4, 100.3, 101.4, 102.6, 104.0, 105.60] # 23-32
|
||||
CLOSES = UP_A + CORR + UP_B
|
||||
assert len(CLOSES) == 33, len(CLOSES)
|
||||
PEAK, BIM, ENTRY, LAST = 14, 21, 22, 32
|
||||
|
||||
# fitile (sus, jos) per bară; jos pe corecție ținut ≥ 0 ca minimul să fie fix 96.00
|
||||
WICKS = ([(0.30, 0.30)] * 14
|
||||
+ [(0.00, 0.30)] # 14: vârful atinge exact 100.00
|
||||
+ [(0.00, 0.00), (0.05, 0.10), (0.05, 0.10), (0.05, 0.15),
|
||||
(0.05, 0.15), (0.05, 0.20), (0.05, 0.00), (0.12, 0.00)] # 15-22
|
||||
+ [(0.25, 0.25)] * 9
|
||||
+ [(0.00, 0.20)]) # 32: închide/atinge exact 105.60
|
||||
assert len(WICKS) == len(CLOSES)
|
||||
|
||||
VOLS = ([44, 47, 42, 50, 45, 41, 48, 43, 46, 51, 44, 49, 46, 52, 58] # 0-14 urcare
|
||||
+ [64, 70, 66, 61, 58, 74, 96, 88] # 15-22 corecție + BIM/intrare
|
||||
+ [55, 58, 52, 60, 57, 63, 59, 66, 72, 84]) # 23-32 urcare spre TP
|
||||
assert len(VOLS) == len(CLOSES)
|
||||
|
||||
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=88.0)
|
||||
|
||||
# ---------------------------------------------------------------- assert-urile din spec
|
||||
high = 100.00
|
||||
entry = cds[ENTRY].c
|
||||
tp = cds[LAST].h
|
||||
assert abs((high - 96.00) / high * 100 - 4.0) < 0.05 # corecția e 4.0 %
|
||||
assert abs(entry - 96.00) < 0.05 # intrarea = close-ul candelei verzi
|
||||
assert abs(tp / entry - 1.10) < 0.005 # TP = +10 %
|
||||
assert abs(tp - max(c.h for c in cds[-10:])) < 1e-9 # TP atins la final, e maximul barelor
|
||||
assert BIM < ENTRY # BIM înainte de candela verde
|
||||
assert abs(min(c.l for c in cds[15:23]) - 96.00) < 1e-9 # minimul corecției = 96.00
|
||||
assert cds[ENTRY].c >= cds[ENTRY].o # candela de intrare e verde
|
||||
assert abs(cds[PEAK].h - high) < 1e-9 # swing high = 100.00
|
||||
assert abs(cds[LAST].c - tp) < 1e-9 # ultima bară închide pe TP
|
||||
|
||||
levels = [
|
||||
Level(high, "100.00 — swing high recent", "#333", label_at="left"),
|
||||
Level(entry, f"intrare {entry:.2f}", GREEN, label_at="right"),
|
||||
Level(tp, f"TP +10 % = {tp:.2f}", BLUE, label_at="right"),
|
||||
]
|
||||
# nicio linie de stop-loss și niciun nivel sub intrare (absența SL e regula)
|
||||
assert all("stop" not in lv.text.lower() for lv in levels)
|
||||
assert all(lv.price >= entry - 1e-9 for lv in levels)
|
||||
|
||||
marks = [
|
||||
Mark(15, "corecție 4.0 %", ORANGE, "above"),
|
||||
Mark(BIM, "BIM", ORANGE, "above"),
|
||||
Mark(ENTRY, "BIM + candelă verde → intrare", GREEN, "below", dy=20),
|
||||
]
|
||||
|
||||
ch = Chart(candles=cds, levels=levels, marks=marks,
|
||||
mas=[MA(20, ORANGE, dash="4 3", text="SMA-20 (ilustrativ)")],
|
||||
scale=18.0, gap=16, body_w=9, left=44, width=720, vol_h=70, top=70,
|
||||
vol_label="volum",
|
||||
caption="Niveluri (100.00 / 96.00 / 105.60) din curs; PPQ/SQ schematice, fără cifre în material.")
|
||||
|
||||
svg = ch.render()
|
||||
|
||||
|
||||
def band(price: float, half: float, color: str, label: str) -> str:
|
||||
"""Bandă schematică orizontală, desenată în spatele candelelor.
|
||||
|
||||
Se injectează în SVG după randare, pentru că `Chart` nu are benzi; y-ul
|
||||
se calculează cu aceeași scară (`ch._y`), deci banda e aliniată corect.
|
||||
"""
|
||||
y_top, y_bot = ch._y(price + half), ch._y(price - half)
|
||||
return (f'<rect x="24" y="{y_top:.1f}" width="{ch.width - 38}" '
|
||||
f'height="{y_bot - y_top:.1f}" fill="{color}" opacity="0.10"/>'
|
||||
f'<text x="28" y="{y_top - 4:.1f}" fill="{color}">{label}</text>')
|
||||
|
||||
|
||||
# SQ e zona joasă (intrarea exact pe ea), PPQ zona mai sus, retragere mai mică
|
||||
assert (entry + 1.60 - 0.35) > (entry + 0.35), "benzile SQ și PPQ nu se suprapun"
|
||||
sq = band(entry, 0.35, GREEN, "SQ — zonă quarterly")
|
||||
ppq = band(entry + 1.60, 0.35, PURPLE, "PPQ — zonă quarterly")
|
||||
# „fără stop-loss standard" stă sub zona de intrare, unde nu are candele
|
||||
no_sl = (f'<text x="{ch._cx(ENTRY):.0f}" y="{ch._y(entry) + 63:.1f}" fill="{RED}" '
|
||||
f'text-anchor="middle">fără stop-loss standard</text>'
|
||||
f'<text x="{ch._cx(ENTRY):.0f}" y="{ch._y(entry) + 76:.1f}" fill="{GRAY}" '
|
||||
f'text-anchor="middle">(03_strategia_2_atmi.md:164-169)</text>')
|
||||
# titlul e injectat deasupra, ca rând separat de eticheta TP (care stă chiar
|
||||
# sub marginea de sus a panoului); altfel cele două texte se calcă la dreapta
|
||||
titlu = ('<text x="24" y="26" fill="#333" style="font-weight:bold">'
|
||||
'SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 %</text>')
|
||||
|
||||
i = svg.index(">") + 1
|
||||
svg = svg[:i] + "\n " + "\n ".join([sq, ppq, no_sl, titlu]) + svg[i:]
|
||||
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
OUT.write_text(svg)
|
||||
print(f"scris {OUT.name}: {len(cds)} candele, intrare {entry:.2f}, TP {tp:.2f}")
|
||||
119
gen/strategii/atmi_ansamblu.svg
Normal file
@@ -0,0 +1,119 @@
|
||||
<svg viewBox="0 0 720 530" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<rect x="24" y="236.5" width="682" height="12.6" fill="#188a3e" opacity="0.10"/><text x="28" y="232.5" fill="#188a3e">SQ — zonă quarterly</text>
|
||||
<rect x="24" y="207.7" width="682" height="12.6" fill="#7b2fbf" opacity="0.10"/><text x="28" y="203.7" fill="#7b2fbf">PPQ — zonă quarterly</text>
|
||||
<text x="396" y="305.8" fill="#c00" text-anchor="middle">fără stop-loss standard</text><text x="396" y="318.8" fill="#777" text-anchor="middle">(03_strategia_2_atmi.md:164-169)</text>
|
||||
<text x="24" y="26" fill="#333" style="font-weight:bold">SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 %</text>
|
||||
<line x1="24" y1="170.8" x2="706" y2="170.8" stroke="#333" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="164.8" fill="#333">100.00 — swing high recent</text>
|
||||
<line x1="24" y1="242.8" x2="706" y2="242.8" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="236.8" fill="#188a3e" text-anchor="end">intrare 96.00</text>
|
||||
<line x1="24" y1="70.0" x2="706" y2="70.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="64.0" fill="#1a5fb4" text-anchor="end">TP +10 % = 105.60</text>
|
||||
<polyline points="348.0,266.9 364.0,259.6 380.0,253.1 396.0,247.3 412.0,241.4 428.0,235.7 444.0,230.0 460.0,224.5 476.0,219.0 492.0,213.5 508.0,207.9 524.0,201.9 540.0,195.5 556.0,188.5" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="562" y="182" fill="#b06000">SMA-20 (ilustrativ)</text>
|
||||
<line x1="44.0" y1="377.8" x2="44.0" y2="392.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="383.2" width="9" height="3.6" fill="#188a3e"/>
|
||||
<line x1="60.0" y1="367.0" x2="60.0" y2="388.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="55.5" y="372.4" width="9" height="10.8" fill="#188a3e"/>
|
||||
<line x1="76.0" y1="354.4" x2="76.0" y2="377.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="71.5" y="359.8" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="92.0" y1="343.6" x2="92.0" y2="365.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="87.5" y="349.0" width="9" height="10.8" fill="#188a3e"/>
|
||||
<line x1="108.0" y1="331.0" x2="108.0" y2="354.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.5" y="336.4" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="124.0" y1="316.6" x2="124.0" y2="341.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="119.5" y="322.0" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="140.0" y1="300.4" x2="140.0" y2="327.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="135.5" y="305.8" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="156.0" y1="286.0" x2="156.0" y2="311.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="151.5" y="291.4" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="172.0" y1="269.8" x2="172.0" y2="296.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="167.5" y="275.2" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="188.0" y1="253.6" x2="188.0" y2="280.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="183.5" y="259.0" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="204.0" y1="237.4" x2="204.0" y2="264.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="199.5" y="242.8" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="220.0" y1="221.2" x2="220.0" y2="248.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="215.5" y="226.6" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="236.0" y1="205.0" x2="236.0" y2="232.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="231.5" y="210.4" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="252.0" y1="185.2" x2="252.0" y2="215.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="247.5" y="190.6" width="9" height="19.8" fill="#188a3e"/>
|
||||
<line x1="268.0" y1="170.8" x2="268.0" y2="196.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="263.5" y="170.8" width="9" height="19.8" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="170.8" x2="284.0" y2="183.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="170.8" width="9" height="12.6" fill="#c00"/>
|
||||
<line x1="300.0" y1="182.5" x2="300.0" y2="199.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="295.5" y="183.4" width="9" height="14.4" fill="#c00"/>
|
||||
<line x1="316.0" y1="196.9" x2="316.0" y2="212.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="311.5" y="197.8" width="9" height="12.6" fill="#c00"/>
|
||||
<line x1="332.0" y1="209.5" x2="332.0" y2="223.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="327.5" y="210.4" width="9" height="10.8" fill="#c00"/>
|
||||
<line x1="348.0" y1="220.3" x2="348.0" y2="232.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="343.5" y="221.2" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="364.0" y1="229.3" x2="364.0" y2="241.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="359.5" y="230.2" width="9" height="7.2" fill="#c00"/>
|
||||
<line x1="380.0" y1="236.5" x2="380.0" y2="242.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="375.5" y="237.4" width="9" height="5.4" fill="#c00"/>
|
||||
<line x1="396.0" y1="240.6" x2="396.0" y2="242.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="391.5" y="242.8" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="412.0" y1="227.5" x2="412.0" y2="247.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="407.5" y="232.0" width="9" height="10.8" fill="#188a3e"/>
|
||||
<line x1="428.0" y1="216.7" x2="428.0" y2="236.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="423.5" y="221.2" width="9" height="10.8" fill="#188a3e"/>
|
||||
<line x1="444.0" y1="204.1" x2="444.0" y2="225.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="439.5" y="208.6" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="460.0" y1="191.5" x2="460.0" y2="213.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="455.5" y="196.0" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="476.0" y1="177.1" x2="476.0" y2="200.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="471.5" y="181.6" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="492.0" y1="160.9" x2="492.0" y2="186.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="487.5" y="165.4" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="508.0" y1="141.1" x2="508.0" y2="169.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="503.5" y="145.6" width="9" height="19.8" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="119.5" x2="524.0" y2="150.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="124.0" width="9" height="21.6" fill="#188a3e"/>
|
||||
<line x1="540.0" y1="94.3" x2="540.0" y2="128.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="535.5" y="98.8" width="9" height="25.2" fill="#188a3e"/>
|
||||
<line x1="556.0" y1="70.0" x2="556.0" y2="102.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="551.5" y="70.0" width="9" height="28.8" fill="#188a3e"/>
|
||||
<line x1="24" y1="488.2" x2="706" y2="488.2" stroke="#888"/>
|
||||
<rect x="39.5" y="456.1" width="9" height="32.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="55.5" y="453.9" width="9" height="34.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="71.5" y="457.6" width="9" height="30.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="87.5" y="451.7" width="9" height="36.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="103.5" y="455.4" width="9" height="32.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="119.5" y="458.3" width="9" height="29.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="135.5" y="453.2" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="151.5" y="456.8" width="9" height="31.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="167.5" y="454.7" width="9" height="33.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="183.5" y="451.0" width="9" height="37.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="199.5" y="456.1" width="9" height="32.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="215.5" y="452.5" width="9" height="35.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="231.5" y="454.7" width="9" height="33.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="247.5" y="450.3" width="9" height="37.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="263.5" y="445.9" width="9" height="42.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="441.5" width="9" height="46.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="295.5" y="437.2" width="9" height="51.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="311.5" y="440.1" width="9" height="48.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="327.5" y="443.7" width="9" height="44.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="343.5" y="445.9" width="9" height="42.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="359.5" y="434.2" width="9" height="54.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="375.5" y="418.2" width="9" height="70.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="391.5" y="424.0" width="9" height="64.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="407.5" y="448.1" width="9" height="40.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="423.5" y="445.9" width="9" height="42.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="439.5" y="450.3" width="9" height="37.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="455.5" y="444.4" width="9" height="43.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="471.5" y="446.6" width="9" height="41.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="487.5" y="442.3" width="9" height="45.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="503.5" y="445.2" width="9" height="43.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="440.1" width="9" height="48.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="535.5" y="435.7" width="9" height="52.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="551.5" y="426.9" width="9" height="61.2" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="412.2" fill="#555">volum</text>
|
||||
<text x="284.0" y="160.8" fill="#b06000" text-anchor="middle" style="font-weight:bold">corecție 4.0 %</text>
|
||||
<text x="380.0" y="226.5" fill="#b06000" text-anchor="middle" style="font-weight:bold">BIM</text>
|
||||
<text x="396.0" y="282.8" fill="#188a3e" text-anchor="middle" style="font-weight:bold">BIM + candelă verde → intrare</text>
|
||||
<text x="24" y="518" fill="#555">Niveluri (100.00 / 96.00 / 105.60) din curs; PPQ/SQ schematice, fără cifre în material.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.9 KiB |
158
gen/strategii/atmi_aplus.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""ATMI — aceeași corecție: 4.0 % (DA) vs. 3.9 % (NU; cursul zice „3.93 %").
|
||||
|
||||
Citește specificația `gen/strategii/atmi_aplus.spec.txt`.
|
||||
|
||||
O singură serie de 32 de candele daily (open-ul fiecărei bare = close-ul
|
||||
precedentei, deci seria e continuă), care urmează calea DA: urcare la swing
|
||||
high 100.00, corecție exact 4.0 % până la 96.00 (BIM + candelă verde =
|
||||
intrare), apoi urcare la TP +10 % = 105.60. Peste ea, un traseu punctat
|
||||
(calea NU) care se oprește la 96.10 = 3.9 % și apoi coboară ilustrativ — fără
|
||||
intrare, pentru că nu atinge pragul de 4 %.
|
||||
|
||||
Traseele punctate / benzile se inserează în SVG-ul scos de `svg_candles`
|
||||
folosind coordonatele lui (`ch._cx`, `ch._y`), la fel ca în `az_structura.py`
|
||||
— candelele rămân desenate de `svg_candles`, nu de mână.
|
||||
"""
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE, INK = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#333"
|
||||
|
||||
# --- seria: 32 de close-uri, structura din specificație ----------------------
|
||||
# 0-11 urcare 90.00 -> 100.00 (swing high la bara 11)
|
||||
# 12-17 corecție (cădere)
|
||||
# 18 BIM (hammer de reversie, codiță lungă în jos)
|
||||
# 19 prima candelă verde daily => intrare, close 96.00
|
||||
# 20-31 urcare 96.00 -> 105.60 (TP +10 %)
|
||||
closes = [
|
||||
90.00, 90.90, 91.70, 92.40, 93.20, 94.00, 94.70, 95.40, 96.10, 97.00, 98.30, 100.00, # 0-11
|
||||
99.30, 98.50, 97.70, 97.10, 96.60, 96.20, # 12-17
|
||||
95.98, # 18 BIM
|
||||
96.00, # 19 intrare (verde)
|
||||
97.00, 97.90, 98.80, 99.70, 100.50, 101.30, 102.10, 102.90, 103.70, 104.50, 105.10, 105.60, # 20-31
|
||||
]
|
||||
assert len(closes) == 32
|
||||
|
||||
# wick-uri (sus, jos): implicite (0.25, 0.30), cu excepțiile care fixează nivelurile
|
||||
wicks = [(0.25, 0.30)] * 32
|
||||
wicks[11] = (0.00, 0.30) # swing high: high = close = 100.00
|
||||
wicks[18] = (0.20, 0.45) # BIM: codiță lungă în jos (hammer)
|
||||
wicks[19] = (0.20, 0.15) # candela de intrare
|
||||
wicks[31] = (0.00, 0.30) # TP: high = close = 105.60
|
||||
|
||||
# volum relativ ales de generator (materialul nu dă volum pe exemplu):
|
||||
# crește pe corecție (vânzătorii), vârf pe candela de intrare, scade pe urcare.
|
||||
vols = [48, 50, 46, 52, 54, 50, 56, 52, 58, 60, 64, 72,
|
||||
70, 74, 78, 82, 86, 80, 84,
|
||||
88,
|
||||
72, 66, 62, 58, 54, 50, 46, 62, 68, 72, 78, 86]
|
||||
assert len(vols) == 32
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=89.40)
|
||||
|
||||
# --- indicii-cheie și nivelurile ---------------------------------------------
|
||||
SWING_I = 11 # swing high
|
||||
BIM_I = 18 # primul BIM
|
||||
ENTRY_I = 19 # candela verde de intrare
|
||||
SWING = 100.00
|
||||
PRAG4 = 96.00 # prag corecție 4.0 % => DA
|
||||
NU_STOP = 96.10 # cazul real 3.9 % => NU
|
||||
TP = 105.60 # +10 % față de 96.00
|
||||
entry = cds[ENTRY_I].c
|
||||
|
||||
# --- cele 8 assert-uri cerute de specificație --------------------------------
|
||||
# 1. pragul de 4 % corespunde lui 96.00
|
||||
assert abs((SWING - PRAG4) / SWING * 100 - 4.00) < 0.05, "96.00 nu e 4.0 %"
|
||||
# 2. cazul NU: 96.10 = 3.9 % (graficul cursului scrie 3.9 %; textul „3.93 %")
|
||||
assert abs((SWING - NU_STOP) / SWING * 100 - 3.90) < 0.02, "96.10 nu e 3.9 %"
|
||||
# 3. calea DA coboară mai jos decât calea NU
|
||||
assert PRAG4 < NU_STOP, "pragul DA nu e sub oprirea NU"
|
||||
# 4. intrarea = close-ul candelei verzi = 96.00
|
||||
assert abs(entry - 96.00) < 1e-9, f"intrarea {entry} != 96.00"
|
||||
assert cds[ENTRY_I].c >= cds[ENTRY_I].o, "candela de intrare nu e verde"
|
||||
# 5. TP = +10 %
|
||||
assert abs(TP / entry - 1.10) < 0.005, f"TP/entry = {TP / entry:.4f}, nu 1.10"
|
||||
# 6. BIM înainte de candela de intrare
|
||||
assert BIM_I < ENTRY_I, "BIM nu e înainte de candela de intrare"
|
||||
# 7. traseul NU nu atinge pragul de trigger: se oprește la 96.10 > 96.00, deci
|
||||
# rămâne fără intrare; pe traseul punctat nu există nicio candelă / BIM
|
||||
nu_route = [(SWING_I, 100.00), (12, 99.40), (13, 98.65), (14, 97.95),
|
||||
(15, 97.35), (16, 96.80), (17, NU_STOP)]
|
||||
assert min(p for _, p in nu_route) == NU_STOP, "oprirea traseului NU nu e 96.10"
|
||||
assert min(p for _, p in nu_route) > PRAG4, "traseul NU atinge pragul de 4 %"
|
||||
assert not any(i >= BIM_I for i, _ in nu_route), "traseul NU ajunge la candela BIM"
|
||||
# 8. ambele trasee pleacă din același swing high 100.00
|
||||
assert abs(cds[SWING_I].h - SWING) < 1e-9, f"swing high {cds[SWING_I].h} != 100.00"
|
||||
assert abs(nu_route[0][1] - cds[SWING_I].h) < 1e-9, "traseul NU nu pleacă din swing high"
|
||||
|
||||
# verificări de context cerute de STYLE.md (structura susține eticheta)
|
||||
assert abs(TP - max(c.h for c in cds[-10:])) < 1e-9, "TP nu e maximul de la final"
|
||||
assert min(closes[12:20]) < 96.10 and min(closes[12:20]) >= 95.90, \
|
||||
"corecția DA nu coboară în jurul lui 96.00"
|
||||
assert all(b > a for a, b in zip(closes[20:31], closes[21:32])), "urcarea spre TP nu e monotonă"
|
||||
|
||||
print(f"swing {SWING:.2f} prag DA {PRAG4:.2f} (4.0 %) stop NU {NU_STOP:.2f} (3.9 %) "
|
||||
f"intrare {entry:.2f} TP {TP:.2f} (+{TP / entry - 1:.1%}) BIM bara {BIM_I}")
|
||||
|
||||
# --- desenul -----------------------------------------------------------------
|
||||
cds[ENTRY_I].note = ("Regula e pe procent, nu pe cât de dramatică arată linia: "
|
||||
"calea NU pare o cădere mare, dar e 3.9 %.")
|
||||
|
||||
# traseul NU: decizia (până la 96.10) + continuarea ilustrativă în jos
|
||||
nu_cont = [(17, NU_STOP), (18, 95.70), (19, 95.35), (21, 94.95), (23, 94.65), (25, 94.45)]
|
||||
nu_pts = nu_route + nu_cont[1:]
|
||||
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[
|
||||
Level(SWING, "100.00 — swing high", INK),
|
||||
Level(PRAG4, "96.00 = 4.0 % — DA, intri", GREEN),
|
||||
Level(TP, "TP +10 % = 105.60", BLUE),
|
||||
],
|
||||
marks=[
|
||||
Mark(BIM_I, "BIM", GREEN, "above", dy=-2),
|
||||
Mark(ENTRY_I, "candelă verde → trigger", GREEN, "below", dy=20),
|
||||
],
|
||||
scale=18.0, gap=16, body_w=9, left=44, width=680, vol_h=70,
|
||||
vol_label="volum", title="SPY — daily: aceeași corecție, 4.0 % (DA) vs. 3.9 % (NU)",
|
||||
caption="Calea DA (candele): 4.0 % la 96.00. Calea NU (punctat): 3.9 % („3.93 %” în curs) — fără intrare.",
|
||||
)
|
||||
svg = ch.render()
|
||||
|
||||
# --- elemente suplimentare, pe coordonatele graficului -----------------------
|
||||
x0, x1 = 24, ch.width - 14
|
||||
band_top, band_bot = ch._y(96.40), ch._y(95.40)
|
||||
band = (f'<rect x="{x0}" y="{band_top:.1f}" width="{x1 - x0}" height="{band_bot - band_top:.1f}" '
|
||||
f'fill="{GRAY}" opacity="0.10"/>')
|
||||
|
||||
nu_line = ('<polyline points="' + " ".join(f"{ch._cx(i):.1f},{ch._y(p):.1f}" for i, p in nu_pts) + '" '
|
||||
f'fill="none" stroke="{RED}" stroke-width="1.8" stroke-dasharray="5 4"/>')
|
||||
# linia pragului NU (96.10) se desenează manual, ca Level-ul gol să nu lase text vid
|
||||
nu_hline = (f'<line x1="24" y1="{ch._y(NU_STOP):.1f}" x2="{ch.width - 14}" y2="{ch._y(NU_STOP):.1f}" '
|
||||
f'stroke="{RED}" stroke-width="1.5" stroke-dasharray="4 3"/>')
|
||||
# punctul de oprire al traseului NU (96.10, strict deasupra pragului de 4 %)
|
||||
stop_dot = (f'<circle cx="{ch._cx(17):.1f}" cy="{ch._y(NU_STOP):.1f}" r="3.5" fill="{RED}"/>')
|
||||
# eticheta traseului NU, sub linia 96.10 (nu se suprapune cu eticheta verde de 96.00)
|
||||
nu_lab = (f'<text x="{ch.width - 28}" y="{ch._y(NU_STOP) + 12:.1f}" fill="{RED}" text-anchor="end">'
|
||||
f'96.10 = 3.9 % — NU, sub prag</text>')
|
||||
# eticheta zonei quarterly, sub banda (fără cifre: cursul nu le dă)
|
||||
zone_lab = (f'<text x="{ch.width - 28}" y="{ch._y(95.40) + 13:.1f}" fill="{GRAY}" text-anchor="end">'
|
||||
f'zonă quarterly</text>')
|
||||
# legende scurte ale celor două trasee
|
||||
da_lab = (f'<text x="24" y="{ch._y(98.50):.1f}" fill="{GREEN}" '
|
||||
f'style="font-weight:bold">calea DA (candelele)</text>')
|
||||
nu_leg = (f'<text x="{ch._cx(25):.1f}" y="{ch._y(94.45) + 16:.1f}" fill="{RED}" '
|
||||
f'style="font-weight:bold">calea NU (continuă în jos)</text>')
|
||||
|
||||
overlay = [nu_line, nu_hline, stop_dot, nu_lab, zone_lab, da_lab, nu_leg]
|
||||
svg = svg.replace(">", ">\n " + band, 1) # banda, sub candele
|
||||
svg = svg.replace("\n</svg>", "\n " + "\n ".join(overlay) + "\n</svg>") # traseele, peste
|
||||
|
||||
assert "\n\n" not in svg, "linie goală în SVG"
|
||||
assert "</svg>" in svg and "stroke-dasharray=\"5 4\"" in svg, "traseul NU nu a fost inserat"
|
||||
|
||||
with open("/workspace/atm-curs-dl/gen/strategii/atmi_aplus.svg", "w") as f:
|
||||
f.write(svg)
|
||||
print("scris gen/strategii/atmi_aplus.svg")
|
||||
119
gen/strategii/atmi_aplus.svg
Normal file
@@ -0,0 +1,119 @@
|
||||
<svg viewBox="0 0 680 508" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<rect x="24" y="221.6" width="642" height="18.0" fill="#777" opacity="0.10"/>
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">SPY — daily: aceeași corecție, 4.0 % (DA) vs. 3.9 % (NU)</text>
|
||||
<line x1="24" y1="156.8" x2="666" y2="156.8" stroke="#333" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="652" y="150.8" fill="#333" text-anchor="end">100.00 — swing high</text>
|
||||
<line x1="24" y1="228.8" x2="666" y2="228.8" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="652" y="222.8" fill="#188a3e" text-anchor="end">96.00 = 4.0 % — DA, intri</text>
|
||||
<line x1="24" y1="56.0" x2="666" y2="56.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="652" y="50.0" fill="#1a5fb4" text-anchor="end">TP +10 % = 105.60</text>
|
||||
<line x1="44.0" y1="332.3" x2="44.0" y2="353.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="336.8" width="9" height="10.8" fill="#188a3e"/>
|
||||
<line x1="60.0" y1="316.1" x2="60.0" y2="342.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="55.5" y="320.6" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="76.0" y1="301.7" x2="76.0" y2="326.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="71.5" y="306.2" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="92.0" y1="289.1" x2="92.0" y2="311.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="87.5" y="293.6" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="108.0" y1="274.7" x2="108.0" y2="299.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.5" y="279.2" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="124.0" y1="260.3" x2="124.0" y2="284.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="119.5" y="264.8" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="140.0" y1="247.7" x2="140.0" y2="270.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="135.5" y="252.2" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="156.0" y1="235.1" x2="156.0" y2="257.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="151.5" y="239.6" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="172.0" y1="222.5" x2="172.0" y2="245.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="167.5" y="227.0" width="9" height="12.6" fill="#188a3e"/>
|
||||
<line x1="188.0" y1="206.3" x2="188.0" y2="232.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="183.5" y="210.8" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="204.0" y1="182.9" x2="204.0" y2="216.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="199.5" y="187.4" width="9" height="23.4" fill="#188a3e"/>
|
||||
<line x1="220.0" y1="156.8" x2="220.0" y2="192.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="215.5" y="156.8" width="9" height="30.6" fill="#188a3e"/>
|
||||
<line x1="236.0" y1="152.3" x2="236.0" y2="174.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="231.5" y="156.8" width="9" height="12.6" fill="#c00"/>
|
||||
<line x1="252.0" y1="164.9" x2="252.0" y2="189.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="247.5" y="169.4" width="9" height="14.4" fill="#c00"/>
|
||||
<line x1="268.0" y1="179.3" x2="268.0" y2="203.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="263.5" y="183.8" width="9" height="14.4" fill="#c00"/>
|
||||
<line x1="284.0" y1="193.7" x2="284.0" y2="214.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="198.2" width="9" height="10.8" fill="#c00"/>
|
||||
<line x1="300.0" y1="204.5" x2="300.0" y2="223.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="295.5" y="209.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="316.0" y1="213.5" x2="316.0" y2="230.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="311.5" y="218.0" width="9" height="7.2" fill="#c00"/>
|
||||
<line x1="332.0" y1="221.6" x2="332.0" y2="237.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="327.5" y="225.2" width="9" height="4.0" fill="#c00"/>
|
||||
<line x1="348.0" y1="225.2" x2="348.0" y2="231.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="343.5" y="228.8" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="364.0" y1="206.3" x2="364.0" y2="234.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="359.5" y="210.8" width="9" height="18.0" fill="#188a3e"/>
|
||||
<line x1="380.0" y1="190.1" x2="380.0" y2="216.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="375.5" y="194.6" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="396.0" y1="173.9" x2="396.0" y2="200.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="391.5" y="178.4" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="412.0" y1="157.7" x2="412.0" y2="183.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="407.5" y="162.2" width="9" height="16.2" fill="#188a3e"/>
|
||||
<line x1="428.0" y1="143.3" x2="428.0" y2="167.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="423.5" y="147.8" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="444.0" y1="128.9" x2="444.0" y2="153.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="439.5" y="133.4" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="460.0" y1="114.5" x2="460.0" y2="138.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="455.5" y="119.0" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="476.0" y1="100.1" x2="476.0" y2="124.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="471.5" y="104.6" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="492.0" y1="85.7" x2="492.0" y2="110.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="487.5" y="90.2" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="508.0" y1="71.3" x2="508.0" y2="95.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="503.5" y="75.8" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="60.5" x2="524.0" y2="81.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="65.0" width="9" height="10.8" fill="#188a3e"/>
|
||||
<line x1="540.0" y1="56.0" x2="540.0" y2="70.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="535.5" y="56.0" width="9" height="9.0" fill="#188a3e"/>
|
||||
<line x1="24" y1="449.0" x2="666" y2="449.0" stroke="#888"/>
|
||||
<rect x="39.5" y="410.8" width="9" height="38.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="55.5" y="409.2" width="9" height="39.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="71.5" y="412.4" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="87.5" y="407.6" width="9" height="41.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="103.5" y="406.0" width="9" height="43.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="119.5" y="409.2" width="9" height="39.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="135.5" y="404.5" width="9" height="44.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="151.5" y="407.6" width="9" height="41.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="167.5" y="402.9" width="9" height="46.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="183.5" y="401.3" width="9" height="47.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="199.5" y="398.1" width="9" height="50.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="215.5" y="391.7" width="9" height="57.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="231.5" y="393.3" width="9" height="55.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="247.5" y="390.1" width="9" height="58.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="263.5" y="387.0" width="9" height="62.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="383.8" width="9" height="65.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="295.5" y="380.6" width="9" height="68.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="311.5" y="385.4" width="9" height="63.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="327.5" y="382.2" width="9" height="66.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="343.5" y="379.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="359.5" y="391.7" width="9" height="57.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="375.5" y="396.5" width="9" height="52.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="391.5" y="399.7" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="407.5" y="402.9" width="9" height="46.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="423.5" y="406.0" width="9" height="43.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="439.5" y="409.2" width="9" height="39.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="455.5" y="412.4" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="471.5" y="399.7" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="487.5" y="394.9" width="9" height="54.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="503.5" y="391.7" width="9" height="57.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="387.0" width="9" height="62.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="535.5" y="380.6" width="9" height="68.4" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="373.0" fill="#555">volum</text>
|
||||
<text x="332.0" y="209.6" fill="#188a3e" text-anchor="middle" style="font-weight:bold">BIM</text>
|
||||
<text x="348.0" y="271.9" fill="#188a3e" text-anchor="middle" style="font-weight:bold">candelă verde → trigger</text>
|
||||
<text x="24" y="471.0" fill="#188a3e">Regula e pe procent, nu pe cât de dramatică arată linia: calea NU pare o cădere mare, dar e 3.9 %.</text>
|
||||
<text x="24" y="496" fill="#555">Calea DA (candele): 4.0 % la 96.00. Calea NU (punctat): 3.9 % („3.93 %” în curs) — fără intrare.</text>
|
||||
<polyline points="220.0,156.8 236.0,167.6 252.0,181.1 268.0,193.7 284.0,204.5 300.0,214.4 316.0,227.0 332.0,234.2 348.0,240.5 380.0,247.7 412.0,253.1 444.0,256.7" fill="none" stroke="#c00" stroke-width="1.8" stroke-dasharray="5 4"/>
|
||||
<line x1="24" y1="227.0" x2="666" y2="227.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<circle cx="316.0" cy="227.0" r="3.5" fill="#c00"/>
|
||||
<text x="652" y="239.0" fill="#c00" text-anchor="end">96.10 = 3.9 % — NU, sub prag</text>
|
||||
<text x="652" y="252.6" fill="#777" text-anchor="end">zonă quarterly</text>
|
||||
<text x="24" y="183.8" fill="#188a3e" style="font-weight:bold">calea DA (candelele)</text>
|
||||
<text x="444.0" y="272.7" fill="#c00" style="font-weight:bold">calea NU (continuă în jos)</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.8 KiB |
125
gen/strategii/atmi_exemplu.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Diagrama din `gen/strategii/atmi_exemplu.spec.txt` (TIP: PRET).
|
||||
|
||||
Exemplul real din curs (ATMI / SPY weekly): corecție de context ≥4 %,
|
||||
intrare pe 27.01.2005 la 117.43 după BIM + candelă verde, fără stop-loss
|
||||
standard, urcare lentă până la TP1 +10 % = 129.17, atins ~1 an mai târziu
|
||||
(data exactă nesigură în ASR).
|
||||
|
||||
Serie continuă de 38 de candele weekly (from_closes), panou de volum,
|
||||
niveluri reale (intrare / TP1) și marcaje ancorate. Toate cifrele vin din
|
||||
`summaries/04_MODUL_3/03_strategia_2_atmi.md` (liniile citate în spec);
|
||||
singurul număr construit de mine e swing high-ul ~123.00, care trebuie să
|
||||
conțină corecția de context.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "atmi_exemplu.svg"
|
||||
|
||||
# ── cifrele din material ────────────────────────────────────────────────────
|
||||
ENTRY = 117.43 # 04_MODUL_3/03_strategia_2_atmi.md:358-363
|
||||
TP = 129.17 # TP1 = +10 % pe 117.43 → 129.17 (":170-171, 358-363")
|
||||
SWING_HIGH = 123.00 # construit de mine ca să conțină corecția ≥4 %
|
||||
ENTRY_I = 17 # candela de intrare (verde, precedată de BIM)
|
||||
TP_I = 37 # ultima candelă: TP1 atins
|
||||
BIM_I = ENTRY_I - 1 # BIM (bară de inversare bearish) înaintea candelei verzi
|
||||
|
||||
# ── structura seriei: 12 sus → 6 corecție → intrare → 20 spre TP ───────────
|
||||
closes = [
|
||||
# ~12 candele de urcare spre swing high ~123.00
|
||||
115.80, 116.50, 117.20, 117.90, 117.60, 118.30,
|
||||
119.00, 119.70, 120.40, 121.00, 121.70, 122.70,
|
||||
# ~6 candele de corecție, de la 123.00 în zona de intrare
|
||||
122.00, 121.10, 120.10, 119.00, 117.00,
|
||||
117.43, # candela de intrare (verde)
|
||||
# ~20 candele de urcare de la 117.43 spre 129.17
|
||||
118.00, 118.55, 119.05, 119.50, 119.95,
|
||||
120.40, 120.85, 121.25, 121.65, 122.10,
|
||||
122.50, 122.95, 123.40, 123.85, 124.30,
|
||||
125.00, 126.00, 127.10, 128.40, 129.17,
|
||||
]
|
||||
assert len(closes) == 38, len(closes)
|
||||
|
||||
wicks = [(0.25, 0.30)] * len(closes)
|
||||
wicks[0] = (0.35, 0.35)
|
||||
wicks[11] = (0.30, 0.25) # high = 122.70 + 0.30 = 123.00 (swing high exact)
|
||||
wicks[BIM_I] = (0.15, 0.45)
|
||||
wicks[ENTRY_I] = (0.20, 0.40)
|
||||
wicks[TP_I] = (0.00, 0.30) # high = 129.17 exact, ca TP să fie max(high)
|
||||
|
||||
vols = [42, 40, 44, 47, 39, 45, 48, 52, 50, 55, 58, 62,
|
||||
57, 61, 66, 72, 84,
|
||||
70,
|
||||
58, 55, 53, 57, 54, 56, 59, 55, 57, 60, 58, 61,
|
||||
63, 60, 64, 66, 68, 72, 76, 88]
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=115.30)
|
||||
|
||||
# ── assert-urile din specificație ───────────────────────────────────────────
|
||||
entry = cds[ENTRY_I].c
|
||||
tp = cds[TP_I].c
|
||||
swing_high = max(c.h for c in cds[:ENTRY_I])
|
||||
|
||||
assert abs(entry - 117.43) < 0.05, entry
|
||||
assert abs(tp - 129.17) < 0.05, tp
|
||||
assert abs(tp / entry - 1.10) < 0.005, tp / entry
|
||||
assert (swing_high - entry) / swing_high >= 0.04, (swing_high, entry)
|
||||
assert abs(entry - cds[ENTRY_I].c) < 0.05
|
||||
assert abs(max(c.h for c in cds[-10:]) - tp) < 0.05, max(c.h for c in cds[-10:])
|
||||
|
||||
# structura care susține afirmațiile din text
|
||||
assert abs(swing_high - SWING_HIGH) < 0.05, swing_high
|
||||
assert cds[ENTRY_I].c > cds[ENTRY_I].o, "candela de intrare trebuie să fie verde"
|
||||
assert cds[BIM_I].c < cds[BIM_I].o, "BIM-ul dinaintea intrării trebuie să fie bearish"
|
||||
assert any(c.vol is not None for c in cds), "lipsește panoul de volum"
|
||||
|
||||
n_entry_to_tp = TP_I - ENTRY_I + 1
|
||||
assert 18 <= n_entry_to_tp <= 23, n_entry_to_tp
|
||||
|
||||
TP_LABEL = "TP1 atins ~1 an mai târziu (data exactă neclară în transcriere — ASR)"
|
||||
assert "~1 an" in TP_LABEL
|
||||
|
||||
levels = [
|
||||
Level(ENTRY, "intrare 117.43", "#188a3e", label_at="left"),
|
||||
Level(TP, "TP1 129.17 (+10 %)", "#1a5fb4"),
|
||||
]
|
||||
|
||||
marks = [
|
||||
Mark(11, f"swing high ~{SWING_HIGH:.2f}", "#c00", "above"),
|
||||
Mark(BIM_I, "BIM", "#777", "above"),
|
||||
Mark(ENTRY_I, "27.01.2005", "#188a3e", "below"),
|
||||
Mark(TP_I, "TP1", "#1a5fb4", "below"),
|
||||
]
|
||||
|
||||
# nu există nicio linie de stop-loss în desen
|
||||
assert not any("stop" in t.lower() for t in
|
||||
[lv.text for lv in levels] + [mk.text for mk in marks])
|
||||
assert len(levels) == 2, "doar intrare și TP; strategic nu există stop standard"
|
||||
|
||||
# etichetele lungi merg ca linii de text sub grafic, ca să nu se bată cu
|
||||
# candelele din dreapta-sus (unde urcarea spre TP nu lasă loc liber)
|
||||
cds[TP_I - 1].note = "după TP: capital + profit → cash până la următorul trigger valid"
|
||||
cds[TP_I].note = TP_LABEL
|
||||
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=levels,
|
||||
marks=marks,
|
||||
mas=[MA(20, "#b06000", dash="4 3")],
|
||||
scale=9.5, width=660, gap=15, body_w=9, left=40, vol_h=70,
|
||||
vol_label="volum", title="SPY — weekly: intrare 117.43 (27.01.2005) → TP +10 % = 129.17",
|
||||
caption="Swing high ~123.00 construit de mine; cifrele de intrare/TP sunt din material.",
|
||||
)
|
||||
|
||||
svg = ch.render()
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG — rupe markdown-ul"
|
||||
OUT.write_text(svg)
|
||||
|
||||
print(f"serie {len(cds)} candele | entry {entry:.2f} @ {ENTRY_I} | "
|
||||
f"tp {tp:.2f} @ {TP_I} | swing high {swing_high:.2f} "
|
||||
f"(corecție {(swing_high - entry) / swing_high * 100:.2f} %) | "
|
||||
f"entry→TP {n_entry_to_tp} candele (~1 an)")
|
||||
print(f"scris {OUT}")
|
||||
131
gen/strategii/atmi_exemplu.svg
Normal file
@@ -0,0 +1,131 @@
|
||||
<svg viewBox="0 0 660 363" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">SPY — weekly: intrare 117.43 (27.01.2005) → TP +10 % = 129.17</text>
|
||||
<line x1="24" y1="167.5" x2="646" y2="167.5" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="161.5" fill="#188a3e">intrare 117.43</text>
|
||||
<line x1="24" y1="56.0" x2="646" y2="56.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="632" y="50.0" fill="#1a5fb4" text-anchor="end">TP1 129.17 (+10 %)</text>
|
||||
<polyline points="325.0,152.1 340.0,150.6 355.0,149.2 370.0,147.9 385.0,146.7 400.0,145.1 415.0,143.7 430.0,142.5 445.0,141.3 460.0,140.3 475.0,139.4 490.0,138.6 505.0,138.1 520.0,137.0 535.0,135.1 550.0,132.3 565.0,128.5 580.0,123.1 595.0,117.5" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<line x1="40.0" y1="179.7" x2="40.0" y2="191.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="35.5" y="183.0" width="9" height="4.8" fill="#188a3e"/>
|
||||
<line x1="55.0" y1="174.0" x2="55.0" y2="185.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="50.5" y="176.4" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="70.0" y1="167.3" x2="70.0" y2="179.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="65.5" y="169.7" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="85.0" y1="160.7" x2="85.0" y2="172.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="80.5" y="163.1" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="100.0" y1="160.7" x2="100.0" y2="168.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="95.5" y="163.1" width="9" height="2.9" fill="#c00"/>
|
||||
<line x1="115.0" y1="156.9" x2="115.0" y2="168.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="110.5" y="159.3" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="130.0" y1="150.2" x2="130.0" y2="162.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="125.5" y="152.6" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="145.0" y1="143.6" x2="145.0" y2="155.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="140.5" y="146.0" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="160.0" y1="136.9" x2="160.0" y2="148.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="155.5" y="139.3" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="175.0" y1="131.2" x2="175.0" y2="142.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="170.5" y="133.6" width="9" height="5.7" fill="#188a3e"/>
|
||||
<line x1="190.0" y1="124.6" x2="190.0" y2="136.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="185.5" y="127.0" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="205.0" y1="114.6" x2="205.0" y2="129.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="200.5" y="117.5" width="9" height="9.5" fill="#188a3e"/>
|
||||
<line x1="220.0" y1="115.1" x2="220.0" y2="127.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="215.5" y="117.5" width="9" height="6.7" fill="#c00"/>
|
||||
<line x1="235.0" y1="121.7" x2="235.0" y2="135.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="230.5" y="124.1" width="9" height="8.6" fill="#c00"/>
|
||||
<line x1="250.0" y1="130.3" x2="250.0" y2="145.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="245.5" y="132.7" width="9" height="9.5" fill="#c00"/>
|
||||
<line x1="265.0" y1="139.8" x2="265.0" y2="155.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="260.5" y="142.2" width="9" height="10.4" fill="#c00"/>
|
||||
<line x1="280.0" y1="151.2" x2="280.0" y2="175.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="275.5" y="152.6" width="9" height="19.0" fill="#c00"/>
|
||||
<line x1="295.0" y1="165.6" x2="295.0" y2="175.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="290.5" y="167.5" width="9" height="4.1" fill="#188a3e"/>
|
||||
<line x1="310.0" y1="159.7" x2="310.0" y2="170.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="305.5" y="162.1" width="9" height="5.4" fill="#188a3e"/>
|
||||
<line x1="325.0" y1="154.5" x2="325.0" y2="165.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="320.5" y="156.9" width="9" height="5.2" fill="#188a3e"/>
|
||||
<line x1="340.0" y1="149.8" x2="340.0" y2="159.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="335.5" y="152.1" width="9" height="4.8" fill="#188a3e"/>
|
||||
<line x1="355.0" y1="145.5" x2="355.0" y2="155.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="350.5" y="147.9" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="370.0" y1="141.2" x2="370.0" y2="150.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="365.5" y="143.6" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="385.0" y1="136.9" x2="385.0" y2="146.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="380.5" y="139.3" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="400.0" y1="132.7" x2="400.0" y2="142.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="395.5" y="135.0" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="415.0" y1="128.9" x2="415.0" y2="137.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="410.5" y="131.2" width="9" height="3.8" fill="#188a3e"/>
|
||||
<line x1="430.0" y1="125.1" x2="430.0" y2="134.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="425.5" y="127.4" width="9" height="3.8" fill="#188a3e"/>
|
||||
<line x1="445.0" y1="120.8" x2="445.0" y2="130.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="440.5" y="123.2" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="460.0" y1="117.0" x2="460.0" y2="126.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="455.5" y="119.4" width="9" height="3.8" fill="#188a3e"/>
|
||||
<line x1="475.0" y1="112.7" x2="475.0" y2="122.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="470.5" y="115.1" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="490.0" y1="108.4" x2="490.0" y2="117.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="485.5" y="110.8" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="505.0" y1="104.2" x2="505.0" y2="113.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="500.5" y="106.5" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="520.0" y1="99.9" x2="520.0" y2="109.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="515.5" y="102.3" width="9" height="4.3" fill="#188a3e"/>
|
||||
<line x1="535.0" y1="93.2" x2="535.0" y2="105.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="530.5" y="95.6" width="9" height="6.7" fill="#188a3e"/>
|
||||
<line x1="550.0" y1="83.7" x2="550.0" y2="98.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="545.5" y="86.1" width="9" height="9.5" fill="#188a3e"/>
|
||||
<line x1="565.0" y1="73.3" x2="565.0" y2="89.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="560.5" y="75.7" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="580.0" y1="60.9" x2="580.0" y2="78.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="575.5" y="63.3" width="9" height="12.4" fill="#188a3e"/>
|
||||
<line x1="595.0" y1="56.0" x2="595.0" y2="66.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="590.5" y="56.0" width="9" height="7.3" fill="#188a3e"/>
|
||||
<line x1="24" y1="287.1" x2="646" y2="287.1" stroke="#888"/>
|
||||
<rect x="35.5" y="253.7" width="9" height="33.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="50.5" y="255.3" width="9" height="31.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="65.5" y="252.1" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="80.5" y="249.7" width="9" height="37.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="95.5" y="256.1" width="9" height="31.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="110.5" y="251.3" width="9" height="35.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="125.5" y="248.9" width="9" height="38.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="140.5" y="245.7" width="9" height="41.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="155.5" y="247.3" width="9" height="39.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="170.5" y="243.3" width="9" height="43.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="185.5" y="241.0" width="9" height="46.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="200.5" y="237.8" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="215.5" y="241.7" width="9" height="45.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="230.5" y="238.6" width="9" height="48.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="245.5" y="234.6" width="9" height="52.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="260.5" y="229.8" width="9" height="57.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="275.5" y="220.3" width="9" height="66.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="290.5" y="231.4" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="305.5" y="241.0" width="9" height="46.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="320.5" y="243.3" width="9" height="43.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="335.5" y="244.9" width="9" height="42.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="350.5" y="241.7" width="9" height="45.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="365.5" y="244.1" width="9" height="43.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="380.5" y="242.5" width="9" height="44.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="395.5" y="240.2" width="9" height="46.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="410.5" y="243.3" width="9" height="43.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="425.5" y="241.7" width="9" height="45.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="440.5" y="239.4" width="9" height="47.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="455.5" y="241.0" width="9" height="46.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="470.5" y="238.6" width="9" height="48.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="485.5" y="237.0" width="9" height="50.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="500.5" y="239.4" width="9" height="47.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="515.5" y="236.2" width="9" height="50.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="530.5" y="234.6" width="9" height="52.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="545.5" y="233.0" width="9" height="54.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="560.5" y="229.8" width="9" height="57.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="575.5" y="226.6" width="9" height="60.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="590.5" y="217.1" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="211.1" fill="#555">volum</text>
|
||||
<text x="205.0" y="104.6" fill="#c00" text-anchor="middle" style="font-weight:bold">swing high ~123.00</text>
|
||||
<text x="280.0" y="141.2" fill="#777" text-anchor="middle" style="font-weight:bold">BIM</text>
|
||||
<text x="295.0" y="195.4" fill="#188a3e" text-anchor="middle" style="font-weight:bold">27.01.2005</text>
|
||||
<text x="607.0" y="86.2" fill="#1a5fb4" text-anchor="end" style="font-weight:bold">TP1</text>
|
||||
<text x="24" y="309.1" fill="#188a3e">după TP: capital + profit → cash până la următorul trigger valid</text>
|
||||
<text x="24" y="326.1" fill="#188a3e">TP1 atins ~1 an mai târziu (data exactă neclară în transcriere — ASR)</text>
|
||||
<text x="24" y="351" fill="#555">Swing high ~123.00 construit de mine; cifrele de intrare/TP sunt din material.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
230
gen/strategii/atmi_orizont.py
Normal file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Orizontul de ținere al ATMI — două descrieri incompatibile, pe aceeași axă.
|
||||
|
||||
Pentru `strategii/03_atmi.md`, marcajul `<!-- DIAGRAMA: orizont -->`.
|
||||
Nu e grafic de preț, e o linie de timp comparativă: SVG scris de mână, doar
|
||||
stdlib — nu se folosește svg_candles / pchart.
|
||||
|
||||
Ce arată (toate cifrele / citatele vin din spec, nu se inventează prețuri):
|
||||
- bara de sus, albastră: „ATMI clasică — fișierul principal", orizont luni
|
||||
până la ~1 an, fără stop-loss, TP1 atins „aproximativ un an mai târziu";
|
||||
- bara de jos, portocalie: „ATMI ca adaptare pentru prop — Q&A 11.06.2025",
|
||||
orizont 2 săptămâni;
|
||||
- cutia de anotare: pe prop, limita zilnică se calculează și pe pozițiile
|
||||
deschise, iar la atingere firma închide automat; și nota că descrierile nu
|
||||
se rezolvă aici, de clarificat cu autorul.
|
||||
|
||||
Barele sunt pe o axă comună 0 → 1 an, ca diferența să fie vizibilă; bara de
|
||||
2 săptămâni e scurtă exact pentru că atât ține, nu din greșeală de scară.
|
||||
|
||||
Rulare: python3 gen/strategii/atmi_orizont.py -> gen/strategii/atmi_orizont.svg
|
||||
"""
|
||||
from pathlib import Path
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
W, H = 700, 670
|
||||
BLACK, GRAY, BLUE, ORANGE, WHITE = "#000000", "#777777", "#1a5fb4", "#b06000", "#ffffff"
|
||||
|
||||
AX_X0, AX_X1 = 60.0, 650.0
|
||||
AX_W = AX_X1 - AX_X0 # 590 px = 1 an
|
||||
AX_Y = 112.0
|
||||
TICKS = [0, 3, 6, 9, 12] # luni
|
||||
TICK_LABELS = {0: "0", 3: "3 luni", 6: "6 luni", 9: "9 luni", 12: "~1 an"}
|
||||
|
||||
WEEKS_YEAR = 52.0
|
||||
LEN_PROP = AX_W * 2 / WEEKS_YEAR # ~22.7 px = 2 săptămâni
|
||||
|
||||
BAR_TOP_Y = 172.0
|
||||
BAR_PROP_Y = 298.0
|
||||
BAR_H = 42.0
|
||||
|
||||
CARD_X, CARD_Y, CARD_W, CARD_H = 160.0, 292.0, 500.0, 112.0
|
||||
BOX_X, BOX_Y, BOX_W, BOX_H = 40.0, 440.0, 620.0, 150.0
|
||||
|
||||
TEXTS = [] # (x, y, text, size, weight, anchor) pentru verificare
|
||||
|
||||
|
||||
def txt(x, y, s, size=13, weight="400", fill=BLACK, anchor="middle"):
|
||||
TEXTS.append((x, y, s, size, weight, anchor))
|
||||
return (
|
||||
f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" font-weight="{weight}" '
|
||||
f'fill="{fill}" text-anchor="{anchor}">{escape(s)}</text>'
|
||||
)
|
||||
|
||||
|
||||
def line(x1, y1, x2, y2, stroke=GRAY, width=1.0, dash=None):
|
||||
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||
return (f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" '
|
||||
f'stroke="{stroke}" stroke-width="{width}"{d}/>')
|
||||
|
||||
|
||||
def tick_x(months):
|
||||
return AX_X0 + AX_W * months / 12.0
|
||||
|
||||
|
||||
def build():
|
||||
p = [
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
|
||||
f'width="{W}" height="{H}" role="img" '
|
||||
f'aria-label="Orizontul de tinere al ATMI: fisiarul principal spune luni '
|
||||
f'pana la un an, adaptarea pentru prop spune doua saptamani; cele doua '
|
||||
f'descrieri raman nerezolvate">',
|
||||
'<g font-family="system-ui, sans-serif">',
|
||||
f'<rect x="0" y="0" width="{W}" height="{H}" fill="{WHITE}"/>',
|
||||
txt(W / 2, 34, "Cât ții poziția? Două descrieri, același nume", 19, "700", BLACK),
|
||||
txt(W / 2, 58, "fișierul principal vs. sfatul din Q&A pentru calificarea "
|
||||
"pe prop", 13, "400", GRAY),
|
||||
txt(W / 2, 84, "Aceeași axă de timp pentru ambele bare (0 → 1 an); "
|
||||
"fără axe de preț.", 12, "400", GRAY),
|
||||
]
|
||||
|
||||
# grid vertical, în spatele barelor
|
||||
for m in TICKS:
|
||||
x = tick_x(m)
|
||||
p.append(line(x, 120, x, 425, "#ddd", 1.0, "3 4"))
|
||||
|
||||
# axa de timp
|
||||
p.append(line(AX_X0, AX_Y, AX_X1, AX_Y, GRAY, 1.2))
|
||||
for m in TICKS:
|
||||
x = tick_x(m)
|
||||
p.append(line(x, AX_Y - 6, x, AX_Y + 6, GRAY, 1.2))
|
||||
p.append(txt(x, 132, TICK_LABELS[m], 12, "400", GRAY))
|
||||
|
||||
# ------- bara de sus: ATMI clasică
|
||||
p.append(txt(AX_X0, 162, "ATMI clasică — fișierul principal", 15, "700", BLUE,
|
||||
"start"))
|
||||
p.append(f'<rect x="{AX_X0}" y="{BAR_TOP_Y}" width="{AX_W:g}" height="{BAR_H:g}" '
|
||||
f'rx="6" fill="#eef4fd" stroke="{BLUE}" stroke-width="1.5"/>')
|
||||
p.append(txt(W / 2, BAR_TOP_Y + 26,
|
||||
"„fără stop-loss, poziția ținută luni/ani”", 13, "400", BLACK))
|
||||
p.append(line(AX_X1, 164, AX_X1, 226, BLUE, 1.2, "4 3"))
|
||||
p.append(txt(AX_X1, 244, "TP1 atins — aproximativ un an mai târziu",
|
||||
13, "400", BLUE, "end"))
|
||||
p.append(txt(AX_X0, 262, "Sursă: 04_MODUL_3/03_strategia_2_atmi.md:26-29; "
|
||||
":358-363", 11, "400", GRAY, "start"))
|
||||
|
||||
# ------- bara de jos: adaptarea pentru prop
|
||||
p.append(txt(AX_X0, 282, "ATMI ca adaptare pentru prop — Q&A 11.06.2025",
|
||||
15, "700", ORANGE, "start"))
|
||||
p.append(f'<rect x="{AX_X0}" y="{BAR_PROP_Y}" width="{LEN_PROP:.1f}" '
|
||||
f'height="{BAR_H:g}" rx="6" fill="#fdf0e0" stroke="{ORANGE}" '
|
||||
f'stroke-width="1.5"/>')
|
||||
p.append(line(AX_X0 + LEN_PROP, 290, AX_X0 + LEN_PROP, 360, ORANGE, 1.2, "4 3"))
|
||||
p.append(line(AX_X0 + LEN_PROP, BAR_PROP_Y + BAR_H / 2, CARD_X,
|
||||
BAR_PROP_Y + BAR_H / 2, ORANGE, 1.0, "3 3"))
|
||||
p.append(txt(AX_X0 + 30, 312, "2 săptămâni", 11, "700", ORANGE, "start"))
|
||||
|
||||
p.append(f'<rect x="{CARD_X}" y="{CARD_Y}" width="{CARD_W}" height="{CARD_H}" '
|
||||
f'rx="8" fill="{WHITE}" stroke="{ORANGE}" stroke-width="1.5"/>')
|
||||
p.append(txt(CARD_X + CARD_W / 2, 316,
|
||||
"„aștepți o corecție și intri pe ATMI la suport quarterly,",
|
||||
13, "400", BLACK))
|
||||
p.append(txt(CARD_X + CARD_W / 2, 334, "swing, 2 săptămâni în poziție”",
|
||||
13, "400", BLACK))
|
||||
p.append(txt(CARD_X + CARD_W / 2, 358,
|
||||
"„Ai nevoie de 3 transacții să califici propul.”",
|
||||
13, "400", BLACK))
|
||||
p.append(txt(CARD_X + CARD_W / 2, 382,
|
||||
"Sursă: 09_QA/26_2025-06-11.md:225-227", 11, "400", GRAY))
|
||||
|
||||
# ------- cutia de anotare
|
||||
p.append(f'<rect x="{BOX_X}" y="{BOX_Y}" width="{BOX_W}" height="{BOX_H}" '
|
||||
f'rx="8" fill="#fafafa" stroke="{GRAY}" stroke-width="1.2"/>')
|
||||
p.append(txt(BOX_X + 20, 466,
|
||||
"Anotare separată — mecanismul care face diferența",
|
||||
14, "700", BLACK, "start"))
|
||||
p.append(txt(BOX_X + 20, 492,
|
||||
"Pe prop, limita zilnică se calculează și pe pozițiile deschise,",
|
||||
13, "400", BLACK, "start"))
|
||||
p.append(txt(BOX_X + 20, 510, "iar la atingere firma închide automat poziția.",
|
||||
13, "400", BLACK, "start"))
|
||||
p.append(txt(BOX_X + 20, 530, "Sursă: 09_QA/26_2025-06-11.md:216-222",
|
||||
11, "400", GRAY, "start"))
|
||||
p.append(txt(BOX_X + 20, 560,
|
||||
"Notă: cele două descrieri nu se rezolvă aici — de clarificat "
|
||||
"cu autorul.", 13, "700", BLACK, "start"))
|
||||
|
||||
p.append(txt(W / 2, 626, "Fără axe de preț și fără să alegem una dintre "
|
||||
"variante.", 12, "400", GRAY))
|
||||
p.append(txt(W / 2, 646, "Bara de 2 săptămâni e scurtă exact pentru că "
|
||||
"atât ține, pe aceeași scară.", 12, "400", GRAY))
|
||||
p.append('</g></svg>')
|
||||
return "\n".join(p) + "\n"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- verificare
|
||||
def est_w(s, size, weight):
|
||||
factor = 0.62 if weight == "700" else 0.56
|
||||
return len(s) * size * factor
|
||||
|
||||
|
||||
def verify(svg):
|
||||
# 1. geometria barelor, față de axa comună
|
||||
assert abs(AX_W - (AX_X1 - AX_X0)) < 1e-9
|
||||
assert abs(LEN_PROP - AX_W * 2 / WEEKS_YEAR) < 1e-9
|
||||
assert LEN_PROP < AX_W / 20, "bara de 2 săptămâni ar trebui să fie vizibil scurtă"
|
||||
assert AX_X0 + LEN_PROP < CARD_X, "bara prop intră în card"
|
||||
assert BAR_TOP_Y + BAR_H < BAR_PROP_Y
|
||||
assert CARD_X + CARD_W <= AX_X1 + 10
|
||||
|
||||
# 2. toate etichetele în panză
|
||||
for x, y, s, size, weight, anchor in TEXTS:
|
||||
ew = est_w(s, size, weight)
|
||||
left = x - ew if anchor == "end" else (x - ew / 2 if anchor == "middle" else x)
|
||||
right = left + ew
|
||||
assert -2 <= left and right <= W + 2, f"text în afara panzei: {s!r}"
|
||||
assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y})"
|
||||
|
||||
# 3. etichete care nu se suprapun
|
||||
for i in range(len(TEXTS)):
|
||||
xi, yi, si, szi, wi, ai = TEXTS[i]
|
||||
we = est_w(si, szi, wi)
|
||||
li = xi - we if ai == "end" else (xi - we / 2 if ai == "middle" else xi)
|
||||
ri = li + we
|
||||
for j in range(i + 1, len(TEXTS)):
|
||||
xj, yj, sj, szj, wj, aj = TEXTS[j]
|
||||
wej = est_w(sj, szj, wj)
|
||||
lj = xj - wej if aj == "end" else (xj - wej / 2 if aj == "middle" else xj)
|
||||
rj = lj + wej
|
||||
if abs(yi - yj) < 13 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
# 4. textul cardului încape în card
|
||||
for x, y, s, size, weight, anchor in TEXTS:
|
||||
if CARD_Y <= y <= CARD_Y + CARD_H and CARD_X <= x <= CARD_X + CARD_W:
|
||||
ew = est_w(s, size, weight)
|
||||
assert ew <= CARD_W - 24, f"eticheta nu încape în card: {s!r} ({ew:.0f})"
|
||||
|
||||
# 5. cutia de anotare: textul nu iese
|
||||
for x, y, s, size, weight, anchor in TEXTS:
|
||||
if BOX_Y <= y <= BOX_Y + BOX_H and x >= BOX_X:
|
||||
ew = est_w(s, size, weight)
|
||||
assert BOX_X + 20 + ew <= BOX_X + BOX_W - 12, f"text iese din cutie: {s!r}"
|
||||
|
||||
# 6. citatele și sursele din spec sunt prezente
|
||||
for cheie in ("fără stop-loss, poziția ținută luni/ani",
|
||||
"aproximativ un an mai târziu",
|
||||
"suport quarterly",
|
||||
"2 săptămâni în poziție",
|
||||
"3 transacții să califici propul",
|
||||
"limita zilnică se calculează",
|
||||
"închide automat",
|
||||
"nu se rezolvă aici",
|
||||
"03_strategia_2_atmi.md:26-29",
|
||||
"09_QA/26_2025-06-11.md:225-227",
|
||||
"09_QA/26_2025-06-11.md:216-222"):
|
||||
assert cheie in svg, f"lipsește din desen: {cheie!r}"
|
||||
|
||||
# 7. fără cifre de preț inventate
|
||||
for interzis in ("$", "USD", "SL ", "intrare "):
|
||||
assert interzis not in svg, f"nu ar trebui să apară preț: {interzis!r}"
|
||||
return True
|
||||
|
||||
|
||||
SVG = build()
|
||||
assert verify(SVG), "verificare eșuată"
|
||||
OUT.write_text(SVG, encoding="utf-8")
|
||||
print(f"scris {OUT} ({W}x{H}), verificare OK: {len(TEXTS)} etichete, "
|
||||
f"bara prop {LEN_PROP:.1f}px din {AX_W:g}px")
|
||||
47
gen/strategii/atmi_orizont.svg
Normal file
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 670" width="700" height="670" role="img" aria-label="Orizontul de tinere al ATMI: fisiarul principal spune luni pana la un an, adaptarea pentru prop spune doua saptamani; cele doua descrieri raman nerezolvate">
|
||||
<g font-family="system-ui, sans-serif">
|
||||
<rect x="0" y="0" width="700" height="670" fill="#ffffff"/>
|
||||
<text x="350.0" y="34.0" font-size="19" font-weight="700" fill="#000000" text-anchor="middle">Cât ții poziția? Două descrieri, același nume</text>
|
||||
<text x="350.0" y="58.0" font-size="13" font-weight="400" fill="#777777" text-anchor="middle">fișierul principal vs. sfatul din Q&A pentru calificarea pe prop</text>
|
||||
<text x="350.0" y="84.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">Aceeași axă de timp pentru ambele bare (0 → 1 an); fără axe de preț.</text>
|
||||
<line x1="60.0" y1="120.0" x2="60.0" y2="425.0" stroke="#ddd" stroke-width="1.0" stroke-dasharray="3 4"/>
|
||||
<line x1="207.5" y1="120.0" x2="207.5" y2="425.0" stroke="#ddd" stroke-width="1.0" stroke-dasharray="3 4"/>
|
||||
<line x1="355.0" y1="120.0" x2="355.0" y2="425.0" stroke="#ddd" stroke-width="1.0" stroke-dasharray="3 4"/>
|
||||
<line x1="502.5" y1="120.0" x2="502.5" y2="425.0" stroke="#ddd" stroke-width="1.0" stroke-dasharray="3 4"/>
|
||||
<line x1="650.0" y1="120.0" x2="650.0" y2="425.0" stroke="#ddd" stroke-width="1.0" stroke-dasharray="3 4"/>
|
||||
<line x1="60.0" y1="112.0" x2="650.0" y2="112.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<line x1="60.0" y1="106.0" x2="60.0" y2="118.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="60.0" y="132.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">0</text>
|
||||
<line x1="207.5" y1="106.0" x2="207.5" y2="118.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="207.5" y="132.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">3 luni</text>
|
||||
<line x1="355.0" y1="106.0" x2="355.0" y2="118.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="355.0" y="132.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">6 luni</text>
|
||||
<line x1="502.5" y1="106.0" x2="502.5" y2="118.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="502.5" y="132.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">9 luni</text>
|
||||
<line x1="650.0" y1="106.0" x2="650.0" y2="118.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="650.0" y="132.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">~1 an</text>
|
||||
<text x="60.0" y="162.0" font-size="15" font-weight="700" fill="#1a5fb4" text-anchor="start">ATMI clasică — fișierul principal</text>
|
||||
<rect x="60.0" y="172.0" width="590" height="42" rx="6" fill="#eef4fd" stroke="#1a5fb4" stroke-width="1.5"/>
|
||||
<text x="350.0" y="198.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">„fără stop-loss, poziția ținută luni/ani”</text>
|
||||
<line x1="650.0" y1="164.0" x2="650.0" y2="226.0" stroke="#1a5fb4" stroke-width="1.2" stroke-dasharray="4 3"/>
|
||||
<text x="650.0" y="244.0" font-size="13" font-weight="400" fill="#1a5fb4" text-anchor="end">TP1 atins — aproximativ un an mai târziu</text>
|
||||
<text x="60.0" y="262.0" font-size="11" font-weight="400" fill="#777777" text-anchor="start">Sursă: 04_MODUL_3/03_strategia_2_atmi.md:26-29; :358-363</text>
|
||||
<text x="60.0" y="282.0" font-size="15" font-weight="700" fill="#b06000" text-anchor="start">ATMI ca adaptare pentru prop — Q&A 11.06.2025</text>
|
||||
<rect x="60.0" y="298.0" width="22.7" height="42" rx="6" fill="#fdf0e0" stroke="#b06000" stroke-width="1.5"/>
|
||||
<line x1="82.7" y1="290.0" x2="82.7" y2="360.0" stroke="#b06000" stroke-width="1.2" stroke-dasharray="4 3"/>
|
||||
<line x1="82.7" y1="319.0" x2="160.0" y2="319.0" stroke="#b06000" stroke-width="1.0" stroke-dasharray="3 3"/>
|
||||
<text x="90.0" y="312.0" font-size="11" font-weight="700" fill="#b06000" text-anchor="start">2 săptămâni</text>
|
||||
<rect x="160.0" y="292.0" width="500.0" height="112.0" rx="8" fill="#ffffff" stroke="#b06000" stroke-width="1.5"/>
|
||||
<text x="410.0" y="316.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">„aștepți o corecție și intri pe ATMI la suport quarterly,</text>
|
||||
<text x="410.0" y="334.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">swing, 2 săptămâni în poziție”</text>
|
||||
<text x="410.0" y="358.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">„Ai nevoie de 3 transacții să califici propul.”</text>
|
||||
<text x="410.0" y="382.0" font-size="11" font-weight="400" fill="#777777" text-anchor="middle">Sursă: 09_QA/26_2025-06-11.md:225-227</text>
|
||||
<rect x="40.0" y="440.0" width="620.0" height="150.0" rx="8" fill="#fafafa" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="60.0" y="466.0" font-size="14" font-weight="700" fill="#000000" text-anchor="start">Anotare separată — mecanismul care face diferența</text>
|
||||
<text x="60.0" y="492.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">Pe prop, limita zilnică se calculează și pe pozițiile deschise,</text>
|
||||
<text x="60.0" y="510.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">iar la atingere firma închide automat poziția.</text>
|
||||
<text x="60.0" y="530.0" font-size="11" font-weight="400" fill="#777777" text-anchor="start">Sursă: 09_QA/26_2025-06-11.md:216-222</text>
|
||||
<text x="60.0" y="560.0" font-size="13" font-weight="700" fill="#000000" text-anchor="start">Notă: cele două descrieri nu se rezolvă aici — de clarificat cu autorul.</text>
|
||||
<text x="350.0" y="626.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">Fără axe de preț și fără să alegem una dintre variante.</text>
|
||||
<text x="350.0" y="646.0" font-size="12" font-weight="400" fill="#777777" text-anchor="middle">Bara de 2 săptămâni e scurtă exact pentru că atât ține, pe aceeași scară.</text>
|
||||
</g></svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
103
gen/strategii/buy_doji_ansamblu.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""Buy Doji — diagrama de ansamblu (daily, exemplu construit, fără ticker).
|
||||
|
||||
Citește specificația `gen/strategii/buy_doji_ansamblu.spec.txt`. Seria de
|
||||
32 de candele e construită din lista de close-uri dată, cu open-ul fiecărei
|
||||
bare = close-ul precedentului; wick-urile sunt fixate ca nivelurile
|
||||
(doji 50.10/49.20, intrare 50.40, SL 49.20, TP1 51.60, trail 52.60, ieșire
|
||||
sub trail la bara 28) să iasă exact din serie, nu doar din etichetă.
|
||||
"""
|
||||
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes
|
||||
|
||||
# --- seria: 32 de close-uri, exact ca în specificație -----------------------
|
||||
closes = [
|
||||
60.20, 59.20, 59.60, 58.40, 57.30, 57.70, 56.50, 55.40, 55.80, 54.70, # 0-9
|
||||
53.50, 52.30, 51.00, 49.65, # 10-13 downtrend
|
||||
49.60, # 14 doji
|
||||
49.90, # 15 +1 (nu confirmă)
|
||||
50.40, # 16 +2 confirmare => intrare
|
||||
50.90, 51.20, 51.50, 51.80, 52.10, 52.40, 52.20, 52.70, 52.95, 53.20, # 17-26
|
||||
52.80, 52.30, 52.00, 51.80, 51.90, # 27-31
|
||||
]
|
||||
assert len(closes) == 32
|
||||
|
||||
# wick-uri: (up, down) implicite (0.25, 0.30) + excepțiile din specificație
|
||||
wicks = [(0.25, 0.30)] * 32
|
||||
wicks[14] = (0.45, 0.40) # doji: high 50.10, low 49.20
|
||||
wicks[15] = (0.15, 0.15) # high 50.05, low 49.45
|
||||
wicks[16] = (0.15, 0.15) # high 50.55, low 49.75
|
||||
for k in range(24, 28): # barele 24-27
|
||||
wicks[k] = (0.25, 0.20)
|
||||
|
||||
# volum relativ ales de generator (materialul nu dă volum pe exemplu);
|
||||
# peste medie pe doji (14) și pe confirmare (16), apoi normal
|
||||
vols = [42, 44, 40, 46, 43, 41, 45, 42, 44, 47,
|
||||
43, 45, 48, 52, 78, 46, 88, 55,
|
||||
50, 48, 52, 47, 45, 49, 46, 44, 48, 51,
|
||||
54, 47, 45, 46]
|
||||
assert len(vols) == 32
|
||||
media_vol = sum(vols) / len(vols)
|
||||
assert vols[14] > media_vol and vols[16] > media_vol, "volum peste medie pe doji/confirmare"
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=60.20)
|
||||
|
||||
# adnotările pe bara +1 și pe confirmare, ca linii sub grafic (fără suprapunere)
|
||||
cds[15].color = "#777" # bara de așteptare: neutră, nu confirmă
|
||||
cds[15].note = "bara 15: close 49.90 sub high-ul dojiului 50.10 — NU confirmă"
|
||||
cds[16].note = "bara 16: close 50.40 peste 50.10 — confirmare (intrare)"
|
||||
|
||||
doji, wait, conf = cds[14], cds[15], cds[16]
|
||||
entry, sl, tp1, trail = 50.40, 49.20, 51.60, 52.60
|
||||
|
||||
# --- cele 9 assert-uri cerute de specificație ------------------------------
|
||||
# 1. SL-ul e exact low-ul dojiului, cel mai jos din fereastra de 4 candele
|
||||
assert abs(min(cd.l for cd in cds[13:17]) - 49.20) < 1e-9, "min low[13:17] != 49.20"
|
||||
# 2. intrarea e close-ul barei 16
|
||||
assert abs(conf.c - entry) < 1e-9, f"close[16]={conf.c} != 50.40"
|
||||
# 3. confirmarea închide peste high-ul dojiului
|
||||
assert abs(doji.h - 50.10) < 1e-9 and conf.c > doji.h, "close[16] nu e peste high[14]"
|
||||
# 4. bara +1 NU confirmă
|
||||
assert wait.c <= doji.h, "bara 15 ar confirma, fereastra n-ar avea sens"
|
||||
# 5. 1:1 exact
|
||||
assert abs(abs(entry - sl) - abs(tp1 - entry)) < 1e-9 and abs((entry - sl) - 1.20) < 1e-9, \
|
||||
"risc != recompensă (nu e 1:1 exact)"
|
||||
# 6. primul high >= TP1 după intrare e bara 19
|
||||
primul_tp = next(i for i in range(17, len(cds)) if cds[i].h >= tp1)
|
||||
assert primul_tp == 19, f"TP1 51.60 atins întâi la bara {primul_tp}, nu 19"
|
||||
# 7. trailing 52.60 = low[27]; ieșire la bara 28 sub el; ieșirea e pe profit
|
||||
assert abs(cds[27].l - trail) < 1e-9, f"low[27]={cds[27].l} != 52.60"
|
||||
assert abs(cds[28].c - 52.30) < 1e-9 and cds[28].c < trail, "close[28] nu e sub trail"
|
||||
assert trail > entry, "trail sub intrare, ieșirea n-ar fi pe profit"
|
||||
# 8. doji: corp <= 10% din amplitudine
|
||||
assert abs(doji.o - doji.c) <= 0.10 * (doji.h - doji.l), "bara 14 nu e doji"
|
||||
# 9. downtrend vizibil
|
||||
assert cds[13].c < cds[0].c - 10, "downtrend prea mic"
|
||||
|
||||
print(f"doji {doji.h:.2f}/{doji.l:.2f} intrare {conf.c:.2f} "
|
||||
f"SL {sl:.2f} TP1 {tp1:.2f} trail {trail:.2f} ieșire bara 28 @ {cds[28].c:.2f}")
|
||||
|
||||
# --- desenul ----------------------------------------------------------------
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[
|
||||
Level(sl, "SL 49.20 (low 4 candele)", "#c00", label_at="left"),
|
||||
Level(entry, "intrare 50.40", "#188a3e", label_at="left"),
|
||||
Level(tp1, "TP1 51.60 = 1R", "#1a5fb4", label_at="left"),
|
||||
Level(trail, "trail 52.60", "#b06000"),
|
||||
],
|
||||
marks=[
|
||||
Mark(14, "doji 50.10 / 49.20", "#b06000", "below"),
|
||||
Mark(28, "ieșire pe trail ~52.60", "#c00", "above", dy=-6),
|
||||
],
|
||||
mas=[MA(20, "#777", dash="4 3", text="MA(20)")],
|
||||
scale=24.0, gap=15, body_w=9, left=44, width=640, vol_h=70,
|
||||
vol_label="volum", title="Buy Doji — daily (exemplu construit, fără ticker)",
|
||||
caption="Intrare la close-ul confirmării, SL la low-ul ultimelor 4 candele, "
|
||||
"TP1 1:1, apoi trailing stop.",
|
||||
)
|
||||
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
|
||||
with open("/workspace/atm-curs-dl/gen/strategii/buy_doji_ansamblu.svg", "w") as f:
|
||||
f.write(svg)
|
||||
print("scris gen/strategii/buy_doji_ansamblu.svg")
|
||||
116
gen/strategii/buy_doji_ansamblu.svg
Normal file
@@ -0,0 +1,116 @@
|
||||
<svg viewBox="0 0 640 498" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Buy Doji — daily (exemplu construit, fără ticker)</text>
|
||||
<line x1="24" y1="326.0" x2="626" y2="326.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="320.0" fill="#c00">SL 49.20 (low 4 candele)</text>
|
||||
<line x1="24" y1="297.2" x2="626" y2="297.2" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="291.2" fill="#188a3e">intrare 50.40</text>
|
||||
<line x1="24" y1="268.4" x2="626" y2="268.4" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="262.4" fill="#1a5fb4">TP1 51.60 = 1R</text>
|
||||
<line x1="24" y1="244.4" x2="626" y2="244.4" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="612" y="238.4" fill="#b06000" text-anchor="end">trail 52.60</text>
|
||||
<polyline points="329.0,205.1 344.0,215.2 359.0,223.7 374.0,232.3 389.0,239.8 404.0,245.3 419.0,251.0 434.0,255.0 449.0,258.1 464.0,262.3 479.0,265.5 494.0,267.6 509.0,268.0" fill="none" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="515" y="262" fill="#777">MA(20)</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="69.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="62.0" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="56.0" x2="59.0" y2="93.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="62.0" width="9" height="24.0" fill="#c00"/>
|
||||
<line x1="74.0" y1="70.4" x2="74.0" y2="93.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="76.4" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="70.4" x2="89.0" y2="112.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="76.4" width="9" height="28.8" fill="#c00"/>
|
||||
<line x1="104.0" y1="99.2" x2="104.0" y2="138.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="105.2" width="9" height="26.4" fill="#c00"/>
|
||||
<line x1="119.0" y1="116.0" x2="119.0" y2="138.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="122.0" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="116.0" x2="134.0" y2="158.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="122.0" width="9" height="28.8" fill="#c00"/>
|
||||
<line x1="149.0" y1="144.8" x2="149.0" y2="184.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="150.8" width="9" height="26.4" fill="#c00"/>
|
||||
<line x1="164.0" y1="161.6" x2="164.0" y2="184.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="167.6" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="161.6" x2="179.0" y2="201.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="167.6" width="9" height="26.4" fill="#c00"/>
|
||||
<line x1="194.0" y1="188.0" x2="194.0" y2="230.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="194.0" width="9" height="28.8" fill="#c00"/>
|
||||
<line x1="209.0" y1="216.8" x2="209.0" y2="258.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="222.8" width="9" height="28.8" fill="#c00"/>
|
||||
<line x1="224.0" y1="245.6" x2="224.0" y2="290.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="251.6" width="9" height="31.2" fill="#c00"/>
|
||||
<line x1="239.0" y1="276.8" x2="239.0" y2="322.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="282.8" width="9" height="32.4" fill="#c00"/>
|
||||
<line x1="254.0" y1="304.4" x2="254.0" y2="326.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="315.2" width="9" height="1.6" fill="#c00"/>
|
||||
<line x1="269.0" y1="305.6" x2="269.0" y2="320.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="309.2" width="9" height="7.2" fill="#777"/>
|
||||
<line x1="284.0" y1="293.6" x2="284.0" y2="312.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="297.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="279.2" x2="299.0" y2="304.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="285.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="272.0" x2="314.0" y2="292.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="278.0" width="9" height="7.2" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="264.8" x2="329.0" y2="285.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="270.8" width="9" height="7.2" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="257.6" x2="344.0" y2="278.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="263.6" width="9" height="7.2" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="250.4" x2="359.0" y2="270.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="256.4" width="9" height="7.2" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="243.2" x2="374.0" y2="263.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="249.2" width="9" height="7.2" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="243.2" x2="389.0" y2="261.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="249.2" width="9" height="4.8" fill="#c00"/>
|
||||
<line x1="404.0" y1="236.0" x2="404.0" y2="258.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="242.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="419.0" y1="230.0" x2="419.0" y2="246.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="236.0" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="224.0" x2="434.0" y2="240.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="230.0" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="224.0" x2="449.0" y2="244.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="230.0" width="9" height="9.6" fill="#c00"/>
|
||||
<line x1="464.0" y1="233.6" x2="464.0" y2="258.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="239.6" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="479.0" y1="245.6" x2="479.0" y2="266.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="251.6" width="9" height="7.2" fill="#c00"/>
|
||||
<line x1="494.0" y1="252.8" x2="494.0" y2="270.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="258.8" width="9" height="4.8" fill="#c00"/>
|
||||
<line x1="509.0" y1="255.2" x2="509.0" y2="270.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="261.2" width="9" height="2.4" fill="#188a3e"/>
|
||||
<line x1="24" y1="422.0" x2="626" y2="422.0" stroke="#888"/>
|
||||
<rect x="39.5" y="388.6" width="9" height="33.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="387.0" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="390.2" width="9" height="31.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="385.4" width="9" height="36.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="387.8" width="9" height="34.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="389.4" width="9" height="32.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="386.2" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="388.6" width="9" height="33.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="387.0" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="384.6" width="9" height="37.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="387.8" width="9" height="34.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="386.2" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="383.8" width="9" height="38.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="380.6" width="9" height="41.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="360.0" width="9" height="62.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="385.4" width="9" height="36.6" fill="#777" opacity="0.55"/>
|
||||
<rect x="279.5" y="352.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="294.5" y="378.2" width="9" height="43.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="382.2" width="9" height="39.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="383.8" width="9" height="38.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="380.6" width="9" height="41.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="384.6" width="9" height="37.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="386.2" width="9" height="35.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="383.0" width="9" height="39.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.5" y="385.4" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="414.5" y="387.0" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="383.8" width="9" height="38.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="381.4" width="9" height="40.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="459.5" y="379.0" width="9" height="43.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="474.5" y="384.6" width="9" height="37.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="489.5" y="386.2" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="504.5" y="385.4" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="346.0" fill="#555">volum</text>
|
||||
<text x="254.0" y="340.0" fill="#b06000" text-anchor="middle" style="font-weight:bold">doji 50.10 / 49.20</text>
|
||||
<text x="464.0" y="217.6" fill="#c00" text-anchor="middle" style="font-weight:bold">ieșire pe trail ~52.60</text>
|
||||
<text x="24" y="444.0" fill="#777">bara 15: close 49.90 sub high-ul dojiului 50.10 — NU confirmă</text>
|
||||
<text x="24" y="461.0" fill="#188a3e">bara 16: close 50.40 peste 50.10 — confirmare (intrare)</text>
|
||||
<text x="24" y="486" fill="#555">Intrare la close-ul confirmării, SL la low-ul ultimelor 4 candele, TP1 1:1, apoi trailing stop.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
247
gen/strategii/buy_doji_aplus.py
Normal file
@@ -0,0 +1,247 @@
|
||||
"""Buy Doji — A+ vs. mediocru (două panouri, exemple construite).
|
||||
|
||||
Citește `gen/strategii/buy_doji_aplus.spec.txt` (marcajul `<!-- DIAGRAMA: aplus -->`).
|
||||
Același pattern (trend → doji → confirmare), context opus:
|
||||
|
||||
- stânga (A+): downtrend 52 → 42.60, doji cu low 41.95 chiar pe suportul
|
||||
42.00 (0.12 % sub nivel), confirmare close 43.60 pe volum peste media
|
||||
ultimelor 10 bare — acumulare;
|
||||
- dreapta (NU): uptrend 70 → 76.80, gap-up la open 78.20, doji lipit de
|
||||
rezistența 78.50, confirmare close 79.30 pe volum sub medie („varză"), apoi
|
||||
prețul cade înapoi sub 78.00.
|
||||
|
||||
Nivelurile 42.00 / 78.50 și cifrele de preț sunt construite de mine ca să
|
||||
susțină etichetele și assert-urile din spec (materialul nu dă prețuri pentru
|
||||
acest contrast). Două `Chart`-uri randate separat, lipite într-un SVG wrapper:
|
||||
coordonatele fiecărui panou rămân corecte prin construcție.
|
||||
|
||||
Rulează: python3 gen/strategii/buy_doji_aplus.py
|
||||
Scrie: gen/strategii/buy_doji_aplus.svg
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Candle, Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "buy_doji_aplus.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
DOJI_A, CONF_A = 10, 11 # indicii în panoul A+
|
||||
DOJI_N, CONF_N = 14, 15 # indicii în panoul NU
|
||||
|
||||
# ============================================================ PANOUL A+ (24 bare)
|
||||
# 10 bare de downtrend de la 52.00 spre 42.60, apoi doji, apoi confirmare.
|
||||
CLOSES_A = [
|
||||
51.60, 50.80, 50.00, 49.20, 48.40, 47.60, 46.80, 45.50, 44.20, 42.60, # 0-9 downtrend
|
||||
42.60, # 10 doji (open = close)
|
||||
43.60, # 11 confirmare
|
||||
44.10, 44.50, 44.20, 44.80, 45.20, 45.00, 45.50, 45.90, 46.20, 46.50, 46.80, 47.10, # 12-23 acumulare
|
||||
]
|
||||
assert len(CLOSES_A) == 24, len(CLOSES_A)
|
||||
|
||||
WICKS_A = [(0.30, 0.30)] * 24
|
||||
WICKS_A[DOJI_A] = (0.30, 0.65) # high 42.90, low 41.95 (pe suportul 42.00)
|
||||
WICKS_A[CONF_A] = (0.25, 0.30) # high 43.85
|
||||
|
||||
VOLS_A = [56, 53, 51, 49, 47, 45, 44, 42, 41, 39, # 0-9 vânzare în scădere
|
||||
60, # 10 doji: exces de vânzare
|
||||
86, # 11 confirmare: volum mare
|
||||
72, 75, 70, 78, 82, 80, 85, 88, 92, 95, 98, 100] # 12-23 acumulare
|
||||
|
||||
cds_a = from_closes(CLOSES_A, wicks=WICKS_A, vols=VOLS_A, start=52.00)
|
||||
doji_a, conf_a = cds_a[DOJI_A], cds_a[CONF_A]
|
||||
|
||||
# ============================================================ PANOUL NU (24 bare)
|
||||
# uptrend 70 → 76.80 (14 bare), gap-up la open 78.20 + doji, confirmare 79.30
|
||||
# pe volum slab, apoi cădere înapoi sub 78.00. Lista e manuală pentru că gap-ul
|
||||
# (open > high-ul barei precedente) nu iese din `from_closes`.
|
||||
BARE_NU = [
|
||||
# o, h, l, c, vol
|
||||
(70.00, 70.70, 69.80, 70.50, 48), (70.50, 71.20, 70.30, 71.00, 50),
|
||||
(71.00, 71.75, 70.85, 71.55, 49), (71.55, 72.30, 71.40, 72.10, 52),
|
||||
(72.10, 72.80, 71.95, 72.60, 51), (72.60, 73.30, 72.45, 73.10, 54),
|
||||
(73.10, 73.80, 72.95, 73.60, 53), (73.60, 74.25, 73.45, 74.05, 56),
|
||||
(74.05, 74.70, 73.90, 74.50, 55), (74.50, 75.15, 74.35, 74.95, 58),
|
||||
(74.95, 75.60, 74.80, 75.40, 57), (75.40, 76.00, 75.25, 75.80, 60),
|
||||
(75.80, 76.40, 75.65, 76.20, 59), (76.20, 76.80, 76.05, 76.80, 62), # 13: high 76.80
|
||||
(78.20, 78.50, 78.10, 78.20, 58), # 14: GAP-UP + doji (o = c = 78.20)
|
||||
(78.20, 79.50, 78.10, 79.30, 30), # 15: confirmare, volum slab
|
||||
(79.30, 79.40, 78.40, 78.60, 55), (78.60, 78.70, 77.80, 78.00, 60),
|
||||
(78.00, 78.10, 77.20, 77.40, 64), (77.40, 77.55, 76.80, 77.00, 62),
|
||||
(77.00, 77.15, 76.40, 76.60, 66), (76.60, 76.75, 76.10, 76.30, 63),
|
||||
(76.30, 76.40, 75.80, 76.00, 65), (76.00, 76.15, 75.50, 75.70, 61),
|
||||
]
|
||||
assert len(BARE_NU) == 24, len(BARE_NU)
|
||||
cds_n = [Candle(o, h, l, c, vol=v) for (o, h, l, c, v) in BARE_NU]
|
||||
doji_n, conf_n = cds_n[DOJI_N], cds_n[CONF_N]
|
||||
|
||||
# ============================================================ assert-urile din spec
|
||||
# 1. A+: low-ul dojiului e la ≤ 0,5 % de 42.00 (41.95 e la 0.12 %)
|
||||
dist_a = abs(doji_a.l - 42.00) / 42.00
|
||||
assert dist_a <= 0.005, f"low doji A+ {doji_a.l} la {dist_a * 100:.2f} % de 42.00"
|
||||
assert abs(doji_a.l - 41.95) < 1e-9, f"low doji A+ = {doji_a.l}, aștept 41.95"
|
||||
# 2. A+: volumul pe bara de confirmare > media volumelor pe 10 bare
|
||||
media_a = sum(VOLS_A[CONF_A - 10:CONF_A]) / 10
|
||||
assert VOLS_A[CONF_A] > media_a, f"vol confirmare {VOLS_A[CONF_A]} <= media {media_a}"
|
||||
# 3. NU: doji-ul apare după un gap-up (open doji > high bara precedentă)
|
||||
assert cds_n[DOJI_N].o > cds_n[DOJI_N - 1].h, "doji NU nu e după gap-up"
|
||||
assert abs(cds_n[DOJI_N].o - 78.20) < 1e-9 and abs(cds_n[DOJI_N - 1].h - 76.80) < 1e-9
|
||||
# 4. NU: |high doji - 78.50| / 78.50 <= 0.005
|
||||
dist_n = abs(doji_n.h - 78.50) / 78.50
|
||||
assert dist_n <= 0.005, f"high doji NU {doji_n.h} la {dist_n * 100:.2f} % de 78.50"
|
||||
# 5. NU: volumul pe bara de confirmare < media pe 10 bare
|
||||
media_n = sum(b[4] for b in BARE_NU[CONF_N - 10:CONF_N]) / 10
|
||||
assert conf_n.vol < media_n, f"vol confirmare NU {conf_n.vol} >= media {media_n}"
|
||||
# 6. ambele: close-ul de confirmare > high-ul dojiului (tehnic valid)
|
||||
assert conf_a.c > doji_a.h, f"A+ close {conf_a.c} <= high doji {doji_a.h}"
|
||||
assert conf_n.c > doji_n.h, f"NU close {conf_n.c} <= high doji {doji_n.h}"
|
||||
assert abs(conf_a.c - 43.60) < 1e-9 and abs(conf_n.c - 79.30) < 1e-9
|
||||
# 7. ambele doji: abs(o-c) <= 0.10*(h-l)
|
||||
for tag, d in (("A+", doji_a), ("NU", doji_n)):
|
||||
assert abs(d.o - d.c) <= 0.10 * (d.h - d.l), f"bara doji {tag} nu e doji"
|
||||
# suplimentare: structura promisă de spec
|
||||
assert min(c.l for c in cds_a[:10]) > doji_a.l, "low-ul dojiului A+ nu e minimul"
|
||||
assert cds_a[9].c < cds_a[0].c - 8, "downtrend A+ prea mic"
|
||||
assert cds_n[CONF_N].c > 78.50 and cds_n[-1].c < 78.00, "NU nu cade sub 78.00 la final"
|
||||
assert cds_n[13].c < 78.00 < cds_n[DOJI_N].o, "NU: uptrendul nu se oprește sub 78.00"
|
||||
|
||||
print(f"A+ doji {doji_a.h:.2f}/{doji_a.l:.2f} conf {conf_a.c:.2f} "
|
||||
f"vol {VOLS_A[CONF_A]} > medie {media_a:.1f}")
|
||||
print(f"NU doji {doji_n.h:.2f}/{doji_n.l:.2f} conf {conf_n.c:.2f} "
|
||||
f"vol {conf_n.vol} < medie {media_n:.1f}")
|
||||
|
||||
# ============================================================ desenele celor două panouri
|
||||
PW = 560 # lățimea fiecărui panou
|
||||
ch_a = Chart(
|
||||
candles=cds_a,
|
||||
levels=[Level(42.00, "suport 42.00", GREEN, label_at="left")],
|
||||
marks=[Mark(DOJI_A, "doji la nivel (low 41.95)", ORANGE, "below"),
|
||||
Mark(CONF_A, "acumulare", GREEN, "above", dy=-6)],
|
||||
mas=[], scale=22.0, gap=15, body_w=9, left=44, width=PW, vol_h=60,
|
||||
vol_label="volum", title="A+ — doji la suport, volum în acumulare",
|
||||
caption="acumulare: volum peste media ultimelor 10 bare.",
|
||||
)
|
||||
ch_n = Chart(
|
||||
candles=cds_n,
|
||||
levels=[Level(78.50, "rezistență 78.50", RED, label_at="right")],
|
||||
marks=[Mark(DOJI_N, "doji 78.50 / 78.10", ORANGE, "below", dy=93)],
|
||||
mas=[], scale=22.0, gap=15, body_w=9, left=44, width=PW, vol_h=60,
|
||||
vol_label="volum", title="Mediocru — gap-up în rezistență, volum varză",
|
||||
caption="gap-up + doji în rezistență, confirmare pe volum slab.",
|
||||
)
|
||||
|
||||
# ============================================================ wrapper: două panouri alăturate
|
||||
def inner(svg: str) -> str:
|
||||
"""Conținutul dintre tag-urile <svg> ale unui panou, fără elementul rădăcină."""
|
||||
return svg[svg.index(">") + 1:svg.rindex("</svg>")]
|
||||
|
||||
|
||||
def vb_height(svg: str) -> float:
|
||||
m = re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg)
|
||||
assert m, "nu găsesc viewBox în panoul randat"
|
||||
return float(m.group(1))
|
||||
|
||||
|
||||
PANEL_GAP, PAD = 24, 8
|
||||
TITLE_H, FOOTER_H = 30, 22
|
||||
svg_a, svg_n = ch_a.render(), ch_n.render()
|
||||
|
||||
# „gap-up, volum slab" stă în colțul liber din stânga-sus, cu linie indicatoare
|
||||
# spre golul dintre bara 13 (high 76.80) și dojiul barei 14 (open 78.20).
|
||||
GAP_X = (ch_n._cx(DOJI_N - 1) + ch_n._cx(DOJI_N)) / 2
|
||||
GAP_Y = ch_n._y(77.50)
|
||||
gap_ann = (
|
||||
f'<text x="28" y="120" fill="{RED}" style="font-weight:bold">gap-up, volum slab</text>'
|
||||
f'<line x1="152" y1="116" x2="{GAP_X:.1f}" y2="{GAP_Y:.1f}" stroke="{RED}" stroke-width="1"/>'
|
||||
f'<circle cx="{GAP_X:.1f}" cy="{GAP_Y:.1f}" r="2.5" fill="{RED}"/>'
|
||||
)
|
||||
svg_n = svg_n.replace("</svg>", gap_ann + "</svg>")
|
||||
|
||||
HA, HN = vb_height(svg_a), vb_height(svg_n)
|
||||
assert svg_a.count("\n\n") == 0 and svg_n.count("\n\n") == 0, "linie goală în panou"
|
||||
|
||||
TOTAL_W = PAD + PW + PANEL_GAP + PW + PAD
|
||||
TOTAL_H = PAD + TITLE_H + max(HA, HN) + FOOTER_H + PAD
|
||||
OFF_A = (PAD, PAD + TITLE_H)
|
||||
OFF_N = (PAD + PW + PANEL_GAP, PAD + TITLE_H)
|
||||
|
||||
FOOTER = ("Surse: 13_strategia_3_byh.md:188-190 (doji la nivel); "
|
||||
"01_buy_doji_si_strategia_mici.md:227-235 (context invalid).")
|
||||
|
||||
wrapper = (
|
||||
f'<svg viewBox="0 0 {TOTAL_W} {TOTAL_H:.0f}" width="{TOTAL_W}" height="{TOTAL_H:.0f}" '
|
||||
f'xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">'
|
||||
f'<text x="{TOTAL_W / 2:.0f}" y="20" fill="#333" text-anchor="middle" '
|
||||
f'style="font-weight:bold">Setup A+ vs. setup mediocru (două panouri, exemple construite)</text>'
|
||||
f'<g transform="translate({OFF_A[0]},{OFF_A[1]})">{inner(svg_a)}</g>'
|
||||
f'<g transform="translate({OFF_N[0]},{OFF_N[1]})">{inner(svg_n)}</g>'
|
||||
f'<text x="{TOTAL_W / 2:.0f}" y="{TOTAL_H - PAD:.0f}" fill="#555" '
|
||||
f'text-anchor="middle">{FOOTER}</text>'
|
||||
f'</svg>'
|
||||
)
|
||||
|
||||
# ============================================================ verificare pe SVG-ul final
|
||||
def texts(svg: str):
|
||||
"""Toate (x, y, text-anchor, text) din elementele <text> ale unui fragment."""
|
||||
out = []
|
||||
for m in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', svg):
|
||||
x, y, attrs, t = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4)
|
||||
if t.startswith("<"):
|
||||
continue
|
||||
am = re.search(r'text-anchor="(\w+)"', attrs)
|
||||
out.append((x, y, am.group(1) if am else "start", t))
|
||||
return out
|
||||
|
||||
|
||||
def span(x: float, anchor: str, t: str) -> tuple[float, float]:
|
||||
"""Intervalul orizontal aproximativ al unui text, față de ancora lui."""
|
||||
w = len(t) * 12 * 0.56
|
||||
left = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
|
||||
return left, left + w
|
||||
|
||||
|
||||
assert wrapper.count("\n\n") == 0, "linie goală în SVG"
|
||||
|
||||
# fiecare text (după translatarea panoului) e în întregime în interiorul pânzei
|
||||
all_txt = []
|
||||
for frag, (ox, oy) in ((inner(svg_a), OFF_A), (inner(svg_n), OFF_N)):
|
||||
for x, y, anchor, t in texts(frag):
|
||||
gx, gy = x + ox, y + oy
|
||||
gl, gr = gx + span(0, anchor, t)[0], gx + span(0, anchor, t)[1]
|
||||
assert ox <= gl and gr <= ox + PW, f"text iese din panou: {t!r} ({gl:.0f}..{gr:.0f})"
|
||||
assert -1 <= gl and gr <= TOTAL_W + 1, f"text ieșit pe orizontală: {t!r} ({gl:.0f}..{gr:.0f})"
|
||||
assert 0 <= gy <= TOTAL_H, f"text ieșit pe verticală: {t!r} ({gy:.0f})"
|
||||
all_txt.append((gx, gy, anchor, t))
|
||||
# textele din wrapper (titlu + footer), în coordonate globale
|
||||
for x, y, anchor, t in ((TOTAL_W / 2, 20.0, "middle",
|
||||
"Setup A+ vs. setup mediocru (două panouri, exemple construite)"),
|
||||
(TOTAL_W / 2, TOTAL_H - PAD, "middle", FOOTER)):
|
||||
gl, gr = span(x, anchor, t)
|
||||
assert -1 <= gl and gr <= TOTAL_W + 1, f"text din wrapper iese din pânză: {t!r}"
|
||||
assert 0 <= y <= TOTAL_H, f"text din wrapper în afara înălțimii: {t!r}"
|
||||
all_txt.append((x, y, anchor, t))
|
||||
assert len(all_txt) >= 8, "prea puține etichete"
|
||||
|
||||
# nu se suprapun două texte pe aceeași poziție sau pe rândul apropiat
|
||||
for i in range(len(all_txt)):
|
||||
xi, yi, ai, si = all_txt[i]
|
||||
li, ri = (xi + span(0, ai, si)[0], xi + span(0, ai, si)[1])
|
||||
for j in range(i + 1, len(all_txt)):
|
||||
xj, yj, aj, sj = all_txt[j]
|
||||
lj, rj = xj + span(0, aj, sj)[0], xj + span(0, aj, sj)[1]
|
||||
if abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
# etichetele cerute de spec există în desen
|
||||
for cheie in ("suport 42.00", "doji la nivel", "acumulare",
|
||||
"rezistență 78.50", "gap-up, volum slab",
|
||||
"A+ — doji la suport, volum în acumulare",
|
||||
"Mediocru — gap-up în rezistență, volum varză"):
|
||||
assert cheie in wrapper, f"lipsește din desen: {cheie!r}"
|
||||
|
||||
OUT.write_text(wrapper, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: {TOTAL_W}x{TOTAL_H:.0f}, "
|
||||
f"{len(all_txt)} etichete, verificare OK")
|
||||
162
gen/strategii/buy_doji_aplus.svg
Normal file
@@ -0,0 +1,162 @@
|
||||
<svg viewBox="0 0 1160 480" width="1160" height="480" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif"><text x="580" y="20" fill="#333" text-anchor="middle" style="font-weight:bold">Setup A+ vs. setup mediocru (două panouri, exemple construite)</text><g transform="translate(8,38)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">A+ — doji la suport, volum în acumulare</text>
|
||||
<line x1="24" y1="282.6" x2="546" y2="282.6" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="276.6" fill="#188a3e">suport 42.00</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="78.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="62.6" width="9" height="8.8" fill="#c00"/>
|
||||
<line x1="59.0" y1="64.8" x2="59.0" y2="95.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="71.4" width="9" height="17.6" fill="#c00"/>
|
||||
<line x1="74.0" y1="82.4" x2="74.0" y2="113.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="89.0" width="9" height="17.6" fill="#c00"/>
|
||||
<line x1="89.0" y1="100.0" x2="89.0" y2="130.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="106.6" width="9" height="17.6" fill="#c00"/>
|
||||
<line x1="104.0" y1="117.6" x2="104.0" y2="148.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="124.2" width="9" height="17.6" fill="#c00"/>
|
||||
<line x1="119.0" y1="135.2" x2="119.0" y2="166.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="141.8" width="9" height="17.6" fill="#c00"/>
|
||||
<line x1="134.0" y1="152.8" x2="134.0" y2="183.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="159.4" width="9" height="17.6" fill="#c00"/>
|
||||
<line x1="149.0" y1="170.4" x2="149.0" y2="212.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="177.0" width="9" height="28.6" fill="#c00"/>
|
||||
<line x1="164.0" y1="199.0" x2="164.0" y2="240.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="205.6" width="9" height="28.6" fill="#c00"/>
|
||||
<line x1="179.0" y1="227.6" x2="179.0" y2="276.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="234.2" width="9" height="35.2" fill="#c00"/>
|
||||
<line x1="194.0" y1="262.8" x2="194.0" y2="283.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="269.4" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="241.9" x2="209.0" y2="276.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="247.4" width="9" height="22.0" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="229.8" x2="224.0" y2="254.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="236.4" width="9" height="11.0" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="221.0" x2="239.0" y2="243.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="227.6" width="9" height="8.8" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="221.0" x2="254.0" y2="240.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="227.6" width="9" height="6.6" fill="#c00"/>
|
||||
<line x1="269.0" y1="214.4" x2="269.0" y2="240.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="221.0" width="9" height="13.2" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="205.6" x2="284.0" y2="227.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="212.2" width="9" height="8.8" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="205.6" x2="299.0" y2="223.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="212.2" width="9" height="4.4" fill="#c00"/>
|
||||
<line x1="314.0" y1="199.0" x2="314.0" y2="223.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="205.6" width="9" height="11.0" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="190.2" x2="329.0" y2="212.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="196.8" width="9" height="8.8" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="183.6" x2="344.0" y2="203.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="190.2" width="9" height="6.6" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="177.0" x2="359.0" y2="196.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="183.6" width="9" height="6.6" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="170.4" x2="374.0" y2="190.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="177.0" width="9" height="6.6" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="163.8" x2="389.0" y2="183.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="170.4" width="9" height="6.6" fill="#188a3e"/>
|
||||
<line x1="24" y1="369.7" x2="546" y2="369.7" stroke="#888"/>
|
||||
<rect x="39.5" y="336.1" width="9" height="33.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="337.9" width="9" height="31.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="339.1" width="9" height="30.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="84.5" y="340.3" width="9" height="29.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="341.5" width="9" height="28.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="342.7" width="9" height="27.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="343.3" width="9" height="26.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="344.5" width="9" height="25.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="345.1" width="9" height="24.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="346.3" width="9" height="23.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="333.7" width="9" height="36.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.5" y="318.1" width="9" height="51.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="219.5" y="326.5" width="9" height="43.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="324.7" width="9" height="45.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.5" y="327.7" width="9" height="42.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="322.9" width="9" height="46.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="320.5" width="9" height="49.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="294.5" y="321.7" width="9" height="48.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="318.7" width="9" height="51.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="316.9" width="9" height="52.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="314.5" width="9" height="55.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="312.7" width="9" height="57.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="310.9" width="9" height="58.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="309.7" width="9" height="60.0" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="303.7" fill="#555">volum</text>
|
||||
<text x="194.0" y="297.7" fill="#b06000" text-anchor="middle" style="font-weight:bold">doji la nivel (low 41.95)</text>
|
||||
<text x="209.0" y="225.9" fill="#188a3e" text-anchor="middle" style="font-weight:bold">acumulare</text>
|
||||
<text x="24" y="400" fill="#555">acumulare: volum peste media ultimelor 10 bare.</text>
|
||||
</g><g transform="translate(592,38)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Mediocru — gap-up în rezistență, volum varză</text>
|
||||
<line x1="24" y1="78.0" x2="546" y2="78.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="72.0" fill="#c00" text-anchor="end">rezistență 78.50</text>
|
||||
<line x1="44.0" y1="249.6" x2="44.0" y2="269.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="254.0" width="9" height="11.0" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="238.6" x2="59.0" y2="258.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="243.0" width="9" height="11.0" fill="#188a3e"/>
|
||||
<line x1="74.0" y1="226.5" x2="74.0" y2="246.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="230.9" width="9" height="12.1" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="214.4" x2="89.0" y2="234.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="218.8" width="9" height="12.1" fill="#188a3e"/>
|
||||
<line x1="104.0" y1="203.4" x2="104.0" y2="222.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="207.8" width="9" height="11.0" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="192.4" x2="119.0" y2="211.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="196.8" width="9" height="11.0" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="181.4" x2="134.0" y2="200.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="185.8" width="9" height="11.0" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="171.5" x2="149.0" y2="189.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="175.9" width="9" height="9.9" fill="#188a3e"/>
|
||||
<line x1="164.0" y1="161.6" x2="164.0" y2="179.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="166.0" width="9" height="9.9" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="151.7" x2="179.0" y2="169.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="156.1" width="9" height="9.9" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="141.8" x2="194.0" y2="159.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="146.2" width="9" height="9.9" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="133.0" x2="209.0" y2="149.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="137.4" width="9" height="8.8" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="124.2" x2="224.0" y2="140.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="128.6" width="9" height="8.8" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="115.4" x2="239.0" y2="131.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="115.4" width="9" height="13.2" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="78.0" x2="254.0" y2="86.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="84.6" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="56.0" x2="269.0" y2="86.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="60.4" width="9" height="24.2" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="58.2" x2="284.0" y2="80.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="60.4" width="9" height="15.4" fill="#c00"/>
|
||||
<line x1="299.0" y1="73.6" x2="299.0" y2="93.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="75.8" width="9" height="13.2" fill="#c00"/>
|
||||
<line x1="314.0" y1="86.8" x2="314.0" y2="106.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="89.0" width="9" height="13.2" fill="#c00"/>
|
||||
<line x1="329.0" y1="98.9" x2="329.0" y2="115.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="102.2" width="9" height="8.8" fill="#c00"/>
|
||||
<line x1="344.0" y1="107.7" x2="344.0" y2="124.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="111.0" width="9" height="8.8" fill="#c00"/>
|
||||
<line x1="359.0" y1="116.5" x2="359.0" y2="130.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="119.8" width="9" height="6.6" fill="#c00"/>
|
||||
<line x1="374.0" y1="124.2" x2="374.0" y2="137.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="126.4" width="9" height="6.6" fill="#c00"/>
|
||||
<line x1="389.0" y1="129.7" x2="389.0" y2="144.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="133.0" width="9" height="6.6" fill="#c00"/>
|
||||
<line x1="24" y1="355.4" x2="546" y2="355.4" stroke="#888"/>
|
||||
<rect x="39.5" y="311.8" width="9" height="43.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="309.9" width="9" height="45.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="69.5" y="310.9" width="9" height="44.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="308.1" width="9" height="47.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="99.5" y="309.0" width="9" height="46.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="114.5" y="306.3" width="9" height="49.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="307.2" width="9" height="48.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="304.5" width="9" height="50.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="159.5" y="305.4" width="9" height="50.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="302.7" width="9" height="52.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="189.5" y="303.6" width="9" height="51.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.5" y="300.9" width="9" height="54.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="219.5" y="301.8" width="9" height="53.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="299.0" width="9" height="56.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.5" y="302.7" width="9" height="52.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="264.5" y="328.1" width="9" height="27.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="305.4" width="9" height="50.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="300.9" width="9" height="54.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="297.2" width="9" height="58.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.5" y="299.0" width="9" height="56.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="339.5" y="295.4" width="9" height="60.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="354.5" y="298.1" width="9" height="57.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="369.5" y="296.3" width="9" height="59.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="384.5" y="299.9" width="9" height="55.5" fill="#c00" opacity="0.55"/>
|
||||
<text x="24" y="289.4" fill="#555">volum</text>
|
||||
<text x="254.0" y="199.8" fill="#b06000" text-anchor="middle" style="font-weight:bold">doji 78.50 / 78.10</text>
|
||||
<text x="24" y="385" fill="#555">gap-up + doji în rezistență, confirmare pe volum slab.</text>
|
||||
<text x="28" y="120" fill="#c00" style="font-weight:bold">gap-up, volum slab</text><line x1="152" y1="116" x2="246.5" y2="100.0" stroke="#c00" stroke-width="1"/><circle cx="246.5" cy="100.0" r="2.5" fill="#c00"/></g><text x="580" y="472" fill="#555" text-anchor="middle">Surse: 13_strategia_3_byh.md:188-190 (doji la nivel); 01_buy_doji_si_strategia_mici.md:227-235 (context invalid).</text></svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
188
gen/strategii/buy_doji_fereastra.py
Normal file
@@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Buy Doji — fereastra de 3 candele: DA (valid) vs. NU (expirat).
|
||||
|
||||
Citește `gen/strategii/buy_doji_fereastra.spec.txt` și scrie
|
||||
`gen/strategii/buy_doji_fereastra.svg`. Două panouri mici, unul sub altul,
|
||||
cu aceeași serie până la doji: sus confirmarea vine la bara +2 (în fereastră),
|
||||
jos niciuna din +1..+3 nu confirmă, iar a 4-a închide sub low-ul dojiului.
|
||||
|
||||
Toate cifrele din desen vin din specificație: doji h 50.10 / l 49.20,
|
||||
closes-urile DA/NU, pragul 50.10, low-ul 49.20. Seriile sunt construite
|
||||
(open = close-ul barei anterioare, mai puțin open-ul primei bare, start-ul),
|
||||
ca nivelurile afirmate să iasă exact din serie.
|
||||
|
||||
Rulare: python3 gen/strategii/buy_doji_fereastra.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
W = 460
|
||||
VGAP = 34
|
||||
SCALE = 45.0 # px / unitate, aceeași pe ambele panouri (comparație)
|
||||
|
||||
# --- seriile din specificație ----------------------------------------------
|
||||
CLOSES_DA = [52.20, 51.40, 50.60, 49.65,
|
||||
49.60, # doji, h 50.10 / l 49.20
|
||||
49.90, # +1 — sub 50.10
|
||||
50.40, # +2 — peste 50.10 => confirmă
|
||||
50.80]
|
||||
CLOSES_NU = [52.20, 51.40, 50.60, 49.65,
|
||||
49.60, # doji, h 50.10 / l 49.20
|
||||
49.95, # +1
|
||||
50.05, # +2
|
||||
49.85, # +3
|
||||
49.15] # a 4-a — sub low-ul dojiului
|
||||
assert len(CLOSES_DA) == 8 and len(CLOSES_NU) == 9
|
||||
assert CLOSES_DA[:5] == CLOSES_NU[:5]
|
||||
|
||||
HIGH_DOJI, LOW_DOJI = 50.10, 49.20
|
||||
|
||||
# wick-uri: (up, down). Doji-ul (index 4) primește mecioni ca să iasă exact
|
||||
# h=50.10 / l=49.20; restul au mecioni mici, egali.
|
||||
wicks_da = [(0.15, 0.15)] * len(CLOSES_DA)
|
||||
wicks_nu = [(0.15, 0.15)] * len(CLOSES_NU)
|
||||
wicks_da[4] = wicks_nu[4] = (0.45, 0.40)
|
||||
|
||||
START = CLOSES_DA[0] + 0.15 # prima bară roșie, open > close
|
||||
cds_da = from_closes(CLOSES_DA, wicks=wicks_da, start=START)
|
||||
cds_nu = from_closes(CLOSES_NU, wicks=wicks_nu, start=START)
|
||||
|
||||
doji_da, doji_nu = cds_da[4], cds_nu[4]
|
||||
entry = cds_da[6].c # intrare la close-ul confirmării
|
||||
|
||||
# --- assert-urile din specificație -----------------------------------------
|
||||
# doji-ul iese exact la nivelurile afirmate
|
||||
assert abs(doji_da.h - HIGH_DOJI) < 1e-9 and abs(doji_da.l - LOW_DOJI) < 1e-9, \
|
||||
f"doji DA {doji_da.h}/{doji_da.l} != {HIGH_DOJI}/{LOW_DOJI}"
|
||||
assert abs(doji_nu.h - HIGH_DOJI) < 1e-9 and abs(doji_nu.l - LOW_DOJI) < 1e-9, \
|
||||
f"doji NU {doji_nu.h}/{doji_nu.l} != {HIGH_DOJI}/{LOW_DOJI}"
|
||||
|
||||
# 1. DA: primul close peste 50.10 DUPĂ doji e bara +2 (index 6), în fereastră;
|
||||
# bara +1 nu confirmă
|
||||
primul_da = next(i for i in range(5, len(cds_da)) if cds_da[i].c > HIGH_DOJI)
|
||||
assert primul_da == 6, f"prima confirmare la index {primul_da}, nu 6 (+2)"
|
||||
assert primul_da - 4 <= 3, "confirmarea nu e în fereastra de 3 candele"
|
||||
assert abs(cds_da[5].c - 49.90) < 1e-9 and cds_da[5].c <= HIGH_DOJI, \
|
||||
"bara +1 nu trebuie să confirme"
|
||||
assert abs(cds_da[6].c - entry) < 1e-9 and abs(entry - 50.40) < 1e-9, \
|
||||
"intrarea nu e close-ul de 50.40 al barei +2"
|
||||
|
||||
# 2. NU: niciun close din +1..+3 nu depășește 50.10; a 4-a închide sub 49.20
|
||||
assert all(cds_nu[i].c <= HIGH_DOJI for i in (5, 6, 7)), \
|
||||
"un close din +1..+3 ar confirma, fereastra n-ar fi expirat"
|
||||
assert cds_nu[8].c < LOW_DOJI, f"close[+4]={cds_nu[8].c} nu e sub {LOW_DOJI}"
|
||||
|
||||
# 3. ambele doji: corp <= 10% din amplitudine
|
||||
for d in (doji_da, doji_nu):
|
||||
assert abs(d.o - d.c) <= 0.10 * (d.h - d.l), "bara 4 nu e doji"
|
||||
|
||||
# 4. aceeași serie până la doji (aceleași 5 bare)
|
||||
ser_da = [(round(c.o, 9), round(c.h, 9), round(c.l, 9), round(c.c, 9)) for c in cds_da[:5]]
|
||||
ser_nu = [(round(c.o, 9), round(c.h, 9), round(c.l, 9), round(c.c, 9)) for c in cds_nu[:5]]
|
||||
assert ser_da == ser_nu, "panourile nu au aceeași serie până la doji"
|
||||
|
||||
# --- desen: două Chart-uri, lipite vertical într-un singur SVG -------------
|
||||
def _panel(ch: Chart):
|
||||
svg = ch.render()
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([\d.]+)"', svg)
|
||||
body = svg[svg.index(">") + 1:svg.rindex("</svg>")]
|
||||
return body, float(m.group(2))
|
||||
|
||||
|
||||
# DA — confirmarea vine la +2
|
||||
cds_da[5].note = "+1: close 49.90 — sub 50.10, nu confirmă"
|
||||
cds_da[6].note = "+2: close 50.40 — peste 50.10, confirmă (intrare la close)"
|
||||
ch_da = Chart(
|
||||
candles=cds_da,
|
||||
levels=[Level(HIGH_DOJI, "high doji 50.10", "#b06000", label_at="left"),
|
||||
Level(LOW_DOJI, "low doji 49.20", "#c00", label_at="left")],
|
||||
marks=[Mark(4, "doji", "#b06000", "above"),
|
||||
Mark(6, f"confirmare +2 — intrare {entry:.2f}", "#188a3e", "above")],
|
||||
scale=SCALE, gap=40, body_w=13, left=44, width=W,
|
||||
title="DA — confirmare la +2, în fereastră",
|
||||
)
|
||||
|
||||
# NU — nicio confirmare în 3 candele, apoi setup mort
|
||||
cds_nu[5].note = "+1: 49.95; +2: 50.05; +3: 49.85 — toate sub 50.10"
|
||||
cds_nu[8].note = "a 4-a: close 49.15 — sub low-ul dojiului 49.20, setup mort"
|
||||
ch_nu = Chart(
|
||||
candles=cds_nu,
|
||||
levels=[Level(HIGH_DOJI, "high doji 50.10", "#b06000", label_at="left"),
|
||||
Level(LOW_DOJI, "low doji 49.20", "#c00", label_at="left")],
|
||||
marks=[Mark(4, "doji", "#b06000", "above"),
|
||||
Mark(8, "a 4-a", "#c00", "above")],
|
||||
scale=SCALE, gap=40, body_w=13, left=44, width=W,
|
||||
title="NU — nicio confirmare în 3 candele",
|
||||
)
|
||||
|
||||
body_da, h_da = _panel(ch_da)
|
||||
body_nu, h_nu = _panel(ch_nu)
|
||||
|
||||
cap1 = "Serii construite: open = close-ul barei anterioare."
|
||||
cap2 = "Doji: h 50.10 / l 49.20. Fereastra = maximum 3 candele după doji."
|
||||
cap_top = h_da + VGAP + h_nu + 20
|
||||
H = cap_top + 22
|
||||
|
||||
parts = [
|
||||
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">',
|
||||
f'<g transform="translate(0,0)">{body_da}</g>',
|
||||
f'<g transform="translate(0,{h_da + VGAP:.0f})">{body_nu}</g>',
|
||||
f'<text x="24" y="{cap_top:.0f}" fill="#555">{cap1}</text>',
|
||||
f'<text x="24" y="{cap_top + 17:.0f}" fill="#555">{cap2}</text>',
|
||||
"</svg>",
|
||||
]
|
||||
svg = "\n ".join(parts)
|
||||
assert "\n\n" not in svg, "linie goală în SVG"
|
||||
assert "<" not in cap1 and "<" not in cap2
|
||||
|
||||
# --- verificarea citirii: etichete în panză, fără suprapuneri ---------------
|
||||
TEXT = re.compile(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', re.S)
|
||||
|
||||
|
||||
def _texts(body: str, y_off: float, tag: str):
|
||||
out = []
|
||||
for m in TEXT.finditer(body):
|
||||
x, y = float(m.group(1)), float(m.group(2)) + y_off
|
||||
attrs, s = m.group(3), m.group(4)
|
||||
anchor = "start" # implicit în SVG
|
||||
if 'text-anchor="end"' in attrs:
|
||||
anchor = "end"
|
||||
elif 'text-anchor="middle"' in attrs:
|
||||
anchor = "middle"
|
||||
bold = "bold" in attrs
|
||||
w = len(s) * 12 * (0.62 if bold else 0.52)
|
||||
left = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
|
||||
out.append((left, left + w, y, s, tag))
|
||||
return out
|
||||
|
||||
|
||||
labels = _texts(body_da, 0.0, "DA") + _texts(body_nu, h_da + VGAP, "NU")
|
||||
for left, right, y, s, tag in labels:
|
||||
assert left >= -2 and right <= W + 2, f"[{tag}] text iese din panză: {s!r}"
|
||||
assert 0 <= y <= H, f"[{tag}] text în afara înălțimii: {s!r} (y={y:.0f})"
|
||||
|
||||
for i in range(len(labels)):
|
||||
li, ri, yi, si, ti = labels[i]
|
||||
for j in range(i + 1, len(labels)):
|
||||
lj, rj, yj, sj, tj = labels[j]
|
||||
if ti == tj and abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"[{ti}] texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
print(f"DA: doji {doji_da.h:.2f}/{doji_da.l:.2f} +1 {cds_da[5].c:.2f} "
|
||||
f"+2 {entry:.2f} (confirmă) bare {len(cds_da)}")
|
||||
print(f"NU: +1 {cds_nu[5].c:.2f} +2 {cds_nu[6].c:.2f} +3 {cds_nu[7].c:.2f} "
|
||||
f"a 4-a {cds_nu[8].c:.2f} (< {LOW_DOJI}) bare {len(cds_nu)}")
|
||||
|
||||
# 5. distanța în pixeli 50.10 -> 50.40 e vizibilă, cu scară comună
|
||||
dist = ch_da._y(HIGH_DOJI) - ch_da._y(entry)
|
||||
assert ch_da.scale == ch_nu.scale == SCALE, "scara nu e comună între panouri"
|
||||
assert dist > 0 and dist > 10, f"distanța 50.10->50.40 = {dist:.1f}px, prea mică"
|
||||
print(f"scris {OUT} ({W}x{H:.0f}); {len(labels)} etichete verificate; "
|
||||
f"50.10->50.40 = {dist:.1f}px")
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
60
gen/strategii/buy_doji_fereastra.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<svg viewBox="0 0 460 614" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<g transform="translate(0,0)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">DA — confirmare la +2, în fereastră</text>
|
||||
<line x1="24" y1="164.0" x2="446" y2="164.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="158.0" fill="#b06000">high doji 50.10</text>
|
||||
<line x1="24" y1="204.5" x2="446" y2="204.5" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="198.5" fill="#c00">low doji 49.20</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="76.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="37.5" y="62.7" width="13" height="6.7" fill="#c00"/>
|
||||
<line x1="84.0" y1="62.7" x2="84.0" y2="112.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="77.5" y="69.5" width="13" height="36.0" fill="#c00"/>
|
||||
<line x1="124.0" y1="98.8" x2="124.0" y2="148.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="117.5" y="105.5" width="13" height="36.0" fill="#c00"/>
|
||||
<line x1="164.0" y1="134.8" x2="164.0" y2="191.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="157.5" y="141.5" width="13" height="42.8" fill="#c00"/>
|
||||
<line x1="204.0" y1="164.0" x2="204.0" y2="204.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="197.5" y="184.3" width="13" height="2.2" fill="#c00"/>
|
||||
<line x1="244.0" y1="166.3" x2="244.0" y2="193.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="237.5" y="173.0" width="13" height="13.5" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="143.8" x2="284.0" y2="179.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="277.5" y="150.5" width="13" height="22.5" fill="#188a3e"/>
|
||||
<line x1="324.0" y1="125.8" x2="324.0" y2="157.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="317.5" y="132.5" width="13" height="18.0" fill="#188a3e"/>
|
||||
<text x="204.0" y="154.0" fill="#b06000" text-anchor="middle" style="font-weight:bold">doji</text>
|
||||
<text x="284.0" y="133.8" fill="#188a3e" text-anchor="middle" style="font-weight:bold">confirmare +2 — intrare 50.40</text>
|
||||
<text x="24" y="226.5" fill="#188a3e">+1: close 49.90 — sub 50.10, nu confirmă</text>
|
||||
<text x="24" y="243.5" fill="#188a3e">+2: close 50.40 — peste 50.10, confirmă (intrare la close)</text>
|
||||
</g>
|
||||
<g transform="translate(0,298)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">NU — nicio confirmare în 3 candele</text>
|
||||
<line x1="24" y1="164.0" x2="446" y2="164.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="158.0" fill="#b06000">high doji 50.10</text>
|
||||
<line x1="24" y1="204.5" x2="446" y2="204.5" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="198.5" fill="#c00">low doji 49.20</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="76.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="37.5" y="62.7" width="13" height="6.7" fill="#c00"/>
|
||||
<line x1="84.0" y1="62.7" x2="84.0" y2="112.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="77.5" y="69.5" width="13" height="36.0" fill="#c00"/>
|
||||
<line x1="124.0" y1="98.8" x2="124.0" y2="148.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="117.5" y="105.5" width="13" height="36.0" fill="#c00"/>
|
||||
<line x1="164.0" y1="134.8" x2="164.0" y2="191.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="157.5" y="141.5" width="13" height="42.8" fill="#c00"/>
|
||||
<line x1="204.0" y1="164.0" x2="204.0" y2="204.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="197.5" y="184.3" width="13" height="2.2" fill="#c00"/>
|
||||
<line x1="244.0" y1="164.0" x2="244.0" y2="193.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="237.5" y="170.7" width="13" height="15.8" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="159.5" x2="284.0" y2="177.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="277.5" y="166.3" width="13" height="4.5" fill="#188a3e"/>
|
||||
<line x1="324.0" y1="159.5" x2="324.0" y2="182.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="317.5" y="166.3" width="13" height="9.0" fill="#c00"/>
|
||||
<line x1="364.0" y1="168.5" x2="364.0" y2="213.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="357.5" y="175.2" width="13" height="31.5" fill="#c00"/>
|
||||
<text x="204.0" y="154.0" fill="#b06000" text-anchor="middle" style="font-weight:bold">doji</text>
|
||||
<text x="364.0" y="158.5" fill="#c00" text-anchor="middle" style="font-weight:bold">a 4-a</text>
|
||||
<text x="24" y="235.5" fill="#188a3e">+1: 49.95; +2: 50.05; +3: 49.85 — toate sub 50.10</text>
|
||||
<text x="24" y="252.5" fill="#c00">a 4-a: close 49.15 — sub low-ul dojiului 49.20, setup mort</text>
|
||||
</g>
|
||||
<text x="24" y="592" fill="#555">Serii construite: open = close-ul barei anterioare.</text>
|
||||
<text x="24" y="609" fill="#555">Doji: h 50.10 / l 49.20. Fereastra = maximum 3 candele după doji.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
190
gen/strategii/buy_doji_labu.py
Normal file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python3
|
||||
"""LABU — Buy Doji la pivotul monthly (Nivel 2.5).
|
||||
|
||||
Citește `gen/strategii/buy_doji_labu.spec.txt` și scrie
|
||||
`gen/strategii/buy_doji_labu.svg`: 49 de candele daily, downtrend → triple
|
||||
bottom → doji la pivotul monthly 86.00 → confirmare (close 89.50 peste
|
||||
high-ul dojiului) → bull reversal → higher high, cu SL 81.45 (−9 %) și
|
||||
TP 116.35 (+30 %).
|
||||
|
||||
Toate cifrele din desen vin din specificație: pivot 86.00, intrare 89.50,
|
||||
SL 81.45, TP 116.35, high/low-ul dojiului (86.30 / 84.20) și low-urile
|
||||
triple bottom-ului (82.40 / 81.45 / 81.45). Procentele 9 % și 30 % sunt ale
|
||||
cursului (reale); nivelurile de preț sunt construite de sumarizator —
|
||||
legenda o spune explicit.
|
||||
|
||||
Rulare: python3 gen/strategii/buy_doji_labu.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, MA, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
# --- nivelurile din specificație -------------------------------------------
|
||||
PIVOT = 86.00 # pivot monthly (construit)
|
||||
ENTRY = 89.50 # intrare = close-ul confirmării (construit)
|
||||
SL = 81.45 # SL = −9 % față de intrare (construit; procentul e real)
|
||||
TP = 116.35 # TP = +30 % față de intrare (construit; procentul e real)
|
||||
DOJI_H, DOJI_L = 86.30, 84.20
|
||||
|
||||
# --- seria de close-uri din specificație -----------------------------------
|
||||
closes = [
|
||||
# downtrend 0-14
|
||||
108.0, 106.5, 105.0, 103.0, 101.5, 100.0, 98.0, 96.5, 95.0, 93.5,
|
||||
92.5, 91.0, 89.5, 87.5, 85.5,
|
||||
# triple bottom 15 / 23 / 31
|
||||
82.80, 84.5, 86.5, 88.0, 88.50,
|
||||
87.0, 85.0, 83.0, 81.50,
|
||||
83.5, 85.5, 87.0, 87.80, 87.0, 85.0,
|
||||
83.0, 81.60, 85.00,
|
||||
84.90, # 33 = doji (o 85.00, c 84.90, h 86.30, l 84.20)
|
||||
89.50, # 34 = confirmare, close > 86.30 => intrare
|
||||
# bull reversal / trend up 35-48
|
||||
91.0, 93.0, 92.0, 95.0, 97.0, 96.0, 99.0, 102.0, 101.0, 105.0, 108.0,
|
||||
112.0, 116.35, 118.0,
|
||||
]
|
||||
assert len(closes) == 49, f"seria are {len(closes)} bare, spec cere 49"
|
||||
|
||||
# wick-uri: (up, down). Restul (0.4, 0.5); ajustate ca low-urile/high-urile
|
||||
# cerute de asserts să iasă exact (bar 15/23/31 = triple bottom; bar 16/24
|
||||
# nu au voie să coboare sub piciorul din stânga; bar 33 = doji).
|
||||
wicks = [(0.4, 0.5)] * len(closes)
|
||||
wicks[15] = (0.4, 0.40) # low 82.40
|
||||
wicks[16] = (0.4, 0.20) # low 82.60 > 82.40 (piciorul 1 rămâne minim local)
|
||||
wicks[23] = (0.4, 0.05) # low 81.45
|
||||
wicks[24] = (0.4, 0.00) # low 81.50 > 81.45 (piciorul 2 rămâne minim local)
|
||||
wicks[31] = (0.4, 0.15) # low 81.45
|
||||
wicks[32] = (0.4, 0.10) # low 81.50 (nu sub 81.45)
|
||||
wicks[33] = (1.30, 0.70) # doji: high 86.30, low 84.20
|
||||
|
||||
# volume construite: vânzare peste medie la triple bottom și la confirmare.
|
||||
# Nu sunt numere din curs (nu sunt etichetate), doar panoul de volum cerut.
|
||||
vols = [
|
||||
55, 52, 48, 50, 45, 47, 44, 46, 42, 44,
|
||||
40, 42, 38, 41, 58,
|
||||
62, 50, 46, 44, 43,
|
||||
45, 44, 47, 66, 55,
|
||||
50, 47, 45, 44, 42,
|
||||
43, 45, 60, 72, 84,
|
||||
78, 70, 64, 60, 58,
|
||||
54, 52, 56, 60, 66,
|
||||
74, 82, 90, 98,
|
||||
]
|
||||
assert len(vols) == len(closes), f"volume {len(vols)} != bare {len(closes)}"
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=110.0)
|
||||
assert abs(cds[0].o - 110.0) < 1e-9 and cds[0].c < cds[0].o, "prima bară nu e roșie"
|
||||
|
||||
# nota explicativă a dojiului (bloc stânga-jos, nu se suprapune cu candelele)
|
||||
cds[33].note = ("bara 33 = doji: o 85.00, c 84.90, h 86.30, l 84.20 — "
|
||||
"corp mic exact pe pivotul monthly 86.00 (indecizie la suport)")
|
||||
|
||||
# --- assert-urile din specificație (toate, ca assert reale) ----------------
|
||||
# 1. pivot 86.00 e straddled de doji
|
||||
assert cds[33].l <= PIVOT <= cds[33].h, "doji-ul nu straddlează 86.00"
|
||||
|
||||
# 2. confirmarea: close[34] == 89.50 == intrare și > high[33]
|
||||
assert abs(cds[34].c - ENTRY) < 1e-9, f"close[34]={cds[34].c} != intrare 89.50"
|
||||
assert cds[34].c > cds[33].h, "confirmarea nu închide peste high-ul dojiului"
|
||||
|
||||
# 3. SL = min(low[30:35]) (regula: extrema ultimelor 4 candele de la confirmare)
|
||||
min_30_35 = min(c.l for c in cds[30:35])
|
||||
assert abs(min_30_35 - SL) < 1e-9, f"min low[30:35]={min_30_35} != SL {SL}"
|
||||
|
||||
# 4./5. procentele REALE ale cursului
|
||||
assert round((ENTRY - SL) / ENTRY * 100, 1) == 9.0, "stopul nu iese −9 %"
|
||||
assert round((TP - ENTRY) / ENTRY * 100, 1) == 30.0, "TP nu iese +30 %"
|
||||
|
||||
# 6. triple bottom: exact trei minime locale sub 86.00, în zona 81-83,
|
||||
# al doilea și al treilea fără low nou semnificativ față de primul
|
||||
lows = [c.l for c in cds]
|
||||
bottom_idx = [i for i in range(1, len(cds) - 1)
|
||||
if lows[i] < lows[i - 1] and lows[i] < lows[i + 1]
|
||||
and lows[i] < PIVOT and 81.0 <= lows[i] <= 83.0]
|
||||
assert bottom_idx == [15, 23, 31], f"triple bottom la {bottom_idx}, nu [15, 23, 31]"
|
||||
b1, b2, b3 = lows[15], lows[23], lows[31]
|
||||
tol = 0.02 * b1 # „semnificativ" = > 2 % sub primul low
|
||||
assert b2 >= b1 - tol and b3 >= b1 - tol, \
|
||||
f"al doilea/treilea picior coboară semnificativ sub primul: {b1}/{b2}/{b3}"
|
||||
assert abs(b1 - 82.40) < 1e-9 and abs(b2 - SL) < 1e-9 and abs(b3 - SL) < 1e-9, \
|
||||
f"low-urile triple bottom: {b1}/{b2}/{b3}"
|
||||
|
||||
# 7. TP atins: există i > 34 cu high[i] >= 116.35
|
||||
assert any(c.h >= TP for c in cds[35:]), "TP 116.35 nu e atins după intrare"
|
||||
|
||||
# 8. doji: corp <= 10 % din amplitudine
|
||||
assert abs(cds[33].o - cds[33].c) <= 0.10 * (cds[33].h - cds[33].l), "bara 33 nu e doji"
|
||||
assert abs(cds[33].h - DOJI_H) < 1e-9 and abs(cds[33].l - DOJI_L) < 1e-9, \
|
||||
"doji h/l nu ies 86.30/84.20"
|
||||
|
||||
# în plus: confirmarea vine cu volum peste medie (cerut de spec)
|
||||
mean_vol = sum(vols) / len(vols)
|
||||
assert vols[34] > mean_vol, f"volumul confirmării {vols[34]} <= media {mean_vol:.0f}"
|
||||
|
||||
entry = cds[34].c
|
||||
w = 820 # lățime mărită: 49 bare × gap 15 + etichetele din dreapta
|
||||
# (spec sugera 760, dar ultima candelă ajunge la x=764)
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[Level(PIVOT, "pivot monthly 86.00 (construit)", "#777",
|
||||
dash="4 3", label_at="left"),
|
||||
Level(SL, "SL 81.45 (construit) = −9 % (procent real)", "#c00",
|
||||
label_at="left"),
|
||||
Level(ENTRY, "intrare 89.50 (construit)", "#188a3e"),
|
||||
Level(TP, "TP 116.35 (construit) = +30 % (procent real)", "#1a5fb4")],
|
||||
marks=[Mark(15, f"bottom 1 {lows[15]:.2f}", "#c00", "below"),
|
||||
Mark(23, f"bottom 2 {lows[23]:.2f}", "#c00", "below"),
|
||||
Mark(31, f"bottom 3 {lows[31]:.2f}", "#c00", "below"),
|
||||
Mark(33, "doji", "#b06000", "above"),
|
||||
Mark(34, "confirmare (intrare)", "#188a3e", "above", dy=-14),
|
||||
Mark(48, "higher high — TP atins", "#188a3e", "above")],
|
||||
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
|
||||
scale=8.0, gap=15, body_w=9, left=44, width=w, vol_h=70,
|
||||
vol_label="volum", title="LABU — daily",
|
||||
caption=("Procentele (stop 9 %, +30 %) sunt ale cursului; nivelurile de "
|
||||
"preț sunt construite de sumarizator (18_2025-09-23.md:713)."),
|
||||
)
|
||||
svg = ch.render()
|
||||
assert "\n\n" not in svg and "<svg" in svg, "SVG invalid (linie goală?)"
|
||||
|
||||
# --- citirea SVG-ului: etichete în panză, fără suprapuneri -----------------
|
||||
TEXT = re.compile(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', re.S)
|
||||
H = float(re.search(r'viewBox="0 0 (\d+) ([\d.]+)"', svg).group(2))
|
||||
labels = []
|
||||
for m in TEXT.finditer(svg):
|
||||
x, y, attrs, s = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4)
|
||||
anchor = "end" if 'text-anchor="end"' in attrs else \
|
||||
("middle" if 'text-anchor="middle"' in attrs else "start")
|
||||
bold = "bold" in attrs
|
||||
wpx = len(s) * 12 * (0.62 if bold else 0.52)
|
||||
left = x - wpx if anchor == "end" else (x - wpx / 2 if anchor == "middle" else x)
|
||||
labels.append((left, left + wpx, y, s))
|
||||
|
||||
for left, right, y, s in labels:
|
||||
assert left >= -2 and right <= w + 2, f"text iese din panză: {s!r} [{left:.0f},{right:.0f}]"
|
||||
assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y:.0f})"
|
||||
|
||||
for i in range(len(labels)):
|
||||
li, ri, yi, si = labels[i]
|
||||
for j in range(i + 1, len(labels)):
|
||||
lj, rj, yj, sj = labels[j]
|
||||
if abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}\n {labels[i]}\n {labels[j]}")
|
||||
|
||||
# candelele sunt o serie continuă, lipită
|
||||
xs = [ch._cx(i) for i in range(len(cds))]
|
||||
assert xs[-1] + ch.body_w / 2 < w, f"ultima candelă iese din panză (x={xs[-1]})"
|
||||
assert all(xs[i + 1] - xs[i] == ch.gap for i in range(len(xs) - 1)), "serie cu goluri"
|
||||
for a, b in zip(cds, cds[1:]):
|
||||
assert abs(a.c - b.o) < 1e-9, "open != close-ul barei anterioare"
|
||||
|
||||
print(f"LABU: {len(cds)} bare, doji {cds[33].l:.2f}-{cds[33].h:.2f} peste pivot "
|
||||
f"{PIVOT:.2f}, intrare {entry:.2f}, SL {SL:.2f} (−9 %), TP {TP:.2f} (+30 %)")
|
||||
print(f"triple bottom: {b1:.2f} / {b2:.2f} / {b3:.2f} "
|
||||
f"volum confirmare {vols[34]} > medie {mean_vol:.0f}")
|
||||
print(f"scris {OUT} ({w}x{H:.0f}); {len(labels)} etichete verificate, fără suprapuneri")
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
170
gen/strategii/buy_doji_labu.svg
Normal file
@@ -0,0 +1,170 @@
|
||||
<svg viewBox="0 0 820 507" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">LABU — daily</text>
|
||||
<line x1="24" y1="315.2" x2="806" y2="315.2" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="309.2" fill="#777">pivot monthly 86.00 (construit)</text>
|
||||
<line x1="24" y1="351.6" x2="806" y2="351.6" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="345.6" fill="#c00">SL 81.45 (construit) = −9 % (procent real)</text>
|
||||
<line x1="24" y1="287.2" x2="806" y2="287.2" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="792" y="281.2" fill="#188a3e" text-anchor="end">intrare 89.50 (construit)</text>
|
||||
<line x1="24" y1="72.4" x2="806" y2="72.4" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="792" y="66.4" fill="#1a5fb4" text-anchor="end">TP 116.35 (construit) = +30 % (procent real)</text>
|
||||
<polyline points="329.0,249.9 344.0,258.3 359.0,266.9 374.0,275.7 389.0,284.3 404.0,291.5 419.0,297.3 434.0,301.7 449.0,305.2 464.0,308.4 479.0,311.8 494.0,315.6 509.0,319.3 524.0,321.1 539.0,322.2 554.0,320.6 569.0,317.3 584.0,313.9 599.0,311.7 614.0,308.9 629.0,305.5 644.0,301.9 659.0,296.3 674.0,288.7 689.0,280.9 704.0,272.3 719.0,263.3 734.0,253.3 749.0,241.9 764.0,229.5" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="770" y="224" fill="#b06000">SMA-20</text>
|
||||
<line x1="44.0" y1="120.0" x2="44.0" y2="143.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="123.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="59.0" y1="136.0" x2="59.0" y2="155.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="139.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="74.0" y1="148.0" x2="74.0" y2="167.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="151.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="89.0" y1="160.0" x2="89.0" y2="183.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="163.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="104.0" y1="176.0" x2="104.0" y2="195.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="179.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="119.0" y1="188.0" x2="119.0" y2="207.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="191.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="134.0" y1="200.0" x2="134.0" y2="223.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="203.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="149.0" y1="216.0" x2="149.0" y2="235.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="219.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="164.0" y1="228.0" x2="164.0" y2="247.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="231.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="179.0" y1="240.0" x2="179.0" y2="259.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="243.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="194.0" y1="252.0" x2="194.0" y2="267.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="255.2" width="9" height="8.0" fill="#c00"/>
|
||||
<line x1="209.0" y1="260.0" x2="209.0" y2="279.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="263.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="224.0" y1="272.0" x2="224.0" y2="291.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="275.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="239.0" y1="284.0" x2="239.0" y2="307.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="287.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="254.0" y1="300.0" x2="254.0" y2="323.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="303.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="269.0" y1="316.0" x2="269.0" y2="344.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="319.2" width="9" height="21.6" fill="#c00"/>
|
||||
<line x1="284.0" y1="324.0" x2="284.0" y2="342.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="327.2" width="9" height="13.6" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="308.0" x2="299.0" y2="331.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="311.2" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="296.0" x2="314.0" y2="315.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="299.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="292.0" x2="329.0" y2="303.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="295.2" width="9" height="4.0" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="292.0" x2="344.0" y2="311.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="295.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="359.0" y1="304.0" x2="359.0" y2="327.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="307.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="374.0" y1="320.0" x2="374.0" y2="343.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="323.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="389.0" y1="336.0" x2="389.0" y2="351.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="339.2" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="404.0" y1="332.0" x2="404.0" y2="351.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="335.2" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="419.0" y1="316.0" x2="419.0" y2="339.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="319.2" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="304.0" x2="434.0" y2="323.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="307.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="297.6" x2="449.0" y2="311.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="300.8" width="9" height="6.4" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="297.6" x2="464.0" y2="311.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="300.8" width="9" height="6.4" fill="#c00"/>
|
||||
<line x1="479.0" y1="304.0" x2="479.0" y2="327.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="307.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="494.0" y1="320.0" x2="494.0" y2="343.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="323.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="509.0" y1="336.0" x2="509.0" y2="351.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="339.2" width="9" height="11.2" fill="#c00"/>
|
||||
<line x1="524.0" y1="320.0" x2="524.0" y2="351.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="323.2" width="9" height="27.2" fill="#188a3e"/>
|
||||
<line x1="539.0" y1="312.8" x2="539.0" y2="329.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="534.5" y="323.2" width="9" height="1.6" fill="#c00"/>
|
||||
<line x1="554.0" y1="284.0" x2="554.0" y2="328.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="549.5" y="287.2" width="9" height="36.8" fill="#188a3e"/>
|
||||
<line x1="569.0" y1="272.0" x2="569.0" y2="291.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="564.5" y="275.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="584.0" y1="256.0" x2="584.0" y2="279.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="579.5" y="259.2" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="599.0" y1="256.0" x2="599.0" y2="271.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="594.5" y="259.2" width="9" height="8.0" fill="#c00"/>
|
||||
<line x1="614.0" y1="240.0" x2="614.0" y2="271.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="609.5" y="243.2" width="9" height="24.0" fill="#188a3e"/>
|
||||
<line x1="629.0" y1="224.0" x2="629.0" y2="247.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="624.5" y="227.2" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="644.0" y1="224.0" x2="644.0" y2="239.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="639.5" y="227.2" width="9" height="8.0" fill="#c00"/>
|
||||
<line x1="659.0" y1="208.0" x2="659.0" y2="239.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="654.5" y="211.2" width="9" height="24.0" fill="#188a3e"/>
|
||||
<line x1="674.0" y1="184.0" x2="674.0" y2="215.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="669.5" y="187.2" width="9" height="24.0" fill="#188a3e"/>
|
||||
<line x1="689.0" y1="184.0" x2="689.0" y2="199.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="684.5" y="187.2" width="9" height="8.0" fill="#c00"/>
|
||||
<line x1="704.0" y1="160.0" x2="704.0" y2="199.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="699.5" y="163.2" width="9" height="32.0" fill="#188a3e"/>
|
||||
<line x1="719.0" y1="136.0" x2="719.0" y2="167.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="714.5" y="139.2" width="9" height="24.0" fill="#188a3e"/>
|
||||
<line x1="734.0" y1="104.0" x2="734.0" y2="143.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="729.5" y="107.2" width="9" height="32.0" fill="#188a3e"/>
|
||||
<line x1="749.0" y1="69.2" x2="749.0" y2="111.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="744.5" y="72.4" width="9" height="34.8" fill="#188a3e"/>
|
||||
<line x1="764.0" y1="56.0" x2="764.0" y2="76.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="759.5" y="59.2" width="9" height="13.2" fill="#188a3e"/>
|
||||
<line x1="24" y1="447.6" x2="806" y2="447.6" stroke="#888"/>
|
||||
<rect x="39.5" y="408.3" width="9" height="39.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="410.5" width="9" height="37.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="413.3" width="9" height="34.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="84.5" y="411.9" width="9" height="35.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="415.5" width="9" height="32.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="414.0" width="9" height="33.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="416.2" width="9" height="31.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="414.7" width="9" height="32.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="417.6" width="9" height="30.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="416.2" width="9" height="31.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="419.0" width="9" height="28.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="417.6" width="9" height="30.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="420.5" width="9" height="27.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="418.3" width="9" height="29.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="406.2" width="9" height="41.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="403.3" width="9" height="44.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="411.9" width="9" height="35.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="294.5" y="414.7" width="9" height="32.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="416.2" width="9" height="31.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="416.9" width="9" height="30.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="415.5" width="9" height="32.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="354.5" y="416.2" width="9" height="31.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="369.5" y="414.0" width="9" height="33.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="384.5" y="400.5" width="9" height="47.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.5" y="408.3" width="9" height="39.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="414.5" y="411.9" width="9" height="35.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="414.0" width="9" height="33.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="415.5" width="9" height="32.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="416.2" width="9" height="31.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="474.5" y="417.6" width="9" height="30.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="489.5" y="416.9" width="9" height="30.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="504.5" y="415.5" width="9" height="32.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="519.5" y="404.7" width="9" height="42.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="534.5" y="396.2" width="9" height="51.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="549.5" y="387.6" width="9" height="60.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="564.5" y="391.9" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="579.5" y="397.6" width="9" height="50.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="594.5" y="401.9" width="9" height="45.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="609.5" y="404.7" width="9" height="42.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="624.5" y="406.2" width="9" height="41.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="639.5" y="409.0" width="9" height="38.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="654.5" y="410.5" width="9" height="37.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="669.5" y="407.6" width="9" height="40.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="684.5" y="404.7" width="9" height="42.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="699.5" y="400.5" width="9" height="47.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="714.5" y="394.7" width="9" height="52.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="729.5" y="389.0" width="9" height="58.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="744.5" y="383.3" width="9" height="64.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="759.5" y="377.6" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="371.6" fill="#555">volum</text>
|
||||
<text x="269.0" y="364.0" fill="#c00" text-anchor="middle" style="font-weight:bold">bottom 1 82.40</text>
|
||||
<text x="389.0" y="365.6" fill="#c00" text-anchor="middle" style="font-weight:bold">bottom 2 81.45</text>
|
||||
<text x="509.0" y="365.6" fill="#c00" text-anchor="middle" style="font-weight:bold">bottom 3 81.45</text>
|
||||
<text x="539.0" y="302.8" fill="#b06000" text-anchor="middle" style="font-weight:bold">doji</text>
|
||||
<text x="554.0" y="260.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">confirmare (intrare)</text>
|
||||
<text x="776.0" y="46.0" fill="#188a3e" text-anchor="end" style="font-weight:bold">higher high — TP atins</text>
|
||||
<text x="24" y="469.6" fill="#c00">bara 33 = doji: o 85.00, c 84.90, h 86.30, l 84.20 — corp mic exact pe pivotul monthly 86.00 (indecizie la suport)</text>
|
||||
<text x="24" y="495" fill="#555">Procentele (stop 9 %, +30 %) sunt ale cursului; nivelurile de preț sunt construite de sumarizator (18_2025-09-23.md:713).</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
114
gen/strategii/buy_doji_sell.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""Sell Doji — oglinda short a ansamblului Buy Doji (daily, exemplu construit).
|
||||
|
||||
Citește specificația `gen/strategii/buy_doji_sell.spec.txt`. Seria de 31 de
|
||||
candele e construită din lista de close-uri dată, cu open-ul fiecărei bare =
|
||||
close-ul precedentului; wick-urile sunt fixate ca nivelurile (doji 99.20 /
|
||||
100.10, intrare short 99.00, SL 100.10, TP 97.90, trail 96.45, ieșire la 96.60)
|
||||
să iasă exact din serie, nu doar din etichetă.
|
||||
"""
|
||||
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes
|
||||
|
||||
# --- seria: 31 de close-uri, exact ca în specificație -----------------------
|
||||
closes = [
|
||||
93.00, 94.10, 93.70, 95.00, 96.10, 95.80, 97.00, 98.10, 97.80, 98.90, # 0-9
|
||||
99.80, 100.20, 99.90, 99.65, # 10-13 uptrend
|
||||
99.60, # 14 doji
|
||||
99.40, # 15 +1 (nu confirmă)
|
||||
99.00, # 16 +2 confirmare => intrare short
|
||||
98.50, 98.30, 97.80, 97.40, 97.00, 96.70, 96.40, 96.10, 95.80, # 17-25
|
||||
96.20, 96.60, 96.40, 96.10, 96.30, # 26-30
|
||||
]
|
||||
assert len(closes) == 31
|
||||
|
||||
# wick-uri: (up, down) implicite (0.30, 0.25) + excepțiile din specificație
|
||||
wicks = [(0.30, 0.25)] * 31
|
||||
# ajustat: cu wu=0.30, high[13] = 99.90+0.30 = 100.20 > SL; îl coborâm la
|
||||
# 100.10 ca max(high[13:17]) == 100.10 (high-ul ultimelor 4 candele = SL)
|
||||
wicks[13] = (0.20, 0.25)
|
||||
wicks[14] = (0.45, 0.40) # doji: high 100.10, low 99.20
|
||||
wicks[15] = (0.20, 0.15) # high 99.80, low 99.25 (peste 99.20 => nu confirmă)
|
||||
wicks[16] = (0.20, 0.15) # high 99.60, low 98.85
|
||||
wicks[25] = (0.35, 0.20) # high 96.45 (= trail), low 95.60 (minimul corecției)
|
||||
# ajustat: cu wd=0.25, low[26] = 95.80-0.25 = 95.55 < 95.60, adică bara 26 ar
|
||||
# fi minimul, nu bara 25; wd=0.10 păstrează bara 25 ca minim (low 95.60)
|
||||
wicks[26] = (0.30, 0.10)
|
||||
|
||||
# volum relativ ales de generator (materialul nu dă volum pe exemplu);
|
||||
# peste medie pe doji (14) și pe confirmare (16) — distribuție — apoi normal
|
||||
vols = [44, 42, 46, 40, 45, 48, 43, 47, 50, 45,
|
||||
52, 58, 49, 55, 90, 62, 84,
|
||||
56, 52, 50, 48, 46, 44, 42, 40, 45,
|
||||
50, 46, 44, 43, 45]
|
||||
assert len(vols) == 31
|
||||
media_vol = sum(vols) / len(vols)
|
||||
assert vols[14] > media_vol and vols[16] > media_vol, "volum peste medie pe doji/confirmare"
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=closes[0])
|
||||
|
||||
# adnotările pe bara +1 și pe confirmare, ca linii sub grafic (fără suprapunere)
|
||||
cds[15].color = "#777" # bara de așteptare: neutră, nu confirmă
|
||||
cds[15].note = "bara 15: close 99.40 peste low-ul dojiului 99.20 — NU confirmă"
|
||||
cds[16].note = "bara 16: close 99.00 sub 99.20 — confirmare (intrare short)"
|
||||
|
||||
doji, wait, conf = cds[14], cds[15], cds[16]
|
||||
entry, sl, tp = 99.00, 100.10, 97.90
|
||||
|
||||
# --- cele 9 assert-uri cerute de specificație ------------------------------
|
||||
# 1. SL-ul e exact high-ul ultimelor 4 candele
|
||||
assert abs(max(cd.h for cd in cds[13:17]) - 100.10) < 1e-9, "max high[13:17] != 100.10"
|
||||
# 2. intrarea e close-ul barei 16
|
||||
assert abs(conf.c - entry) < 1e-9, f"close[16]={conf.c} != 99.00"
|
||||
# 3. confirmarea închide sub low-ul dojiului
|
||||
assert abs(doji.l - 99.20) < 1e-9 and conf.c < doji.l, "close[16] nu e sub low[14]"
|
||||
# 4. bara +1 NU confirmă
|
||||
assert wait.c >= doji.l, "bara 15 ar confirma, fereastra n-ar avea sens"
|
||||
# 5. 1:1 exact
|
||||
assert abs(abs(sl - entry) - abs(entry - tp)) < 1e-9 and abs((entry - tp) - 1.10) < 1e-9, \
|
||||
"risc != recompensă (nu e 1:1 exact)"
|
||||
# 6. primul low <= TP după intrare e bara 19
|
||||
primul_tp = next(i for i in range(17, len(cds)) if cds[i].l <= tp)
|
||||
assert primul_tp == 19, f"TP 97.90 atins întâi la bara {primul_tp}, nu 19"
|
||||
assert abs(cds[18].l - 98.05) < 1e-9 and abs(cds[19].l - 97.55) < 1e-9, "low-urile 18/19 nu ies"
|
||||
# 7. trailing = high[25] = 96.45; primul close peste el e bara 27; ieșirea e pe profit
|
||||
trail = cds[25].h
|
||||
assert abs(trail - 96.45) < 1e-9, f"high[25]={trail} != 96.45"
|
||||
primul_exit = next(i for i in range(26, len(cds)) if cds[i].c > trail)
|
||||
assert primul_exit == 27 and abs(cds[27].c - 96.60) < 1e-9, "ieșirea nu e bara 27 @ 96.60"
|
||||
assert cds[27].c < entry, "ieșirea n-ar fi pe profit"
|
||||
# 8. doji: corp <= 10% din amplitudine
|
||||
assert abs(doji.o - doji.c) <= 0.10 * (doji.h - doji.l), "bara 14 nu e doji"
|
||||
# 9. uptrend vizibil
|
||||
assert cds[13].c > cds[0].c + 5, "uptrend prea mic"
|
||||
|
||||
print(f"doji {doji.l:.2f}/{doji.h:.2f} intrare short {conf.c:.2f} "
|
||||
f"SL {sl:.2f} TP {tp:.2f} trail {trail:.2f} ieșire bara 27 @ {cds[27].c:.2f}")
|
||||
|
||||
# --- desenul ----------------------------------------------------------------
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[
|
||||
# toate etichetele în dreapta: în stânga sus se suprapuneau cu mark-ul
|
||||
# dojiului (SL la y=62, text până în ~x196; doji la y=58, din ~x193)
|
||||
Level(sl, "SL 100.10 (high 4 candele)", "#c00"),
|
||||
Level(entry, "intrare short 99.00", "#188a3e"),
|
||||
Level(tp, "TP 97.90 = 1R", "#1a5fb4"),
|
||||
Level(trail, "trail 96.45", "#b06000"),
|
||||
],
|
||||
marks=[
|
||||
Mark(14, "doji 99.20 / 100.10", "#b06000", "above"),
|
||||
Mark(27, "ieșire pe trail 96.60", "#c00", "above", dy=-6),
|
||||
],
|
||||
scale=30.0, gap=15, body_w=9, left=44, width=640, vol_h=70,
|
||||
vol_label="volum", title="Sell Doji — daily (exemplu construit, fără ticker)",
|
||||
# scurt: o linie de caption mai lungă de ~90 de caractere iese din viewBox
|
||||
# (640px) și se taie la randare
|
||||
caption="Intrare short la close sub low-ul dojiului, SL la high 4 candele, "
|
||||
"TP 1:1, apoi trail.",
|
||||
)
|
||||
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
|
||||
with open("/workspace/atm-curs-dl/gen/strategii/buy_doji_sell.svg", "w") as f:
|
||||
f.write(svg)
|
||||
print("scris gen/strategii/buy_doji_sell.svg")
|
||||
111
gen/strategii/buy_doji_sell.svg
Normal file
@@ -0,0 +1,111 @@
|
||||
<svg viewBox="0 0 640 460" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Sell Doji — daily (exemplu construit, fără ticker)</text>
|
||||
<line x1="24" y1="68.0" x2="626" y2="68.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="612" y="62.0" fill="#c00" text-anchor="end">SL 100.10 (high 4 candele)</text>
|
||||
<line x1="24" y1="101.0" x2="626" y2="101.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="612" y="95.0" fill="#188a3e" text-anchor="end">intrare short 99.00</text>
|
||||
<line x1="24" y1="134.0" x2="626" y2="134.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="612" y="128.0" fill="#1a5fb4" text-anchor="end">TP 97.90 = 1R</text>
|
||||
<line x1="24" y1="177.5" x2="626" y2="177.5" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="612" y="171.5" fill="#b06000" text-anchor="end">trail 96.45</text>
|
||||
<line x1="44.0" y1="272.0" x2="44.0" y2="288.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="281.0" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="239.0" x2="59.0" y2="288.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="248.0" width="9" height="33.0" fill="#188a3e"/>
|
||||
<line x1="74.0" y1="239.0" x2="74.0" y2="267.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="248.0" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="89.0" y1="212.0" x2="89.0" y2="267.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="221.0" width="9" height="39.0" fill="#188a3e"/>
|
||||
<line x1="104.0" y1="179.0" x2="104.0" y2="228.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="188.0" width="9" height="33.0" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="179.0" x2="119.0" y2="204.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="188.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="134.0" y1="152.0" x2="134.0" y2="204.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="161.0" width="9" height="36.0" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="119.0" x2="149.0" y2="168.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="128.0" width="9" height="33.0" fill="#188a3e"/>
|
||||
<line x1="164.0" y1="119.0" x2="164.0" y2="144.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="128.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="179.0" y1="95.0" x2="179.0" y2="144.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="104.0" width="9" height="33.0" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="68.0" x2="194.0" y2="111.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="77.0" width="9" height="27.0" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="56.0" x2="209.0" y2="84.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="65.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="56.0" x2="224.0" y2="81.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="65.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="239.0" y1="68.0" x2="239.0" y2="89.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="74.0" width="9" height="7.5" fill="#c00"/>
|
||||
<line x1="254.0" y1="68.0" x2="254.0" y2="95.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="81.5" width="9" height="1.6" fill="#c00"/>
|
||||
<line x1="269.0" y1="77.0" x2="269.0" y2="93.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="83.0" width="9" height="6.0" fill="#777"/>
|
||||
<line x1="284.0" y1="83.0" x2="284.0" y2="105.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="89.0" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="299.0" y1="92.0" x2="299.0" y2="123.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="101.0" width="9" height="15.0" fill="#c00"/>
|
||||
<line x1="314.0" y1="107.0" x2="314.0" y2="129.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="116.0" width="9" height="6.0" fill="#c00"/>
|
||||
<line x1="329.0" y1="113.0" x2="329.0" y2="144.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="122.0" width="9" height="15.0" fill="#c00"/>
|
||||
<line x1="344.0" y1="128.0" x2="344.0" y2="156.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="137.0" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="359.0" y1="140.0" x2="359.0" y2="168.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="149.0" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="374.0" y1="152.0" x2="374.0" y2="177.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="161.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="389.0" y1="161.0" x2="389.0" y2="186.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="170.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="404.0" y1="170.0" x2="404.0" y2="195.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="179.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="419.0" y1="177.5" x2="419.0" y2="203.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="188.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="434.0" y1="176.0" x2="434.0" y2="200.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="185.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="164.0" x2="449.0" y2="192.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="173.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="164.0" x2="464.0" y2="186.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="173.0" width="9" height="6.0" fill="#c00"/>
|
||||
<line x1="479.0" y1="170.0" x2="479.0" y2="195.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="179.0" width="9" height="9.0" fill="#c00"/>
|
||||
<line x1="494.0" y1="173.0" x2="494.0" y2="195.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="182.0" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="24" y1="384.5" x2="626" y2="384.5" stroke="#888"/>
|
||||
<rect x="39.5" y="350.3" width="9" height="34.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="351.8" width="9" height="32.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="69.5" y="348.7" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="84.5" y="353.4" width="9" height="31.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="99.5" y="349.5" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="114.5" y="347.2" width="9" height="37.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="351.1" width="9" height="33.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="347.9" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="159.5" y="345.6" width="9" height="38.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="349.5" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="189.5" y="344.1" width="9" height="40.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.5" y="339.4" width="9" height="45.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="219.5" y="346.4" width="9" height="38.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="341.7" width="9" height="42.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="314.5" width="9" height="70.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="336.3" width="9" height="48.2" fill="#777" opacity="0.55"/>
|
||||
<rect x="279.5" y="319.2" width="9" height="65.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="340.9" width="9" height="43.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="344.1" width="9" height="40.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.5" y="345.6" width="9" height="38.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="339.5" y="347.2" width="9" height="37.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="354.5" y="348.7" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="369.5" y="350.3" width="9" height="34.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="384.5" y="351.8" width="9" height="32.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.5" y="353.4" width="9" height="31.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="414.5" y="349.5" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="429.5" y="345.6" width="9" height="38.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="348.7" width="9" height="35.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="350.3" width="9" height="34.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="474.5" y="351.1" width="9" height="33.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="489.5" y="349.5" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="308.5" fill="#555">volum</text>
|
||||
<text x="254.0" y="58.0" fill="#b06000" text-anchor="middle" style="font-weight:bold">doji 99.20 / 100.10</text>
|
||||
<text x="449.0" y="148.0" fill="#c00" text-anchor="middle" style="font-weight:bold">ieșire pe trail 96.60</text>
|
||||
<text x="24" y="406.5" fill="#777">bara 15: close 99.40 peste low-ul dojiului 99.20 — NU confirmă</text>
|
||||
<text x="24" y="423.5" fill="#c00">bara 16: close 99.00 sub 99.20 — confirmare (intrare short)</text>
|
||||
<text x="24" y="448" fill="#555">Intrare short la close sub low-ul dojiului, SL la high 4 candele, TP 1:1, apoi trail.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
118
gen/strategii/hammer_mbt10_bh_ansamblu.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""Buy hammer — diagrama de ansamblu (daily, ticker ilustrativ XYZ).
|
||||
|
||||
Citește specificația `gen/strategii/hammer_mbt10_bh_ansamblu.spec.txt`.
|
||||
Seria de 34 de candele e construită din lista de close-uri dată, cu open-ul
|
||||
fiecărei bare = close-ul precedentului; wick-urile sunt fixate ca nivelurile
|
||||
(hammer H=C 63.30 / L 62.10, intrare 63.35, SL 62.05, TP 65.95) să iasă
|
||||
exact din serie, nu doar din etichetă.
|
||||
"""
|
||||
import re
|
||||
import sys; sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes
|
||||
|
||||
# --- seria: 34 de close-uri, exact ca în specificație ------------------------
|
||||
closes = [
|
||||
72.00, 71.20, 71.50, 70.40, 69.60, 70.00, 68.90, 68.10, 68.40, 67.30, # 0-9
|
||||
66.50, 66.80, 65.70, 64.90, 65.20, 64.10, 63.40, 63.70, 63.05, # 10-18
|
||||
63.30, # 19 = hammer: o 63.05, c/h 63.30, l 62.10 (H = C, fitil sus 0)
|
||||
63.35, # 20 = confirmare: close peste 63.30 => intrare
|
||||
63.80, 64.20, 64.60, 64.40, 64.90, 65.20, 65.50, 65.80, 66.10, # 21-29
|
||||
66.00, 65.70, 65.40, 65.60, # 30-33
|
||||
]
|
||||
assert len(closes) == 34
|
||||
|
||||
# wick-uri: (up, down) implicite (0.25, 0.30) + excepțiile din specificație
|
||||
wicks = [(0.25, 0.30)] * 34
|
||||
wicks[19] = (0.00, 0.95) # hammer: high 63.30 (= close), low 62.10
|
||||
# bara 20: wu=0.15 => high 63.50. Spec-ul dă wd=0.15 cu „low 63.20", dar
|
||||
# wd se măsoară de la min(o,c)=63.30, deci 0.15 dă 63.15; folosim tuplul
|
||||
# literal din specificație (0.15, 0.15), low-ul barei 20 nu e etichetat.
|
||||
wicks[20] = (0.15, 0.15)
|
||||
|
||||
# volum relativ ales de generator (materialul nu dă volum pe exemplul construit):
|
||||
# normal pe downtrend, în creștere pe hammer (19) și pe confirmare (20), normal după
|
||||
vols = [50, 48, 47, 46, 45, 46, 44, 43, 45, 42,
|
||||
41, 43, 40, 39, 41, 38, 37, 39, 55,
|
||||
90, 95,
|
||||
58, 55, 53, 50, 52, 54, 56, 60, 64,
|
||||
58, 55, 52, 50]
|
||||
assert len(vols) == 34
|
||||
media_vol = sum(vols) / len(vols)
|
||||
assert vols[19] > media_vol and vols[20] > media_vol, "volum peste medie pe hammer/confirmare"
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=72.00)
|
||||
|
||||
hammer, conf = cds[19], cds[20]
|
||||
# confirmarea o punem ca notă sub grafic: un Mark deasupra barei 20 s-ar
|
||||
# suprapune cu fitilul lung al hammer-ului (bara 19)
|
||||
conf.note = "bara 20: confirmare — close 63.35 peste high-ul hammer-ului 63.30 (intrare)"
|
||||
intrare, sl, tp = 63.35, 62.05, 65.95
|
||||
|
||||
# --- cele 8 assert-uri cerute de specificație -------------------------------
|
||||
# 1. hammer: H = C = 63.30, L = 62.10
|
||||
assert abs(cds[19].c - 63.30) < 1e-9 and abs(cds[19].h - 63.30) < 1e-9, "H != C la hammer"
|
||||
assert abs(cds[19].l - 62.10) < 1e-9, f"low[19]={cds[19].l} != 62.10"
|
||||
# 2. open[19] = close[18]; corp 0.25; fitil jos 0.95 >= 2*0.25
|
||||
assert abs(cds[19].o - cds[18].c) < 1e-9 and abs(cds[19].o - 63.05) < 1e-9, "open[19] != close[18]"
|
||||
corp = hammer.c - hammer.o
|
||||
fitil_jos = hammer.o - hammer.l
|
||||
assert abs(corp - 0.25) < 1e-9, f"corp={corp} != 0.25"
|
||||
assert abs(fitil_jos - 0.95) < 1e-9, f"fitil jos={fitil_jos} != 0.95"
|
||||
assert fitil_jos >= 2 * corp, "fitilul nu e cel puțin dublul corpului"
|
||||
# 3. confirmarea închide peste high-ul hammer-ului
|
||||
assert abs(conf.c - intrare) < 1e-9, f"close[20]={conf.c} != 63.35"
|
||||
assert conf.c > hammer.h and abs(hammer.h - 63.30) < 1e-9, "close[20] nu e peste high[19]"
|
||||
# 4. SL = low-ul hammer-ului - 0.05
|
||||
assert abs(sl - (cds[19].l - 0.05)) < 1e-9, "SL != low[19] - 0.05"
|
||||
# 5. risc 1.30, TP 2.60 = 2R exact
|
||||
assert abs(abs(intrare - sl) - 1.30) < 1e-9, "risc != 1.30"
|
||||
assert abs(abs(tp - intrare) - 2.60) < 1e-9, "TP != 2R exact"
|
||||
# 6. primul high >= TP e bara 28 (65.80 + wick 0.25 = 66.05)
|
||||
primul_tp = next(i for i in range(21, len(cds)) if cds[i].h >= tp)
|
||||
assert primul_tp == 28, f"TP 65.95 atins întâi la bara {primul_tp}, nu 28"
|
||||
# 7. downtrend vizibil
|
||||
assert cds[18].c < cds[0].c - 8, "downtrend prea mic"
|
||||
# 8. ordinea nivelurilor
|
||||
assert sl < intrare < tp, "SL / intrare / TP nu sunt în ordine"
|
||||
|
||||
print(f"hammer H/C {hammer.h:.2f} L {hammer.l:.2f} intrare {conf.c:.2f} "
|
||||
f"SL {sl:.2f} TP {tp:.2f} = {abs(tp-intrare)/abs(intrare-sl):.1f}R "
|
||||
f"TP atins la bara {primul_tp} (high {cds[primul_tp].h:.2f})")
|
||||
|
||||
# --- desenul ----------------------------------------------------------------
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[
|
||||
Level(sl, "SL 62.05 — sub low-ul hammer-ului", "#c00", label_at="left"),
|
||||
Level(intrare, "intrare 63.35", "#188a3e"),
|
||||
Level(tp, "TP 65.95 = 2R", "#1a5fb4"),
|
||||
],
|
||||
marks=[
|
||||
Mark(19, "hammer 63.30 / 62.10", "#b06000", "below"),
|
||||
],
|
||||
scale=32.0, gap=15, body_w=9, left=44, width=720, vol_h=70,
|
||||
vol_label="volum", title="Buy hammer — daily (ticker ilustrativ XYZ, din lecția de pattern)",
|
||||
caption="", # legenda e pusă manual, pe două rânduri, ca să nu iasă din pânză
|
||||
)
|
||||
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
|
||||
|
||||
# legenda din specificație e mai lată decât pânza: o rupem pe două rânduri și
|
||||
# extindem viewBox-ul ca să nu fie tăiată
|
||||
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
||||
W, H = int(m.group(1)), int(m.group(2))
|
||||
legenda = [
|
||||
"Exemplu ilustrativ (XYZ) din lecția de pattern (01_MODUL_1/05_patternuri_candele.md:210-215);",
|
||||
"prețurile sunt cele ale lecției, structura barelor dintre ele e construită.",
|
||||
]
|
||||
extra = 16 * len(legenda) + 6
|
||||
svg = svg.replace(f'viewBox="0 0 {W} {H}"', f'viewBox="0 0 {W} {H + extra}"')
|
||||
svg = svg.replace("</svg>", "".join(
|
||||
f'<text x="24" y="{H + 12 + 16 * i}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(legenda)) + "</svg>")
|
||||
assert "\n\n" not in svg, "linie goală în SVG după legendă"
|
||||
|
||||
with open("/workspace/atm-curs-dl/gen/strategii/hammer_mbt10_bh_ansamblu.svg", "w") as f:
|
||||
f.write(svg)
|
||||
print("scris gen/strategii/hammer_mbt10_bh_ansamblu.svg")
|
||||
115
gen/strategii/hammer_mbt10_bh_ansamblu.svg
Normal file
@@ -0,0 +1,115 @@
|
||||
<svg viewBox="0 0 720 559" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Buy hammer — daily (ticker ilustrativ XYZ, din lecția de pattern)</text>
|
||||
<line x1="24" y1="382.4" x2="706" y2="382.4" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="376.4" fill="#c00">SL 62.05 — sub low-ul hammer-ului</text>
|
||||
<line x1="24" y1="340.8" x2="706" y2="340.8" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="334.8" fill="#188a3e" text-anchor="end">intrare 63.35</text>
|
||||
<line x1="24" y1="257.6" x2="706" y2="257.6" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="251.6" fill="#1a5fb4" text-anchor="end">TP 65.95 = 2R</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="73.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="64.0" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="56.0" x2="59.0" y2="99.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="64.0" width="9" height="25.6" fill="#c00"/>
|
||||
<line x1="74.0" y1="72.0" x2="74.0" y2="99.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="80.0" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="72.0" x2="89.0" y2="124.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="80.0" width="9" height="35.2" fill="#c00"/>
|
||||
<line x1="104.0" y1="107.2" x2="104.0" y2="150.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="115.2" width="9" height="25.6" fill="#c00"/>
|
||||
<line x1="119.0" y1="120.0" x2="119.0" y2="150.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="128.0" width="9" height="12.8" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="120.0" x2="134.0" y2="172.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="128.0" width="9" height="35.2" fill="#c00"/>
|
||||
<line x1="149.0" y1="155.2" x2="149.0" y2="198.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="163.2" width="9" height="25.6" fill="#c00"/>
|
||||
<line x1="164.0" y1="171.2" x2="164.0" y2="198.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="179.2" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="171.2" x2="179.0" y2="224.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="179.2" width="9" height="35.2" fill="#c00"/>
|
||||
<line x1="194.0" y1="206.4" x2="194.0" y2="249.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="214.4" width="9" height="25.6" fill="#c00"/>
|
||||
<line x1="209.0" y1="222.4" x2="209.0" y2="249.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="230.4" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="222.4" x2="224.0" y2="275.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="230.4" width="9" height="35.2" fill="#c00"/>
|
||||
<line x1="239.0" y1="257.6" x2="239.0" y2="300.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="265.6" width="9" height="25.6" fill="#c00"/>
|
||||
<line x1="254.0" y1="273.6" x2="254.0" y2="300.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="281.6" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="273.6" x2="269.0" y2="326.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="281.6" width="9" height="35.2" fill="#c00"/>
|
||||
<line x1="284.0" y1="308.8" x2="284.0" y2="348.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="316.8" width="9" height="22.4" fill="#c00"/>
|
||||
<line x1="299.0" y1="321.6" x2="299.0" y2="348.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="329.6" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="321.6" x2="314.0" y2="360.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="329.6" width="9" height="20.8" fill="#c00"/>
|
||||
<line x1="329.0" y1="342.4" x2="329.0" y2="380.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="342.4" width="9" height="8.0" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="336.0" x2="344.0" y2="347.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="340.8" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="318.4" x2="359.0" y2="350.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="326.4" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="305.6" x2="374.0" y2="336.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="313.6" width="9" height="12.8" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="292.8" x2="389.0" y2="323.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="300.8" width="9" height="12.8" fill="#188a3e"/>
|
||||
<line x1="404.0" y1="292.8" x2="404.0" y2="316.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="300.8" width="9" height="6.4" fill="#c00"/>
|
||||
<line x1="419.0" y1="283.2" x2="419.0" y2="316.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="291.2" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="273.6" x2="434.0" y2="300.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="281.6" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="264.0" x2="449.0" y2="291.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="272.0" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="254.4" x2="464.0" y2="281.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="262.4" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="244.8" x2="479.0" y2="272.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="252.8" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="494.0" y1="244.8" x2="494.0" y2="265.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="252.8" width="9" height="3.2" fill="#c00"/>
|
||||
<line x1="509.0" y1="248.0" x2="509.0" y2="275.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="256.0" width="9" height="9.6" fill="#c00"/>
|
||||
<line x1="524.0" y1="257.6" x2="524.0" y2="284.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="265.6" width="9" height="9.6" fill="#c00"/>
|
||||
<line x1="539.0" y1="260.8" x2="539.0" y2="284.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="534.5" y="268.8" width="9" height="6.4" fill="#188a3e"/>
|
||||
<line x1="24" y1="478.4" x2="706" y2="478.4" stroke="#888"/>
|
||||
<rect x="39.5" y="441.6" width="9" height="36.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="443.0" width="9" height="35.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="443.8" width="9" height="34.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="444.5" width="9" height="33.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="445.2" width="9" height="33.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="444.5" width="9" height="33.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="446.0" width="9" height="32.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="446.7" width="9" height="31.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="445.2" width="9" height="33.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="447.5" width="9" height="30.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="448.2" width="9" height="30.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="446.7" width="9" height="31.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="219.5" y="448.9" width="9" height="29.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="449.7" width="9" height="28.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="448.2" width="9" height="30.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="264.5" y="450.4" width="9" height="28.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="451.1" width="9" height="27.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="449.7" width="9" height="28.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="437.9" width="9" height="40.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.5" y="412.1" width="9" height="66.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="408.4" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="435.7" width="9" height="42.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="437.9" width="9" height="40.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="439.3" width="9" height="39.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="399.5" y="441.6" width="9" height="36.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="414.5" y="440.1" width="9" height="38.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="438.6" width="9" height="39.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="437.1" width="9" height="41.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="434.2" width="9" height="44.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.5" y="431.2" width="9" height="47.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="489.5" y="435.7" width="9" height="42.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="504.5" y="437.9" width="9" height="40.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="519.5" y="440.1" width="9" height="38.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="534.5" y="441.6" width="9" height="36.8" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="402.4" fill="#555">volum</text>
|
||||
<text x="329.0" y="396.4" fill="#b06000" text-anchor="middle" style="font-weight:bold">hammer 63.30 / 62.10</text>
|
||||
<text x="24" y="500.4" fill="#188a3e">bara 20: confirmare — close 63.35 peste high-ul hammer-ului 63.30 (intrare)</text>
|
||||
<text x="24" y="533" fill="#555">Exemplu ilustrativ (XYZ) din lecția de pattern (01_MODUL_1/05_patternuri_candele.md:210-215);</text><text x="24" y="549" fill="#555">prețurile sunt cele ale lecției, structura barelor dintre ele e construită.</text></svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
279
gen/strategii/hammer_mbt10_bh_aplus.py
Normal file
@@ -0,0 +1,279 @@
|
||||
"""Buy hammer — A+ vs. mediocru (două panouri, exemple construite).
|
||||
|
||||
Citește `gen/strategii/hammer_mbt10_bh_aplus.spec.txt`
|
||||
(marcajul `<!-- DIAGRAMA: bh_aplus -->`, Partea A §2.2).
|
||||
|
||||
Același pattern ca formă (downtrend → hammer → confirmare), context opus:
|
||||
|
||||
- stânga (A+): downtrend de 12 candele de la 50.00 spre 42.80, hammer-ul cu
|
||||
low 41.90 chiar pe suportul monthly 42.00 (0,24 % sub nivel; corp 0.10,
|
||||
fitil jos 0.90), confirmarea închide 42.95 peste high-ul 42.90, pe volum
|
||||
peste media ultimelor 10 bare — „acumulare";
|
||||
- dreapta (NU): fără downtrend real (range lateral 44–46), hammer-ul în
|
||||
mijlocul intervalului (o=45.00, c=h=45.10, l=44.10), departe de orice
|
||||
nivel (suportul construit 42.50 e la 3,6 % sub low), confirmarea închide
|
||||
tot 45.15 peste 45.10, dar pe volum slab — context invalid.
|
||||
|
||||
Nivelurile 42.00 / 42.50 și cifrele de preț sunt construite (materialul nu
|
||||
dă prețuri pentru acest contrast); regulile (locația la nivel, downtrend,
|
||||
volum) sunt din curs. Două `Chart`-uri randate separat, lipite într-un SVG
|
||||
wrapper: coordonatele fiecărui panou rămân corecte prin construcție.
|
||||
|
||||
Notă de serie: specificația listează `close[11]=42.40` (A+) și `44.60` (NU),
|
||||
dar descrie hammer-ul cu `o=42.80 / c=42.90`, respectiv `o=45.00 / c=45.10`,
|
||||
iar assert 9 cere `close == high`. Cum open-ul fiecărei bare = close-ul
|
||||
precedentei (STYLE.md), am ridicat close[11] la 42.80 / 45.00 și close[12]
|
||||
la 42.90 / 45.10, ca seria să susțină exact OHLC-ul din etichetă. Restul
|
||||
close-urilor sunt cele din specificație.
|
||||
|
||||
Rulează: python3 gen/strategii/hammer_mbt10_bh_aplus.py
|
||||
Scrie: gen/strategii/hammer_mbt10_bh_aplus.svg
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "hammer_mbt10_bh_aplus.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
HAM_A, CONF_A = 12, 13 # indicii în panoul A+
|
||||
HAM_N, CONF_N = 12, 13 # indicii în panoul NU
|
||||
SUPORT_A = 42.00 # suport monthly (A+)
|
||||
SUPORT_N = 42.50 # cel mai apropiat nivel construit (NU)
|
||||
|
||||
# ============================================================ PANOUL A+ (20 bare)
|
||||
# 12 bare de downtrend 50.00 -> 42.80, hammer la 42.00, confirmare, apoi urcare.
|
||||
CLOSES_A = [
|
||||
50.00, 49.10, 49.30, 48.20, 47.30, 47.60, 46.40, 45.50, 44.60, 43.70,
|
||||
43.00, 42.80,
|
||||
42.90, # 12 = hammer: o=42.80, c=h=42.90, l=41.90 (H=C, fitil jos 0.90)
|
||||
42.95, # 13 = confirmare, close > 42.90 => intrare
|
||||
43.40, 43.80, 44.20, 44.00, 44.50, 44.80,
|
||||
]
|
||||
assert len(CLOSES_A) == 20, len(CLOSES_A)
|
||||
|
||||
WICKS_A = [(0.25, 0.30)] * 20
|
||||
WICKS_A[HAM_A] = (0.00, 0.90) # high 42.90, low 41.90 (pe suportul 42.00)
|
||||
WICKS_A[CONF_A] = (0.20, 0.20) # confirmarea: o=42.90, c=42.95
|
||||
|
||||
# Volum ales de generator (materialul nu dă volum): scade pe downtrend, climax
|
||||
# pe hammer, spike pe confirmare („acumulare"), moderat după.
|
||||
VOLS_A = [54, 52, 51, 49, 48, 47, 46, 45, 44, 43,
|
||||
42, 45,
|
||||
64, # 12 = hammer: exces de vânzare
|
||||
90, # 13 = confirmare: volum peste medie
|
||||
74, 70, 66, 62, 60, 58]
|
||||
assert len(VOLS_A) == 20
|
||||
|
||||
cds_a = from_closes(CLOSES_A, wicks=WICKS_A, vols=VOLS_A, start=50.60)
|
||||
ham_a, conf_a = cds_a[HAM_A], cds_a[CONF_A]
|
||||
|
||||
# ============================================================ PANOUL NU (20 bare)
|
||||
# range lateral 44-46, fără downtrend; hammer în mijloc, confirmare pe volum slab.
|
||||
CLOSES_N = [
|
||||
46.00, 45.30, 45.80, 45.10, 45.60, 44.90, 45.40, 44.80, 45.20, 44.70,
|
||||
45.10, 45.00,
|
||||
45.10, # 12 = hammer: o=45.00, c=h=45.10, l=44.10 (H=C, fitil jos 0.90)
|
||||
45.15, # 13 = confirmare, close > 45.10
|
||||
44.90, 44.70, 44.40, 44.20, 44.00, 43.80,
|
||||
]
|
||||
assert len(CLOSES_N) == 20, len(CLOSES_N)
|
||||
|
||||
WICKS_N = [(0.25, 0.30)] * 20
|
||||
WICKS_N[HAM_N] = (0.00, 0.90) # high 45.10, low 44.10
|
||||
WICKS_N[CONF_N] = (0.20, 0.20)
|
||||
|
||||
# Volum ales de generator: lateral, constant, confirmarea pe volum slab.
|
||||
VOLS_N = [52, 50, 53, 49, 51, 48, 52, 50, 49, 51,
|
||||
50, 49,
|
||||
56, # 12
|
||||
31, # 13 = confirmare: volum slab
|
||||
52, 50, 48, 51, 49, 47]
|
||||
assert len(VOLS_N) == 20
|
||||
|
||||
cds_n = from_closes(CLOSES_N, wicks=WICKS_N, vols=VOLS_N, start=45.60)
|
||||
ham_n, conf_n = cds_n[HAM_N], cds_n[CONF_N]
|
||||
|
||||
# ============================================================ assert-urile din spec
|
||||
# 1. A+: low[12] la <= 0,5 % de 42.00 (41.90 e la 0,24 %)
|
||||
dist_a = abs(ham_a.l - SUPORT_A) / SUPORT_A
|
||||
assert dist_a <= 0.005, f"low hammer A+ {ham_a.l} la {dist_a * 100:.2f} % de 42.00"
|
||||
# low-ul hammer-ului chiar trece sub nivel (fitilul sparge suportul)
|
||||
assert ham_a.l < SUPORT_A <= ham_a.h, "hammer-ul A+ nu are fitilul pe/la nivel"
|
||||
# 2. A+: close[13] > high[12] (tehnic valid)
|
||||
assert conf_a.c > ham_a.h, f"A+ close {conf_a.c} <= high hammer {ham_a.h}"
|
||||
assert abs(conf_a.c - 42.95) < 1e-9
|
||||
# 3. A+: volumul pe bara 13 > media volumelor pe 10 bare
|
||||
media_a = sum(VOLS_A[CONF_A - 10:CONF_A]) / 10
|
||||
assert VOLS_A[CONF_A] > media_a, f"vol confirmare A+ {VOLS_A[CONF_A]} <= media {media_a}"
|
||||
# 4. A+: close[12] < close[0] - 5 (downtrend vizibil)
|
||||
assert cds_a[HAM_A].c < cds_a[0].c - 5, "downtrendul A+ nu e vizibil"
|
||||
# 5. NU: (low[12] - 42.50) / 42.50 > 0.02 (hammer-ul NU e departe de nivel)
|
||||
dist_n = (ham_n.l - SUPORT_N) / SUPORT_N
|
||||
assert dist_n > 0.02, f"low hammer NU {ham_n.l} prea aproape de 42.50 ({dist_n:.1%})"
|
||||
# eticheta „la -3,6 %" din spec: (44.10 - 42.50) / 44.10
|
||||
pct_n = (ham_n.l - SUPORT_N) / ham_n.l * 100
|
||||
assert round(pct_n, 1) == 3.6, f"procentul NU e {pct_n:.2f} %, nu 3,6 %"
|
||||
# 6. NU: close[13] > high[12] (tehnic valid)
|
||||
assert conf_n.c > ham_n.h, f"NU close {conf_n.c} <= high hammer {ham_n.h}"
|
||||
assert abs(conf_n.c - 45.15) < 1e-9
|
||||
# 7. NU: volumul pe bara 13 < media volumelor pe 10 bare
|
||||
media_n = sum(VOLS_N[CONF_N - 10:CONF_N]) / 10
|
||||
assert VOLS_N[CONF_N] < media_n, f"vol confirmare NU {VOLS_N[CONF_N]} >= media {media_n}"
|
||||
# 8. NU: |close[12] - close[0]| <= 1.5 (fără downtrend real, range lateral)
|
||||
assert abs(cds_n[HAM_N].c - cds_n[0].c) <= 1.5, "NU nu e range lateral"
|
||||
# 9. ambele hammer: fitil jos >= 2x corp si close == high (fitil de sus inexistent)
|
||||
for tag, o, l, c, h in (
|
||||
("A+", ham_a.o, ham_a.l, ham_a.c, ham_a.h),
|
||||
("NU", ham_n.o, ham_n.l, ham_n.c, ham_n.h)):
|
||||
assert (o - l) >= 2 * (c - o), f"fitilul hammer-ului {tag} < 2x corpul"
|
||||
assert abs(c - h) < 1e-9, f"hammer-ul {tag} are fitil de sus"
|
||||
# OHLC-ul descris în spec (eticheta e adevărul)
|
||||
assert abs(ham_a.o - 42.80) < 1e-9 and abs(ham_a.c - 42.90) < 1e-9 and abs(ham_a.l - 41.90) < 1e-9
|
||||
assert abs(ham_n.o - 45.00) < 1e-9 and abs(ham_n.c - 45.10) < 1e-9 and abs(ham_n.l - 44.10) < 1e-9
|
||||
# context: hammer-ul A+ e la baza downtrendului, NU nu are downtrend
|
||||
assert min(c.l for c in cds_a[:HAM_A]) > ham_a.l, "low-ul hammer-ului A+ nu e minimul"
|
||||
assert abs(cds_a[-1].c - 44.80) < 1e-9 and cds_a[-1].c > conf_a.c, "A+ nu urcă după intrare"
|
||||
|
||||
print(f"A+ hammer {ham_a.o:.2f}/{ham_a.c:.2f}/{ham_a.h:.2f}/{ham_a.l:.2f} "
|
||||
f"conf {conf_a.c:.2f} > high {ham_a.h:.2f} vol {VOLS_A[CONF_A]} > medie {media_a:.1f}")
|
||||
print(f"NU hammer {ham_n.o:.2f}/{ham_n.c:.2f}/{ham_n.h:.2f}/{ham_n.l:.2f} "
|
||||
f"conf {conf_n.c:.2f} > high {ham_n.h:.2f} vol {VOLS_N[CONF_N]} < medie {media_n:.1f}")
|
||||
print(f"NU low {ham_n.l:.2f} vs suport {SUPORT_N:.2f} = -{pct_n:.1f} %")
|
||||
|
||||
# ============================================================ desenele celor două panouri
|
||||
PW = 430 # lățimea fiecărui panou
|
||||
SCALE = 26.0 # aceeași scară în ambele panouri (comparație cinstită)
|
||||
ch_a = Chart(
|
||||
candles=cds_a,
|
||||
levels=[Level(SUPORT_A, "suport monthly 42.00", GREEN, label_at="left")],
|
||||
marks=[Mark(HAM_A, "hammer la nivel (low 41.90)", ORANGE, "below", dy=0),
|
||||
Mark(CONF_A, "acumulare", GREEN, "above", dy=-26)],
|
||||
mas=[], scale=SCALE, gap=15, body_w=9, left=44, width=PW, vol_h=60,
|
||||
vol_label="volum",
|
||||
title="A+ — hammer la suport monthly, volum în acumulare",
|
||||
caption="bara 13: 42.95 > high 42.90, pe volum peste medie.",
|
||||
)
|
||||
ch_n = Chart(
|
||||
candles=cds_n,
|
||||
levels=[Level(SUPORT_N, "fără nivel apropiat (suport 42.50 la −3,6 %)", GRAY,
|
||||
label_at="left")],
|
||||
marks=[Mark(HAM_N, "hammer în mijloc de range", ORANGE, "below", dy=0),
|
||||
Mark(CONF_N, "volum slab", RED, "above", dy=0)],
|
||||
mas=[], scale=SCALE, gap=15, body_w=9, left=44, width=PW, vol_h=60,
|
||||
vol_label="volum",
|
||||
title="Mediocru — hammer în mijloc de range, fără nivel",
|
||||
caption="bara 13: 45.15 > 45.10, dar pe volum slab.",
|
||||
)
|
||||
|
||||
# ============================================================ wrapper: două panouri alăturate
|
||||
def inner(svg: str) -> str:
|
||||
"""Conținutul dintre tag-urile <svg> ale unui panou, fără elementul rădăcină."""
|
||||
return svg[svg.index(">") + 1:svg.rindex("</svg>")]
|
||||
|
||||
|
||||
def vb_height(svg: str) -> float:
|
||||
m = re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg)
|
||||
assert m, "nu găsesc viewBox în panoul randat"
|
||||
return float(m.group(1))
|
||||
|
||||
|
||||
PANEL_GAP, PAD = 24, 8
|
||||
TITLE_H, FOOTER_H = 30, 38
|
||||
svg_a, svg_n = ch_a.render(), ch_n.render()
|
||||
|
||||
HA, HN = vb_height(svg_a), vb_height(svg_n)
|
||||
assert svg_a.count("\n\n") == 0 and svg_n.count("\n\n") == 0, "linie goală în panou"
|
||||
|
||||
TOTAL_W = PAD + PW + PANEL_GAP + PW + PAD
|
||||
TOTAL_H = PAD + TITLE_H + max(HA, HN) + FOOTER_H + PAD
|
||||
OFF_A = (PAD, PAD + TITLE_H)
|
||||
OFF_N = (PAD + PW + PANEL_GAP, PAD + TITLE_H)
|
||||
|
||||
TOP_TITLE = "Buy hammer A+ vs. mediocru (două panouri, exemple construite)"
|
||||
FOOTER1 = ("Reguli din curs: 26_2025-06-11.md:701-703 (locația la nivel), "
|
||||
"05_patternuri_candele.md:77-80, 05_volum.md:2169.")
|
||||
FOOTER2 = ("Nivelurile 42.00 / 42.50 și cifrele de preț sunt construite "
|
||||
"(materialul nu dă prețuri pentru acest contrast).")
|
||||
FOOTER = FOOTER1 + " " + FOOTER2
|
||||
|
||||
wrapper = (
|
||||
f'<svg viewBox="0 0 {TOTAL_W} {TOTAL_H:.0f}" width="{TOTAL_W}" height="{TOTAL_H:.0f}" '
|
||||
f'xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">'
|
||||
f'<text x="{TOTAL_W / 2:.0f}" y="20" fill="#333" text-anchor="middle" '
|
||||
f'style="font-weight:bold">{TOP_TITLE}</text>'
|
||||
f'<g transform="translate({OFF_A[0]},{OFF_A[1]})">{inner(svg_a)}</g>'
|
||||
f'<g transform="translate({OFF_N[0]},{OFF_N[1]})">{inner(svg_n)}</g>'
|
||||
f'<text x="{TOTAL_W / 2:.0f}" y="{TOTAL_H - PAD - 14:.0f}" fill="#555" '
|
||||
f'text-anchor="middle" style="font-size:10px">{FOOTER1}</text>'
|
||||
f'<text x="{TOTAL_W / 2:.0f}" y="{TOTAL_H - PAD:.0f}" fill="#555" '
|
||||
f'text-anchor="middle" style="font-size:10px">{FOOTER2}</text>'
|
||||
f'</svg>'
|
||||
)
|
||||
|
||||
# ============================================================ verificare pe SVG-ul final
|
||||
def texts(svg: str):
|
||||
"""Toate (x, y, text-anchor, text) din elementele <text> ale unui fragment."""
|
||||
out = []
|
||||
for m in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', svg):
|
||||
x, y, attrs, t = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4)
|
||||
if t.startswith("<"):
|
||||
continue
|
||||
am = re.search(r'text-anchor="(\w+)"', attrs)
|
||||
out.append((x, y, am.group(1) if am else "start", t))
|
||||
return out
|
||||
|
||||
|
||||
def span(x: float, anchor: str, t: str) -> tuple[float, float]:
|
||||
"""Intervalul orizontal aproximativ al unui text, față de ancora lui."""
|
||||
w = len(t) * 12 * 0.56
|
||||
left = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
|
||||
return left, left + w
|
||||
|
||||
|
||||
assert wrapper.count("\n\n") == 0, "linie goală în SVG"
|
||||
|
||||
# fiecare text (după translatarea panoului) e în întregime în interiorul pânzei
|
||||
all_txt = []
|
||||
for frag, (ox, oy) in ((inner(svg_a), OFF_A), (inner(svg_n), OFF_N)):
|
||||
for x, y, anchor, t in texts(frag):
|
||||
gx, gy = x + ox, y + oy
|
||||
gl, gr = gx + span(0, anchor, t)[0], gx + span(0, anchor, t)[1]
|
||||
assert ox <= gl and gr <= ox + PW, f"text iese din panou: {t!r} ({gl:.0f}..{gr:.0f})"
|
||||
assert -1 <= gl and gr <= TOTAL_W + 1, f"text ieșit pe orizontală: {t!r} ({gl:.0f}..{gr:.0f})"
|
||||
assert 0 <= gy <= TOTAL_H, f"text ieșit pe verticală: {t!r} ({gy:.0f})"
|
||||
all_txt.append((gx, gy, anchor, t))
|
||||
# textele din wrapper (titlu + footer), în coordonate globale
|
||||
for x, y, anchor, t in ((TOTAL_W / 2, 20.0, "middle", TOP_TITLE),
|
||||
(TOTAL_W / 2, TOTAL_H - PAD - 14, "middle", FOOTER1),
|
||||
(TOTAL_W / 2, TOTAL_H - PAD, "middle", FOOTER2)):
|
||||
gl, gr = span(x, anchor, t)
|
||||
assert -1 <= gl and gr <= TOTAL_W + 1, f"text din wrapper iese din pânză: {t!r}"
|
||||
assert 0 <= y <= TOTAL_H, f"text din wrapper în afara înălțimii: {t!r}"
|
||||
all_txt.append((x, y, anchor, t))
|
||||
assert len(all_txt) >= 10, "prea puține etichete"
|
||||
|
||||
# nu se suprapun două texte pe aceeași poziție sau pe rândul apropiat
|
||||
for i in range(len(all_txt)):
|
||||
xi, yi, ai, si = all_txt[i]
|
||||
li, ri = (xi + span(0, ai, si)[0], xi + span(0, ai, si)[1])
|
||||
for j in range(i + 1, len(all_txt)):
|
||||
xj, yj, aj, sj = all_txt[j]
|
||||
lj, rj = xj + span(0, aj, sj)[0], xj + span(0, aj, sj)[1]
|
||||
if abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
# etichetele cerute de spec există în desen
|
||||
for cheie in ("suport monthly 42.00", "hammer la nivel", "acumulare",
|
||||
"fără nivel apropiat (suport 42.50 la −3,6 %)", "volum slab",
|
||||
"A+ — hammer la suport monthly, volum în acumulare",
|
||||
"Mediocru — hammer în mijloc de range, fără nivel"):
|
||||
assert cheie in wrapper, f"lipsește din desen: {cheie!r}"
|
||||
|
||||
OUT.write_text(wrapper, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: {TOTAL_W}x{TOTAL_H:.0f}, "
|
||||
f"{len(all_txt)} etichete, verificare OK")
|
||||
139
gen/strategii/hammer_mbt10_bh_aplus.svg
Normal file
@@ -0,0 +1,139 @@
|
||||
<svg viewBox="0 0 900 501" width="900" height="501" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif"><text x="450" y="20" fill="#333" text-anchor="middle" style="font-weight:bold">Buy hammer A+ vs. mediocru (două panouri, exemple construite)</text><g transform="translate(8,38)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">A+ — hammer la suport monthly, volum în acumulare</text>
|
||||
<line x1="24" y1="286.1" x2="416" y2="286.1" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="280.1" fill="#188a3e">suport monthly 42.00</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="85.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="62.5" width="9" height="15.6" fill="#c00"/>
|
||||
<line x1="59.0" y1="71.6" x2="59.0" y2="109.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="78.1" width="9" height="23.4" fill="#c00"/>
|
||||
<line x1="74.0" y1="89.8" x2="74.0" y2="109.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="96.3" width="9" height="5.2" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="89.8" x2="89.0" y2="132.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="96.3" width="9" height="28.6" fill="#c00"/>
|
||||
<line x1="104.0" y1="118.4" x2="104.0" y2="156.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="124.9" width="9" height="23.4" fill="#c00"/>
|
||||
<line x1="119.0" y1="134.0" x2="119.0" y2="156.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="140.5" width="9" height="7.8" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="134.0" x2="134.0" y2="179.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="140.5" width="9" height="31.2" fill="#c00"/>
|
||||
<line x1="149.0" y1="165.2" x2="149.0" y2="202.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="171.7" width="9" height="23.4" fill="#c00"/>
|
||||
<line x1="164.0" y1="188.6" x2="164.0" y2="226.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="195.1" width="9" height="23.4" fill="#c00"/>
|
||||
<line x1="179.0" y1="212.0" x2="179.0" y2="249.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="218.5" width="9" height="23.4" fill="#c00"/>
|
||||
<line x1="194.0" y1="235.4" x2="194.0" y2="267.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="241.9" width="9" height="18.2" fill="#c00"/>
|
||||
<line x1="209.0" y1="253.6" x2="209.0" y2="273.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="260.1" width="9" height="5.2" fill="#c00"/>
|
||||
<line x1="224.0" y1="262.7" x2="224.0" y2="288.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="262.7" width="9" height="2.6" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="256.2" x2="239.0" y2="267.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="261.4" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="243.2" x2="254.0" y2="269.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="249.7" width="9" height="11.7" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="232.8" x2="269.0" y2="257.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="239.3" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="222.4" x2="284.0" y2="247.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="228.9" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="222.4" x2="299.0" y2="241.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="228.9" width="9" height="5.2" fill="#c00"/>
|
||||
<line x1="314.0" y1="214.6" x2="314.0" y2="241.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="221.1" width="9" height="13.0" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="206.8" x2="329.0" y2="228.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="213.3" width="9" height="7.8" fill="#188a3e"/>
|
||||
<line x1="24" y1="374.7" x2="416" y2="374.7" stroke="#888"/>
|
||||
<rect x="39.5" y="338.7" width="9" height="36.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="340.0" width="9" height="34.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="340.7" width="9" height="34.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="342.0" width="9" height="32.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="342.7" width="9" height="32.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="343.4" width="9" height="31.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="344.0" width="9" height="30.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="344.7" width="9" height="30.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="345.4" width="9" height="29.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="346.0" width="9" height="28.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="346.7" width="9" height="28.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="344.7" width="9" height="30.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="332.0" width="9" height="42.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="314.7" width="9" height="60.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.5" y="325.4" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="264.5" y="328.0" width="9" height="46.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="330.7" width="9" height="44.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="294.5" y="333.4" width="9" height="41.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="334.7" width="9" height="40.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="336.0" width="9" height="38.7" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="308.7" fill="#555">volum</text>
|
||||
<text x="224.0" y="302.7" fill="#b06000" text-anchor="middle" style="font-weight:bold">hammer la nivel (low 41.90)</text>
|
||||
<text x="239.0" y="220.2" fill="#188a3e" text-anchor="middle" style="font-weight:bold">acumulare</text>
|
||||
<text x="24" y="405" fill="#555">bara 13: 42.95 > high 42.90, pe volum peste medie.</text>
|
||||
</g><g transform="translate(462,38)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Mediocru — hammer în mijloc de range, fără nivel</text>
|
||||
<line x1="24" y1="153.5" x2="416" y2="153.5" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="147.5" fill="#777">fără nivel apropiat (suport 42.50 la −3,6 %)</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="80.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="62.5" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="56.0" x2="59.0" y2="88.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="62.5" width="9" height="18.2" fill="#c00"/>
|
||||
<line x1="74.0" y1="61.2" x2="74.0" y2="88.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="67.7" width="9" height="13.0" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="61.2" x2="89.0" y2="93.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="67.7" width="9" height="18.2" fill="#c00"/>
|
||||
<line x1="104.0" y1="66.4" x2="104.0" y2="93.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="72.9" width="9" height="13.0" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="66.4" x2="119.0" y2="98.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="72.9" width="9" height="18.2" fill="#c00"/>
|
||||
<line x1="134.0" y1="71.6" x2="134.0" y2="98.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="78.1" width="9" height="13.0" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="71.6" x2="149.0" y2="101.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="78.1" width="9" height="15.6" fill="#c00"/>
|
||||
<line x1="164.0" y1="76.8" x2="164.0" y2="101.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="83.3" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="76.8" x2="179.0" y2="104.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="83.3" width="9" height="13.0" fill="#c00"/>
|
||||
<line x1="194.0" y1="79.4" x2="194.0" y2="104.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="85.9" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="79.4" x2="209.0" y2="96.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="85.9" width="9" height="2.6" fill="#c00"/>
|
||||
<line x1="224.0" y1="85.9" x2="224.0" y2="111.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="85.9" width="9" height="2.6" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="79.4" x2="239.0" y2="91.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="84.6" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="78.1" x2="254.0" y2="98.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="84.6" width="9" height="6.5" fill="#c00"/>
|
||||
<line x1="269.0" y1="84.6" x2="269.0" y2="104.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="91.1" width="9" height="5.2" fill="#c00"/>
|
||||
<line x1="284.0" y1="89.8" x2="284.0" y2="111.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="96.3" width="9" height="7.8" fill="#c00"/>
|
||||
<line x1="299.0" y1="97.6" x2="299.0" y2="117.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="104.1" width="9" height="5.2" fill="#c00"/>
|
||||
<line x1="314.0" y1="102.8" x2="314.0" y2="122.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="109.3" width="9" height="5.2" fill="#c00"/>
|
||||
<line x1="329.0" y1="108.0" x2="329.0" y2="127.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="114.5" width="9" height="5.2" fill="#c00"/>
|
||||
<line x1="24" y1="239.5" x2="416" y2="239.5" stroke="#888"/>
|
||||
<rect x="39.5" y="183.8" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="185.9" width="9" height="53.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="182.7" width="9" height="56.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="187.0" width="9" height="52.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="184.9" width="9" height="54.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="114.5" y="188.1" width="9" height="51.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="183.8" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="185.9" width="9" height="53.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="187.0" width="9" height="52.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="184.9" width="9" height="54.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="185.9" width="9" height="53.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.5" y="187.0" width="9" height="52.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="179.5" width="9" height="60.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="206.3" width="9" height="33.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.5" y="183.8" width="9" height="55.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="185.9" width="9" height="53.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="188.1" width="9" height="51.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="184.9" width="9" height="54.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="187.0" width="9" height="52.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.5" y="189.1" width="9" height="50.4" fill="#c00" opacity="0.55"/>
|
||||
<text x="24" y="173.5" fill="#555">volum</text>
|
||||
<text x="224.0" y="131.9" fill="#b06000" text-anchor="middle" style="font-weight:bold">hammer în mijloc de range</text>
|
||||
<text x="239.0" y="69.4" fill="#c00" text-anchor="middle" style="font-weight:bold">volum slab</text>
|
||||
<text x="24" y="270" fill="#555">bara 13: 45.15 > 45.10, dar pe volum slab.</text>
|
||||
</g><text x="450" y="479" fill="#555" text-anchor="middle" style="font-size:10px">Reguli din curs: 26_2025-06-11.md:701-703 (locația la nivel), 05_patternuri_candele.md:77-80, 05_volum.md:2169.</text><text x="450" y="493" fill="#555" text-anchor="middle" style="font-size:10px">Nivelurile 42.00 / 42.50 și cifrele de preț sunt construite (materialul nu dă prețuri pentru acest contrast).</text></svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
177
gen/strategii/hammer_mbt10_bh_labu.py
Normal file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""LABU — buy hammer la suportul monthly, +20 % în jumătate de lună (Nivel 2.5).
|
||||
|
||||
Citește `gen/strategii/hammer_mbt10_bh_labu.spec.txt` și scrie
|
||||
`gen/strategii/hammer_mbt10_bh_labu.svg`: 38 de candele daily, downtrend +
|
||||
consolidare până pe suportul monthly 80.00, hammer chiar pe nivel
|
||||
(o 80.80 / h 80.90 / l 79.60 / c 80.90), confirmarea de la 81.30 (close peste
|
||||
high-ul hammer-ului) și urcarea în ~11 bare până la 97.56 = +20,0 % față de
|
||||
intrare.
|
||||
|
||||
REAL din curs (`18_2025-09-23.md:721-726`): că e buy hammer la suportul
|
||||
monthly și că a dat +20 % în jumătate de lună. CONSTRUITE de sumarizator:
|
||||
suportul 80.00, cifrele hammer-ului (80.80/80.90/79.60), intrarea 81.30, SL
|
||||
79.60 și seria de prețuri — materialul nu dă entry, SL sau TP pentru cazul
|
||||
ăsta. Legenda din desen spune exact asta.
|
||||
|
||||
Rulare: python3 gen/strategii/hammer_mbt10_bh_labu.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, MA, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
# --- nivelurile din specificație (toate CONSTRUITE) ------------------------
|
||||
SUPPORT = 80.00 # suport monthly (construit)
|
||||
HAMMER_H = 80.90 # high-ul hammer-ului = close (H=C)
|
||||
HAMMER_L = 79.60 # low-ul hammer-ului = SL propus
|
||||
ENTRY = 81.30 # intrare = close-ul confirmării, peste 80.90 (construit)
|
||||
SL = 79.60 # SL sub low-ul hammer-ului (construit)
|
||||
PEAK = 97.56 # max close de după intrare: 97.56 / 81.30 = +20,0 % (real)
|
||||
|
||||
# --- seria de close-uri din specificație (38 bare, o = close precedent) ----
|
||||
closes = [
|
||||
# downtrend + consolidare 0-22
|
||||
92.00, 90.50, 91.20, 89.40, 88.10, 88.60, 87.00, 85.80, 86.30, 84.90,
|
||||
83.60, 82.40, 83.00, 81.80, 80.70, 81.20, 80.20, 80.80, 81.50, 80.40,
|
||||
80.10, 80.60, 80.80,
|
||||
80.90, # 23 = hammer: o 80.80, c 80.90, h 80.90, l 79.60
|
||||
81.30, # 24 = confirmare, close > high[23] = intrare
|
||||
# 25..37 = 13 bare de urcare; max close 97.56 / 81.30 - 1 = +20,0 %
|
||||
81.90, 82.60, 83.40, 84.20, 85.10, 86.00, 87.00, 88.10, 89.30, 90.60,
|
||||
92.00, 93.50, 97.56,
|
||||
]
|
||||
assert len(closes) == 38, f"seria are {len(closes)} bare, spec cere 38"
|
||||
|
||||
# wick-uri: (up, down). Restul (0.30, 0.40); hammer-ul are fitil jos 1.20 și
|
||||
# zero fitil sus ca low-ul/high-urile cerute de asserts să iasă exact.
|
||||
wicks = [(0.30, 0.40)] * len(closes)
|
||||
wicks[23] = (0.00, 1.20) # hammer: h 80.90, l 79.60
|
||||
|
||||
# volume construite (nu sunt cifre de curs, doar panoul de volum cerut de
|
||||
# stil): vânzare în creștere spre suport, explozie la hammer + confirmare.
|
||||
vols = [
|
||||
52, 50, 47, 49, 45, 46, 43, 45, 41, 43,
|
||||
44, 47, 42, 45, 48, 46, 50, 44, 43, 46,
|
||||
51, 49, 55, 88, 84,
|
||||
70, 62, 58, 54, 52, 50, 48, 46, 45, 47,
|
||||
55, 62, 72,
|
||||
]
|
||||
assert len(vols) == len(closes), f"volume {len(vols)} != bare {len(closes)}"
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=94.00)
|
||||
assert abs(cds[0].o - 94.00) < 1e-9 and cds[0].c < cds[0].o, "prima bară nu e roșie"
|
||||
for a, b in zip(cds, cds[1:]):
|
||||
assert abs(a.c - b.o) < 1e-9, "open != close-ul barei anterioare"
|
||||
|
||||
# --- assert-urile din specificație (toate, ca assert reale) ----------------
|
||||
# 1. hammer-ul stă pe suportul monthly 80.00
|
||||
assert cds[23].l == 79.60 <= SUPPORT <= cds[23].h == HAMMER_H, \
|
||||
f"hammer 79.60 <= 80.00 <= 80.90 nu iese: {cds[23].l}/{cds[23].h}"
|
||||
|
||||
# 2. forma hammer-ului: o = close[22] = 80.80, corp 0.10, fitil jos 1.20,
|
||||
# 1.20 >= 2 * 0.10, close == high (fără fitil sus)
|
||||
assert cds[23].o == cds[22].c == 80.80, "open[23] nu e close[22] = 80.80"
|
||||
body = abs(cds[23].c - cds[23].o)
|
||||
lower_wick = min(cds[23].o, cds[23].c) - cds[23].l
|
||||
upper_wick = cds[23].h - max(cds[23].o, cds[23].c)
|
||||
assert abs(body - 0.10) < 1e-9, f"corpul hammer-ului e {body}, nu 0.10"
|
||||
assert abs(lower_wick - 1.20) < 1e-9, f"fitilul jos e {lower_wick}, nu 1.20"
|
||||
assert abs(upper_wick) < 1e-9, f"hammer-ul are fitil sus {upper_wick}"
|
||||
assert lower_wick >= 2 * body, f"1.20 < 2 * {body} (nu e hammer valid)"
|
||||
assert cds[23].c == cds[23].h == HAMMER_H, "close[23] != high[23] == 80.90"
|
||||
|
||||
# 3. confirmarea: close[24] == 81.30 == intrare și > high[23] == 80.90
|
||||
assert abs(cds[24].c - ENTRY) < 1e-9, f"close[24]={cds[24].c} != intrare 81.30"
|
||||
assert cds[24].c > cds[23].h == HAMMER_H, "confirmarea nu închide peste 80.90"
|
||||
|
||||
# 4. SL = min(low[22:25]) = low-ul hammer-ului = 79.60
|
||||
min_22_25 = min(c.l for c in cds[22:25])
|
||||
assert abs(min_22_25 - SL) < 1e-9, f"min low[22:25]={min_22_25} != SL {SL}"
|
||||
assert abs(SL - cds[23].l) < 1e-9, "SL nu e low-ul hammer-ului"
|
||||
|
||||
# 5. procentul REAL al cursului: +20,0 % de la intrare la vârf
|
||||
peak = max(c.c for c in cds[25:38])
|
||||
pct = round((peak / ENTRY - 1) * 100, 1)
|
||||
assert abs(peak - PEAK) < 1e-9, f"max close[25:38]={peak} != 97.56"
|
||||
assert pct == 20.0, f"procentul iese {pct}, nu +20,0 %"
|
||||
assert abs(peak / ENTRY - 1.20) < 1e-9, "97.56 / 81.30 nu e exact 1.20"
|
||||
|
||||
# 6. downtrend/consolidare înainte de hammer
|
||||
assert cds[23].c < cds[0].c - 8, f"close[23]={cds[23].c} nu e cu 8 sub close[0]"
|
||||
|
||||
# 7. ordinea nivelurilor
|
||||
assert SL < ENTRY < peak, f"SL/entry/vârf: {SL}/{ENTRY}/{peak}"
|
||||
|
||||
# --- verificarea serii (stil + geometrie) ----------------------------------
|
||||
entry = cds[24].c
|
||||
w = 760
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[
|
||||
Level(SUPPORT, "suport monthly 80.00", "#777",
|
||||
dash="4 3", label_at="left"),
|
||||
Level(SL, "SL sub low-ul hammer-ului 79.60 (construit)", "#c00",
|
||||
label_at="right"),
|
||||
Level(ENTRY, "intrare 81.30 (construit)", "#188a3e", label_at="right"),
|
||||
],
|
||||
marks=[
|
||||
Mark(23, "hammer 80.90 / 79.60 (construit)", "#b06000", "below"),
|
||||
Mark(37, f"+20 % (cifra cursului): {peak:.2f} / {ENTRY:.2f}",
|
||||
"#188a3e", "above"),
|
||||
],
|
||||
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
|
||||
scale=8.0, gap=15, body_w=9, left=44, width=w, vol_h=70,
|
||||
vol_label="volum", title="LABU — daily",
|
||||
caption="nivelurile de preț sunt construite de sumarizator.",
|
||||
)
|
||||
# note pe bare: explicațiile hammer-ului/confirmării și legenda obligatorie
|
||||
# (textul lung de nivel ar intra peste candele, deci stă în blocul de jos).
|
||||
cds[0].note = ("bara 23 = hammer: o 80.80, h 80.90, l 79.60, c 80.90 — "
|
||||
"corp 0.10, fitil jos 1.20 (H=C), pe suportul 80.00")
|
||||
cds[1].note = ("bara 24 = confirmare: close 81.30 — close peste high-ul "
|
||||
"hammer-ului 80.90 = intrare 81.30 (construit)")
|
||||
cds[2].note = ("Procentul (+20 % în jumătate de lună) și contextul (suport "
|
||||
"monthly) sunt ale cursului (18_2025-09-23.md:721-726);")
|
||||
|
||||
svg = ch.render()
|
||||
assert "\n\n" not in svg and "<svg" in svg, "SVG invalid (linie goală?)"
|
||||
|
||||
# --- citirea SVG-ului: etichete în panză, fără suprapuneri -----------------
|
||||
TEXT = re.compile(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', re.S)
|
||||
H = float(re.search(r'viewBox="0 0 (\d+) ([\d.]+)"', svg).group(2))
|
||||
labels = []
|
||||
for m in TEXT.finditer(svg):
|
||||
x, y, attrs, s = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4)
|
||||
anchor = "end" if 'text-anchor="end"' in attrs else \
|
||||
("middle" if 'text-anchor="middle"' in attrs else "start")
|
||||
bold = "bold" in attrs
|
||||
wpx = len(s) * 12 * (0.62 if bold else 0.52)
|
||||
left = x - wpx if anchor == "end" else (x - wpx / 2 if anchor == "middle" else x)
|
||||
labels.append((left, left + wpx, y, s))
|
||||
|
||||
for left, right, y, s in labels:
|
||||
assert left >= -2 and right <= w + 2, f"text iese din panză: {s!r} [{left:.0f},{right:.0f}]"
|
||||
assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y:.0f})"
|
||||
|
||||
for i in range(len(labels)):
|
||||
li, ri, yi, si = labels[i]
|
||||
for j in range(i + 1, len(labels)):
|
||||
lj, rj, yj, sj = labels[j]
|
||||
if abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}\n {labels[i]}\n {labels[j]}")
|
||||
|
||||
# candelele sunt o serie continuă, lipită
|
||||
xs = [ch._cx(i) for i in range(len(cds))]
|
||||
assert xs[-1] + ch.body_w / 2 < w, f"ultima candelă iese din panză (x={xs[-1]})"
|
||||
assert all(xs[i + 1] - xs[i] == ch.gap for i in range(len(xs) - 1)), "serie cu goluri"
|
||||
|
||||
print(f"LABU: {len(cds)} bare, hammer {cds[23].l:.2f}/{cds[23].h:.2f} pe suport "
|
||||
f"{SUPPORT:.2f}, intrare {entry:.2f}, SL {cds[23].l:.2f}, vârf {peak:.2f}")
|
||||
print(f"procent: {peak:.2f} / {entry:.2f} - 1 = +{pct:.1f} % (cifra cursului)")
|
||||
print(f"scris {OUT} ({w}x{H:.0f}); {len(labels)} etichete verificate, fără suprapuneri")
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
133
gen/strategii/hammer_mbt10_bh_labu.svg
Normal file
@@ -0,0 +1,133 @@
|
||||
<svg viewBox="0 0 760 391" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">LABU — daily</text>
|
||||
<line x1="24" y1="198.9" x2="746" y2="198.9" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="192.9" fill="#777">suport monthly 80.00</text>
|
||||
<line x1="24" y1="202.1" x2="746" y2="202.1" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="732" y="196.1" fill="#c00" text-anchor="end">SL sub low-ul hammer-ului 79.60 (construit)</text>
|
||||
<line x1="24" y1="188.5" x2="746" y2="188.5" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="732" y="182.5" fill="#188a3e" text-anchor="end">intrare 81.30 (construit)</text>
|
||||
<polyline points="329.0,159.1 344.0,163.9 359.0,167.8 374.0,172.0 389.0,175.4 404.0,178.1 419.0,180.8 434.0,182.6 449.0,183.5 464.0,184.4 479.0,184.3 494.0,183.3 509.0,181.5 524.0,179.4 539.0,176.4 554.0,172.5 569.0,168.2 584.0,162.8 599.0,156.1" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="605" y="150" fill="#b06000">SMA-20</text>
|
||||
<line x1="44.0" y1="84.5" x2="44.0" y2="106.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="86.9" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="59.0" y1="100.5" x2="59.0" y2="118.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="102.9" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="74.0" y1="106.9" x2="74.0" y2="118.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="109.3" width="9" height="5.6" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="106.9" x2="89.0" y2="126.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="109.3" width="9" height="14.4" fill="#c00"/>
|
||||
<line x1="104.0" y1="121.3" x2="104.0" y2="137.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="123.7" width="9" height="10.4" fill="#c00"/>
|
||||
<line x1="119.0" y1="127.7" x2="119.0" y2="137.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="130.1" width="9" height="4.0" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="127.7" x2="134.0" y2="146.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="130.1" width="9" height="12.8" fill="#c00"/>
|
||||
<line x1="149.0" y1="140.5" x2="149.0" y2="155.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="142.9" width="9" height="9.6" fill="#c00"/>
|
||||
<line x1="164.0" y1="146.1" x2="164.0" y2="155.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="148.5" width="9" height="4.0" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="146.1" x2="179.0" y2="162.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="148.5" width="9" height="11.2" fill="#c00"/>
|
||||
<line x1="194.0" y1="157.3" x2="194.0" y2="173.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="159.7" width="9" height="10.4" fill="#c00"/>
|
||||
<line x1="209.0" y1="167.7" x2="209.0" y2="182.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="170.1" width="9" height="9.6" fill="#c00"/>
|
||||
<line x1="224.0" y1="172.5" x2="224.0" y2="182.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="174.9" width="9" height="4.8" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="172.5" x2="239.0" y2="187.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="174.9" width="9" height="9.6" fill="#c00"/>
|
||||
<line x1="254.0" y1="182.1" x2="254.0" y2="196.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="184.5" width="9" height="8.8" fill="#c00"/>
|
||||
<line x1="269.0" y1="186.9" x2="269.0" y2="196.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="189.3" width="9" height="4.0" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="186.9" x2="284.0" y2="200.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="189.3" width="9" height="8.0" fill="#c00"/>
|
||||
<line x1="299.0" y1="190.1" x2="299.0" y2="200.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="192.5" width="9" height="4.8" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="184.5" x2="314.0" y2="195.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="186.9" width="9" height="5.6" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="184.5" x2="329.0" y2="198.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="186.9" width="9" height="8.8" fill="#c00"/>
|
||||
<line x1="344.0" y1="193.3" x2="344.0" y2="201.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="195.7" width="9" height="2.4" fill="#c00"/>
|
||||
<line x1="359.0" y1="191.7" x2="359.0" y2="201.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="194.1" width="9" height="4.0" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="190.1" x2="374.0" y2="197.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="192.5" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="191.7" x2="389.0" y2="202.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="191.7" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="404.0" y1="186.1" x2="404.0" y2="194.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="188.5" width="9" height="3.2" fill="#188a3e"/>
|
||||
<line x1="419.0" y1="181.3" x2="419.0" y2="191.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="183.7" width="9" height="4.8" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="175.7" x2="434.0" y2="186.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="178.1" width="9" height="5.6" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="169.3" x2="449.0" y2="181.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="171.7" width="9" height="6.4" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="162.9" x2="464.0" y2="174.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="165.3" width="9" height="6.4" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="155.7" x2="479.0" y2="168.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="158.1" width="9" height="7.2" fill="#188a3e"/>
|
||||
<line x1="494.0" y1="148.5" x2="494.0" y2="161.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="150.9" width="9" height="7.2" fill="#188a3e"/>
|
||||
<line x1="509.0" y1="140.5" x2="509.0" y2="154.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="142.9" width="9" height="8.0" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="131.7" x2="524.0" y2="146.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="134.1" width="9" height="8.8" fill="#188a3e"/>
|
||||
<line x1="539.0" y1="122.1" x2="539.0" y2="137.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="534.5" y="124.5" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="554.0" y1="111.7" x2="554.0" y2="127.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="549.5" y="114.1" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="569.0" y1="100.5" x2="569.0" y2="117.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="564.5" y="102.9" width="9" height="11.2" fill="#188a3e"/>
|
||||
<line x1="584.0" y1="88.5" x2="584.0" y2="106.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="579.5" y="90.9" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="599.0" y1="56.0" x2="599.0" y2="94.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="594.5" y="58.4" width="9" height="32.5" fill="#188a3e"/>
|
||||
<line x1="24" y1="298.1" x2="746" y2="298.1" stroke="#888"/>
|
||||
<rect x="39.5" y="256.7" width="9" height="41.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="258.3" width="9" height="39.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="260.7" width="9" height="37.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="259.1" width="9" height="39.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="262.3" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="261.5" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="263.9" width="9" height="34.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="262.3" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="265.5" width="9" height="32.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="263.9" width="9" height="34.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="263.1" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="260.7" width="9" height="37.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="264.7" width="9" height="33.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="262.3" width="9" height="35.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="259.9" width="9" height="38.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="261.5" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="258.3" width="9" height="39.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="263.1" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="263.9" width="9" height="34.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="261.5" width="9" height="36.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="339.5" y="257.5" width="9" height="40.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="354.5" y="259.1" width="9" height="39.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="254.3" width="9" height="43.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="228.1" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="399.5" y="231.3" width="9" height="66.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="414.5" y="242.4" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="248.8" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="251.9" width="9" height="46.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="255.1" width="9" height="43.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.5" y="256.7" width="9" height="41.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="489.5" y="258.3" width="9" height="39.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="504.5" y="259.9" width="9" height="38.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="261.5" width="9" height="36.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="534.5" y="262.3" width="9" height="35.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="549.5" y="260.7" width="9" height="37.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="564.5" y="254.3" width="9" height="43.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="579.5" y="248.8" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="594.5" y="240.8" width="9" height="57.3" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="222.1" fill="#555">volum</text>
|
||||
<text x="389.0" y="216.1" fill="#b06000" text-anchor="middle" style="font-weight:bold">hammer 80.90 / 79.60 (construit)</text>
|
||||
<text x="599.0" y="46.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">+20 % (cifra cursului): 97.56 / 81.30</text>
|
||||
<text x="24" y="320.1" fill="#c00">bara 23 = hammer: o 80.80, h 80.90, l 79.60, c 80.90 — corp 0.10, fitil jos 1.20 (H=C), pe suportul 80.00</text>
|
||||
<text x="24" y="337.1" fill="#c00">bara 24 = confirmare: close 81.30 — close peste high-ul hammer-ului 80.90 = intrare 81.30 (construit)</text>
|
||||
<text x="24" y="354.1" fill="#188a3e">Procentul (+20 % în jumătate de lună) și contextul (suport monthly) sunt ale cursului (18_2025-09-23.md:721-726);</text>
|
||||
<text x="24" y="379" fill="#555">nivelurile de preț sunt construite de sumarizator.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
266
gen/strategii/hammer_mbt10_harta.py
Normal file
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Hartă de orientare: buy hammer (public) vs. MBT-10 (cutie neagră), același
|
||||
trigger de bottom.
|
||||
|
||||
Pentru `strategii/06_hammer_mbt10.md`, marcajul `<!-- DIAGRAMA: harta -->`.
|
||||
Nu e grafic de preț (spec: TIP CONCEPT, CHEIE harta) — SVG scris de mână,
|
||||
doar stdlib; nu se folosește svg_candles / pchart.
|
||||
|
||||
Ce arată (toate citatele și trimiterile vin din spec, nu se inventează):
|
||||
- bara comună de sus: setup-urile de fond spun UNDE, trigger-ul de candelă
|
||||
spune CÂND (`02_mbt10.md:9-11`, `43-49`);
|
||||
- două cutii alăturate: BUY HAMMER (pattern standard, public) și MBT-10
|
||||
(cutie neagră, indicator proprietar), fiecare cu regulile ei și cu
|
||||
trimiterile de rigoare;
|
||||
- săgeata de jos spre caseta finală: ce rămâne utilizabil pe BVB.
|
||||
|
||||
Verdictele de BVB din cutii sunt judecata mea (secțiunea Transfer), marcate
|
||||
ca atare în legendă, exact cum cere specul.
|
||||
|
||||
Rulare: python3 gen/strategii/hammer_mbt10_harta.py -> hammer_mbt10_harta.svg
|
||||
"""
|
||||
from pathlib import Path
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
W, H = 700, 610
|
||||
BLACK, GRAY, BLUE, GREEN, RED, ORANGE, WHITE = (
|
||||
"#000000", "#777777", "#1a5fb4", "#188a3e", "#c00", "#b06000", "#ffffff")
|
||||
BORDER = "#333333"
|
||||
|
||||
# ---------------------------------------------------------------- geometrie
|
||||
BAR_X0, BAR_X1, BAR_Y, BAR_H = 40.0, 660.0, 82.0, 76.0
|
||||
|
||||
BOX_W = 302.0
|
||||
BOX_LX, BOX_RX, BOX_Y = 40.0, 358.0, 190.0
|
||||
BOX_H = 256.0
|
||||
PAD = 16.0
|
||||
|
||||
ROW0 = BOX_Y + 56.0 # baseline-ul primei reguli
|
||||
ROW_H = 52.0
|
||||
VERDICT_SEP = BOX_Y + 216.0
|
||||
VERDICT_Y = VERDICT_SEP + 26.0
|
||||
|
||||
BUS_Y = 460.0 # linia orizontală de unire a celor două cutii
|
||||
TRI_TIP = 500.0
|
||||
FINAL_X0, FINAL_Y, FINAL_W, FINAL_H = 110.0, 500.0, 480.0, 56.0
|
||||
|
||||
TEXTS = [] # (x, y, text, size, weight, anchor) pentru verificare
|
||||
|
||||
|
||||
def txt(x, y, s, size=13, weight="400", fill=BLACK, anchor="middle"):
|
||||
TEXTS.append((x, y, s, size, weight, anchor))
|
||||
return (
|
||||
f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" font-weight="{weight}" '
|
||||
f'fill="{fill}" text-anchor="{anchor}">{escape(s)}</text>'
|
||||
)
|
||||
|
||||
|
||||
def line(x1, y1, x2, y2, stroke=GRAY, width=1.0, dash=None):
|
||||
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||
return (f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" '
|
||||
f'stroke="{stroke}" stroke-width="{width}"{d}/>')
|
||||
|
||||
|
||||
def rect(x, y, w, h, fill=WHITE, stroke=GRAY, width=1.2):
|
||||
return (f'<rect x="{x:.1f}" y="{y:.1f}" width="{w:.1f}" height="{h:.1f}" '
|
||||
f'rx="8" fill="{fill}" stroke="{stroke}" stroke-width="{width}"/>')
|
||||
|
||||
|
||||
def box(bx, title, title_fill, rows, verdict, verdict_fill, fill):
|
||||
"""O cutie cu chenar: titlu, reguli (linii + trimitere), verdict."""
|
||||
x = bx + PAD
|
||||
area = BOX_W - 2 * PAD
|
||||
out = [rect(bx, BOX_Y, BOX_W, BOX_H, fill=fill, stroke=title_fill, width=1.4)]
|
||||
out.append(txt(x, BOX_Y + 26, title, 15, "700", title_fill, "start"))
|
||||
out.append(line(bx + PAD, BOX_Y + 38, bx + BOX_W - PAD, BOX_Y + 38,
|
||||
title_fill, 1.0))
|
||||
for i, (mains, ref) in enumerate(rows):
|
||||
top = ROW0 + i * ROW_H
|
||||
for k, m in enumerate(mains):
|
||||
out.append(txt(x, top + 14 + k * 17, m, 13, "400", BLACK, "start"))
|
||||
if ref is not None:
|
||||
out.append(txt(x, top + 48, ref, 13, "400", GRAY, "start"))
|
||||
out.append(line(bx + PAD, VERDICT_SEP, bx + BOX_W - PAD, VERDICT_SEP,
|
||||
verdict_fill, 1.2))
|
||||
out.append(txt(x, VERDICT_Y, verdict, 13, "700", verdict_fill, "start"))
|
||||
return out, area
|
||||
|
||||
|
||||
def build():
|
||||
p = [
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
|
||||
f'width="{W}" height="{H}" role="img" '
|
||||
f'aria-label="Harta capitolului: acelasi trigger de bottom, doua naturi '
|
||||
f'- buy hammer, pattern standard public, si MBT-10, cutie neagra cu '
|
||||
f'indicator proprietar">',
|
||||
'<g font-family="system-ui, sans-serif">',
|
||||
f'<rect x="0" y="0" width="{W}" height="{H}" fill="{WHITE}"/>',
|
||||
txt(W / 2, 34, "Același trigger de bottom, două naturi", 19, "700", BLACK),
|
||||
txt(W / 2, 58, "buy hammer (public) vs. MBT-10 (cutie neagră)",
|
||||
14, "400", GRAY),
|
||||
]
|
||||
|
||||
# ------- zona 1: bara comună
|
||||
p.append(rect(BAR_X0, BAR_Y, BAR_X1 - BAR_X0, BAR_H, fill="#f2f4f7",
|
||||
stroke=GRAY, width=1.2))
|
||||
p.append(txt(BAR_X0 + PAD, BAR_Y + 26, "Trigger de bottom: unde + când",
|
||||
15, "700", BLUE, "start"))
|
||||
p.append(txt(BAR_X0 + PAD, BAR_Y + 48,
|
||||
"Setup-urile de fond — suport, locul crimei, double bottom, buy "
|
||||
"doji,", 13, "400", BLACK, "start"))
|
||||
p.append(txt(BAR_X0 + PAD, BAR_Y + 66,
|
||||
"divergență de volum — spun UNDE. Trigger-ul de candelă spune "
|
||||
"CÂND.", 13, "400", BLACK, "start"))
|
||||
p.append(txt(BAR_X1 - PAD, BAR_Y + 26, "02_mbt10.md:9-11, 43-49",
|
||||
13, "400", GRAY, "end"))
|
||||
|
||||
# ------- conector bară -> cutii (un cot, fără săgeți redundante)
|
||||
lx = BOX_LX + BOX_W / 2
|
||||
rx = BOX_RX + BOX_W / 2
|
||||
p.append(line(W / 2, BAR_Y + BAR_H, W / 2, 172.0, GRAY, 1.2))
|
||||
p.append(line(lx, 172.0, rx, 172.0, GRAY, 1.2))
|
||||
p.append(line(lx, 172.0, lx, BOX_Y, GRAY, 1.2))
|
||||
p.append(line(rx, 172.0, rx, BOX_Y, GRAY, 1.2))
|
||||
|
||||
# ------- zona 2: cele două cutii
|
||||
left_rows = [
|
||||
(['„formă de candelă + downtrend”'], "05_patternuri_candele.md:77-88"),
|
||||
(['„trigger: close peste', 'high-ul hammer-ului”'],
|
||||
"05_patternuri_candele.md:99-124"),
|
||||
(['„fără niciun indicator proprietar”'], None),
|
||||
]
|
||||
right_rows = [
|
||||
(['„formula nu e publică nicăieri”'], "02_mbt10.md:21-39"),
|
||||
(['„reconstituibil doar:', 'close peste pivotul daily”'],
|
||||
"02_mbt10.md:179-188"),
|
||||
(['„scanner + indicator proprietar”'], "02_mbt10.md:853-863"),
|
||||
]
|
||||
left_svg, left_area = box(
|
||||
BOX_LX, "BUY HAMMER — pattern standard", BLUE, left_rows,
|
||||
"„pe BVB: se poate rula long-only”", GREEN, "#eef4fd")
|
||||
right_svg, right_area = box(
|
||||
BOX_RX, "MBT-10 — cutie neagră", BORDER, right_rows,
|
||||
"„pe BVB: nu, ca sistem”", RED, "#f5f5f5")
|
||||
p += left_svg + right_svg
|
||||
|
||||
# ------- zona 3: ambele cutii -> caseta finală
|
||||
p.append(line(lx, BOX_Y + BOX_H, lx, BUS_Y, GRAY, 1.2))
|
||||
p.append(line(rx, BOX_Y + BOX_H, rx, BUS_Y, GRAY, 1.2))
|
||||
p.append(line(lx, BUS_Y, rx, BUS_Y, GRAY, 1.2))
|
||||
p.append(line(W / 2, BUS_Y, W / 2, TRI_TIP - 11, GRAY, 1.4))
|
||||
p.append(f'<polygon points="{W/2:.1f},{TRI_TIP:.1f} '
|
||||
f'{W/2-6:.1f},{TRI_TIP-11:.1f} {W/2+6:.1f},{TRI_TIP-11:.1f}" '
|
||||
f'fill="{GRAY}"/>')
|
||||
|
||||
p.append(rect(FINAL_X0, FINAL_Y, FINAL_W, FINAL_H, fill="#fdf0e0",
|
||||
stroke=ORANGE, width=1.6))
|
||||
p.append(txt(W / 2, FINAL_Y + 24,
|
||||
"Ce rămâne pe BVB: hammer la un nivel major", 14, "700",
|
||||
BLACK))
|
||||
p.append(txt(W / 2, FINAL_Y + 44, "+ close peste PPD", 14, "700", BLACK))
|
||||
|
||||
# ------- legendă
|
||||
p.append(txt(W / 2, 578,
|
||||
"Regulile = materialul cursului (cu trimiteri); verdictul de "
|
||||
"BVB = judecata mea", 13, "400", GRAY))
|
||||
p.append(txt(W / 2, 596, "din secțiunea Transfer.", 13, "400", GRAY))
|
||||
|
||||
p.append('</g></svg>')
|
||||
return "\n".join(p) + "\n", left_area, right_area
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- verificare
|
||||
def est_w(s, size, weight):
|
||||
factor = 0.62 if weight == "700" else 0.56
|
||||
return len(s) * size * factor
|
||||
|
||||
|
||||
def box_of(x, y):
|
||||
for bx in (BOX_LX, BOX_RX):
|
||||
if bx <= x <= bx + BOX_W and BOX_Y <= y <= BOX_Y + BOX_H:
|
||||
return bx
|
||||
return None
|
||||
|
||||
|
||||
def verify(svg, left_area, right_area):
|
||||
# 1. geometrie: zonele nu se calcă
|
||||
assert BAR_Y + BAR_H < BOX_Y, "bara comună intră în cutii"
|
||||
assert BOX_Y + BOX_H < BUS_Y < TRI_TIP <= FINAL_Y, "săgeata nu are loc"
|
||||
assert FINAL_Y + FINAL_H < 578, "caseta finală intră în legendă"
|
||||
assert left_area == right_area == BOX_W - 2 * PAD
|
||||
|
||||
# 2. toate textele în panză
|
||||
for x, y, s, size, weight, anchor in TEXTS:
|
||||
ew = est_w(s, size, weight)
|
||||
left = x - ew if anchor == "end" else (x - ew / 2 if anchor == "middle" else x)
|
||||
right = left + ew
|
||||
assert -2 <= left and right <= W + 2, f"text în afara panzei: {s!r}"
|
||||
assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y})"
|
||||
|
||||
# 3. texte care nu se suprapun
|
||||
for i in range(len(TEXTS)):
|
||||
xi, yi, si, szi, wi, ai = TEXTS[i]
|
||||
we = est_w(si, szi, wi)
|
||||
li = xi - we if ai == "end" else (xi - we / 2 if ai == "middle" else xi)
|
||||
ri = li + we
|
||||
for j in range(i + 1, len(TEXTS)):
|
||||
xj, yj, sj, szj, wj, aj = TEXTS[j]
|
||||
wej = est_w(sj, szj, wj)
|
||||
lj = xj - wej if aj == "end" else (xj - wej / 2 if aj == "middle" else xj)
|
||||
rj = lj + wej
|
||||
if abs(yi - yj) < 13 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
# 4. textele din cutii încap în cutii
|
||||
for x, y, s, size, weight, anchor in TEXTS:
|
||||
b = box_of(x, y)
|
||||
if b is not None:
|
||||
ew = est_w(s, size, weight)
|
||||
assert x + ew <= b + BOX_W - PAD + 1, f"text iese din cutie: {s!r}"
|
||||
|
||||
# 5. verdictul e sub linia lui de separare
|
||||
assert VERDICT_SEP < VERDICT_Y < BOX_Y + BOX_H
|
||||
|
||||
# 6. conținutul obligatoriu din spec
|
||||
for cheie in (
|
||||
"Trigger de bottom: unde + când",
|
||||
"Setup-urile de fond — suport, locul crimei, double bottom, buy doji,",
|
||||
"divergență de volum — spun UNDE. Trigger-ul de candelă spune CÂND.",
|
||||
"02_mbt10.md:9-11, 43-49",
|
||||
"BUY HAMMER — pattern standard",
|
||||
"„formă de candelă + downtrend”",
|
||||
"05_patternuri_candele.md:77-88",
|
||||
"„trigger: close peste",
|
||||
"high-ul hammer-ului”",
|
||||
"05_patternuri_candele.md:99-124",
|
||||
"„fără niciun indicator proprietar”",
|
||||
"„pe BVB: se poate rula long-only”",
|
||||
"MBT-10 — cutie neagră",
|
||||
"„formula nu e publică nicăieri”",
|
||||
"02_mbt10.md:21-39",
|
||||
"„reconstituibil doar:",
|
||||
"close peste pivotul daily”",
|
||||
"02_mbt10.md:179-188",
|
||||
"„scanner + indicator proprietar”",
|
||||
"02_mbt10.md:853-863",
|
||||
"„pe BVB: nu, ca sistem”",
|
||||
"Ce rămâne pe BVB: hammer la un nivel major",
|
||||
"+ close peste PPD",
|
||||
"verdictul de BVB = judecata mea",
|
||||
"din secțiunea Transfer.",
|
||||
):
|
||||
assert cheie in svg, f"lipsește din desen: {cheie!r}"
|
||||
|
||||
# 7. fără axe de preț / candele / cifre inventate
|
||||
for interzis in ("candela", "SL ", "intrare ", "TP1", "TP2", "$", "USD"):
|
||||
assert interzis not in svg, f"nu ar trebui să apară preț: {interzis!r}"
|
||||
return True
|
||||
|
||||
|
||||
SVG, LEFT_AREA, RIGHT_AREA = build()
|
||||
assert verify(SVG, LEFT_AREA, RIGHT_AREA), "verificare eșuată"
|
||||
OUT.write_text(SVG, encoding="utf-8")
|
||||
print(f"scris {OUT} ({W}x{H}), verificare OK: {len(TEXTS)} etichete, "
|
||||
f"cutii {BOX_W:g}x{BOX_H:g} la {BOX_LX:g}/{BOX_RX:g}")
|
||||
48
gen/strategii/hammer_mbt10_harta.svg
Normal file
@@ -0,0 +1,48 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 610" width="700" height="610" role="img" aria-label="Harta capitolului: acelasi trigger de bottom, doua naturi - buy hammer, pattern standard public, si MBT-10, cutie neagra cu indicator proprietar">
|
||||
<g font-family="system-ui, sans-serif">
|
||||
<rect x="0" y="0" width="700" height="610" fill="#ffffff"/>
|
||||
<text x="350.0" y="34.0" font-size="19" font-weight="700" fill="#000000" text-anchor="middle">Același trigger de bottom, două naturi</text>
|
||||
<text x="350.0" y="58.0" font-size="14" font-weight="400" fill="#777777" text-anchor="middle">buy hammer (public) vs. MBT-10 (cutie neagră)</text>
|
||||
<rect x="40.0" y="82.0" width="620.0" height="76.0" rx="8" fill="#f2f4f7" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="56.0" y="108.0" font-size="15" font-weight="700" fill="#1a5fb4" text-anchor="start">Trigger de bottom: unde + când</text>
|
||||
<text x="56.0" y="130.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">Setup-urile de fond — suport, locul crimei, double bottom, buy doji,</text>
|
||||
<text x="56.0" y="148.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">divergență de volum — spun UNDE. Trigger-ul de candelă spune CÂND.</text>
|
||||
<text x="644.0" y="108.0" font-size="13" font-weight="400" fill="#777777" text-anchor="end">02_mbt10.md:9-11, 43-49</text>
|
||||
<line x1="350.0" y1="158.0" x2="350.0" y2="172.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<line x1="191.0" y1="172.0" x2="509.0" y2="172.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<line x1="191.0" y1="172.0" x2="191.0" y2="190.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<line x1="509.0" y1="172.0" x2="509.0" y2="190.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<rect x="40.0" y="190.0" width="302.0" height="256.0" rx="8" fill="#eef4fd" stroke="#1a5fb4" stroke-width="1.4"/>
|
||||
<text x="56.0" y="216.0" font-size="15" font-weight="700" fill="#1a5fb4" text-anchor="start">BUY HAMMER — pattern standard</text>
|
||||
<line x1="56.0" y1="228.0" x2="326.0" y2="228.0" stroke="#1a5fb4" stroke-width="1.0"/>
|
||||
<text x="56.0" y="260.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">„formă de candelă + downtrend”</text>
|
||||
<text x="56.0" y="294.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">05_patternuri_candele.md:77-88</text>
|
||||
<text x="56.0" y="312.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">„trigger: close peste</text>
|
||||
<text x="56.0" y="329.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">high-ul hammer-ului”</text>
|
||||
<text x="56.0" y="346.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">05_patternuri_candele.md:99-124</text>
|
||||
<text x="56.0" y="364.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">„fără niciun indicator proprietar”</text>
|
||||
<line x1="56.0" y1="406.0" x2="326.0" y2="406.0" stroke="#188a3e" stroke-width="1.2"/>
|
||||
<text x="56.0" y="432.0" font-size="13" font-weight="700" fill="#188a3e" text-anchor="start">„pe BVB: se poate rula long-only”</text>
|
||||
<rect x="358.0" y="190.0" width="302.0" height="256.0" rx="8" fill="#f5f5f5" stroke="#333333" stroke-width="1.4"/>
|
||||
<text x="374.0" y="216.0" font-size="15" font-weight="700" fill="#333333" text-anchor="start">MBT-10 — cutie neagră</text>
|
||||
<line x1="374.0" y1="228.0" x2="644.0" y2="228.0" stroke="#333333" stroke-width="1.0"/>
|
||||
<text x="374.0" y="260.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">„formula nu e publică nicăieri”</text>
|
||||
<text x="374.0" y="294.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">02_mbt10.md:21-39</text>
|
||||
<text x="374.0" y="312.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">„reconstituibil doar:</text>
|
||||
<text x="374.0" y="329.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">close peste pivotul daily”</text>
|
||||
<text x="374.0" y="346.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">02_mbt10.md:179-188</text>
|
||||
<text x="374.0" y="364.0" font-size="13" font-weight="400" fill="#000000" text-anchor="start">„scanner + indicator proprietar”</text>
|
||||
<text x="374.0" y="398.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">02_mbt10.md:853-863</text>
|
||||
<line x1="374.0" y1="406.0" x2="644.0" y2="406.0" stroke="#c00" stroke-width="1.2"/>
|
||||
<text x="374.0" y="432.0" font-size="13" font-weight="700" fill="#c00" text-anchor="start">„pe BVB: nu, ca sistem”</text>
|
||||
<line x1="191.0" y1="446.0" x2="191.0" y2="460.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<line x1="509.0" y1="446.0" x2="509.0" y2="460.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<line x1="191.0" y1="460.0" x2="509.0" y2="460.0" stroke="#777777" stroke-width="1.2"/>
|
||||
<line x1="350.0" y1="460.0" x2="350.0" y2="489.0" stroke="#777777" stroke-width="1.4"/>
|
||||
<polygon points="350.0,500.0 344.0,489.0 356.0,489.0" fill="#777777"/>
|
||||
<rect x="110.0" y="500.0" width="480.0" height="56.0" rx="8" fill="#fdf0e0" stroke="#b06000" stroke-width="1.6"/>
|
||||
<text x="350.0" y="524.0" font-size="14" font-weight="700" fill="#000000" text-anchor="middle">Ce rămâne pe BVB: hammer la un nivel major</text>
|
||||
<text x="350.0" y="544.0" font-size="14" font-weight="700" fill="#000000" text-anchor="middle">+ close peste PPD</text>
|
||||
<text x="350.0" y="578.0" font-size="13" font-weight="400" fill="#777777" text-anchor="middle">Regulile = materialul cursului (cu trimiteri); verdictul de BVB = judecata mea</text>
|
||||
<text x="350.0" y="596.0" font-size="13" font-weight="400" fill="#777777" text-anchor="middle">din secțiunea Transfer.</text>
|
||||
</g></svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
196
gen/strategii/hammer_mbt10_mbt_ansamblu.py
Normal file
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python3
|
||||
"""MBT-10 — pullback în locul crimei + hammer, intrare la close 54.60 (ansamblu).
|
||||
|
||||
Citește `gen/strategii/hammer_mbt10_mbt_ansamblu.spec.txt` și scrie
|
||||
`gen/strategii/hammer_mbt10_mbt_ansamblu.svg`: 41 de candele daily pe exemplul
|
||||
construit al sumarizatorului (`summaries/08_ANALIZA_ZILNICA/02_mbt10.md:165-173`,
|
||||
`978-984`) — uptrend până la fostul vârf 52.10, spargere, vârf nou 58.90,
|
||||
pullback în locul crimei, hammer cu codița exact pe 52.10, iar bara următoare
|
||||
se închide în MBT-10 la 54.60 (intrare la close). SL 52.00, TP 58.90 = 1.65R.
|
||||
|
||||
Toate prețurile/nivelurile sunt cele din specificație. Seria de close-uri e
|
||||
cea dată; open-ul fiecărei bare = close-ul precedentului. MBT-10 nu se
|
||||
desenează (formula nu e publică) — se marchează doar bara la care semnalul
|
||||
s-a închis.
|
||||
|
||||
Rulare: python3 gen/strategii/hammer_mbt10_mbt_ansamblu.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
# --- nivelurile din specificație -------------------------------------------
|
||||
CRIMEA = 52.10 # locul crimei = fostul vârf spart, retestat ca suport
|
||||
HAMMER_H = 53.40 # high = close al hammer-ului
|
||||
HAMMER_L = 52.10 # codița hammer-ului, exact pe locul crimei
|
||||
ENTRY = 54.60 # close-ul barei care s-a închis în MBT-10 = intrare
|
||||
SL = 52.00 # sub codița hammer-ului și sub locul crimei
|
||||
TP = 58.90 # vârful anterior = 1.65R (risc 2.60)
|
||||
|
||||
# --- seria: 41 de close-uri, exact ca în specificație -----------------------
|
||||
closes = [
|
||||
# 0-8: uptrend care duce spre fostul vârf
|
||||
46.00, 46.80, 47.50, 48.30, 49.00, 49.80, 50.50, 51.20, 52.00,
|
||||
52.10, # 9 = fostul vârf 52.10 (high cu wick 52.40)
|
||||
# 10-20: spargerea nivelului și urcarea spre vârful nou
|
||||
51.40, 50.80, 51.50, 52.40, 53.20, 54.00, 54.80, 55.60, 56.40, 57.20,
|
||||
58.00,
|
||||
58.90, # 21 = vârf nou
|
||||
# 22-28: pullback înapoi în locul crimei
|
||||
58.20, 57.40, 56.50, 55.60, 54.70, 53.60, 52.20,
|
||||
# 29-30: hammer pe locul crimei + ziua care se închide în MBT-10
|
||||
53.40, # 29 = hammer: o 52.20, h/c 53.40, l 52.10 (H = C)
|
||||
54.60, # 30 = MBT-10 închis la close => intrare
|
||||
# 31-40: urcarea spre ținta de la vârful anterior
|
||||
55.20, 55.80, 56.40, 57.00, 57.60, 58.10, 58.60,
|
||||
58.90, # 38 = TP (vârful anterior)
|
||||
58.70, 58.30,
|
||||
]
|
||||
assert len(closes) == 41, f"seria are {len(closes)} bare, spec cere 41"
|
||||
|
||||
# wick-uri (sus, jos): restul (0.30, 0.40); excepțiile din specificație.
|
||||
# `from_closes` măsoară wick-ul de jos de la min(o, c), nu de la close: pentru
|
||||
# hammer-ul cu o 52.20 / c 53.40, ca low-ul să iasă exact 52.10, wick-ul de jos
|
||||
# e 0.10 (nu 1.30, cum dă specificația măsurând de la close).
|
||||
wicks = [(0.30, 0.40)] * 41
|
||||
wicks[9] = (0.30, 0.30) # high 52.40, low 51.70 (fostul vârf)
|
||||
wicks[28] = (0.30, 0.10) # low 52.10: pullback-ul atinge locul crimei
|
||||
wicks[29] = (0.00, 0.10) # hammer: high = close = 53.40, low = 52.10
|
||||
# bara 30: specificația dă (0.20, 0.20) cu „low 54.40"; 54.40 ar fi peste
|
||||
# open-ul 53.40, deci imposibil. Cu (0.20, 0.20) low-ul iese 53.20; nu e
|
||||
# etichetat nicăieri și nu intră în niciun assert.
|
||||
wicks[30] = (0.20, 0.20)
|
||||
|
||||
# volum „normal", fără argument de volum (specificația o spune explicit):
|
||||
# doar panoul de volum cerut de stil, fără spike-uri pe hammer/confirmare.
|
||||
vols = [
|
||||
50, 49, 51, 50, 52, 51, 50, 52, 51,
|
||||
52,
|
||||
50, 49, 51, 50, 52, 51, 50, 52, 51, 52,
|
||||
51,
|
||||
52,
|
||||
50, 49, 50, 51, 50, 49, 50,
|
||||
51, 52,
|
||||
50, 51, 52, 51, 50, 52, 51,
|
||||
52,
|
||||
51, 50,
|
||||
]
|
||||
assert len(vols) == 41
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=45.60)
|
||||
|
||||
# seria continuă: open = close-ul barei anterioare
|
||||
for a, b in zip(cds, cds[1:]):
|
||||
assert abs(a.c - b.o) < 1e-9, "open != close-ul barei anterioare"
|
||||
|
||||
intrare = cds[30].c
|
||||
sl, tp = SL, TP
|
||||
|
||||
# --- cele 8 assert-uri cerute de specificație ------------------------------
|
||||
# 1. hammer 29: low exact pe locul crimei, close == high (fără fitil de sus)
|
||||
assert abs(cds[29].l - CRIMEA) < 1e-9, f"low[29]={cds[29].l} != 52.10"
|
||||
assert abs(cds[29].c - HAMMER_H) < 1e-9 and abs(cds[29].h - HAMMER_H) < 1e-9, \
|
||||
f"close[29]/high[29] != 53.40 ({cds[29].c}/{cds[29].h})"
|
||||
assert cds[29].c == cds[29].h, "hammer-ul are fitil de sus"
|
||||
# 2. bara 30 se închide la 54.60 = intrare și peste high-ul hammer-ului
|
||||
assert abs(intrare - ENTRY) < 1e-9, f"close[30]={intrare} != intrare 54.60"
|
||||
assert intrare > cds[29].h == HAMMER_H, "close[30] nu e peste high[29] == 53.40"
|
||||
# 3. SL = low-ul hammer-ului - 0.10
|
||||
assert abs(sl - (cds[29].l - 0.10)) < 1e-9, "SL != low[29] - 0.10"
|
||||
# 4. risc 2.60; TP 58.90 = 1.65R
|
||||
risc = intrare - sl
|
||||
assert abs(risc - 2.60) < 1e-9, f"risc={risc} != 2.60"
|
||||
assert abs(tp - 58.90) < 1e-9, "TP != 58.90"
|
||||
assert round((tp - intrare) / (intrare - sl), 2) == 1.65, "TP != 1.65R"
|
||||
# 5. fostul vârf 52.10 există înainte de breakout (bara 9, high 52.40)
|
||||
assert any(abs(cds[i].h - 52.10) <= 0.30 for i in range(15)), \
|
||||
"niciun high din primele 15 bare nu e în 0.30 de 52.10"
|
||||
assert abs(cds[9].h - 52.40) < 1e-9, f"high[9]={cds[9].h} != 52.40"
|
||||
# 6. TP atins după intrare
|
||||
assert max(c.c for c in cds[30:41]) >= 58.90, "TP 58.90 nu e atins după intrare"
|
||||
# 7. vârful nou 58.90 e la bara 21, înainte de pullback; pullback-ul coboară
|
||||
# exact la locul crimei (minimul secțiunii 21-29 e codița hammer-ului)
|
||||
assert abs(cds[21].c - 58.90) < 1e-9, f"close[21]={cds[21].c} != 58.90"
|
||||
assert abs(min(c.l for c in cds[21:30]) - CRIMEA) < 1e-9, \
|
||||
f"min low[21:30]={min(c.l for c in cds[21:30])} != 52.10"
|
||||
# 8. prima etapă e urcare
|
||||
assert cds[9].c > cds[0].c, "prima etapă nu e uptrend"
|
||||
|
||||
print(f"hammer H/C {cds[29].h:.2f} L {cds[29].l:.2f} intrare {intrare:.2f} "
|
||||
f"SL {sl:.2f} TP {tp:.2f} = {round((tp-intrare)/(intrare-sl),2)}R "
|
||||
f"vârf nou {cds[21].c:.2f} (bara 21)")
|
||||
|
||||
# --- desenul ----------------------------------------------------------------
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[
|
||||
Level(CRIMEA, "locul crimei 52.10 (fost vârf spart)", "#777",
|
||||
label_at="left"),
|
||||
Level(SL, "SL 52.00 — sub codița hammer-ului", "#c00"),
|
||||
Level(ENTRY, "intrare 54.60 (close în MBT-10)", "#188a3e"),
|
||||
Level(TP, "TP 58.90 = 1.65R", "#1a5fb4"),
|
||||
],
|
||||
marks=[
|
||||
Mark(29, "hammer — codița pe 52.10", "#b06000", "below"),
|
||||
# dy negativ: bara 30 e urmată imediat de bare mai înalte (31-32), iar
|
||||
# textul lipit de high-ul barei 30 ar cădea peste corpurile lor.
|
||||
Mark(30, "MBT-10 închis", "#188a3e", "above", dy=-24),
|
||||
],
|
||||
scale=20.0, gap=15, body_w=9, left=44, width=780, vol_h=70,
|
||||
vol_label="volum",
|
||||
title="MBT-10 — pullback în locul crimei + hammer, închidere la 54.60 (exemplu construit)",
|
||||
)
|
||||
|
||||
# explicațiile și legenda obligatorie: bloc de text sub grafic (nu se
|
||||
# suprapun cu candelele, iar textele lungi nu încap pe verticală lângă bare).
|
||||
cds[0].note = ("Legendă: MBT-10 nu se desenează — formula nu e publică "
|
||||
"(02_mbt10.md:21-39); se marchează doar bara la care")
|
||||
cds[1].note = ("semnalul s-a închis. Nivelurile sunt exemplul construit al "
|
||||
"sumarizatorului (02_mbt10.md:165-173, 978-984).")
|
||||
cds[2].note = ("bara 9 = fostul vârf 52.10 (locul crimei); bara 21 = vârful "
|
||||
"nou 58.90.")
|
||||
cds[3].note = ("bara 29 = hammer: o 52.20, h/c 53.40, l 52.10 (codița exact "
|
||||
"pe locul crimei); bara 30 = ziua închisă în MBT-10.")
|
||||
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and "\n\n" not in svg, "SVG invalid (linie goală?)"
|
||||
|
||||
# --- citirea SVG-ului: etichete în pânză, fără suprapuneri -----------------
|
||||
TEXT = re.compile(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', re.S)
|
||||
W = float(re.search(r'viewBox="0 0 (\d+) ', svg).group(1))
|
||||
H = float(re.search(r'viewBox="0 0 \d+ ([\d.]+)"', svg).group(1))
|
||||
labels = []
|
||||
for m in TEXT.finditer(svg):
|
||||
x, y, attrs, s = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4)
|
||||
anchor = "end" if 'text-anchor="end"' in attrs else \
|
||||
("middle" if 'text-anchor="middle"' in attrs else "start")
|
||||
bold = "bold" in attrs
|
||||
wpx = len(s) * 12 * (0.62 if bold else 0.52)
|
||||
left = x - wpx if anchor == "end" else (x - wpx / 2 if anchor == "middle" else x)
|
||||
labels.append((left, left + wpx, y, s))
|
||||
|
||||
for left, right, y, s in labels:
|
||||
assert left >= -2 and right <= W + 2, \
|
||||
f"text iese din pânză: {s!r} [{left:.0f},{right:.0f}]"
|
||||
assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y:.0f})"
|
||||
|
||||
for i in range(len(labels)):
|
||||
li, ri, yi, si = labels[i]
|
||||
for j in range(i + 1, len(labels)):
|
||||
lj, rj, yj, sj = labels[j]
|
||||
if abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}\n"
|
||||
f" {labels[i]}\n {labels[j]}")
|
||||
|
||||
# candelele sunt o serie continuă, lipită, în pânză
|
||||
xs = [ch._cx(i) for i in range(len(cds))]
|
||||
assert all(xs[i + 1] - xs[i] == ch.gap for i in range(len(xs) - 1)), "serie cu goluri"
|
||||
assert xs[-1] + ch.body_w / 2 < W, f"ultima candelă iese din pânză (x={xs[-1]})"
|
||||
|
||||
print(f"scris {OUT} ({W:.0f}x{H:.0f}); {len(labels)} etichete verificate")
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
142
gen/strategii/hammer_mbt10_mbt_ansamblu.svg
Normal file
@@ -0,0 +1,142 @@
|
||||
<svg viewBox="0 0 780 526" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">MBT-10 — pullback în locul crimei + hammer, închidere la 54.60 (exemplu construit)</text>
|
||||
<line x1="24" y1="198.0" x2="766" y2="198.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="192.0" fill="#777">locul crimei 52.10 (fost vârf spart)</text>
|
||||
<line x1="24" y1="200.0" x2="766" y2="200.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="752" y="194.0" fill="#c00" text-anchor="end">SL 52.00 — sub codița hammer-ului</text>
|
||||
<line x1="24" y1="148.0" x2="766" y2="148.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="752" y="142.0" fill="#188a3e" text-anchor="end">intrare 54.60 (close în MBT-10)</text>
|
||||
<line x1="24" y1="62.0" x2="766" y2="62.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="752" y="56.0" fill="#1a5fb4" text-anchor="end">TP 58.90 = 1.65R</text>
|
||||
<line x1="44.0" y1="314.0" x2="44.0" y2="336.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="320.0" width="9" height="8.0" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="298.0" x2="59.0" y2="328.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="304.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="74.0" y1="284.0" x2="74.0" y2="312.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="290.0" width="9" height="14.0" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="268.0" x2="89.0" y2="298.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="274.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="104.0" y1="254.0" x2="104.0" y2="282.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="260.0" width="9" height="14.0" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="238.0" x2="119.0" y2="268.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="244.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="224.0" x2="134.0" y2="252.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="230.0" width="9" height="14.0" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="210.0" x2="149.0" y2="238.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="216.0" width="9" height="14.0" fill="#188a3e"/>
|
||||
<line x1="164.0" y1="194.0" x2="164.0" y2="224.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="200.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="192.0" x2="179.0" y2="206.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="198.0" width="9" height="2.0" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="192.0" x2="194.0" y2="220.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="198.0" width="9" height="14.0" fill="#c00"/>
|
||||
<line x1="209.0" y1="206.0" x2="209.0" y2="232.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="212.0" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="224.0" y1="204.0" x2="224.0" y2="232.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="210.0" width="9" height="14.0" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="186.0" x2="239.0" y2="218.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="192.0" width="9" height="18.0" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="170.0" x2="254.0" y2="200.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="176.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="154.0" x2="269.0" y2="184.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="160.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="138.0" x2="284.0" y2="168.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="144.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="122.0" x2="299.0" y2="152.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="128.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="106.0" x2="314.0" y2="136.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="112.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="90.0" x2="329.0" y2="120.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="96.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="74.0" x2="344.0" y2="104.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="80.0" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="56.0" x2="359.0" y2="88.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="62.0" width="9" height="18.0" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="56.0" x2="374.0" y2="84.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="62.0" width="9" height="14.0" fill="#c00"/>
|
||||
<line x1="389.0" y1="70.0" x2="389.0" y2="100.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="76.0" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="404.0" y1="86.0" x2="404.0" y2="118.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="92.0" width="9" height="18.0" fill="#c00"/>
|
||||
<line x1="419.0" y1="104.0" x2="419.0" y2="136.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="110.0" width="9" height="18.0" fill="#c00"/>
|
||||
<line x1="434.0" y1="122.0" x2="434.0" y2="154.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="128.0" width="9" height="18.0" fill="#c00"/>
|
||||
<line x1="449.0" y1="140.0" x2="449.0" y2="176.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="146.0" width="9" height="22.0" fill="#c00"/>
|
||||
<line x1="464.0" y1="162.0" x2="464.0" y2="198.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="168.0" width="9" height="28.0" fill="#c00"/>
|
||||
<line x1="479.0" y1="172.0" x2="479.0" y2="198.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="172.0" width="9" height="24.0" fill="#188a3e"/>
|
||||
<line x1="494.0" y1="144.0" x2="494.0" y2="176.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="148.0" width="9" height="24.0" fill="#188a3e"/>
|
||||
<line x1="509.0" y1="130.0" x2="509.0" y2="156.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="136.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="118.0" x2="524.0" y2="144.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="124.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="539.0" y1="106.0" x2="539.0" y2="132.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="534.5" y="112.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="554.0" y1="94.0" x2="554.0" y2="120.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="549.5" y="100.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="569.0" y1="82.0" x2="569.0" y2="108.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="564.5" y="88.0" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="584.0" y1="72.0" x2="584.0" y2="96.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="579.5" y="78.0" width="9" height="10.0" fill="#188a3e"/>
|
||||
<line x1="599.0" y1="62.0" x2="599.0" y2="86.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="594.5" y="68.0" width="9" height="10.0" fill="#188a3e"/>
|
||||
<line x1="614.0" y1="56.0" x2="614.0" y2="76.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="609.5" y="62.0" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="629.0" y1="56.0" x2="629.0" y2="74.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="624.5" y="62.0" width="9" height="4.0" fill="#c00"/>
|
||||
<line x1="644.0" y1="60.0" x2="644.0" y2="82.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="639.5" y="66.0" width="9" height="8.0" fill="#c00"/>
|
||||
<line x1="24" y1="432.0" x2="766" y2="432.0" stroke="#888"/>
|
||||
<rect x="39.5" y="364.7" width="9" height="67.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="366.0" width="9" height="66.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="69.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="364.7" width="9" height="67.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="99.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="114.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="364.7" width="9" height="67.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="159.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="189.5" y="364.7" width="9" height="67.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="366.0" width="9" height="66.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="364.7" width="9" height="67.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="264.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="364.7" width="9" height="67.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="294.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="364.7" width="9" height="67.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="384.5" y="366.0" width="9" height="66.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.5" y="364.7" width="9" height="67.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="414.5" y="363.3" width="9" height="68.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="429.5" y="364.7" width="9" height="67.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="444.5" y="366.0" width="9" height="66.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="459.5" y="364.7" width="9" height="67.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="474.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="489.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="504.5" y="364.7" width="9" height="67.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="534.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="549.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="564.5" y="364.7" width="9" height="67.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="579.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="594.5" y="363.3" width="9" height="68.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="609.5" y="362.0" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="624.5" y="363.3" width="9" height="68.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="639.5" y="364.7" width="9" height="67.3" fill="#c00" opacity="0.55"/>
|
||||
<text x="24" y="356.0" fill="#555">volum</text>
|
||||
<text x="479.0" y="218.0" fill="#b06000" text-anchor="middle" style="font-weight:bold">hammer — codița pe 52.10</text>
|
||||
<text x="494.0" y="110.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">MBT-10 închis</text>
|
||||
<text x="24" y="454.0" fill="#188a3e">Legendă: MBT-10 nu se desenează — formula nu e publică (02_mbt10.md:21-39); se marchează doar bara la care</text>
|
||||
<text x="24" y="471.0" fill="#188a3e">semnalul s-a închis. Nivelurile sunt exemplul construit al sumarizatorului (02_mbt10.md:165-173, 978-984).</text>
|
||||
<text x="24" y="488.0" fill="#188a3e">bara 9 = fostul vârf 52.10 (locul crimei); bara 21 = vârful nou 58.90.</text>
|
||||
<text x="24" y="505.0" fill="#188a3e">bara 29 = hammer: o 52.20, h/c 53.40, l 52.10 (codița exact pe locul crimei); bara 30 = ziua închisă în MBT-10.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
181
gen/strategii/hammer_mbt10_mbt_gapdown.py
Normal file
@@ -0,0 +1,181 @@
|
||||
"""După gap down — primul MBT-10 te scoate, al doilea merge (exemplu construit).
|
||||
|
||||
Citește `gen/strategii/hammer_mbt10_mbt_gapdown.spec.txt`
|
||||
(marcajul `<!-- DIAGRAMA: mbt_gapdown -->`, Partea B §2.3 Gotchas).
|
||||
|
||||
Serie continuă de 33 candele daily, construită din close-urile date în
|
||||
specificație, cu open-ul fiecărei bare = close-ul precedentei (STYLE.md),
|
||||
cu două excepții impuse de exemplu:
|
||||
|
||||
- bara 13 (ultima dinainte de gap): ca să iasă ȘI ``low[13] = 70.60``
|
||||
(nivelul de închidere a gap-ului, cerut de assert 1 și 6) ȘI
|
||||
``high[13] = 71.00`` (structura seriei, „de la ~71"), bara trebuie să
|
||||
fie un doji ``o = c = 70.60``. De aceea am ridicat ``close[12]`` de la
|
||||
69.80 (cât dă lista) la 70.60: altfel ``min(open,close)=69.80`` și
|
||||
low-ul ar ieși 69.80, nu 70.60. Wu=0.40 => high 71.00, wd=0.00 => low
|
||||
70.60. (Linia „Mecioni" cere wu=0.20 => high 70.80; am ales 71.00,
|
||||
cifra din structura seriei și din textul „~71".)
|
||||
- bara 14 (gap down): ``open[14] = 66.40``, NU close[13]=70.60 — singura
|
||||
bară cu open diferit de close-ul precedent. O construim manual:
|
||||
o=c=66.40, wu=0.30 => high 66.70, wd=0.30 => low 66.10.
|
||||
|
||||
Wick-urile speciale: bara 16 wd=0.20 (low 64.60), bara 18 wd=0.20 (low
|
||||
63.20), bara 21 wd=0.20 (low 62.00); restul (0.30, 0.40).
|
||||
|
||||
Rulează: python3 gen/strategii/hammer_mbt10_mbt_gapdown.py
|
||||
Scrie: gen/strategii/hammer_mbt10_mbt_gapdown.svg
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Candle, Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "hammer_mbt10_mbt_gapdown.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# --- seria: 33 de close-uri, exact ca în specificație ------------------------
|
||||
# Singura abatere: closes[12] 69.80 -> 70.60, ca bara 13 să poată avea
|
||||
# low 70.60 (vezi docstring-ul).
|
||||
closes = [
|
||||
60.00, 60.80, 61.60, 62.50, 63.30, 64.20, 65.00, 65.80, 66.60, 67.40, # 0-9
|
||||
68.20, 69.00, 70.60, 70.60, # 10-13
|
||||
66.40, # 14 = bara de GAP DOWN (open impus 66.40, NU close[13])
|
||||
65.50, # 15
|
||||
64.80, # 16 = MBT-10 #1, închidere => intrare #1
|
||||
64.20, # 17
|
||||
63.40, # 18 = low 63.20 => stop #1
|
||||
62.90, # 19
|
||||
62.50, # 20
|
||||
62.20, # 21 = MBT-10 #2, închidere => intrare #2
|
||||
62.80, 63.60, 64.50, 65.50, 66.50, 67.50, 68.50, 69.50, # 22-29
|
||||
70.60, # 30 = gap închis (TP)
|
||||
70.90, # 31
|
||||
70.50, # 32
|
||||
]
|
||||
assert len(closes) == 33, len(closes)
|
||||
|
||||
WICKS = [(0.30, 0.40)] * 33 # restul barelor
|
||||
WICKS[13] = (0.40, 0.00) # high 71.00, low 70.60
|
||||
WICKS[14] = (0.30, 0.30) # high 66.70, low 66.10 (bara de gap)
|
||||
WICKS[16] = (0.30, 0.20) # low 64.60
|
||||
WICKS[18] = (0.30, 0.20) # low 63.20
|
||||
WICKS[21] = (0.30, 0.20) # low 62.00
|
||||
|
||||
# Volume relative, alese de generator (materialul nu dă volum aici):
|
||||
# în scădere pe piciorul coborârii după gap (14 -> 21), apoi recuperare.
|
||||
vols = [
|
||||
48, 46, 44, 45, 43, 42, 44, 41, 40, 42, # 0-9
|
||||
43, 41, 46, 58, # 10-13
|
||||
90, # 14 = gap down
|
||||
76, 66, 57, 50, 45, 42, # 15-20
|
||||
40, # 21 = MBT-10 #2 (minim)
|
||||
46, 52, 58, 63, 68, 73, 78, 82, 86, 88, 80, # 22-32
|
||||
]
|
||||
assert len(vols) == 33, len(vols)
|
||||
coborare = vols[14:22]
|
||||
assert all(coborare[i] >= coborare[i + 1] for i in range(len(coborare) - 1)), \
|
||||
f"volumul nu scade pe piciorul coborârii: {coborare}"
|
||||
assert vols[-1] > vols[21], "volumul nu recuperează după bara 21"
|
||||
|
||||
cds = from_closes(closes, wicks=WICKS, vols=vols, start=60.00)
|
||||
# bara de gap: open = 66.40, nu close[13] = 70.60
|
||||
cds[14] = Candle(66.40, 66.70, 66.10, 66.40, vol=vols[14], color=RED)
|
||||
|
||||
# --- assert-urile din specificație -----------------------------------------
|
||||
# 1. gap down real
|
||||
assert abs(cds[14].o - 66.40) < 1e-9, cds[14].o
|
||||
assert abs(cds[13].l - 70.60) < 1e-9, f"low[13]={cds[13].l} != 70.60"
|
||||
assert abs(cds[13].c - 70.60) < 1e-9, cds[13].c
|
||||
assert cds[14].o < cds[13].l and cds[14].h < cds[13].l, "gapul nu e real"
|
||||
assert abs(cds[14].o - cds[13].c) > 1e-9, "open[14] nu e excepția de la serie"
|
||||
|
||||
# 2. #1
|
||||
intrare1, sl1 = 64.80, 63.20
|
||||
assert abs(cds[16].c - intrare1) < 1e-9, f"close[16]={cds[16].c} != 64.80"
|
||||
assert abs(sl1 - 63.20) < 1e-9
|
||||
assert min(c.l for c in cds[16:19]) <= sl1, "stopul #1 nu e atins"
|
||||
assert cds[16].c > sl1, "intrarea #1 e sub stopul ei"
|
||||
|
||||
# 3. #2
|
||||
intrare2, sl2 = 62.20, 60.40
|
||||
assert abs(cds[21].c - intrare2) < 1e-9, f"close[21]={cds[21].c} != 62.20"
|
||||
assert abs(sl2 - 60.40) < 1e-9
|
||||
assert not any(c.l <= sl2 for c in cds[22:]), "un low atinge SL2 după bara 21"
|
||||
|
||||
# 4. riscuri
|
||||
risc1 = intrare1 - sl1 # 1.60
|
||||
risc2 = intrare2 - sl2 # 1.80
|
||||
assert abs(risc1 - 1.60) < 1e-9 and abs(risc2 - 1.80) < 1e-9, (risc1, risc2)
|
||||
|
||||
# 5. TP = închiderea gap-ului
|
||||
TP = 70.60
|
||||
reward2 = TP - intrare2 # 8.40
|
||||
assert abs(reward2 - 8.40) < 1e-9, reward2
|
||||
assert round(reward2 / risc2, 1) == 4.7, round(reward2 / risc2, 1)
|
||||
|
||||
# 6. low[13] e nivelul de închidere a gap-ului și TP-ul e atins în urcare
|
||||
assert abs(cds[13].l - TP) < 1e-9, "low[13] != 70.60 (nivelul gap-ului)"
|
||||
assert max(c.h for c in cds[22:33]) >= TP, "TP 70.60 nu e atins după #2"
|
||||
primul_tp = next(i for i in range(22, 33) if cds[i].h >= TP)
|
||||
|
||||
# 7. același risc în bani: mărimea #2 = mărimea #1 × (risc1 / risc2)
|
||||
marime1 = 1.0
|
||||
marime2 = marime1 * (risc1 / risc2)
|
||||
assert abs(marime2 - 1.60 / 1.80) < 1e-9, marime2
|
||||
|
||||
print(f"gap {cds[13].l:.2f} -> {cds[14].o:.2f} "
|
||||
f"#1 intrare {intrare1:.2f} SL {sl1:.2f} (risc {risc1:.2f}) "
|
||||
f"#2 intrare {intrare2:.2f} SL {sl2:.2f} (risc {risc2:.2f}) "
|
||||
f"TP {TP:.2f} = {reward2 / risc2:.1f}R "
|
||||
f"atins la bara {primul_tp} (high {cds[primul_tp].h:.2f}) "
|
||||
f"mărime #2 = {marime2:.3f} × mărime #1")
|
||||
|
||||
# --- desenul ----------------------------------------------------------------
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=[
|
||||
# toate etichetele stau în dreapta (x > ultima candelă), fără suprapuneri
|
||||
Level(intrare1, "#1 intrare 64.80", GRAY, label_at="right"),
|
||||
Level(sl1, "#1 SL 63.20", GRAY, label_at="right"),
|
||||
Level(intrare2, "#2 intrare 62.20", GRAY, label_at="right"),
|
||||
Level(sl2, "#2 SL 60.40", GRAY, label_at="right"),
|
||||
Level(TP, "TP 70.60 = 4.7R (gap închis)", BLUE, label_at="right"),
|
||||
],
|
||||
marks=[
|
||||
# decalate pe verticală cât să nu atingă candelele din coborâre
|
||||
Mark(14, "gap down", RED, "above", dy=-22),
|
||||
Mark(16, "MBT-10 #1", GRAY, "above", dy=-25),
|
||||
Mark(18, "stop #1 atins", RED, "below", dy=30),
|
||||
Mark(21, "MBT-10 #2", GREEN, "below", dy=25),
|
||||
],
|
||||
scale=28.0, gap=15, body_w=9, left=44, width=820, vol_h=70,
|
||||
vol_label="volum",
|
||||
title="După gap down — primul MBT-10 te scoate, al doilea merge (exemplu construit)",
|
||||
caption="",
|
||||
)
|
||||
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and "\n\n" not in svg, "linie goală în SVG"
|
||||
|
||||
# --- blocul de text de jos: cine are același risc + legenda -----------------
|
||||
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
||||
W, H = int(m.group(1)), int(m.group(2))
|
||||
linii = [
|
||||
"MBT-10 #1: intrare 64.80 / SL 63.20 — risc 1R în bani",
|
||||
"MBT-10 #2: intrare 62.20 / SL 60.40 — risc 1R în bani (mărime ajustată)",
|
||||
"Exemplu construit (02_mbt10.md:728-734): #1 și #2 au același risc în bani; "
|
||||
"#2 singur dă 4.7R.",
|
||||
"De aceea mărimea se împarte pe 2-3 încercări.",
|
||||
]
|
||||
extra = 16 * len(linii) + 6
|
||||
svg = svg.replace(f'viewBox="0 0 {W} {H}"', f'viewBox="0 0 {W} {H + extra}"')
|
||||
svg = svg.replace("</svg>", "".join(
|
||||
f'<text x="24" y="{H + 12 + 16 * i}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(linii)) + "</svg>")
|
||||
assert "\n\n" not in svg, "linie goală în SVG după legendă"
|
||||
|
||||
OUT.write_text(svg)
|
||||
print(f"scris {OUT.relative_to('/workspace/atm-curs-dl')}")
|
||||
118
gen/strategii/hammer_mbt10_mbt_gapdown.svg
Normal file
@@ -0,0 +1,118 @@
|
||||
<svg viewBox="0 0 820 573" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">După gap down — primul MBT-10 te scoate, al doilea merge (exemplu construit)</text>
|
||||
<line x1="24" y1="235.2" x2="806" y2="235.2" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="792" y="229.2" fill="#777" text-anchor="end">#1 intrare 64.80</text>
|
||||
<line x1="24" y1="280.0" x2="806" y2="280.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="792" y="274.0" fill="#777" text-anchor="end">#1 SL 63.20</text>
|
||||
<line x1="24" y1="308.0" x2="806" y2="308.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="792" y="302.0" fill="#777" text-anchor="end">#2 intrare 62.20</text>
|
||||
<line x1="24" y1="358.4" x2="806" y2="358.4" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="792" y="352.4" fill="#777" text-anchor="end">#2 SL 60.40</text>
|
||||
<line x1="24" y1="72.8" x2="806" y2="72.8" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="792" y="66.8" fill="#1a5fb4" text-anchor="end">TP 70.60 = 4.7R (gap închis)</text>
|
||||
<line x1="44.0" y1="361.2" x2="44.0" y2="380.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="369.6" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="338.8" x2="59.0" y2="380.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="347.2" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="74.0" y1="316.4" x2="74.0" y2="358.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="324.8" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="291.2" x2="89.0" y2="336.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="299.6" width="9" height="25.2" fill="#188a3e"/>
|
||||
<line x1="104.0" y1="268.8" x2="104.0" y2="310.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="277.2" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="243.6" x2="119.0" y2="288.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="252.0" width="9" height="25.2" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="221.2" x2="134.0" y2="263.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="229.6" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="198.8" x2="149.0" y2="240.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="207.2" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="164.0" y1="176.4" x2="164.0" y2="218.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="184.8" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="154.0" x2="179.0" y2="196.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="162.4" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="131.6" x2="194.0" y2="173.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="140.0" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="109.2" x2="209.0" y2="151.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="117.6" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="64.4" x2="224.0" y2="128.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="72.8" width="9" height="44.8" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="61.6" x2="239.0" y2="72.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="72.8" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="182.0" x2="254.0" y2="198.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="190.4" width="9" height="1.6" fill="#c00"/>
|
||||
<line x1="269.0" y1="182.0" x2="269.0" y2="226.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="190.4" width="9" height="25.2" fill="#c00"/>
|
||||
<line x1="284.0" y1="207.2" x2="284.0" y2="240.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="215.6" width="9" height="19.6" fill="#c00"/>
|
||||
<line x1="299.0" y1="226.8" x2="299.0" y2="263.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="235.2" width="9" height="16.8" fill="#c00"/>
|
||||
<line x1="314.0" y1="243.6" x2="314.0" y2="280.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="252.0" width="9" height="22.4" fill="#c00"/>
|
||||
<line x1="329.0" y1="266.0" x2="329.0" y2="299.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="274.4" width="9" height="14.0" fill="#c00"/>
|
||||
<line x1="344.0" y1="280.0" x2="344.0" y2="310.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="288.4" width="9" height="11.2" fill="#c00"/>
|
||||
<line x1="359.0" y1="291.2" x2="359.0" y2="313.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="299.6" width="9" height="8.4" fill="#c00"/>
|
||||
<line x1="374.0" y1="282.8" x2="374.0" y2="319.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="291.2" width="9" height="16.8" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="260.4" x2="389.0" y2="302.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="268.8" width="9" height="22.4" fill="#188a3e"/>
|
||||
<line x1="404.0" y1="235.2" x2="404.0" y2="280.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="243.6" width="9" height="25.2" fill="#188a3e"/>
|
||||
<line x1="419.0" y1="207.2" x2="419.0" y2="254.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="215.6" width="9" height="28.0" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="179.2" x2="434.0" y2="226.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="187.6" width="9" height="28.0" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="151.2" x2="449.0" y2="198.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="159.6" width="9" height="28.0" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="123.2" x2="464.0" y2="170.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="131.6" width="9" height="28.0" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="95.2" x2="479.0" y2="142.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="103.6" width="9" height="28.0" fill="#188a3e"/>
|
||||
<line x1="494.0" y1="64.4" x2="494.0" y2="114.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="72.8" width="9" height="30.8" fill="#188a3e"/>
|
||||
<line x1="509.0" y1="56.0" x2="509.0" y2="84.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="64.4" width="9" height="8.4" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="56.0" x2="524.0" y2="86.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="64.4" width="9" height="11.2" fill="#c00"/>
|
||||
<line x1="24" y1="476.8" x2="806" y2="476.8" stroke="#888"/>
|
||||
<rect x="39.5" y="439.5" width="9" height="37.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="441.0" width="9" height="35.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="69.5" y="442.6" width="9" height="34.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="441.8" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="99.5" y="443.4" width="9" height="33.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="114.5" y="444.1" width="9" height="32.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="442.6" width="9" height="34.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="444.9" width="9" height="31.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="159.5" y="445.7" width="9" height="31.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="444.1" width="9" height="32.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="189.5" y="443.4" width="9" height="33.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.5" y="444.9" width="9" height="31.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="219.5" y="441.0" width="9" height="35.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="431.7" width="9" height="45.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.5" y="406.8" width="9" height="70.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="417.7" width="9" height="59.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="425.5" width="9" height="51.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="432.5" width="9" height="44.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="437.9" width="9" height="38.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.5" y="441.8" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="339.5" y="444.1" width="9" height="32.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="354.5" y="445.7" width="9" height="31.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="369.5" y="441.0" width="9" height="35.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="436.4" width="9" height="40.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="399.5" y="431.7" width="9" height="45.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="414.5" y="427.8" width="9" height="49.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="423.9" width="9" height="52.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="420.0" width="9" height="56.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="416.1" width="9" height="60.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.5" y="413.0" width="9" height="63.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="489.5" y="409.9" width="9" height="66.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="504.5" y="408.4" width="9" height="68.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="414.6" width="9" height="62.2" fill="#c00" opacity="0.55"/>
|
||||
<text x="24" y="400.8" fill="#555">volum</text>
|
||||
<text x="254.0" y="150.0" fill="#c00" text-anchor="middle" style="font-weight:bold">gap down</text>
|
||||
<text x="284.0" y="172.2" fill="#777" text-anchor="middle" style="font-weight:bold">MBT-10 #1</text>
|
||||
<text x="314.0" y="330.0" fill="#c00" text-anchor="middle" style="font-weight:bold">stop #1 atins</text>
|
||||
<text x="359.0" y="358.6" fill="#188a3e" text-anchor="middle" style="font-weight:bold">MBT-10 #2</text>
|
||||
<text x="24" y="515" fill="#555">MBT-10 #1: intrare 64.80 / SL 63.20 — risc 1R în bani</text><text x="24" y="531" fill="#555">MBT-10 #2: intrare 62.20 / SL 60.40 — risc 1R în bani (mărime ajustată)</text><text x="24" y="547" fill="#555">Exemplu construit (02_mbt10.md:728-734): #1 și #2 au același risc în bani; #2 singur dă 4.7R.</text><text x="24" y="563" fill="#555">De aceea mărimea se împarte pe 2-3 încercări.</text></svg>
|
||||
|
After Width: | Height: | Size: 9.9 KiB |
151
gen/strategii/hammer_mbt10_mbt_tesla.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""Diagrama din `gen/strategii/hammer_mbt10_mbt_tesla.spec.txt` (TIP: PRET).
|
||||
|
||||
Exemplul real Tesla din Q&A (`03_2026-08-11.md:118-120`): trigger MBT-10 după
|
||||
o cădere anterioară, intrare la închiderea zilei la 311.21, ieșire la 333.00
|
||||
șase zile mai târziu = +7.0 % pe acțiune.
|
||||
|
||||
Reale: 311.21 / 333.00 / +7.0 % și contextul calitativ. Construită de mine:
|
||||
structura barelor dintre cele două niveluri. SL-ul lipsește din material, deci
|
||||
NU se trasează nicio linie de stop (spec, „LIPSĂ").
|
||||
|
||||
Serie continuă de 25 de candele daily din lista de close-uri din specificație,
|
||||
open = close-ul precedentului, wick-uri implicite (0.50, 0.60), fără panou de
|
||||
volum (materialul nu discută volumul exemplului). Legenda e spartă pe trei
|
||||
rânduri și lipește sub grafic, cu viewBox extins.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "hammer_mbt10_mbt_tesla.svg"
|
||||
|
||||
# ── cifrele reale din material (`03_2026-08-11.md:118-120`) ─────────────────
|
||||
INTRARE = 311.21 # intrarea la închiderea zilei
|
||||
REZULTAT = 333.00 # six zile mai târziu
|
||||
ZILE = 6 # (21 - 15)
|
||||
PROCENT = 7.0 # +7.0 % pe acțiune
|
||||
I_INTRARE = 15 # candela de intrare (trigger MBT-10)
|
||||
I_REZULTAT = 21
|
||||
|
||||
# ── seria: 25 de close-uri, exact ca în specificație ───────────────────────
|
||||
closes = [
|
||||
360.00, 356.00, 358.00, 352.00, 348.00, 349.00, 343.00, 338.00, 340.00,
|
||||
334.00, 329.00, 331.00, 325.00, 320.00, 315.00,
|
||||
311.21, # 15 = trigger MBT-10, intrare la închiderea zilei
|
||||
314.00, 318.00, 322.00, 326.00, 330.00,
|
||||
333.00, # 21 = 6 bare după intrare
|
||||
331.00, 328.00, 330.00,
|
||||
]
|
||||
assert len(closes) == 25, len(closes)
|
||||
|
||||
# wick-uri implicite pentru toate barele
|
||||
wicks = [(0.50, 0.60)] * len(closes)
|
||||
cds = from_closes(closes, wicks=wicks, start=closes[0])
|
||||
|
||||
# ── assert-urile cerute de specificație ────────────────────────────────────
|
||||
# 1. close[15] == 311.21 == intrare (la închiderea zilei)
|
||||
assert abs(cds[I_INTRARE].c - 311.21) < 1e-9, cds[I_INTRARE].c
|
||||
assert abs(cds[I_INTRARE].c - INTRARE) < 1e-9, (cds[I_INTRARE].c, INTRARE)
|
||||
|
||||
# 2. close[21] == 333.00 și (21 - 15) == 6
|
||||
assert abs(cds[I_REZULTAT].c - 333.00) < 1e-9, cds[I_REZULTAT].c
|
||||
assert (I_REZULTAT - I_INTRARE) == 6, I_REZULTAT - I_INTRARE
|
||||
|
||||
# 3. procentul REAL
|
||||
pct = round((cds[I_REZULTAT].c / cds[I_INTRARE].c - 1) * 100, 1)
|
||||
assert pct == 7.0, pct
|
||||
|
||||
# 4. cădere anterioară: close[15] < close[0] - 40
|
||||
assert cds[I_INTRARE].c < cds[0].c - 40, (cds[I_INTRARE].c, cds[0].c)
|
||||
|
||||
# 5. nu se trasează linie de SL; în structură nu apare niciun nivel de stop
|
||||
# (verificat mai jos, după construirea listelor de niveluri/marcaje)
|
||||
|
||||
# 6. entry < exit, iar exit e atins după exact 6 bare (nu mai devreme)
|
||||
assert cds[I_INTRARE].c < cds[I_REZULTAT].c
|
||||
for i in range(I_INTRARE + 1, I_REZULTAT):
|
||||
assert cds[i].c < REZULTAT, f"rezultatul 333.00 e atins deja la bara {i}"
|
||||
assert cds[I_REZULTAT].c == REZULTAT
|
||||
|
||||
# serie continuă: open-ul fiecărei bare = close-ul precedentului
|
||||
for a, b in zip(cds, cds[1:]):
|
||||
assert abs(a.c - b.o) < 1e-9, "serie cu gol artificial între bare"
|
||||
|
||||
# ── desenul ────────────────────────────────────────────────────────────────
|
||||
levels = [
|
||||
Level(INTRARE, "intrare 311.21 (close)", "#188a3e", label_at="left"),
|
||||
Level(REZULTAT, "rezultat 333.00 = +7.0 % în 6 zile", "#1a5fb4"),
|
||||
]
|
||||
marks = [
|
||||
Mark(I_INTRARE, "trigger MBT-10", "#188a3e", "below"),
|
||||
Mark(I_REZULTAT, "exact 6 bare", "#1a5fb4", "above", dy=-16),
|
||||
]
|
||||
|
||||
# 5. niciun nivel/marcaj de stop în structură
|
||||
assert not any("stop" in t.lower() for t in
|
||||
[lv.text for lv in levels] + [mk.text for mk in marks])
|
||||
assert not any(t.strip().upper().startswith("SL") for t in
|
||||
[lv.text for lv in levels] + [mk.text for mk in marks])
|
||||
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=levels,
|
||||
marks=marks,
|
||||
scale=7.5, gap=15, body_w=9, left=44, width=620,
|
||||
title="Tesla — daily: trigger MBT-10, intrare 311.21 → 333.00 = +7.0 % în 6 zile",
|
||||
caption="", # legenda obligatorie e pusă manual, pe rânduri, cu viewBox extins
|
||||
)
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and svg.count("\n\n") == 0, "linie goală în SVG"
|
||||
|
||||
# ── legenda obligatorie, spartă ca să nu iasă din pânză ────────────────────
|
||||
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
||||
W, H = int(m.group(1)), int(m.group(2))
|
||||
legenda = [
|
||||
"Cifrele 311.21 / 333.00 / +7.0 % sunt reale (03_2026-08-11.md:118-120);",
|
||||
"structura barelor dintre ele e construită.",
|
||||
"SL: materialul nu îl dă pentru acest exemplu.",
|
||||
]
|
||||
# fiecare rând încape în pânză (≈6 px/caracter la 12px)
|
||||
for rand in legenda:
|
||||
assert 24 + len(rand) * 6.0 <= W - 10, f"legenda iese din pânză: {rand!r}"
|
||||
extra = 16 * len(legenda) + 6
|
||||
svg = svg.replace(f'viewBox="0 0 {W} {H}"', f'viewBox="0 0 {W} {H + extra}"')
|
||||
svg = svg.replace("</svg>", "".join(
|
||||
f'<text x="24" y="{H + 12 + 16 * i}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(legenda)) + "</svg>")
|
||||
assert "\n\n" not in svg, "linie goală în SVG după legendă"
|
||||
|
||||
# legenda completă există, rând cu rând
|
||||
for rand in legenda:
|
||||
assert rand in svg, f"lipsește din legendă: {rand!r}"
|
||||
|
||||
# ── verificare: fiecare etichetă e în pânză și nu se suprapune alta ────────
|
||||
texte = re.findall(r'<text x="([\d.]+)" y="([\d.]+)"([^>]*)>([^<]*)</text>', svg)
|
||||
cutii = []
|
||||
for xs, ys, attrs, continut in texte:
|
||||
x, y = float(xs), float(ys)
|
||||
w = len(continut) * (7.44 if "bold" in attrs else 6.72)
|
||||
if 'text-anchor="end"' in attrs:
|
||||
x0 = x - w
|
||||
elif 'text-anchor="middle"' in attrs:
|
||||
x0 = x - w / 2
|
||||
else:
|
||||
x0 = x
|
||||
assert -2 <= x0 and x0 + w <= W + 2, f"etichetă în afara pânzei: {continut!r}"
|
||||
assert 0 <= y <= H + extra, f"etichetă în afara înălțimii: {continut!r}"
|
||||
cutii.append((x0, x0 + w, y, continut))
|
||||
for i in range(len(cutii)):
|
||||
for j in range(i + 1, len(cutii)):
|
||||
a, b = cutii[i], cutii[j]
|
||||
if abs(a[2] - b[2]) < 13 and a[0] < b[1] - 2 and b[0] < a[1] - 2:
|
||||
raise AssertionError(f"texte suprapuse: {a[3]!r} / {b[3]!r}")
|
||||
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"serie {len(cds)} candele | intrare {INTRARE:.2f} @ bara {I_INTRARE} | "
|
||||
f"rezultat {REZULTAT:.2f} @ bara {I_REZULTAT} (+{pct:.1f} % în "
|
||||
f"{I_REZULTAT - I_INTRARE} zile, atins nu mai devreme)")
|
||||
print(f"scris {OUT}")
|
||||
59
gen/strategii/hammer_mbt10_mbt_tesla.svg
Normal file
@@ -0,0 +1,59 @@
|
||||
<svg viewBox="0 0 620 510" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Tesla — daily: trigger MBT-10, intrare 311.21 → 333.00 = +7.0 % în 6 zile</text>
|
||||
<line x1="24" y1="425.7" x2="606" y2="425.7" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="419.7" fill="#188a3e">intrare 311.21 (close)</text>
|
||||
<line x1="24" y1="262.2" x2="606" y2="262.2" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="592" y="256.2" fill="#1a5fb4" text-anchor="end">rezultat 333.00 = +7.0 % în 6 zile</text>
|
||||
<line x1="44.0" y1="56.0" x2="44.0" y2="64.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="59.8" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="56.0" x2="59.0" y2="94.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="59.8" width="9" height="30.0" fill="#c00"/>
|
||||
<line x1="74.0" y1="71.0" x2="74.0" y2="94.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="74.8" width="9" height="15.0" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="71.0" x2="89.0" y2="124.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="74.8" width="9" height="45.0" fill="#c00"/>
|
||||
<line x1="104.0" y1="116.0" x2="104.0" y2="154.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="119.8" width="9" height="30.0" fill="#c00"/>
|
||||
<line x1="119.0" y1="138.5" x2="119.0" y2="154.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="142.2" width="9" height="7.5" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="138.5" x2="134.0" y2="191.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="142.2" width="9" height="45.0" fill="#c00"/>
|
||||
<line x1="149.0" y1="183.5" x2="149.0" y2="229.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="187.2" width="9" height="37.5" fill="#c00"/>
|
||||
<line x1="164.0" y1="206.0" x2="164.0" y2="229.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="209.8" width="9" height="15.0" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="206.0" x2="179.0" y2="259.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="209.8" width="9" height="45.0" fill="#c00"/>
|
||||
<line x1="194.0" y1="251.0" x2="194.0" y2="296.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="254.8" width="9" height="37.5" fill="#c00"/>
|
||||
<line x1="209.0" y1="273.5" x2="209.0" y2="296.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="277.2" width="9" height="15.0" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="273.5" x2="224.0" y2="326.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="277.2" width="9" height="45.0" fill="#c00"/>
|
||||
<line x1="239.0" y1="318.5" x2="239.0" y2="364.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="322.2" width="9" height="37.5" fill="#c00"/>
|
||||
<line x1="254.0" y1="356.0" x2="254.0" y2="401.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="359.8" width="9" height="37.5" fill="#c00"/>
|
||||
<line x1="269.0" y1="393.5" x2="269.0" y2="430.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="397.2" width="9" height="28.4" fill="#c00"/>
|
||||
<line x1="284.0" y1="401.0" x2="284.0" y2="430.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="404.8" width="9" height="20.9" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="371.0" x2="299.0" y2="409.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="374.8" width="9" height="30.0" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="341.0" x2="314.0" y2="379.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="344.8" width="9" height="30.0" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="311.0" x2="329.0" y2="349.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="314.8" width="9" height="30.0" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="281.0" x2="344.0" y2="319.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="284.8" width="9" height="30.0" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="258.5" x2="359.0" y2="289.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="262.2" width="9" height="22.5" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="258.5" x2="374.0" y2="281.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="262.2" width="9" height="15.0" fill="#c00"/>
|
||||
<line x1="389.0" y1="273.5" x2="389.0" y2="304.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="277.2" width="9" height="22.5" fill="#c00"/>
|
||||
<line x1="404.0" y1="281.0" x2="404.0" y2="304.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="284.8" width="9" height="15.0" fill="#188a3e"/>
|
||||
<text x="269.0" y="444.2" fill="#188a3e" text-anchor="middle" style="font-weight:bold">trigger MBT-10</text>
|
||||
<text x="359.0" y="232.5" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">exact 6 bare</text>
|
||||
<text x="24" y="468" fill="#555">Cifrele 311.21 / 333.00 / +7.0 % sunt reale (03_2026-08-11.md:118-120);</text><text x="24" y="484" fill="#555">structura barelor dintre ele e construită.</text><text x="24" y="500" fill="#555">SL: materialul nu îl dă pentru acest exemplu.</text></svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
136
gen/strategii/m2d_ansamblu.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""Diagrama de ansamblu M2D (strategii/03_m2d.md, marcajul `ansamblu`).
|
||||
|
||||
„Setup M2D LONG complet pe TF mic (3 min), cu TF mare (15 min) în Buy":
|
||||
urcare -> pullback (M = Sell Momentum) -> trigger (A = primul Buy) -> intrare
|
||||
-> SL de 4 candele -> TP1 la 1:1 -> continuare -> vârf -> ieșire pe bull trail.
|
||||
|
||||
Toate close-urile, wick-urile, volumele și nivelurile vin din
|
||||
`gen/strategii/m2d_ansamblu.spec.txt`. Seria e construită (exemplu generic,
|
||||
fără ticker), la scara 1.00 $ risc / 2.2R randament — nu e un trade real.
|
||||
|
||||
Rulează:
|
||||
python3 gen/strategii/m2d_ansamblu.py
|
||||
Scrie lângă el `m2d_ansamblu.svg`.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "m2d_ansamblu.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# ---------------------------------------------------------------- seria (32 bare)
|
||||
CLOSES = [
|
||||
96.8, 97.2, 97.7, 98.1, 98.6, 99.1, 99.5, 100.0, # 0-7 urcare
|
||||
99.8, 99.4, 99.0, 99.2, 98.9, 99.15, 99.3, # 8-14 pullback (M)
|
||||
99.8, 100.2, # 15-16 trigger (A)
|
||||
100.7, 101.2, 101.6, 102.0, 102.4, 102.7, 103.0, # 17-23 continuare
|
||||
103.3, 103.1, 102.8, 102.7, 102.9, 102.5, 102.2, 102.0, # 24-31 vârf + violare
|
||||
]
|
||||
assert len(CLOSES) == 32, len(CLOSES)
|
||||
|
||||
# fitile (sus, jos); jos mai mari pe 10-12, ca minimul de 4 candele (11:15) să
|
||||
# fie clar sub intrare, la exact 98.80 (candela 12).
|
||||
WICKS = (
|
||||
[(0.15, 0.10)] * 8 # 0-7
|
||||
+ [(0.10, 0.15), (0.10, 0.20), (0.10, 0.15), # 8-10
|
||||
(0.10, 0.15), (0.10, 0.10), (0.10, 0.05), (0.10, 0.10)] # 11-14
|
||||
+ [(0.15, 0.10), (0.15, 0.10)] # 15-16
|
||||
+ [(0.15, 0.10)] * 7 # 17-23
|
||||
+ [(0.20, 0.10), (0.10, 0.15), (0.10, 0.15), (0.12, 0.15), # 24-27
|
||||
(0.10, 0.15), (0.10, 0.15), (0.10, 0.15), (0.10, 0.20)] # 28-31
|
||||
)
|
||||
assert len(WICKS) == 32, len(WICKS)
|
||||
|
||||
VOLS = (
|
||||
[62, 66, 60, 68, 64, 70, 66, 74] # 0-7 trend
|
||||
+ [44, 40, 38, 36, 34, 33, 35] # 8-14 pullback mic
|
||||
+ [82, 88] # 15-16 trigger
|
||||
+ [72, 70, 74, 68, 72, 66, 70] # 17-23 trend
|
||||
+ [76, 64, 60, 58, 62, 56, 54, 66] # 24-31 vârf + ieșire
|
||||
)
|
||||
assert len(VOLS) == 32, len(VOLS)
|
||||
|
||||
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=96.4)
|
||||
|
||||
# ---------------------------------------------------------------- assert-urile din spec
|
||||
entry = cds[15].c # 1. intrarea = close-ul trigger-ului
|
||||
SL = min(c.l for c in cds[11:15]) # 2. minimul ultimelor 4 dinainte
|
||||
TP1 = entry + (entry - SL) # 4. TP1 exact 1:1
|
||||
|
||||
assert entry == round(cds[15].c, 2), (entry, cds[15].c)
|
||||
assert SL == min(c.l for c in cds[11:15]), SL
|
||||
assert SL < entry, (SL, entry)
|
||||
assert abs(TP1 - (entry + (entry - SL))) < 1e-6
|
||||
for a, b in zip(cds, cds[1:]): # 5. serie continuă, fără goluri
|
||||
assert a.c == b.o, (a.c, b.o)
|
||||
assert cds[15].c > cds[14].c # 6. trigger în sus după pullback
|
||||
assert any(c.c < c.o for c in cds[8:15]) # 7. pullback = Sell
|
||||
assert any(c.c > c.o for c in cds[0:8]) # context în sus
|
||||
exit_price = cds[31].c
|
||||
assert exit_price > entry # 8. ieșirea e peste intrare
|
||||
|
||||
# bull trail = low-ul ultimei candele care a închis peste high-ul precedentei
|
||||
qual = [i for i in range(1, 31) if cds[i].c > cds[i - 1].h]
|
||||
assert qual and qual[-1] == 24, qual
|
||||
bull = cds[24].l
|
||||
assert cds[31].c < bull, (cds[31].c, bull) # finala închide sub linie = ieșire aici
|
||||
|
||||
r_mult = (exit_price - entry) / (entry - SL) # 9. R-ul afișat
|
||||
assert round(r_mult, 1) == 2.2, r_mult
|
||||
|
||||
# ---------------------------------------------------------------- niveluri, etichete, marks
|
||||
levels = [
|
||||
Level(entry, f"intrare {entry:.2f}", GREEN, label_at="left"),
|
||||
Level(SL, f"SL {SL:.2f} (4 candele)", RED, label_at="right"),
|
||||
Level(TP1, f"TP1 {TP1:.2f} = 1:1", BLUE, label_at="right"),
|
||||
]
|
||||
assert [round(lv.price, 2) for lv in levels] == [99.80, 98.80, 100.80]
|
||||
|
||||
exit_text = f"ieșire {exit_price:.2f} pe bull trail ({r_mult:.1f}R)"
|
||||
assert f"{r_mult:.1f}R" in exit_text
|
||||
|
||||
marks = [
|
||||
Mark(0, "TF mare (15 min) în Buy", BLUE, "above", dy=-227),
|
||||
Mark(11, "M: Sell Momentum (pullback)", RED, "below"),
|
||||
Mark(15, "A: primul Buy = trigger", GREEN, "above", dy=-122),
|
||||
# „vârf" stă sub candela 24: deasupra ei, orice text de la y-ul vârfului se
|
||||
# calcă pe titlul din stânga-sus (ambele pornesc din `top`).
|
||||
Mark(24, "vârf 103.30", GRAY, "below", dy=25),
|
||||
Mark(31, "close sub bull trail = ieși", RED, "below", dy=55),
|
||||
Mark(31, exit_text, RED, "below", dy=73),
|
||||
]
|
||||
|
||||
caption = (f"Serie construită (32 bare, 3 min). R = ({exit_price:.2f} − {entry:.2f}) / "
|
||||
f"{entry - SL:.2f} = {r_mult:.1f}R.")
|
||||
assert f"{r_mult:.1f}R" in caption
|
||||
|
||||
ch = Chart(
|
||||
candles=cds, levels=levels, marks=marks,
|
||||
mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")],
|
||||
scale=52.0, gap=16, body_w=9, left=44, width=720, vol_h=70,
|
||||
vol_label="volum",
|
||||
title="Simbol construit — 3 min (TF mare 15 min în Buy)",
|
||||
caption=caption,
|
||||
)
|
||||
svg = ch.render()
|
||||
|
||||
# bull trail: nu e un `Level` (spec-ul cere exact trei niveluri), deci se
|
||||
# injectează ca linie separată, la aceeași scară (`ch._y`).
|
||||
yb = ch._y(bull)
|
||||
bull_layer = (
|
||||
f'<line x1="24" y1="{yb:.1f}" x2="{ch.width - 14}" y2="{yb:.1f}" '
|
||||
f'stroke="{ORANGE}" stroke-width="1.5" stroke-dasharray="2 3"/>'
|
||||
f'<text x="{ch.width - 28}" y="{yb - 6:.1f}" fill="{ORANGE}" '
|
||||
f'text-anchor="end">bull trail {bull:.2f}</text>'
|
||||
)
|
||||
i = svg.index(">") + 1
|
||||
svg = svg[:i] + "\n " + bull_layer + svg[i:]
|
||||
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
OUT.write_text(svg)
|
||||
print(f"scris {OUT.name}: {len(cds)} candele, intrare {entry:.2f}, SL {SL:.2f}, "
|
||||
f"TP1 {TP1:.2f}, ieșire {exit_price:.2f} = {r_mult:.1f}R, bull trail {bull:.2f}")
|
||||
117
gen/strategii/m2d_ansamblu.svg
Normal file
@@ -0,0 +1,117 @@
|
||||
<svg viewBox="0 0 720 568" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<line x1="24" y1="87.2" x2="706" y2="87.2" stroke="#b06000" stroke-width="1.5" stroke-dasharray="2 3"/><text x="692" y="81.2" fill="#b06000" text-anchor="end">bull trail 102.90</text>
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Simbol construit — 3 min (TF mare 15 min în Buy)</text>
|
||||
<line x1="24" y1="248.4" x2="706" y2="248.4" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="242.4" fill="#188a3e">intrare 99.80</text>
|
||||
<line x1="24" y1="300.4" x2="706" y2="300.4" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="294.4" fill="#c00" text-anchor="end">SL 98.80 (4 candele)</text>
|
||||
<line x1="24" y1="196.4" x2="706" y2="196.4" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="190.4" fill="#1a5fb4" text-anchor="end">TP1 100.80 = 1:1</text>
|
||||
<polyline points="348.0,276.3 364.0,262.8 380.0,249.3 396.0,236.3 412.0,223.6 428.0,211.3 444.0,200.9 460.0,192.4 476.0,185.3 492.0,177.3 508.0,169.2 524.0,160.9 540.0,153.6" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="546" y="148" fill="#b06000">SMA-20</text>
|
||||
<line x1="44.0" y1="396.6" x2="44.0" y2="430.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="404.4" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="60.0" y1="375.8" x2="60.0" y2="409.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="55.5" y="383.6" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="76.0" y1="349.8" x2="76.0" y2="388.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="71.5" y="357.6" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="92.0" y1="329.0" x2="92.0" y2="362.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="87.5" y="336.8" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="108.0" y1="303.0" x2="108.0" y2="342.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.5" y="310.8" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="124.0" y1="277.0" x2="124.0" y2="316.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="119.5" y="284.8" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="140.0" y1="256.2" x2="140.0" y2="290.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="135.5" y="264.0" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="156.0" y1="230.2" x2="156.0" y2="269.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="151.5" y="238.0" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="172.0" y1="232.8" x2="172.0" y2="256.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="167.5" y="238.0" width="9" height="10.4" fill="#c00"/>
|
||||
<line x1="188.0" y1="243.2" x2="188.0" y2="279.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="183.5" y="248.4" width="9" height="20.8" fill="#c00"/>
|
||||
<line x1="204.0" y1="264.0" x2="204.0" y2="297.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="199.5" y="269.2" width="9" height="20.8" fill="#c00"/>
|
||||
<line x1="220.0" y1="274.4" x2="220.0" y2="297.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="215.5" y="279.6" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="236.0" y1="274.4" x2="236.0" y2="300.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="231.5" y="279.6" width="9" height="15.6" fill="#c00"/>
|
||||
<line x1="252.0" y1="277.0" x2="252.0" y2="297.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="247.5" y="282.2" width="9" height="13.0" fill="#188a3e"/>
|
||||
<line x1="268.0" y1="269.2" x2="268.0" y2="287.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="263.5" y="274.4" width="9" height="7.8" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="240.6" x2="284.0" y2="279.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="248.4" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="300.0" y1="219.8" x2="300.0" y2="253.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="295.5" y="227.6" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="316.0" y1="193.8" x2="316.0" y2="232.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="311.5" y="201.6" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="332.0" y1="167.8" x2="332.0" y2="206.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="327.5" y="175.6" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="348.0" y1="147.0" x2="348.0" y2="180.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="343.5" y="154.8" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="364.0" y1="126.2" x2="364.0" y2="160.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="359.5" y="134.0" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="380.0" y1="105.4" x2="380.0" y2="139.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="375.5" y="113.2" width="9" height="20.8" fill="#188a3e"/>
|
||||
<line x1="396.0" y1="89.8" x2="396.0" y2="118.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="391.5" y="97.6" width="9" height="15.6" fill="#188a3e"/>
|
||||
<line x1="412.0" y1="74.2" x2="412.0" y2="102.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="407.5" y="82.0" width="9" height="15.6" fill="#188a3e"/>
|
||||
<line x1="428.0" y1="56.0" x2="428.0" y2="87.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="423.5" y="66.4" width="9" height="15.6" fill="#188a3e"/>
|
||||
<line x1="444.0" y1="61.2" x2="444.0" y2="84.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="439.5" y="66.4" width="9" height="10.4" fill="#c00"/>
|
||||
<line x1="460.0" y1="71.6" x2="460.0" y2="100.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="455.5" y="76.8" width="9" height="15.6" fill="#c00"/>
|
||||
<line x1="476.0" y1="86.2" x2="476.0" y2="105.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="471.5" y="92.4" width="9" height="5.2" fill="#c00"/>
|
||||
<line x1="492.0" y1="82.0" x2="492.0" y2="105.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="487.5" y="87.2" width="9" height="10.4" fill="#188a3e"/>
|
||||
<line x1="508.0" y1="82.0" x2="508.0" y2="115.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="503.5" y="87.2" width="9" height="20.8" fill="#c00"/>
|
||||
<line x1="524.0" y1="102.8" x2="524.0" y2="131.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="108.0" width="9" height="15.6" fill="#c00"/>
|
||||
<line x1="540.0" y1="118.4" x2="540.0" y2="144.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="535.5" y="123.6" width="9" height="10.4" fill="#c00"/>
|
||||
<line x1="24" y1="526.4" x2="706" y2="526.4" stroke="#888"/>
|
||||
<rect x="39.5" y="477.1" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="55.5" y="473.9" width="9" height="52.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="71.5" y="478.7" width="9" height="47.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="87.5" y="472.3" width="9" height="54.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="103.5" y="475.5" width="9" height="50.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="119.5" y="470.7" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="135.5" y="473.9" width="9" height="52.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="151.5" y="467.5" width="9" height="58.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="167.5" y="491.4" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="183.5" y="494.6" width="9" height="31.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="199.5" y="496.2" width="9" height="30.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="215.5" y="497.8" width="9" height="28.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="231.5" y="499.4" width="9" height="27.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="247.5" y="500.1" width="9" height="26.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="263.5" y="498.6" width="9" height="27.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="461.2" width="9" height="65.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="295.5" y="456.4" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="311.5" y="469.1" width="9" height="57.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="327.5" y="470.7" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="343.5" y="467.5" width="9" height="58.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="359.5" y="472.3" width="9" height="54.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="375.5" y="469.1" width="9" height="57.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="391.5" y="473.9" width="9" height="52.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="407.5" y="470.7" width="9" height="55.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="423.5" y="465.9" width="9" height="60.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="439.5" y="475.5" width="9" height="50.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="455.5" y="478.7" width="9" height="47.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="471.5" y="480.3" width="9" height="46.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="487.5" y="477.1" width="9" height="49.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="503.5" y="481.9" width="9" height="44.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="519.5" y="483.4" width="9" height="43.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="535.5" y="473.9" width="9" height="52.5" fill="#c00" opacity="0.55"/>
|
||||
<text x="24" y="450.4" fill="#555">volum</text>
|
||||
<text x="32.0" y="159.6" fill="#1a5fb4" text-anchor="start" style="font-weight:bold">TF mare (15 min) în Buy</text>
|
||||
<text x="220.0" y="317.8" fill="#c00" text-anchor="middle" style="font-weight:bold">M: Sell Momentum (pullback)</text>
|
||||
<text x="284.0" y="108.6" fill="#188a3e" text-anchor="middle" style="font-weight:bold">A: primul Buy = trigger</text>
|
||||
<text x="428.0" y="132.2" fill="#777" text-anchor="middle" style="font-weight:bold">vârf 103.30</text>
|
||||
<text x="540.0" y="219.4" fill="#c00" text-anchor="middle" style="font-weight:bold">close sub bull trail = ieși</text>
|
||||
<text x="540.0" y="237.4" fill="#c00" text-anchor="middle" style="font-weight:bold">ieșire 102.00 pe bull trail (2.2R)</text>
|
||||
<text x="24" y="556" fill="#555">Serie construită (32 bare, 3 min). R = (102.00 − 99.80) / 1.00 = 2.2R.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
289
gen/strategii/m2d_exemplu.py
Normal file
@@ -0,0 +1,289 @@
|
||||
"""Diagrama din `gen/strategii/m2d_exemplu.spec.txt` (TIP: PRET).
|
||||
|
||||
Exemplul real Riot din curs (`14_strategia_1_m2d.md:227-236`): SHORT pe TF mic
|
||||
3 min, cu TF mare 15 min în Sell. Pullback-ul Buy de pe TF mic (M) e urmat de
|
||||
primul Sell (A = trigger); SL = cel mai sus high din 4 candele = +0.85%,
|
||||
TP1 = 1:1 = -0.85%, iar ieșirea se face pe violarea bear trail-ului la +4.55%
|
||||
(risc/recompensă efectiv ≈ 1:5.3).
|
||||
|
||||
Reale din curs: procentele 0.85% / 1.42% / 4.55% și contextul calitativ.
|
||||
Construită de mine: axa de preț absolută, intrarea fixată la 100.00 ca reper.
|
||||
Legenda spune explicit lucrul ăsta.
|
||||
|
||||
Serie continuă de 30 de candele din lista de close-uri din specificație,
|
||||
open = close-ul precedentului, wick-uri alese ca să scoată exact nivelurile
|
||||
(candela 13: high 100.85 = SL), panou de volum dedesubt.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "m2d_exemplu.svg"
|
||||
|
||||
# ── nivelurile cerute de specificație ──────────────────────────────────────
|
||||
ENTRY = 100.00 # close-ul candelei de trigger 14
|
||||
SL = 100.85 # max high pe candelele 11:15 = +0.85%
|
||||
TP1 = 99.15 # entry - 0.85% (1:1)
|
||||
EXIT = 95.45 # close-ul candelei 29 = +4.55%
|
||||
|
||||
# ── seria: 30 de close-uri, exact ca în specificație ───────────────────────
|
||||
closes = [
|
||||
97.6, 97.1, 96.7, 96.3, 95.9, 95.6, 95.9, # 0-6 coborâre
|
||||
96.4, 96.9, 97.5, 98.2, 99.0, 99.7, 100.3, # 7-13 pullback Buy (M)
|
||||
100.00, # 14 trigger Sell (A)
|
||||
99.5, 99.0, 98.5, 98.0, 97.6, 97.2, 96.9, 96.6, 96.3, 96.1,
|
||||
95.8, 95.5, 95.2, 95.35, 95.45, # 15-29 bear trail
|
||||
]
|
||||
assert len(closes) == 30, len(closes)
|
||||
|
||||
# wick-uri: candela 13 are nevoie de un wick sus până la 100.85 (SL-ul de 4
|
||||
# candele); restul sunt mici, ca seria să arate ca un grafic real.
|
||||
wicks = [
|
||||
(0.15, 0.20), (0.12, 0.18), (0.15, 0.15), (0.10, 0.20), (0.12, 0.25),
|
||||
(0.10, 0.30), (0.25, 0.15),
|
||||
(0.20, 0.15), (0.15, 0.20), (0.20, 0.15), (0.20, 0.20), (0.20, 0.18),
|
||||
(0.25, 0.15), (SL - 100.3, 0.15), # 13: high = 100.85
|
||||
(0.15, 0.25), # 14: trigger Sell
|
||||
(0.15, 0.20), (0.15, 0.18), (0.12, 0.20), (0.15, 0.18), (0.15, 0.20),
|
||||
(0.12, 0.18), (0.15, 0.20), (0.12, 0.18), (0.15, 0.20), (0.12, 0.18),
|
||||
(0.15, 0.20), (0.12, 0.18), (0.15, 0.20), (0.20, 0.15), (0.30, 0.10),
|
||||
]
|
||||
assert len(wicks) == 30, len(wicks)
|
||||
|
||||
vols = [
|
||||
48, 45, 42, 40, 44, 50, 46,
|
||||
42, 44, 40, 43, 47, 52, 68,
|
||||
85,
|
||||
60, 55, 52, 48, 46, 44, 42, 40, 43, 41, 44, 42, 40, 45, 72,
|
||||
]
|
||||
assert len(vols) == 30, len(vols)
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=98.0)
|
||||
|
||||
# ── assert-urile cerute de specificație ────────────────────────────────────
|
||||
# 1. entry == close-ul candelei de trigger 14
|
||||
assert round(cds[14].c, 2) == ENTRY == 100.00, cds[14].c
|
||||
|
||||
# 2. SL == max high pe candelele 11:15
|
||||
max_h = max(c.h for c in cds[11:15])
|
||||
assert round(max_h, 2) == SL == 100.85, max_h
|
||||
|
||||
# 3. SL = +0.85% față de entry
|
||||
assert abs((SL - ENTRY) / ENTRY - 0.0085) < 1e-4
|
||||
|
||||
# 4. TP1 = entry − (SL − entry) = 1:1
|
||||
assert TP1 == round(ENTRY - (SL - ENTRY), 2) == 99.15, TP1
|
||||
assert abs((ENTRY - TP1) / ENTRY - 0.0085) < 1e-4
|
||||
|
||||
# 5. exit == close-ul candelei 29
|
||||
assert round(cds[29].c, 2) == EXIT == 95.45, cds[29].c
|
||||
|
||||
# 6. R efectiv ≈ 1:5.3
|
||||
assert abs((ENTRY - EXIT) / (SL - ENTRY) - 5.35) < 0.05
|
||||
|
||||
# 7. niciun close după trigger nu trece peste SL
|
||||
assert max(c.c for c in cds[15:]) < SL, max(c.c for c in cds[15:])
|
||||
|
||||
# 8. fiecare open == close-ul barei anterioare
|
||||
for a, b in zip(cds, cds[1:]):
|
||||
assert abs(a.c - b.o) < 1e-9, "serie cu gol artificial între bare"
|
||||
|
||||
# 9. candela 14 închide în jos după pullback-ul Buy (7-13)
|
||||
assert cds[14].c < cds[13].c, (cds[13].c, cds[14].c)
|
||||
assert any(cds[i].c > cds[i].o for i in range(7, 14)), "pullback Buy absent"
|
||||
|
||||
# ── geometria, ca să așez marks-urile „below" fără suprapunere ─────────────
|
||||
SCALE = 42
|
||||
levels = [
|
||||
Level(SL, "SL 100.85 = +0.85%", "#c00"),
|
||||
Level(ENTRY, "intrare 100.00 (trigger Sell)", "#333"),
|
||||
Level(TP1, "TP1 99.15 = 1:1", "#1a5fb4"),
|
||||
Level(EXIT, "ieșire 95.45 = +4.55% ≈ 1:5.3", "#188a3e"),
|
||||
]
|
||||
prices = [p for c in cds for p in (c.o, c.h, c.l, c.c)] + [lv.price for lv in levels]
|
||||
pmax, pmin = max(prices), min(prices)
|
||||
top = 34 + 22 # implicit + titlu, fără segment de trend
|
||||
body_bottom = top + (pmax - pmin) * SCALE
|
||||
|
||||
|
||||
def y(price: float) -> float:
|
||||
return top + (pmax - price) * SCALE
|
||||
|
||||
|
||||
# ── așezarea etichetelor: caut un y unde textul nu atinge vreun corp de
|
||||
# candelă și nu se suprapune cu altă etichetă. Sub grafic sunt doar ~14 px
|
||||
# liberi (body_bottom .. body_bottom+14), deci etichetele lungi trebuie
|
||||
# coborâte/diferite pe rânduri. Fără asta, „A" și „M" trec peste candele.
|
||||
GAP, LEFT, WIDTH, BODY_W = 15, 44, 700, 10
|
||||
|
||||
|
||||
def cx(i: int) -> float:
|
||||
return LEFT + i * GAP
|
||||
|
||||
|
||||
def text_x(i: int, text: str) -> tuple[float, float]:
|
||||
"""(x0, x1) al textului bold ancorat pe candela i, ca în svg_candles."""
|
||||
x = cx(i)
|
||||
w = len(text) * 7.44
|
||||
if x < 90:
|
||||
return x - 12, x - 12 + w
|
||||
if x > WIDTH - 90:
|
||||
return x + 12 - w, x + 12
|
||||
return x - w / 2, x + w / 2
|
||||
|
||||
|
||||
corpuri = []
|
||||
for i, c in enumerate(cds):
|
||||
y0, y1 = y(max(c.o, c.c)), y(min(c.o, c.c))
|
||||
y1 = max(y1, y0 + 1.6)
|
||||
corpuri.append((cx(i) - BODY_W / 2, cx(i) + BODY_W / 2, y0, y1))
|
||||
|
||||
linii = [y(lv.price) for lv in levels]
|
||||
|
||||
|
||||
def liber_fara_puse(i: int, text: str, yv: float) -> bool:
|
||||
x0, x1 = text_x(i, text)
|
||||
ty0, ty1 = yv - 10, yv + 3
|
||||
if x0 < -2 or x1 > WIDTH + 2 or yv < top + 12 or yv > body_bottom + 14:
|
||||
return False
|
||||
for bx0, bx1, by0, by1 in corpuri:
|
||||
if x0 < bx1 - 2 and bx0 < x1 - 2 and ty0 < by1 - 2 and by0 < ty1 - 2:
|
||||
return False
|
||||
# nicio linie de nivel (SL/entry/TP1/exit) prin mijlocul textului
|
||||
for ly in linii:
|
||||
if ty0 - 1 <= ly <= ty1 + 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def conflict(a: tuple, b: tuple) -> bool:
|
||||
"""Două etichete se bat dacă au aceeași bandă de y și se suprapun pe x."""
|
||||
ax0, ax1, ay = a
|
||||
bx0, bx1, by = b
|
||||
return abs(ay - by) < 13 and ax0 < bx1 - 2 and bx0 < ax1 - 2
|
||||
|
||||
|
||||
puse: list[tuple] = []
|
||||
definiții = [
|
||||
(0, "TF mare (15 min) în Sell", "#c00", "above"),
|
||||
(7, "M: Buy Momentum (pullback)", "#0891b2", "above"),
|
||||
(14, "A: primul Sell = trigger, intri short", "#c00", "below"),
|
||||
(17, "deja peste TP1 (1.42% din curs)", "#188a3e", "below"),
|
||||
(29, "close peste bear trail = ieși", "#188a3e", "below"),
|
||||
]
|
||||
|
||||
|
||||
def natural_y(i: int, where: str) -> float:
|
||||
return (y(cds[i].h) - 10 if where == "above"
|
||||
else min(y(cds[i].l) + 20, body_bottom + 14))
|
||||
|
||||
|
||||
# candidați: toate y-urile care nu ating niciun corp de candelă, ordonate
|
||||
# după cât de aproape sunt de poziția naturală (cu preferință pentru partea
|
||||
# cerută de specificație: „above" în sus, „below" în jos)
|
||||
candidati = []
|
||||
for i, text, _color, where in definiții:
|
||||
nat = natural_y(i, where)
|
||||
ys = [yy for yy in range(int(top + 12), int(body_bottom + 14) + 1)
|
||||
if liber_fara_puse(i, text, yy)]
|
||||
if where == "below":
|
||||
# o etichetă „below" rămâne sub candelă: nu urcă peste preț
|
||||
ys = [yy for yy in ys if yy >= nat]
|
||||
ys.sort(key=lambda yv: yv - nat)
|
||||
else:
|
||||
# „above" preferă în sus, dar dacă nu încape coboară sub serie
|
||||
ys.sort(key=lambda yv: (abs(yv - nat), 0 if yv <= nat else 1))
|
||||
candidati.append(ys)
|
||||
assert ys, f"nicio poziție liberă pentru {text!r}"
|
||||
|
||||
# backtracking: plasez întâi etichetele cu cele mai puține opțiuni
|
||||
ordine = sorted(range(len(definiții)), key=lambda k: len(candidati[k]))
|
||||
y_final = [None] * len(definiții)
|
||||
|
||||
|
||||
def rezolva(k: int) -> bool:
|
||||
if k == len(ordine):
|
||||
return True
|
||||
idx = ordine[k]
|
||||
ci = definiții[idx][0]
|
||||
for yv in candidati[idx]:
|
||||
box = (*text_x(ci, definiții[idx][1]), yv)
|
||||
if all(not conflict(box, p) for p in puse):
|
||||
puse.append(box)
|
||||
y_final[idx] = yv
|
||||
if rezolva(k + 1):
|
||||
return True
|
||||
puse.pop()
|
||||
return False
|
||||
|
||||
|
||||
assert rezolva(0), "nu găsesc o așezare fără suprapuneri pentru etichete"
|
||||
|
||||
marks = [Mark(i, text, color, where, dy=y_final[k] - natural_y(i, where))
|
||||
for k, (i, text, color, where) in enumerate(definiții)]
|
||||
assert all(y_final), y_final
|
||||
|
||||
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=levels,
|
||||
marks=marks,
|
||||
scale=SCALE, gap=GAP, body_w=BODY_W, left=LEFT, width=WIDTH, vol_h=70,
|
||||
vol_label="volum",
|
||||
title="RIOT — 3 min (TF mare 15 min în Sell)",
|
||||
caption="Axa de preț e construită (intrarea = 100.00); procentele sunt reale din curs.",
|
||||
)
|
||||
svg = ch.render()
|
||||
assert "<svg" in svg and svg.count("\n\n") == 0, "linie goală în SVG"
|
||||
|
||||
# ── verificare: fiecare etichetă e în pânză și nu se suprapune alta ────────
|
||||
m = re.search(r'viewBox="0 0 (\d+) (\d+)"', svg)
|
||||
W, H = int(m.group(1)), int(m.group(2))
|
||||
texte = re.findall(r'<text x="([\d.]+)" y="([\d.]+)"([^>]*)>([^<]*)</text>', svg)
|
||||
cutii, cutii_text = [], []
|
||||
for xs, ys, attrs, continut in texte:
|
||||
x, yv = float(xs), float(ys)
|
||||
w = len(continut) * (7.44 if "bold" in attrs else 6.72)
|
||||
if 'text-anchor="end"' in attrs:
|
||||
x0 = x - w
|
||||
elif 'text-anchor="middle"' in attrs:
|
||||
x0 = x - w / 2
|
||||
else:
|
||||
x0 = x
|
||||
assert -2 <= x0 and x0 + w <= W + 2, f"etichetă în afara pânzei: {continut!r}"
|
||||
assert 0 <= yv <= H, f"etichetă în afara înălțimii: {continut!r}"
|
||||
cutii.append((x0, x0 + w, yv, continut))
|
||||
cutii_text.append((x0, x0 + w, yv - 10, yv + 3, continut))
|
||||
for i in range(len(cutii)):
|
||||
for j in range(i + 1, len(cutii)):
|
||||
a, b = cutii[i], cutii[j]
|
||||
if abs(a[2] - b[2]) < 13 and a[0] < b[1] - 2 and b[0] < a[1] - 2:
|
||||
raise AssertionError(f"texte suprapuse: {a[3]!r} / {b[3]!r}")
|
||||
|
||||
# ── verificare: nicio etichetă peste un corp de candelă ────────────────────
|
||||
corpuri = []
|
||||
for mm in re.finditer(
|
||||
r'<rect x="([\d.]+)" y="([\d.]+)" width="10" height="([\d.]+)" '
|
||||
r'fill="#[0-9a-f]{3,6}"/>', svg):
|
||||
x0, y0, hh = map(float, mm.groups())
|
||||
corpuri.append((x0, x0 + 10, y0, y0 + hh))
|
||||
for tx0, tx1, ty0, ty1, continut in cutii_text:
|
||||
for cx0, cx1, cy0, cy1 in corpuri:
|
||||
if tx0 < cx1 - 2 and cx0 < tx1 - 2 and ty0 < cy1 - 2 and cy0 < ty1 - 2:
|
||||
raise AssertionError(
|
||||
f"etichetă peste candelă: {continut!r} la y~{ty0:.0f} "
|
||||
f"({tx0:.0f}-{tx1:.0f}) vs rect {cx0:.0f}-{cx1:.0f} "
|
||||
f"y {cy0:.0f}-{cy1:.0f}")
|
||||
|
||||
# legenda mai spune explicit că axa e construită (cerință de fidelitate)
|
||||
assert "construită" in svg and "procentele sunt reale" in svg, \
|
||||
"lipsește nota despre axa construită"
|
||||
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"serie {len(cds)} candele | intrare {ENTRY:.2f} @ bara 14 | "
|
||||
f"SL {SL:.2f} (+0.85%) | TP1 {TP1:.2f} (1:1) | "
|
||||
f"ieșire {EXIT:.2f} (+4.55%, R {(ENTRY - EXIT) / (SL - ENTRY):.2f})")
|
||||
print(f"scris {OUT}")
|
||||
109
gen/strategii/m2d_exemplu.svg
Normal file
@@ -0,0 +1,109 @@
|
||||
<svg viewBox="0 0 700 440" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">RIOT — 3 min (TF mare 15 min în Sell)</text>
|
||||
<line x1="24" y1="56.0" x2="686" y2="56.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="672" y="50.0" fill="#c00" text-anchor="end">SL 100.85 = +0.85%</text>
|
||||
<line x1="24" y1="91.7" x2="686" y2="91.7" stroke="#333" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="672" y="85.7" fill="#333" text-anchor="end">intrare 100.00 (trigger Sell)</text>
|
||||
<line x1="24" y1="127.4" x2="686" y2="127.4" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="672" y="121.4" fill="#1a5fb4" text-anchor="end">TP1 99.15 = 1:1</text>
|
||||
<line x1="24" y1="282.8" x2="686" y2="282.8" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="672" y="276.8" fill="#188a3e" text-anchor="end">ieșire 95.45 = +4.55% ≈ 1:5.3</text>
|
||||
<line x1="44.0" y1="169.4" x2="44.0" y2="200.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.0" y="175.7" width="10" height="16.8" fill="#c00"/>
|
||||
<line x1="59.0" y1="187.5" x2="59.0" y2="221.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.0" y="192.5" width="10" height="21.0" fill="#c00"/>
|
||||
<line x1="74.0" y1="207.2" x2="74.0" y2="236.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.0" y="213.5" width="10" height="16.8" fill="#c00"/>
|
||||
<line x1="89.0" y1="226.1" x2="89.0" y2="255.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.0" y="230.3" width="10" height="16.8" fill="#c00"/>
|
||||
<line x1="104.0" y1="242.1" x2="104.0" y2="274.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.0" y="247.1" width="10" height="16.8" fill="#c00"/>
|
||||
<line x1="119.0" y1="259.7" x2="119.0" y2="289.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.0" y="263.9" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="134.0" y1="253.4" x2="134.0" y2="282.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.0" y="263.9" width="10" height="12.6" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="234.5" x2="149.0" y2="270.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.0" y="242.9" width="10" height="21.0" fill="#188a3e"/>
|
||||
<line x1="164.0" y1="215.6" x2="164.0" y2="251.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.0" y="221.9" width="10" height="21.0" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="188.3" x2="179.0" y2="228.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.0" y="196.7" width="10" height="25.2" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="158.9" x2="194.0" y2="205.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.0" y="167.3" width="10" height="29.4" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="125.3" x2="209.0" y2="174.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.0" y="133.7" width="10" height="33.6" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="93.8" x2="224.0" y2="140.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.0" y="104.3" width="10" height="29.4" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="56.0" x2="239.0" y2="110.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.0" y="79.1" width="10" height="25.2" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="72.8" x2="254.0" y2="102.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.0" y="79.1" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="269.0" y1="85.4" x2="269.0" y2="121.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.0" y="91.7" width="10" height="21.0" fill="#c00"/>
|
||||
<line x1="284.0" y1="106.4" x2="284.0" y2="141.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.0" y="112.7" width="10" height="21.0" fill="#c00"/>
|
||||
<line x1="299.0" y1="128.7" x2="299.0" y2="163.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.0" y="133.7" width="10" height="21.0" fill="#c00"/>
|
||||
<line x1="314.0" y1="148.4" x2="314.0" y2="183.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.0" y="154.7" width="10" height="21.0" fill="#c00"/>
|
||||
<line x1="329.0" y1="169.4" x2="329.0" y2="200.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.0" y="175.7" width="10" height="16.8" fill="#c00"/>
|
||||
<line x1="344.0" y1="187.5" x2="344.0" y2="216.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.0" y="192.5" width="10" height="16.8" fill="#c00"/>
|
||||
<line x1="359.0" y1="203.0" x2="359.0" y2="230.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.0" y="209.3" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="374.0" y1="216.9" x2="374.0" y2="242.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.0" y="221.9" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="389.0" y1="228.2" x2="389.0" y2="255.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.0" y="234.5" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="404.0" y1="242.1" x2="404.0" y2="263.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.0" y="247.1" width="10" height="8.4" fill="#c00"/>
|
||||
<line x1="419.0" y1="249.2" x2="419.0" y2="276.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.0" y="255.5" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="434.0" y1="263.1" x2="434.0" y2="288.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.0" y="268.1" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="449.0" y1="274.4" x2="449.0" y2="301.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.0" y="280.7" width="10" height="12.6" fill="#c00"/>
|
||||
<line x1="464.0" y1="278.6" x2="464.0" y2="299.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.0" y="287.0" width="10" height="6.3" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="270.2" x2="479.0" y2="291.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.0" y="282.8" width="10" height="4.2" fill="#188a3e"/>
|
||||
<line x1="24" y1="397.7" x2="686" y2="397.7" stroke="#888"/>
|
||||
<rect x="39.0" y="358.2" width="10" height="39.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.0" y="360.6" width="10" height="37.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.0" y="363.1" width="10" height="34.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="84.0" y="364.8" width="10" height="32.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.0" y="361.5" width="10" height="36.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.0" y="356.5" width="10" height="41.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.0" y="359.8" width="10" height="37.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.0" y="363.1" width="10" height="34.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="159.0" y="361.5" width="10" height="36.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.0" y="364.8" width="10" height="32.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="189.0" y="362.3" width="10" height="35.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.0" y="359.0" width="10" height="38.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="219.0" y="354.9" width="10" height="42.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.0" y="341.7" width="10" height="56.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.0" y="327.7" width="10" height="70.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.0" y="348.3" width="10" height="49.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.0" y="352.4" width="10" height="45.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.0" y="354.9" width="10" height="42.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.0" y="358.2" width="10" height="39.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.0" y="359.8" width="10" height="37.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="339.0" y="361.5" width="10" height="36.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="354.0" y="363.1" width="10" height="34.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="369.0" y="364.8" width="10" height="32.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="384.0" y="362.3" width="10" height="35.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.0" y="363.9" width="10" height="33.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="414.0" y="361.5" width="10" height="36.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="429.0" y="363.1" width="10" height="34.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="444.0" y="364.8" width="10" height="32.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="459.0" y="360.6" width="10" height="37.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.0" y="338.4" width="10" height="59.3" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="321.7" fill="#555">volum</text>
|
||||
<text x="32.0" y="123.0" fill="#c00" text-anchor="start" style="font-weight:bold">TF mare (15 min) în Sell</text>
|
||||
<text x="149.0" y="307.0" fill="#0891b2" text-anchor="middle" style="font-weight:bold">M: Buy Momentum (pullback)</text>
|
||||
<text x="254.0" y="294.0" fill="#c00" text-anchor="middle" style="font-weight:bold">A: primul Sell = trigger, intri short</text>
|
||||
<text x="299.0" y="264.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">deja peste TP1 (1.42% din curs)</text>
|
||||
<text x="479.0" y="312.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">close peste bear trail = ieși</text>
|
||||
<text x="24" y="428" fill="#555">Axa de preț e construită (intrarea = 100.00); procentele sunt reale din curs.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.9 KiB |
268
gen/strategii/m2d_secventa.py
Normal file
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Secvența M → A a strategiei M2D — schemă conceptuală pe două panouri.
|
||||
|
||||
Pentru `strategii/03_m2d.md`, marcajul `<!-- DIAGRAMA: secventa -->`.
|
||||
Nu e grafic de preț (spec: „schemă pe două panouri, nu candele de trade”):
|
||||
SVG scris de mână, doar stdlib — nu se folosește svg_candles / pchart.
|
||||
|
||||
Ce arată (toate etichetele vin din spec; nicio cifră de preț nu se inventează):
|
||||
- panoul de sus, LONG: pe TF mare (15 min) polilinia urcă și „rămâne în Buy pe
|
||||
tot parcursul”; pe TF mic (3 min) urcă → pullback (M: Sell Momentum) → urcă
|
||||
peste vârful anterior (A: Buy Momentum — TRIGGER, intri long);
|
||||
- panoul de jos, SHORT: oglindit — TF mare coboară („rămâne în Sell”), TF mic
|
||||
coboară → pullback în sus (M: Buy Momentum) → coboară sub minimul anterior
|
||||
(A: Sell Momentum — TRIGGER, intri short);
|
||||
- două linii verticale punctate leagă momentul M și momentul A de rândul de sus
|
||||
(verificarea că TF mare rămâne în aceeași direcție);
|
||||
- textul de subsol: „Secvența contează, nu starea...”.
|
||||
|
||||
Rulare: python3 gen/strategii/m2d_secventa.py -> gen/strategii/m2d_secventa.svg
|
||||
"""
|
||||
from pathlib import Path
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
W, H = 700, 780
|
||||
BLACK, GRAY, RED, GREEN, BLUE, CYAN, WHITE = (
|
||||
"#000000", "#777777", "#c00", "#188a3e", "#1a5fb4", "#00b8c4", "#ffffff")
|
||||
PANEL_BG, PANEL_EDGE = "#fafafa", "#ddd"
|
||||
|
||||
# ---------------------------------------------------------------- geometrie
|
||||
# Fiecare polilinie: listă de (x, y). y mic = sus.
|
||||
# Index: 0 = start, 1 = vârf/trough anterior, 2 = M, 3 = A, 4 = continuare.
|
||||
LONG_TOP = [(40, 162), (150, 148), (250, 138), (430, 128), (660, 118)]
|
||||
LONG_BOT = [(40, 255), (180, 225), (250, 282), (430, 210), (660, 198)]
|
||||
SHORT_TOP = [(40, 430), (150, 444), (250, 454), (430, 464), (660, 474)]
|
||||
SHORT_BOT = [(40, 527), (180, 562), (250, 500), (430, 574), (660, 588)]
|
||||
|
||||
L_M, L_A, S_M, S_A = 2, 3, 2, 3 # indicii momentelor M și A
|
||||
|
||||
PANELS = {
|
||||
"LONG": (14, 92, 672, 250),
|
||||
"SHORT": (14, 362, 672, 270),
|
||||
}
|
||||
|
||||
TEXTS = [] # (x, y, text, size, weight, anchor, color) pentru verificare
|
||||
|
||||
|
||||
def txt(x, y, s, size=13, weight="400", fill=BLACK, anchor="middle"):
|
||||
TEXTS.append((x, y, s, size, weight, anchor, fill))
|
||||
return (f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" '
|
||||
f'font-weight="{weight}" fill="{fill}" text-anchor="{anchor}">'
|
||||
f'{escape(s)}</text>')
|
||||
|
||||
|
||||
def line(x1, y1, x2, y2, stroke=GRAY, width=1.0, dash=None):
|
||||
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||
return (f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" '
|
||||
f'stroke="{stroke}" stroke-width="{width}"{d}/>')
|
||||
|
||||
|
||||
def poly(pts, stroke, width=2.0):
|
||||
p = " ".join(f"{x:.1f},{y:.1f}" for x, y in pts)
|
||||
return f'<polyline points="{p}" fill="none" stroke="{stroke}" stroke-width="{width}"/>'
|
||||
|
||||
|
||||
def dot(x, y, fill, r=5.0):
|
||||
return f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{r:.1f}" fill="{fill}"/>'
|
||||
|
||||
|
||||
def build():
|
||||
p = [
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
|
||||
f'width="{W}" height="{H}" role="img" aria-label="Secventa M apoi A a '
|
||||
f'strategiei M2D, pe doua time frame-uri, pentru long si short: time '
|
||||
f'frame-ul mare ramane in directie cat timp time frame-ul mic face '
|
||||
f'pullback (M) si apoi da trigger-ul in directia mare (A)">',
|
||||
'<g font-family="system-ui, sans-serif">',
|
||||
f'<rect x="0" y="0" width="{W}" height="{H}" fill="{WHITE}"/>',
|
||||
txt(W / 2, 30, "Secvența M → A: nu e o stare, e o secvență",
|
||||
19, "700", BLACK),
|
||||
txt(W / 2, 52, "M2D — același moment, două time frame-uri: "
|
||||
"TF mare dă direcția, TF mic dă trigger-ul",
|
||||
13, "400", GRAY),
|
||||
txt(W / 2, 72, "sus = LONG; jos = SHORT — aceleași momente, în oglindă",
|
||||
13, "400", GRAY),
|
||||
]
|
||||
|
||||
# ------------------------------------------------------ panouri
|
||||
for name, (px, py, pw, ph) in PANELS.items():
|
||||
p.append(f'<rect x="{px}" y="{py}" width="{pw}" height="{ph}" rx="8" '
|
||||
f'fill="{PANEL_BG}" stroke="{PANEL_EDGE}" stroke-width="1.2"/>')
|
||||
|
||||
# ------------------------------------------------------ panoul LONG
|
||||
p.append(txt(30, 116, "LONG", 15, "700", GREEN, "start"))
|
||||
p.append(txt(30, 140, "TF mare (ex. 15 min)", 13, "400", GRAY, "start"))
|
||||
p.append(txt(660, 114, "rămâne în Buy pe tot parcursul", 13, "400",
|
||||
GREEN, "end"))
|
||||
for x, y_top, y_bot in ((LONG_TOP[L_M][0], LONG_TOP[L_M][1], LONG_BOT[L_M][1]),
|
||||
(LONG_TOP[L_A][0], LONG_TOP[L_A][1], LONG_BOT[L_A][1])):
|
||||
p.append(line(x, y_top, x, y_bot, "#999999", 1.2, "4 4"))
|
||||
p.append(poly(LONG_TOP, GREEN))
|
||||
p.append(dot(*LONG_TOP[1], CYAN))
|
||||
p.append(txt(185, 126, "Buy Momentum", 13, "400", CYAN, "start"))
|
||||
|
||||
p.append(txt(30, 202, "TF mic (ex. 3 min)", 13, "400", GRAY, "start"))
|
||||
p.append(poly(LONG_BOT, "#333333"))
|
||||
p.append(dot(*LONG_BOT[L_M], RED))
|
||||
p.append(txt(262, 296, "M: Sell Momentum (pullback)", 13, "400",
|
||||
RED, "start"))
|
||||
p.append(dot(*LONG_BOT[L_A], GREEN, r=6))
|
||||
p.append(txt(660, 240, "A: Buy Momentum — TRIGGER, intri long",
|
||||
13, "400", GREEN, "end"))
|
||||
|
||||
# ------------------------------------------------------ panoul SHORT
|
||||
p.append(txt(30, 386, "SHORT", 15, "700", RED, "start"))
|
||||
p.append(txt(30, 410, "TF mare (ex. 15 min)", 13, "400", GRAY, "start"))
|
||||
p.append(txt(660, 494, "rămâne în Sell pe tot parcursul", 13, "400",
|
||||
RED, "end"))
|
||||
for x, y_top, y_bot in ((SHORT_TOP[S_M][0], SHORT_TOP[S_M][1], SHORT_BOT[S_M][1]),
|
||||
(SHORT_TOP[S_A][0], SHORT_TOP[S_A][1], SHORT_BOT[S_A][1])):
|
||||
p.append(line(x, y_top, x, y_bot, "#999999", 1.2, "4 4"))
|
||||
p.append(poly(SHORT_TOP, RED))
|
||||
p.append(dot(*SHORT_TOP[1], RED))
|
||||
p.append(txt(168, 432, "Sell Momentum", 13, "400", RED, "start"))
|
||||
|
||||
p.append(txt(30, 470, "TF mic (ex. 3 min)", 13, "400", GRAY, "start"))
|
||||
p.append(poly(SHORT_BOT, "#333333"))
|
||||
p.append(dot(*SHORT_BOT[S_M], CYAN))
|
||||
p.append(txt(238, 494, "M: Buy Momentum (pullback)", 13, "400",
|
||||
CYAN, "end"))
|
||||
p.append(dot(*SHORT_BOT[S_A], RED, r=6))
|
||||
p.append(txt(660, 610, "A: Sell Momentum — TRIGGER, intri short",
|
||||
13, "400", RED, "end"))
|
||||
|
||||
# ------------------------------------------------------ subsol
|
||||
p.append(f'<rect x="40" y="648" width="620" height="116" rx="8" '
|
||||
f'fill="#ffffff" stroke="{GRAY}" stroke-width="1.2"/>')
|
||||
p.append(txt(W / 2, 676,
|
||||
"Secvența contează, nu starea: mare în Buy → mic în Sell → "
|
||||
"mic în Buy", 13, "400", BLACK))
|
||||
p.append(txt(W / 2, 696,
|
||||
"(cu mare încă în Buy). Un Buy direct pe TF mic, fără Sell "
|
||||
"între ele,", 13, "400", BLACK))
|
||||
p.append(txt(W / 2, 716, "NU e trigger (vezi 1.3).", 13, "400", BLACK))
|
||||
p.append(txt(W / 2, 746,
|
||||
"Sursă: 14_strategia_1_m2d.md:30-38, 172-180; "
|
||||
"09_QA/08_2026-01-20.md:65-67", 13, "400", GRAY))
|
||||
|
||||
p.append('</g></svg>')
|
||||
return "\n".join(p) + "\n"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- verificare
|
||||
def est_w(s, size, weight):
|
||||
factor = 0.62 if weight == "700" else 0.56
|
||||
return len(s) * size * factor
|
||||
|
||||
|
||||
def verify(svg):
|
||||
# 1. geometria: panourile nu se suprapun, sunt în panză
|
||||
long_ = PANELS["LONG"]
|
||||
short_ = PANELS["SHORT"]
|
||||
assert long_[1] + long_[3] < short_[1], "panourile LONG/SHORT se suprapun"
|
||||
assert short_[1] + short_[3] < 648, "panoul SHORT intră în subsol"
|
||||
for _, (px, py, pw, ph) in PANELS.items():
|
||||
assert px >= 0 and py >= 0 and px + pw <= W and py + ph <= H
|
||||
|
||||
# 2. secvența LONG: TF mare rămâne în Buy (urcă monoton: y strict descrescător)
|
||||
ys = [y for _, y in LONG_TOP]
|
||||
assert all(ys[i] > ys[i + 1] for i in range(len(ys) - 1)), \
|
||||
"LONG: TF mare nu urcă monoton (nu rămâne în Buy)"
|
||||
# M = pullback (minim local), A = reluare peste vârful anterior
|
||||
assert LONG_BOT[L_M][1] > LONG_BOT[L_M - 1][1] and \
|
||||
LONG_BOT[L_M][1] > LONG_BOT[L_M + 1][1], "LONG: M nu e un minim local"
|
||||
assert LONG_BOT[L_A][1] < LONG_BOT[1][1], \
|
||||
"LONG: A nu urcă peste vârful anterior (pullback-ul)"
|
||||
assert LONG_TOP[L_M][0] < LONG_TOP[L_A][0], "LONG: M trebuie înainte de A"
|
||||
assert LONG_TOP[L_M][0] == LONG_BOT[L_M][0] and \
|
||||
LONG_TOP[L_A][0] == LONG_BOT[L_A][0], \
|
||||
"LONG: liniile verticale nu leagă exact M și A de rândul de sus"
|
||||
|
||||
# 3. secvența SHORT: oglindit
|
||||
ys = [y for _, y in SHORT_TOP]
|
||||
assert all(ys[i] < ys[i + 1] for i in range(len(ys) - 1)), \
|
||||
"SHORT: TF mare nu coboară monoton (nu rămâne în Sell)"
|
||||
assert SHORT_BOT[S_M][1] < SHORT_BOT[S_M - 1][1] and \
|
||||
SHORT_BOT[S_M][1] < SHORT_BOT[S_M + 1][1], "SHORT: M nu e un maxim local"
|
||||
assert SHORT_BOT[S_A][1] > SHORT_BOT[1][1], \
|
||||
"SHORT: A nu coboară sub minimul anterior"
|
||||
assert SHORT_TOP[S_M][0] < SHORT_TOP[S_A][0], "SHORT: M trebuie înainte de A"
|
||||
assert SHORT_TOP[S_M][0] == SHORT_BOT[S_M][0] and \
|
||||
SHORT_TOP[S_A][0] == SHORT_BOT[S_A][0], \
|
||||
"SHORT: liniile verticale nu leagă exact M și A de rândul de sus"
|
||||
|
||||
# 4. verticalele chiar traversează de la rândul de sus până la moment
|
||||
assert LONG_TOP[2][1] < LONG_BOT[L_M][1]
|
||||
assert LONG_TOP[3][1] < LONG_BOT[L_A][1]
|
||||
assert SHORT_TOP[2][1] < SHORT_BOT[S_M][1]
|
||||
assert SHORT_TOP[3][1] < SHORT_BOT[S_A][1]
|
||||
|
||||
# 5. toate etichetele în panză
|
||||
for x, y, s, size, weight, anchor, _ in TEXTS:
|
||||
ew = est_w(s, size, weight)
|
||||
left = x - ew if anchor == "end" else (x - ew / 2 if anchor == "middle" else x)
|
||||
right = left + ew
|
||||
assert -2 <= left and right <= W + 2, f"text în afara panzei: {s!r}"
|
||||
assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y})"
|
||||
|
||||
# 6. etichete care nu se suprapun
|
||||
for i in range(len(TEXTS)):
|
||||
xi, yi, si, szi, wi, ai, _ = TEXTS[i]
|
||||
we = est_w(si, szi, wi)
|
||||
li = xi - we if ai == "end" else (xi - we / 2 if ai == "middle" else xi)
|
||||
ri = li + we
|
||||
for j in range(i + 1, len(TEXTS)):
|
||||
xj, yj, sj, szj, wj, aj, _ = TEXTS[j]
|
||||
wej = est_w(sj, szj, wj)
|
||||
lj = xj - wej if aj == "end" else (xj - wej / 2 if aj == "middle" else xj)
|
||||
rj = lj + wej
|
||||
if abs(yi - yj) < 13 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
# 7. niciun text nu se tastează peste o polilinie (coliziune text-linie)
|
||||
segments = [(LONG_TOP, PANELS["LONG"]), (LONG_BOT, PANELS["LONG"]),
|
||||
(SHORT_TOP, PANELS["SHORT"]), (SHORT_BOT, PANELS["SHORT"])]
|
||||
for x, y, s, size, weight, anchor, _ in TEXTS:
|
||||
ew = est_w(s, size, weight)
|
||||
left = x - ew if anchor == "end" else (x - ew / 2 if anchor == "middle" else x)
|
||||
right = left + ew
|
||||
top, bot = y - size + 1, y
|
||||
for pts, _ in segments:
|
||||
for (x1, y1), (x2, y2) in zip(pts, pts[1:]):
|
||||
xa, xb = min(x1, x2), max(x1, x2)
|
||||
if xb < left - 2 or xa > right + 2:
|
||||
continue
|
||||
for xc in (max(xa, left), min(xb, right)):
|
||||
t = 0 if x2 == x1 else (xc - x1) / (x2 - x1)
|
||||
yc = y1 + t * (y2 - y1)
|
||||
if top - 1 <= yc <= bot + 1:
|
||||
raise AssertionError(
|
||||
f"text peste polilinie: {s!r} (y={y}, linia y={yc:.0f})")
|
||||
|
||||
# 8. etichetele obligatorii din spec sunt prezente
|
||||
for cheie in ("LONG", "SHORT",
|
||||
"TF mare (ex. 15 min)", "TF mic (ex. 3 min)",
|
||||
"rămâne în Buy pe tot parcursul",
|
||||
"rămâne în Sell pe tot parcursul",
|
||||
"Buy Momentum", "Sell Momentum",
|
||||
"M: Sell Momentum (pullback)",
|
||||
"A: Buy Momentum — TRIGGER, intri long",
|
||||
"M: Buy Momentum (pullback)",
|
||||
"A: Sell Momentum — TRIGGER, intri short",
|
||||
"Secvența contează, nu starea",
|
||||
"NU e trigger (vezi 1.3)"):
|
||||
assert cheie in svg, f"lipsește din desen: {cheie!r}"
|
||||
|
||||
# 9. fără cifre de preț inventate (spec: diagramă fără niveluri)
|
||||
for interzis in ("$", "%", "€", "intrare ", "SL ", "TP"):
|
||||
assert interzis not in svg, f"nu ar trebui să apară preț: {interzis!r}"
|
||||
return True
|
||||
|
||||
|
||||
SVG = build()
|
||||
assert verify(SVG), "verificare eșuată"
|
||||
OUT.write_text(SVG, encoding="utf-8")
|
||||
print(f"scris {OUT} ({W}x{H}), verificare OK: {len(TEXTS)} etichete, "
|
||||
f"{len(LONG_TOP) + len(LONG_BOT) + len(SHORT_TOP) + len(SHORT_BOT)} puncte")
|
||||
42
gen/strategii/m2d_secventa.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 780" width="700" height="780" role="img" aria-label="Secventa M apoi A a strategiei M2D, pe doua time frame-uri, pentru long si short: time frame-ul mare ramane in directie cat timp time frame-ul mic face pullback (M) si apoi da trigger-ul in directia mare (A)">
|
||||
<g font-family="system-ui, sans-serif">
|
||||
<rect x="0" y="0" width="700" height="780" fill="#ffffff"/>
|
||||
<text x="350.0" y="30.0" font-size="19" font-weight="700" fill="#000000" text-anchor="middle">Secvența M → A: nu e o stare, e o secvență</text>
|
||||
<text x="350.0" y="52.0" font-size="13" font-weight="400" fill="#777777" text-anchor="middle">M2D — același moment, două time frame-uri: TF mare dă direcția, TF mic dă trigger-ul</text>
|
||||
<text x="350.0" y="72.0" font-size="13" font-weight="400" fill="#777777" text-anchor="middle">sus = LONG; jos = SHORT — aceleași momente, în oglindă</text>
|
||||
<rect x="14" y="92" width="672" height="250" rx="8" fill="#fafafa" stroke="#ddd" stroke-width="1.2"/>
|
||||
<rect x="14" y="362" width="672" height="270" rx="8" fill="#fafafa" stroke="#ddd" stroke-width="1.2"/>
|
||||
<text x="30.0" y="116.0" font-size="15" font-weight="700" fill="#188a3e" text-anchor="start">LONG</text>
|
||||
<text x="30.0" y="140.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">TF mare (ex. 15 min)</text>
|
||||
<text x="660.0" y="114.0" font-size="13" font-weight="400" fill="#188a3e" text-anchor="end">rămâne în Buy pe tot parcursul</text>
|
||||
<line x1="250.0" y1="138.0" x2="250.0" y2="282.0" stroke="#999999" stroke-width="1.2" stroke-dasharray="4 4"/>
|
||||
<line x1="430.0" y1="128.0" x2="430.0" y2="210.0" stroke="#999999" stroke-width="1.2" stroke-dasharray="4 4"/>
|
||||
<polyline points="40.0,162.0 150.0,148.0 250.0,138.0 430.0,128.0 660.0,118.0" fill="none" stroke="#188a3e" stroke-width="2.0"/>
|
||||
<circle cx="150.0" cy="148.0" r="5.0" fill="#00b8c4"/>
|
||||
<text x="185.0" y="126.0" font-size="13" font-weight="400" fill="#00b8c4" text-anchor="start">Buy Momentum</text>
|
||||
<text x="30.0" y="202.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">TF mic (ex. 3 min)</text>
|
||||
<polyline points="40.0,255.0 180.0,225.0 250.0,282.0 430.0,210.0 660.0,198.0" fill="none" stroke="#333333" stroke-width="2.0"/>
|
||||
<circle cx="250.0" cy="282.0" r="5.0" fill="#c00"/>
|
||||
<text x="262.0" y="296.0" font-size="13" font-weight="400" fill="#c00" text-anchor="start">M: Sell Momentum (pullback)</text>
|
||||
<circle cx="430.0" cy="210.0" r="6.0" fill="#188a3e"/>
|
||||
<text x="660.0" y="240.0" font-size="13" font-weight="400" fill="#188a3e" text-anchor="end">A: Buy Momentum — TRIGGER, intri long</text>
|
||||
<text x="30.0" y="386.0" font-size="15" font-weight="700" fill="#c00" text-anchor="start">SHORT</text>
|
||||
<text x="30.0" y="410.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">TF mare (ex. 15 min)</text>
|
||||
<text x="660.0" y="494.0" font-size="13" font-weight="400" fill="#c00" text-anchor="end">rămâne în Sell pe tot parcursul</text>
|
||||
<line x1="250.0" y1="454.0" x2="250.0" y2="500.0" stroke="#999999" stroke-width="1.2" stroke-dasharray="4 4"/>
|
||||
<line x1="430.0" y1="464.0" x2="430.0" y2="574.0" stroke="#999999" stroke-width="1.2" stroke-dasharray="4 4"/>
|
||||
<polyline points="40.0,430.0 150.0,444.0 250.0,454.0 430.0,464.0 660.0,474.0" fill="none" stroke="#c00" stroke-width="2.0"/>
|
||||
<circle cx="150.0" cy="444.0" r="5.0" fill="#c00"/>
|
||||
<text x="168.0" y="432.0" font-size="13" font-weight="400" fill="#c00" text-anchor="start">Sell Momentum</text>
|
||||
<text x="30.0" y="470.0" font-size="13" font-weight="400" fill="#777777" text-anchor="start">TF mic (ex. 3 min)</text>
|
||||
<polyline points="40.0,527.0 180.0,562.0 250.0,500.0 430.0,574.0 660.0,588.0" fill="none" stroke="#333333" stroke-width="2.0"/>
|
||||
<circle cx="250.0" cy="500.0" r="5.0" fill="#00b8c4"/>
|
||||
<text x="238.0" y="494.0" font-size="13" font-weight="400" fill="#00b8c4" text-anchor="end">M: Buy Momentum (pullback)</text>
|
||||
<circle cx="430.0" cy="574.0" r="6.0" fill="#c00"/>
|
||||
<text x="660.0" y="610.0" font-size="13" font-weight="400" fill="#c00" text-anchor="end">A: Sell Momentum — TRIGGER, intri short</text>
|
||||
<rect x="40" y="648" width="620" height="116" rx="8" fill="#ffffff" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="350.0" y="676.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">Secvența contează, nu starea: mare în Buy → mic în Sell → mic în Buy</text>
|
||||
<text x="350.0" y="696.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">(cu mare încă în Buy). Un Buy direct pe TF mic, fără Sell între ele,</text>
|
||||
<text x="350.0" y="716.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">NU e trigger (vezi 1.3).</text>
|
||||
<text x="350.0" y="746.0" font-size="13" font-weight="400" fill="#777777" text-anchor="middle">Sursă: 14_strategia_1_m2d.md:30-38, 172-180; 09_QA/08_2026-01-20.md:65-67</text>
|
||||
</g></svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
201
gen/strategii/mbp_ansamblu.py
Normal file
@@ -0,0 +1,201 @@
|
||||
"""Diagrama de ansamblu MBP / MBPA++ (strategii, marcajul `ansamblu`).
|
||||
|
||||
„SPY — 15 min: bandă + TICUS pe aceeași candelă → trigger → intrare → SL →
|
||||
TP1 → trail". Graficul întreg al trade-ului long din exemplul cursului, cu
|
||||
panoul TICUS dedesubt, legat de candela M printr-o linie punctată verticală
|
||||
(aceeași candelă sus, aceeași valoare jos).
|
||||
|
||||
Toate cifrele desenate vin din curs
|
||||
(`summaries/05_MODUL_4/03_strategia_6_mbp_mbappe.md`):
|
||||
- M: close 623.87 < banda inferioară 623.90, TICUS -1150 (liniile 336-342);
|
||||
- bandă superioară 624.70 și linia 624.30 (SMA20 / TP1) — diagrama
|
||||
principală (liniile 114-148);
|
||||
- trigger Buy Momentum → entry 623.95 (liniile 336-342);
|
||||
- SL 623.60 = lowest low din ultimele 4 candele, trigger inclus (93-94);
|
||||
- TP1 624.30 (+0.35, 1:1) (95-96);
|
||||
- „realizat ~1:4 datorită pozițiilor pe trail" (428-429). Prețul de ieșire
|
||||
~625.35 = entry + 4R este ilustrativ, derivat de mine (nu e preț din curs).
|
||||
|
||||
Structura barelor dintre niveluri e construită de mine; o construiesc astfel
|
||||
încât fiecare nivel afirmat în text să iasă exact din serie (assert-uri mai
|
||||
jos). Nu există nivel de suport major cu cifră în exemplul din curs.
|
||||
|
||||
Rulează:
|
||||
python3 gen/strategii/mbp_ansamblu.py
|
||||
Scrie lângă el `mbp_ansamblu.svg`.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "mbp_ansamblu.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# ---------------------------------------------------------------- seria (30 bare)
|
||||
# 14 bare de downtrend 625.60 -> 624.15 (open-ul fiecărei bare = close-ul
|
||||
# precedentei), apoi M (index 14), trigger (15), 14 bare de urcare spre 625.35.
|
||||
DOWN = [625.60, 625.42, 625.55, 625.28, 625.05, 625.12, 624.88,
|
||||
624.65, 624.72, 624.50, 624.35, 624.30, 624.10, 624.15] # 0-13
|
||||
M_IX, TR_IX = 14, 15
|
||||
UP = [624.00, 624.08, 624.16, 624.30, 624.40, 624.50, 624.60, 624.70,
|
||||
624.82, 624.95, 625.08, 625.20, 625.28, 625.35] # 16-29
|
||||
CLOSES = DOWN + [623.87, 623.95] + UP # M, trigger
|
||||
assert len(CLOSES) == 30, len(CLOSES)
|
||||
|
||||
# fitile (sus, jos) per bară. Ultimele 3 bare înainte de M au minimele
|
||||
# 623.95 / 623.80 / 623.72 (peste 623.60), ca SL-ul calculat să fie exact
|
||||
# 623.60 (low-ul lui M).
|
||||
WICKS = ([(0.05, 0.18)] + [(0.18, 0.18)] * 10
|
||||
+ [(0.18, 0.35), # 11: low 623.95
|
||||
(0.18, 0.30), # 12: low 623.80 (open 624.30, close 624.10)
|
||||
(0.10, 0.38)] # 13: low 623.72 (open 624.10, close 624.15)
|
||||
+ [(0.05, 0.27), # 14 M: high 624.20, low 623.60
|
||||
(0.05, 0.12)] # 15 trigger: high 624.00, low 623.75
|
||||
+ [(0.05, 0.12)] * 3 # 16-18
|
||||
+ [(0.00, 0.12)] # 19: high atinge exact 624.30 (TP1)
|
||||
+ [(0.05, 0.12)] * 9
|
||||
+ [(0.00, 0.12)]) # 29: high = close = 625.35 (ieșirea ilustrativă)
|
||||
assert len(WICKS) == len(CLOSES), len(WICKS)
|
||||
|
||||
# TICUS: un singur bar relevant, pe candela M, la -1150 (sub -1000)
|
||||
TICUS = {M_IX: -1150.0}
|
||||
|
||||
# fără panou de volum: panoul de dedesubt e cel de TICUS
|
||||
cds = from_closes(CLOSES, wicks=WICKS, start=625.75)
|
||||
|
||||
# ---------------------------------------------------------------- assert-urile din spec
|
||||
entry = cds[TR_IX].c
|
||||
assert abs(entry - 623.95) < 0.01, entry
|
||||
assert entry == cds[TR_IX].c # intrarea = close-ul trigger-ului
|
||||
assert cds[M_IX].c < 623.90 # M închide sub banda inferioară
|
||||
assert cds[M_IX].c < cds[M_IX].o # M e roșie (close < open)
|
||||
ticus_m = TICUS[M_IX]
|
||||
assert ticus_m <= -1000, ticus_m # =-1150, pragul depășit
|
||||
assert abs(cds[M_IX].c - 623.87) < 0.005
|
||||
assert abs(cds[M_IX].h - 624.20) < 0.005
|
||||
assert abs(cds[TR_IX].h - 624.00) < 0.005
|
||||
assert abs(cds[TR_IX].l - 623.75) < 0.005
|
||||
|
||||
# SL = cel mai jos minim din ultimele 4 candele încheiate de trigger, inclusiv
|
||||
window = cds[TR_IX - 3:TR_IX + 1]
|
||||
assert len(window) == 4
|
||||
sl = min(c.l for c in window)
|
||||
assert abs(sl - 623.60) < 0.01, sl
|
||||
assert sl == cds[M_IX].l # low-ul lui M e minimul ferestrei
|
||||
assert cds[TR_IX].l > sl # trigger-ul nu face low nou
|
||||
# cele trei candele dinaintea lui M au minime peste 623.60 (ex. 623.95/623.80/623.72)
|
||||
for cd in cds[TR_IX - 4:TR_IX - 1]:
|
||||
assert cd.l > sl, (cd.o, cd.c, cd.l)
|
||||
|
||||
risc = entry - sl
|
||||
assert abs(risc - 0.35) < 0.005, risc
|
||||
tp1 = entry + risc
|
||||
assert abs(tp1 - (entry + (entry - sl))) < 0.005
|
||||
assert abs(tp1 - 624.30) < 0.005, tp1
|
||||
# TP1 nu se atinge prea devreme, ci 4 bare după intrare (cum spune spec-ul)
|
||||
assert max(c.h for c in cds[TR_IX + 1:TR_IX + 4]) < tp1 - 1e-9
|
||||
assert abs(cds[TR_IX + 4].h - tp1) < 1e-9
|
||||
|
||||
exit_ill = cds[-1].c # ~625.35, ilustrativ (derivat)
|
||||
assert abs(exit_ill - 625.35) < 0.01, exit_ill
|
||||
assert abs((exit_ill - entry) / risc - 4.0) < 0.15, (exit_ill - entry) / risc
|
||||
|
||||
# nu există nicio linie de stop desenată deasupra intrării
|
||||
assert sl < entry
|
||||
levels = [
|
||||
Level(623.90, "623.90 — bandă inferioară", BLUE, dash="4 3", label_at="left"),
|
||||
Level(624.70, "624.70 — bandă superioară", BLUE, dash="4 3", label_at="right"),
|
||||
Level(624.30, "624.30 — SMA20 / TP1", GRAY, dash="4 3", label_at="left"),
|
||||
Level(sl, f"SL {sl:.2f} (lowest low, 4 candele)", RED, label_at="left"),
|
||||
Level(entry, f"trigger Buy Momentum → intrare {entry:.2f}", GREEN, label_at="right"),
|
||||
Level(625.35, "ieșire ilustrativă ~625.35 (≈ 1:4)", GREEN, dash="2 3", label_at="right"),
|
||||
]
|
||||
assert all("stop" not in lv.text.lower() or lv.price < entry for lv in levels)
|
||||
assert all(not ("sl" in lv.text.lower() and lv.price > entry) for lv in levels)
|
||||
|
||||
marks = [
|
||||
Mark(M_IX, "M: roșie, close 623.87 sub bandă", RED, "below"),
|
||||
]
|
||||
|
||||
# titlul si eticheta de trend sunt injectate manual (nu prin Chart.title, care
|
||||
# ar sta lipit de prima candelă, si nici prin Chart.trend, care s-ar bate cu
|
||||
# titlul) ca sa rămână spațiu liber deasupra seriei.
|
||||
ch = Chart(candles=cds, levels=levels, marks=marks,
|
||||
scale=150.0, gap=16, body_w=10, left=48, width=700, top=96)
|
||||
svg = ch.render()
|
||||
|
||||
TITLE = "SPY — 15 min: bandă + TICUS pe aceeași candelă → trigger → intrare → SL → TP1 → trail"
|
||||
|
||||
# ---------------------------------------------------------------- panoul TICUS
|
||||
# `Chart` nu are panou de indicator; îl adaug după randare, cu aceeași scară
|
||||
# pe orizontală (ch._cx), ca barul TICUS al lui M să cadă exact sub candela M.
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
|
||||
W, H0 = int(m.group(1)), float(m.group(2))
|
||||
|
||||
ticus_top = H0 + 34
|
||||
y0 = ticus_top + 70 # linia 0
|
||||
px1000 = 50.0 # 1000 unități TICUS = 50 px
|
||||
def ty(v: float) -> float:
|
||||
return y0 - v / 1000.0 * px1000
|
||||
|
||||
xm = ch._cx(M_IX)
|
||||
bar_top, bar_bot = y0, ty(ticus_m) # bar de la 0 în jos la -1150
|
||||
|
||||
panel = [
|
||||
f'<text x="24" y="{ticus_top:.0f}" fill="#333">TICUS — presiunea pieței (↑ = peste 0)</text>',
|
||||
# linii de prag
|
||||
f'<line x1="24" y1="{ty(1000):.1f}" x2="{W - 14}" y2="{ty(1000):.1f}" stroke="{RED}" stroke-width="1.5" stroke-dasharray="4 3"/>',
|
||||
f'<text x="28" y="{ty(1000) - 4:.1f}" fill="{RED}">+1000 — supracumpărare</text>',
|
||||
f'<line x1="24" y1="{ty(500):.1f}" x2="{W - 14}" y2="{ty(500):.1f}" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/>',
|
||||
f'<text x="28" y="{ty(500) - 4:.1f}" fill="#caa000">+500 (doar referință vizuală)</text>',
|
||||
f'<line x1="24" y1="{ty(0):.1f}" x2="{W - 14}" y2="{ty(0):.1f}" stroke="#888" stroke-width="1.5"/>',
|
||||
f'<text x="{W - 12}" y="{ty(0) + 4:.1f}" fill="#666" text-anchor="end">0</text>',
|
||||
f'<line x1="24" y1="{ty(-500):.1f}" x2="{W - 14}" y2="{ty(-500):.1f}" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/>',
|
||||
f'<text x="28" y="{ty(-500) + 13:.1f}" fill="#caa000">-500 (doar referință vizuală)</text>',
|
||||
f'<line x1="24" y1="{ty(-1000):.1f}" x2="{W - 14}" y2="{ty(-1000):.1f}" stroke="{GREEN}" stroke-width="1.5" stroke-dasharray="4 3"/>',
|
||||
f'<text x="28" y="{ty(-1000) + 13:.1f}" fill="{GREEN}">-1000 — oversold</text>',
|
||||
# barul TICUS al candelei M, sub -1000 (colorat roșu)
|
||||
f'<rect x="{xm - 5:.1f}" y="{bar_top:.1f}" width="10" height="{bar_bot - bar_top:.1f}" fill="{RED}"/>',
|
||||
f'<text x="{xm + 10:.1f}" y="{bar_bot + 4:.1f}" fill="{RED}">M: TICUS -1150 (sub -1000)</text>',
|
||||
]
|
||||
# linia punctată verticală: aceeași candelă sus și jos (pornește sub mark-ul
|
||||
# lui M, ca să nu taie textul „M: roșie...")
|
||||
y_candle_m = ch._y(cds[M_IX].l) + 26
|
||||
panel.insert(0, f'<line x1="{xm:.1f}" y1="{y_candle_m:.1f}" x2="{xm:.1f}" y2="{bar_bot:.1f}" '
|
||||
f'stroke="#aaa" stroke-width="1" stroke-dasharray="3 3"/>')
|
||||
|
||||
legend_top = ty(-1000) + 44
|
||||
legend = [
|
||||
"SPY 15 min. Cifre din curs: M close 623.87 (sub bandă 623.90), TICUS -1150 pe aceeași candelă,",
|
||||
"trigger Buy Momentum → entry 623.95, SL 623.60 (lowest low din 4 candele, trigger inclus),",
|
||||
"TP1 624.30 (+0.35, 1:1). Ieșirea ~625.35 = entry + 4R (≈1:4) e ilustrativă — preț derivat din R,",
|
||||
"nu din curs. Structura candelelor (30 bare continue) e construită de mine.",
|
||||
]
|
||||
legend_svg = [f'<text x="24" y="{legend_top + 17 * i:.1f}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(legend)]
|
||||
|
||||
header = [
|
||||
f'<text x="24" y="26" fill="#333" style="font-weight:bold">{TITLE}</text>',
|
||||
'<text x="24" y="64" fill="#999">trend descendent anterior (14 bare)</text>',
|
||||
]
|
||||
|
||||
new_h = legend_top + 17 * (len(legend) - 1) + 14
|
||||
svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")', lambda mm: mm.group(1) + f"{new_h:.0f}" + mm.group(2), svg, count=1)
|
||||
svg = svg.replace("\n</svg>", "\n " + "\n ".join(header + panel + legend_svg) + "\n</svg>")
|
||||
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
# barul TICUS cade exact sub candela M (aceeași candelă sus și jos)
|
||||
bar_svg = f'<rect x="{xm - 5:.1f}" y="{bar_top:.1f}" width="10" height="{bar_bot - bar_top:.1f}" fill="{RED}"/>'
|
||||
assert bar_svg in svg, "barul TICUS nu a fost desenat"
|
||||
assert abs((float(re.search(r'x="([\d.]+)"', bar_svg).group(1)) + 5) - ch._cx(M_IX)) < 1e-9
|
||||
# aceeași scară px/unitate: SL-intrare și intrare-TP1 sunt egale și vizual (1:1)
|
||||
assert abs((ch._y(sl) - ch._y(entry)) - (ch._y(entry) - ch._y(tp1))) < 1e-9
|
||||
assert f'x1="{xm:.1f}"' in svg
|
||||
OUT.write_text(svg)
|
||||
print(f"scris {OUT.name}: {len(cds)} candele, entry {entry:.2f}, SL {sl:.2f}, "
|
||||
f"TP1 {tp1:.2f}, ieșire ilustrativă {exit_ill:.2f} ({(exit_ill-entry)/risc:.2f}R), "
|
||||
f"ticus(M)={ticus_m:.0f}, x(M)={xm:.0f}")
|
||||
95
gen/strategii/mbp_ansamblu.svg
Normal file
@@ -0,0 +1,95 @@
|
||||
<svg viewBox="0 0 700 715" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<line x1="24" y1="381.0" x2="686" y2="381.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="375.0" fill="#1a5fb4">623.90 — bandă inferioară</text>
|
||||
<line x1="24" y1="261.0" x2="686" y2="261.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="672" y="255.0" fill="#1a5fb4" text-anchor="end">624.70 — bandă superioară</text>
|
||||
<line x1="24" y1="321.0" x2="686" y2="321.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="315.0" fill="#777">624.30 — SMA20 / TP1</text>
|
||||
<line x1="24" y1="426.0" x2="686" y2="426.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="420.0" fill="#c00">SL 623.60 (lowest low, 4 candele)</text>
|
||||
<line x1="24" y1="373.5" x2="686" y2="373.5" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="672" y="367.5" fill="#188a3e" text-anchor="end">trigger Buy Momentum → intrare 623.95</text>
|
||||
<line x1="24" y1="163.5" x2="686" y2="163.5" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="2 3"/>
|
||||
<text x="672" y="157.5" fill="#188a3e" text-anchor="end">ieșire ilustrativă ~625.35 (≈ 1:4)</text>
|
||||
<line x1="48.0" y1="96.0" x2="48.0" y2="153.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="43.0" y="103.5" width="10" height="22.5" fill="#c00"/>
|
||||
<line x1="64.0" y1="99.0" x2="64.0" y2="180.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="59.0" y="126.0" width="10" height="27.0" fill="#c00"/>
|
||||
<line x1="80.0" y1="106.5" x2="80.0" y2="180.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="75.0" y="133.5" width="10" height="19.5" fill="#188a3e"/>
|
||||
<line x1="96.0" y1="106.5" x2="96.0" y2="201.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="91.0" y="133.5" width="10" height="40.5" fill="#c00"/>
|
||||
<line x1="112.0" y1="147.0" x2="112.0" y2="235.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="107.0" y="174.0" width="10" height="34.5" fill="#c00"/>
|
||||
<line x1="128.0" y1="171.0" x2="128.0" y2="235.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="123.0" y="198.0" width="10" height="10.5" fill="#188a3e"/>
|
||||
<line x1="144.0" y1="171.0" x2="144.0" y2="261.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="139.0" y="198.0" width="10" height="36.0" fill="#c00"/>
|
||||
<line x1="160.0" y1="207.0" x2="160.0" y2="295.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="155.0" y="234.0" width="10" height="34.5" fill="#c00"/>
|
||||
<line x1="176.0" y1="231.0" x2="176.0" y2="295.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="171.0" y="258.0" width="10" height="10.5" fill="#188a3e"/>
|
||||
<line x1="192.0" y1="231.0" x2="192.0" y2="318.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="187.0" y="258.0" width="10" height="33.0" fill="#c00"/>
|
||||
<line x1="208.0" y1="264.0" x2="208.0" y2="340.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="203.0" y="291.0" width="10" height="22.5" fill="#c00"/>
|
||||
<line x1="224.0" y1="286.5" x2="224.0" y2="373.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.0" y="313.5" width="10" height="7.5" fill="#c00"/>
|
||||
<line x1="240.0" y1="294.0" x2="240.0" y2="396.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="235.0" y="321.0" width="10" height="30.0" fill="#c00"/>
|
||||
<line x1="256.0" y1="328.5" x2="256.0" y2="408.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="251.0" y="343.5" width="10" height="7.5" fill="#188a3e"/>
|
||||
<line x1="272.0" y1="336.0" x2="272.0" y2="426.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="267.0" y="343.5" width="10" height="42.0" fill="#c00"/>
|
||||
<line x1="288.0" y1="366.0" x2="288.0" y2="403.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="283.0" y="373.5" width="10" height="12.0" fill="#188a3e"/>
|
||||
<line x1="304.0" y1="358.5" x2="304.0" y2="391.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="299.0" y="366.0" width="10" height="7.5" fill="#188a3e"/>
|
||||
<line x1="320.0" y1="346.5" x2="320.0" y2="384.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="315.0" y="354.0" width="10" height="12.0" fill="#188a3e"/>
|
||||
<line x1="336.0" y1="334.5" x2="336.0" y2="372.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="331.0" y="342.0" width="10" height="12.0" fill="#188a3e"/>
|
||||
<line x1="352.0" y1="321.0" x2="352.0" y2="360.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="347.0" y="321.0" width="10" height="21.0" fill="#188a3e"/>
|
||||
<line x1="368.0" y1="298.5" x2="368.0" y2="339.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="363.0" y="306.0" width="10" height="15.0" fill="#188a3e"/>
|
||||
<line x1="384.0" y1="283.5" x2="384.0" y2="324.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="379.0" y="291.0" width="10" height="15.0" fill="#188a3e"/>
|
||||
<line x1="400.0" y1="268.5" x2="400.0" y2="309.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="395.0" y="276.0" width="10" height="15.0" fill="#188a3e"/>
|
||||
<line x1="416.0" y1="253.5" x2="416.0" y2="294.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="411.0" y="261.0" width="10" height="15.0" fill="#188a3e"/>
|
||||
<line x1="432.0" y1="235.5" x2="432.0" y2="279.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="427.0" y="243.0" width="10" height="18.0" fill="#188a3e"/>
|
||||
<line x1="448.0" y1="216.0" x2="448.0" y2="261.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="443.0" y="223.5" width="10" height="19.5" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="196.5" x2="464.0" y2="241.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.0" y="204.0" width="10" height="19.5" fill="#188a3e"/>
|
||||
<line x1="480.0" y1="178.5" x2="480.0" y2="222.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="475.0" y="186.0" width="10" height="18.0" fill="#188a3e"/>
|
||||
<line x1="496.0" y1="166.5" x2="496.0" y2="204.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="491.0" y="174.0" width="10" height="12.0" fill="#188a3e"/>
|
||||
<line x1="512.0" y1="163.5" x2="512.0" y2="192.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="507.0" y="163.5" width="10" height="10.5" fill="#188a3e"/>
|
||||
<text x="272.0" y="440.0" fill="#c00" text-anchor="middle" style="font-weight:bold">M: roșie, close 623.87 sub bandă</text>
|
||||
<text x="24" y="26" fill="#333" style="font-weight:bold">SPY — 15 min: bandă + TICUS pe aceeași candelă → trigger → intrare → SL → TP1 → trail</text>
|
||||
<text x="24" y="64" fill="#999">trend descendent anterior (14 bare)</text>
|
||||
<line x1="272.0" y1="452.0" x2="272.0" y2="613.5" stroke="#aaa" stroke-width="1" stroke-dasharray="3 3"/>
|
||||
<text x="24" y="486" fill="#333">TICUS — presiunea pieței (↑ = peste 0)</text>
|
||||
<line x1="24" y1="506.0" x2="686" y2="506.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="28" y="502.0" fill="#c00">+1000 — supracumpărare</text>
|
||||
<line x1="24" y1="531.0" x2="686" y2="531.0" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/>
|
||||
<text x="28" y="527.0" fill="#caa000">+500 (doar referință vizuală)</text>
|
||||
<line x1="24" y1="556.0" x2="686" y2="556.0" stroke="#888" stroke-width="1.5"/>
|
||||
<text x="688" y="560.0" fill="#666" text-anchor="end">0</text>
|
||||
<line x1="24" y1="581.0" x2="686" y2="581.0" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/>
|
||||
<text x="28" y="594.0" fill="#caa000">-500 (doar referință vizuală)</text>
|
||||
<line x1="24" y1="606.0" x2="686" y2="606.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="28" y="619.0" fill="#188a3e">-1000 — oversold</text>
|
||||
<rect x="267.0" y="556.0" width="10" height="57.5" fill="#c00"/>
|
||||
<text x="282.0" y="617.5" fill="#c00">M: TICUS -1150 (sub -1000)</text>
|
||||
<text x="24" y="650.0" fill="#555">SPY 15 min. Cifre din curs: M close 623.87 (sub bandă 623.90), TICUS -1150 pe aceeași candelă,</text>
|
||||
<text x="24" y="667.0" fill="#555">trigger Buy Momentum → entry 623.95, SL 623.60 (lowest low din 4 candele, trigger inclus),</text>
|
||||
<text x="24" y="684.0" fill="#555">TP1 624.30 (+0.35, 1:1). Ieșirea ~625.35 = entry + 4R (≈1:4) e ilustrativă — preț derivat din R,</text>
|
||||
<text x="24" y="701.0" fill="#555">nu din curs. Structura candelelor (30 bare continue) e construită de mine.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
242
gen/strategii/mbp_exemplu.py
Normal file
@@ -0,0 +1,242 @@
|
||||
"""Diagrama din `gen/strategii/mbp_exemplu.spec.txt` (TIP: PRET).
|
||||
|
||||
Trade-ul short complet din material (SPY, 15 min, 25.11.2025): ~12 candele
|
||||
de urcare violentă spre vârful ~661, candela M (verde, peste bandă + TICUS
|
||||
≥ +1000), trigger Sell Momentum → intrare short 660.00, SL 662.71
|
||||
(risc 0.41 %), TP1 657.29 (1:1) atins în prima candelă, apoi trail pe 7
|
||||
candele de 15 min până la ieșirea la 646.80 = 2 % = ~4.9R.
|
||||
|
||||
Serie continuă de 32 de candele (`from_closes`, open = close anterior), cu
|
||||
context înainte de urcare, panou TICUS dedesubt (bar ilustrativ la candela M)
|
||||
și legendă. Toate cifrele din desen vin din specificație; singurele valori
|
||||
construite de mine sunt structura barelor dintre niveluri și bara TICUS
|
||||
(marcată explicit „ilustrativ", pentru că valoarea exactă nu e în material).
|
||||
|
||||
Rulare: python3 gen/strategii/mbp_exemplu.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
# ── cifrele din material ────────────────────────────────────────────────────
|
||||
ENTRY = 660.00 # 09_QA/11_2025-11-25.md:565, 632-636
|
||||
SL = 662.71 # risc 0.41 % → (sl-entry)/entry*100
|
||||
TP1 = 657.29 # entry - (sl - entry) = 1:1
|
||||
EXIT = 646.80 # ieșire pe trail = 2 % = ~4.9R
|
||||
BAND = 660.90 # bandă superioară schematică (peste close-ul lui M)
|
||||
W, GAP, BODY_W, LEFT = 660, 15, 9, 40
|
||||
SCALE = 20.0
|
||||
|
||||
M_I, E_I, TP1_I, EXIT_I = 23, 24, 25, 31 # M, trigger, TP1, ieșire
|
||||
N_RISE = M_I - 11 # 12 candele de urcare (idx 12..23)
|
||||
|
||||
# ── seria: context → urcare violentă → M → trigger → TP1 → trail ────────────
|
||||
closes = [
|
||||
# idx 0-11: context în jurul lui 648, înainte de urcarea violentă
|
||||
649.00, 648.60, 649.20, 648.40, 648.90, 648.10,
|
||||
648.70, 647.90, 648.50, 648.00, 648.60, 648.30,
|
||||
# idx 12-23: 12 candele de urcare de la ~648 la vârful ~661; idx 23 = M
|
||||
648.60, 649.40, 650.30, 651.20, 652.10, 653.10,
|
||||
654.10, 655.20, 656.30, 657.50, 658.80, 660.50,
|
||||
# idx 24: trigger Sell Momentum → close = intrarea short
|
||||
660.00,
|
||||
# idx 25: prima candelă după intrare atinge TP1 657.29
|
||||
657.80,
|
||||
# idx 26-31: trail până la ieșirea la 646.80 (6 candele)
|
||||
656.40, 655.00, 653.40, 651.60, 649.40, 646.80,
|
||||
]
|
||||
assert len(closes) == 32, len(closes)
|
||||
|
||||
wicks = [(0.35, 0.35)] * 12 # context
|
||||
wicks += [(0.30, 0.25)] * (M_I - 12) # urcarea (idx 12..22)
|
||||
wicks += [(0.50, 0.20)] # M: high = 661.00 (vârful)
|
||||
wicks += [(0.20, 0.40), # trigger
|
||||
(0.25, 0.51), # TP1: low = 657.80-0.51 = 657.29
|
||||
(0.30, 0.30), (0.30, 0.30), (0.30, 0.30),
|
||||
(0.30, 0.30), (0.30, 0.30), (0.30, 0.30)] # trail
|
||||
assert len(wicks) == len(closes)
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, start=648.90)
|
||||
|
||||
# ── assert-urile din specificație ───────────────────────────────────────────
|
||||
entry = cds[E_I].c
|
||||
sl = SL
|
||||
tp1 = TP1
|
||||
exit_ = cds[EXIT_I].c
|
||||
|
||||
assert abs(entry - 660.00) < 0.01, entry
|
||||
assert entry == cds[E_I].c, "intrarea nu e close-ul candelei trigger"
|
||||
assert abs((sl - entry) / entry * 100 - 0.41) < 0.01, (sl - entry) / entry * 100
|
||||
assert abs((sl - entry) - 2.71) < 0.01, sl - entry
|
||||
assert abs(tp1 - (entry - (sl - entry))) < 0.01, (entry - (sl - entry), tp1)
|
||||
assert cds[TP1_I].l <= tp1 + 1e-9, "prima candelă după intrare nu atinge TP1"
|
||||
assert abs(cds[TP1_I].l - tp1) < 0.01, cds[TP1_I].l
|
||||
assert abs((entry - exit_) / entry * 100 - 2.0) < 0.05, (entry - exit_) / entry * 100
|
||||
assert abs((entry - exit_) / (sl - entry) - 4.9) < 0.1, (entry - exit_) / (sl - entry)
|
||||
assert EXIT_I - E_I == 7, EXIT_I - E_I
|
||||
assert sl > entry > tp1, "short: SL deasupra intrării, TP1 sub intrare"
|
||||
assert BAND > cds[M_I].c, "banda schematică nu e deasupra close-ului lui M"
|
||||
|
||||
# structura care susține afirmațiile din text
|
||||
assert cds[M_I].c > cds[M_I].o, "candela M trebuie să fie verde"
|
||||
assert cds[E_I].c < cds[E_I].o, "candela trigger trebuie să fie roșie"
|
||||
assert M_I == E_I - 1, "trigger-ul nu e imediat după M"
|
||||
assert N_RISE == 12, N_RISE
|
||||
assert 30 <= len(cds) <= 34, len(cds)
|
||||
assert max(cds[i].c for i in range(12, M_I + 1)) == cds[M_I].c, "M nu e vârful urcării"
|
||||
assert max(c.h for c in cds) == cds[M_I].h, "vârful seriei nu e la M"
|
||||
assert all(cds[i].c > cds[i - 1].c for i in range(13, M_I + 1)), "urcarea nu e monotonă"
|
||||
|
||||
# ── panoul de preț ──────────────────────────────────────────────────────────
|
||||
levels = [
|
||||
Level(SL, "SL 662.71 (risc 0.41 %)", "#c00", label_at="right"),
|
||||
Level(ENTRY, "intrare short 660.00", "#188a3e", label_at="left"),
|
||||
Level(BAND, "bandă superioară (schematic)", "#777", dash="2 4", label_at="left"),
|
||||
Level(TP1, "TP1 657.29 (1:1)", "#1a5fb4", label_at="left"),
|
||||
Level(EXIT, "ieșire trail 646.80 = 2 % = ~4.9R", "#1a5fb4", label_at="left"),
|
||||
]
|
||||
|
||||
marks = [
|
||||
Mark(M_I, "M: candelă verde peste bandă + TICUS ≥ +1000", "#188a3e", "above"),
|
||||
]
|
||||
|
||||
cds[M_I].note = ("M: valori exacte NEACOPERIT în material (bandă + TICUS); "
|
||||
"bara ~+1000 din panou e ilustrativă")
|
||||
cds[E_I].note = "trigger Sell Momentum → intrare short 660.00 (risc 0.41 % = 2.71)"
|
||||
cds[TP1_I].note = "TP1 657.29 (1:1) atins în prima candelă după intrare"
|
||||
cds[EXIT_I].note = ("ieșire pe trail 646.80 = 2 % = ~4.9R, după 7 candele "
|
||||
"de 15 min de la intrare")
|
||||
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=levels,
|
||||
marks=marks,
|
||||
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
|
||||
scale=SCALE, width=W, gap=GAP, body_w=BODY_W, left=LEFT,
|
||||
title="SPY — 15 min: MBAP short 25.11.2025 (risc 0.41 % → ieșire 4.9R)",
|
||||
caption="",
|
||||
)
|
||||
price_svg = ch.render()
|
||||
|
||||
# corpul graficului de preț și înălțimea lui (pentru lipirea panoului TICUS)
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([\d.]+)"', price_svg)
|
||||
H1 = float(m.group(2))
|
||||
body1 = price_svg[price_svg.index(">") + 1:price_svg.rindex("</svg>")].strip("\n")
|
||||
|
||||
# ── panoul TICUS dedesubt (bar ilustrativ la candela M) ─────────────────────
|
||||
VGAP = 26
|
||||
Y0 = H1 + VGAP
|
||||
TICUS_H = 100
|
||||
zero_y = Y0 + 84 # linia 0
|
||||
plus_y = Y0 + 32 # linia +1000 (punctată)
|
||||
bar_top = plus_y - 18 # bara depășește +1000
|
||||
m_x = LEFT + M_I * GAP
|
||||
|
||||
assert bar_top < plus_y, "bara TICUS nu depășește linia +1000"
|
||||
assert plus_y < zero_y, "linia +1000 nu e deasupra liniei 0"
|
||||
|
||||
ticus = [
|
||||
f'<text x="24" y="{Y0 + 16:.0f}" fill="#333" style="font-weight:bold">'
|
||||
f'Panou TICUS — linia 0 și pragul +1000 (ilustrativ)</text>',
|
||||
f'<line x1="24" y1="{plus_y:.1f}" x2="{W - 14}" y2="{plus_y:.1f}" '
|
||||
f'stroke="#b06000" stroke-width="1.2" stroke-dasharray="2 4"/>',
|
||||
f'<text x="{W - 28}" y="{plus_y - 5:.1f}" fill="#b06000" text-anchor="end">'
|
||||
f'+1000 (prag)</text>',
|
||||
f'<line x1="24" y1="{zero_y:.1f}" x2="{W - 14}" y2="{zero_y:.1f}" stroke="#888"/>',
|
||||
f'<text x="24" y="{zero_y - 5:.1f}" fill="#555">0</text>',
|
||||
f'<rect x="{m_x - BODY_W / 2:.1f}" y="{bar_top:.1f}" width="{BODY_W}" '
|
||||
f'height="{zero_y - bar_top:.1f}" fill="#188a3e" opacity="0.85"/>',
|
||||
f'<text x="{m_x:.1f}" y="{bar_top - 5:.1f}" fill="#777" text-anchor="middle">'
|
||||
f'ilustrativ</text>',
|
||||
f'<text x="{m_x:.1f}" y="{zero_y + 16:.1f}" fill="#777" text-anchor="middle">'
|
||||
f'candela M</text>',
|
||||
]
|
||||
|
||||
# ── legenda (cifrele/sursele) ───────────────────────────────────────────────
|
||||
legend = [
|
||||
"Legendă: procentele (risc 0.41 %, ieșire 2 %) sunt ale instructorului.",
|
||||
"Prețurile absolute (660.00 / 662.71 / 657.29 / 646.80) sunt construite de sumarizator —",
|
||||
"09_QA/11_2025-11-25.md:716-719.",
|
||||
"Structura de reținut: risc mic → TP1 (1:1) atins imediat → trail lung (7 candele de 15 min).",
|
||||
"Valoarea exactă a benzii și a TICUS la candela M nu e dată în sesiune; bara ~+1000 e ilustrativă.",
|
||||
"„Taurii urcă pe scări, urșii sar de la etaj\" — de ce short-ul se consumă în ore.",
|
||||
"(09_QA/11_2025-11-25.md:643-648).",
|
||||
]
|
||||
cap_top = Y0 + TICUS_H + 20
|
||||
H = cap_top + 16 * (len(legend) - 1) + 14
|
||||
|
||||
parts = [
|
||||
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">',
|
||||
'<g transform="translate(0,0)">',
|
||||
body1,
|
||||
"</g>",
|
||||
*ticus,
|
||||
*[f'<text x="24" y="{cap_top + 16 * i:.1f}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(legend)],
|
||||
"</svg>",
|
||||
]
|
||||
svg = "\n ".join(parts)
|
||||
assert "\n\n" not in svg, "linie goală în SVG — rupe markdown-ul"
|
||||
assert svg.count("<svg") == 1 and svg.count("</svg>") == 1, "SVG-ul trebuie un singur root"
|
||||
|
||||
# ── verificare de citire: etichete în panză, fără suprapuneri ───────────────
|
||||
TEXT = re.compile(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', re.S)
|
||||
|
||||
|
||||
def _fonts():
|
||||
"""Lățimea reală a textului (DejaVuSans ~ system-ui); fallback estimativ."""
|
||||
try:
|
||||
from PIL import ImageFont
|
||||
base = "/usr/share/fonts/truetype/dejavu/"
|
||||
return (ImageFont.truetype(base + "DejaVuSans.ttf", 12),
|
||||
ImageFont.truetype(base + "DejaVuSans-Bold.ttf", 12))
|
||||
except Exception:
|
||||
return None, None
|
||||
|
||||
|
||||
_F_NORM, _F_BOLD = _fonts()
|
||||
|
||||
|
||||
def _tw(s: str, bold: bool) -> float:
|
||||
if _F_BOLD is not None:
|
||||
return (_F_BOLD if bold else _F_NORM).getlength(s)
|
||||
return len(s) * 12 * (0.62 if bold else 0.52)
|
||||
|
||||
|
||||
labels = []
|
||||
for mm in TEXT.finditer(svg):
|
||||
x, y = float(mm.group(1)), float(mm.group(2))
|
||||
attrs, s = mm.group(3), mm.group(4)
|
||||
anchor = "start"
|
||||
if 'text-anchor="end"' in attrs:
|
||||
anchor = "end"
|
||||
elif 'text-anchor="middle"' in attrs:
|
||||
anchor = "middle"
|
||||
w = _tw(s, "bold" in attrs)
|
||||
left_ = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
|
||||
labels.append((left_, left_ + w, y, s))
|
||||
|
||||
for left_, right_, y, s in labels:
|
||||
assert left_ >= -2 and right_ <= W + 2, f"text iese din panză: {s!r}"
|
||||
assert 0 <= y <= H, f"text în afara înălțimii: {s!r} (y={y:.0f})"
|
||||
|
||||
for i in range(len(labels)):
|
||||
li, ri, yi, si = labels[i]
|
||||
for j in range(i + 1, len(labels)):
|
||||
lj, rj, yj, sj = labels[j]
|
||||
if abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r} (y={yi:.0f}/{yj:.0f})")
|
||||
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
|
||||
print(f"serie {len(cds)} candele | M@{M_I} {cds[M_I].c:.2f} (h {cds[M_I].h:.2f}) | "
|
||||
f"trigger@{E_I} {entry:.2f} | SL {sl:.2f} ({(sl-entry)/entry*100:.2f} %) | "
|
||||
f"TP1 {tp1:.2f} (low {cds[TP1_I].l:.2f}) | ieșire@{EXIT_I} {exit_:.2f} "
|
||||
f"({(entry-exit_)/entry*100:.2f} %, {(entry-exit_)/(sl-entry):.2f}R)")
|
||||
print(f"scris {OUT} ({W}x{H:.0f}); {len(labels)} etichete verificate, fără suprapuneri")
|
||||
101
gen/strategii/mbp_exemplu.svg
Normal file
@@ -0,0 +1,101 @@
|
||||
<svg viewBox="0 0 660 730" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<g transform="translate(0,0)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">SPY — 15 min: MBAP short 25.11.2025 (risc 0.41 % → ieșire 4.9R)</text>
|
||||
<line x1="24" y1="56.0" x2="646" y2="56.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="632" y="50.0" fill="#c00" text-anchor="end">SL 662.71 (risc 0.41 %)</text>
|
||||
<line x1="24" y1="110.2" x2="646" y2="110.2" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="104.2" fill="#188a3e">intrare short 660.00</text>
|
||||
<line x1="24" y1="92.2" x2="646" y2="92.2" stroke="#777" stroke-width="1.5" stroke-dasharray="2 4"/>
|
||||
<text x="30" y="86.2" fill="#777">bandă superioară (schematic)</text>
|
||||
<line x1="24" y1="164.4" x2="646" y2="164.4" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="158.4" fill="#1a5fb4">TP1 657.29 (1:1)</text>
|
||||
<line x1="24" y1="374.2" x2="646" y2="374.2" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="368.2" fill="#1a5fb4">ieșire trail 646.80 = 2 % = ~4.9R</text>
|
||||
<polyline points="325.0,314.0 340.0,306.7 355.0,297.8 370.0,288.2 385.0,276.1 400.0,265.0 415.0,255.3 430.0,247.6 445.0,240.5 460.0,235.6 475.0,232.0 490.0,231.2 505.0,232.7" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="511" y="227" fill="#b06000">SMA-20</text>
|
||||
<line x1="40.0" y1="323.2" x2="40.0" y2="339.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="35.5" y="330.2" width="9" height="2.0" fill="#188a3e"/>
|
||||
<line x1="55.0" y1="323.2" x2="55.0" y2="345.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="50.5" y="330.2" width="9" height="8.0" fill="#c00"/>
|
||||
<line x1="70.0" y1="319.2" x2="70.0" y2="345.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="65.5" y="326.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="85.0" y1="319.2" x2="85.0" y2="349.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="80.5" y="326.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="100.0" y1="325.2" x2="100.0" y2="349.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="95.5" y="332.2" width="9" height="10.0" fill="#188a3e"/>
|
||||
<line x1="115.0" y1="325.2" x2="115.0" y2="355.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="110.5" y="332.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="130.0" y1="329.2" x2="130.0" y2="355.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="125.5" y="336.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="145.0" y1="329.2" x2="145.0" y2="359.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="140.5" y="336.2" width="9" height="16.0" fill="#c00"/>
|
||||
<line x1="160.0" y1="333.2" x2="160.0" y2="359.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="155.5" y="340.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="175.0" y1="333.2" x2="175.0" y2="357.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="170.5" y="340.2" width="9" height="10.0" fill="#c00"/>
|
||||
<line x1="190.0" y1="331.2" x2="190.0" y2="357.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="185.5" y="338.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="205.0" y1="331.2" x2="205.0" y2="351.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="200.5" y="338.2" width="9" height="6.0" fill="#c00"/>
|
||||
<line x1="220.0" y1="332.2" x2="220.0" y2="349.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="215.5" y="338.2" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="235.0" y1="316.2" x2="235.0" y2="343.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="230.5" y="322.2" width="9" height="16.0" fill="#188a3e"/>
|
||||
<line x1="250.0" y1="298.2" x2="250.0" y2="327.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="245.5" y="304.2" width="9" height="18.0" fill="#188a3e"/>
|
||||
<line x1="265.0" y1="280.2" x2="265.0" y2="309.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="260.5" y="286.2" width="9" height="18.0" fill="#188a3e"/>
|
||||
<line x1="280.0" y1="262.2" x2="280.0" y2="291.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="275.5" y="268.2" width="9" height="18.0" fill="#188a3e"/>
|
||||
<line x1="295.0" y1="242.2" x2="295.0" y2="273.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="290.5" y="248.2" width="9" height="20.0" fill="#188a3e"/>
|
||||
<line x1="310.0" y1="222.2" x2="310.0" y2="253.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="305.5" y="228.2" width="9" height="20.0" fill="#188a3e"/>
|
||||
<line x1="325.0" y1="200.2" x2="325.0" y2="233.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="320.5" y="206.2" width="9" height="22.0" fill="#188a3e"/>
|
||||
<line x1="340.0" y1="178.2" x2="340.0" y2="211.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="335.5" y="184.2" width="9" height="22.0" fill="#188a3e"/>
|
||||
<line x1="355.0" y1="154.2" x2="355.0" y2="189.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="350.5" y="160.2" width="9" height="24.0" fill="#188a3e"/>
|
||||
<line x1="370.0" y1="128.2" x2="370.0" y2="165.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="365.5" y="134.2" width="9" height="26.0" fill="#188a3e"/>
|
||||
<line x1="385.0" y1="90.2" x2="385.0" y2="138.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="380.5" y="100.2" width="9" height="34.0" fill="#188a3e"/>
|
||||
<line x1="400.0" y1="96.2" x2="400.0" y2="118.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="395.5" y="100.2" width="9" height="10.0" fill="#c00"/>
|
||||
<line x1="415.0" y1="105.2" x2="415.0" y2="164.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="410.5" y="110.2" width="9" height="44.0" fill="#c00"/>
|
||||
<line x1="430.0" y1="148.2" x2="430.0" y2="188.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="425.5" y="154.2" width="9" height="28.0" fill="#c00"/>
|
||||
<line x1="445.0" y1="176.2" x2="445.0" y2="216.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="440.5" y="182.2" width="9" height="28.0" fill="#c00"/>
|
||||
<line x1="460.0" y1="204.2" x2="460.0" y2="248.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="455.5" y="210.2" width="9" height="32.0" fill="#c00"/>
|
||||
<line x1="475.0" y1="236.2" x2="475.0" y2="284.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="470.5" y="242.2" width="9" height="36.0" fill="#c00"/>
|
||||
<line x1="490.0" y1="272.2" x2="490.0" y2="328.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="485.5" y="278.2" width="9" height="44.0" fill="#c00"/>
|
||||
<line x1="505.0" y1="316.2" x2="505.0" y2="380.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="500.5" y="322.2" width="9" height="52.0" fill="#c00"/>
|
||||
<text x="385.0" y="80.2" fill="#188a3e" text-anchor="middle" style="font-weight:bold">M: candelă verde peste bandă + TICUS ≥ +1000</text>
|
||||
<text x="24" y="402.2" fill="#188a3e">M: valori exacte NEACOPERIT în material (bandă + TICUS); bara ~+1000 din panou e ilustrativă</text>
|
||||
<text x="24" y="419.2" fill="#c00">trigger Sell Momentum → intrare short 660.00 (risc 0.41 % = 2.71)</text>
|
||||
<text x="24" y="436.2" fill="#c00">TP1 657.29 (1:1) atins în prima candelă după intrare</text>
|
||||
<text x="24" y="453.2" fill="#c00">ieșire pe trail 646.80 = 2 % = ~4.9R, după 7 candele de 15 min de la intrare</text>
|
||||
</g>
|
||||
<text x="24" y="516" fill="#333" style="font-weight:bold">Panou TICUS — linia 0 și pragul +1000 (ilustrativ)</text>
|
||||
<line x1="24" y1="532.0" x2="646" y2="532.0" stroke="#b06000" stroke-width="1.2" stroke-dasharray="2 4"/>
|
||||
<text x="632" y="527.0" fill="#b06000" text-anchor="end">+1000 (prag)</text>
|
||||
<line x1="24" y1="584.0" x2="646" y2="584.0" stroke="#888"/>
|
||||
<text x="24" y="579.0" fill="#555">0</text>
|
||||
<rect x="380.5" y="514.0" width="9" height="70.0" fill="#188a3e" opacity="0.85"/>
|
||||
<text x="385.0" y="509.0" fill="#777" text-anchor="middle">ilustrativ</text>
|
||||
<text x="385.0" y="600.0" fill="#777" text-anchor="middle">candela M</text>
|
||||
<text x="24" y="620.0" fill="#555">Legendă: procentele (risc 0.41 %, ieșire 2 %) sunt ale instructorului.</text>
|
||||
<text x="24" y="636.0" fill="#555">Prețurile absolute (660.00 / 662.71 / 657.29 / 646.80) sunt construite de sumarizator —</text>
|
||||
<text x="24" y="652.0" fill="#555">09_QA/11_2025-11-25.md:716-719.</text>
|
||||
<text x="24" y="668.0" fill="#555">Structura de reținut: risc mic → TP1 (1:1) atins imediat → trail lung (7 candele de 15 min).</text>
|
||||
<text x="24" y="684.0" fill="#555">Valoarea exactă a benzii și a TICUS la candela M nu e dată în sesiune; bara ~+1000 e ilustrativă.</text>
|
||||
<text x="24" y="700.0" fill="#555">„Taurii urcă pe scări, urșii sar de la etaj" — de ce short-ul se consumă în ore.</text>
|
||||
<text x="24" y="716.0" fill="#555">(09_QA/11_2025-11-25.md:643-648).</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
204
gen/strategii/mbp_samecandle.py
Normal file
@@ -0,0 +1,204 @@
|
||||
"""Diagrama „aceeași candelă" MBP / MBPA++ (marcajul `samecandle`).
|
||||
|
||||
Două panouri alături, fiecare cu un grafic de 10 candele de 15 min și un panou
|
||||
TICUS dedesubt, legate printr-o linie punctată verticală de la candela M la
|
||||
valoarea ei TICUS. Singura diferență dintre panouri: pe care candelă e TICUS
|
||||
extrem.
|
||||
|
||||
- Panoul DA: M (roșie, index 5) închide 623.87 sub banda inferioară 623.90
|
||||
ȘI TICUS(M) = -1150 (≤ -1000) — pe EXACT aceeași candelă. M îndeplinit.
|
||||
- Panoul NU: aceeași structură de preț, dar TICUS(M) = -600 (peste prag),
|
||||
iar extremul de -1200 e pe candela m-1, a cărei închidere e peste bandă.
|
||||
M neîndeplinit, deși vizual „pare" același setup.
|
||||
|
||||
Regula și cifrele vin din `summaries/05_MODUL_4/03_strategia_6_mbp_mbappe.md`:
|
||||
- regula (bandă și TICUS pe ACEEAȘI candelă) — liniile 179-221, 439-442;
|
||||
- verificarea prin click pe candelă, nu din ochi — liniile 217-221, 436-438;
|
||||
- pragurile ±1000, ±500 doar referință vizuală — liniile 56-64;
|
||||
- M close 623.87 sub banda 623.90 și TICUS(M) -1150 (exemplul real) —
|
||||
liniile 336-342.
|
||||
Cifrele din panoul NU (TICUS(M) = -600, TICUS(m-1) = -1200) sunt construite de
|
||||
mine ca ilustrare a cazului „aproape, dar nu" (spec, rândurile 27-31).
|
||||
Structura candelelor e construită de mine, ca fiecare nivel afirmat să iasă
|
||||
exact din serie (assert-urile de mai jos).
|
||||
|
||||
Rulează:
|
||||
python3 gen/strategii/mbp_samecandle.py
|
||||
Scrie lângă el `mbp_samecandle.svg`.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "mbp_samecandle.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# ---------------------------------------------------------------- seria (10 bare)
|
||||
# 5 bare de coborâre până la M (index 5), apoi 4 bare care își revin. Open-ul
|
||||
# fiecărei bare = close-ul precedentei. Aceeași serie în ambele panouri.
|
||||
CLOSES = [625.35, 625.10, 624.90, 624.65, 624.30, # 0-4 downtrend
|
||||
623.87, # 5 = M, roșie, sub bandă
|
||||
623.98, 624.12, 624.28, 624.45] # 6-9 revenire
|
||||
WICKS = [(0.08, 0.08), (0.10, 0.10), (0.08, 0.10), (0.10, 0.10), (0.08, 0.12),
|
||||
(0.06, 0.10), # M: high 624.36, low 623.77
|
||||
(0.06, 0.06), (0.08, 0.06), (0.06, 0.06), (0.08, 0.05)]
|
||||
assert len(CLOSES) == 10 and len(WICKS) == 10, (len(CLOSES), len(WICKS))
|
||||
|
||||
BAND = 623.90
|
||||
M = 5
|
||||
|
||||
cds = from_closes(CLOSES, wicks=WICKS, start=625.55)
|
||||
|
||||
# ---------------------------------------------------------------- assert-urile din spec
|
||||
assert abs(cds[M].c - 623.87) < 0.005, cds[M].c # exemplul real din curs
|
||||
assert cds[M].c < BAND, cds[M].c # M închide sub banda inferioară
|
||||
assert cds[M].c < cds[M].o, (cds[M].o, cds[M].c) # M e roșie
|
||||
assert abs(cds[M].l - 623.77) < 0.005, cds[M].l
|
||||
assert cds[M].l == min(c.l for c in cds) # M are minimul seriei
|
||||
assert cds[M - 1].c > BAND, cds[M - 1].c # candela m-1 închide PESTE bandă
|
||||
assert [round(c.c, 2) for c in cds] == CLOSES # close-urile sunt respectate
|
||||
|
||||
# TICUS: valoarea pe candelă. DA: doar M, sub prag. NU: extremul pe m-1.
|
||||
TICUS_DA = {M: -1150.0}
|
||||
TICUS_NU = {M - 1: -1200.0, M: -600.0}
|
||||
|
||||
assert TICUS_DA[M] <= -1000, TICUS_DA[M] # DA: ticus(M) under -1000
|
||||
assert TICUS_NU[M] > -1000, TICUS_NU[M] # NU: ticus(M) peste prag
|
||||
assert TICUS_NU[M - 1] <= -1000, TICUS_NU[M - 1] # NU: extremul e pe m-1
|
||||
assert not any(i == M and v <= -1000 for i, v in TICUS_NU.items()), \
|
||||
"NU: nu are voie să existe bar TICUS sub -1000 aliniat cu candela M"
|
||||
|
||||
# ---------------------------------------------------------------- construcția panourilor
|
||||
SCALE, GAP, BODY, LEFT, W = 85.0, 16, 10, 44, 380
|
||||
|
||||
|
||||
def _ref_line(Wv, y, color, dash, text, lab_dy):
|
||||
dash_attr = f' stroke-dasharray="{dash}"' if dash else ""
|
||||
return (f'<line x1="24" y1="{y:.1f}" x2="{Wv - 14}" y2="{y:.1f}" stroke="{color}" '
|
||||
f'stroke-width="1"{dash_attr}/>'
|
||||
f'<text x="26" y="{y + lab_dy:.1f}" fill="{color}">{text}</text>')
|
||||
|
||||
|
||||
def build_panel(ticus, labels):
|
||||
"""Un panou = grafic de preț (svg_candles) + panou TICUS dedesubt."""
|
||||
ch = Chart(candles=cds,
|
||||
levels=[Level(BAND, "bandă 623.90", BLUE, dash="4 3", label_at="right")],
|
||||
scale=SCALE, gap=GAP, body_w=BODY, left=LEFT, width=W)
|
||||
raw = ch.render()
|
||||
mm = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', raw)
|
||||
Wv, H0 = int(mm.group(1)), float(mm.group(2))
|
||||
inner = raw[raw.index(">") + 1: raw.rindex("</svg>")].strip("\n")
|
||||
|
||||
ticus_top = H0 + 30
|
||||
y0 = H0 + 100 # linia 0 a TICUS
|
||||
PX = 44.0 # 1000 unități TICUS = 44 px
|
||||
bar_label_x = ch._cx(M) + 16 # etichetele barelor stau în coloană, la dreapta
|
||||
|
||||
def ty(v):
|
||||
return y0 - v / 1000.0 * PX
|
||||
|
||||
total_h = y0 + 96
|
||||
parts = []
|
||||
|
||||
# linia punctată verticală: aceeași candelă sus și jos (desenată prima, ca
|
||||
# etichetele să vină peste ea)
|
||||
for i, v in sorted(ticus.items()):
|
||||
xb = ch._cx(i)
|
||||
assert -1 <= xb <= Wv + 1, xb
|
||||
parts.append(f'<line x1="{xb:.1f}" y1="{ch._y(cds[i].l):.1f}" x2="{xb:.1f}" '
|
||||
f'y2="{ty(v):.1f}" stroke="#aaa" stroke-width="1" stroke-dasharray="3 3"/>')
|
||||
|
||||
parts.append(f'<text x="14" y="{ticus_top:.0f}" fill="#333" '
|
||||
f'style="font-weight:bold">TICUS</text>')
|
||||
# praguri: 0 (linie plină), ±1000 (punctate), ±500 (fine, doar referință)
|
||||
parts.append(_ref_line(Wv, ty(1000), RED, "4 3", "+1000 (prag)", -5))
|
||||
parts.append(_ref_line(Wv, ty(500), "#caa000", "2 2", "+500", -5))
|
||||
parts.append(_ref_line(Wv, ty(0), "#888", "", "0", 4))
|
||||
parts.append(_ref_line(Wv, ty(-500), "#caa000", "2 2", "-500", 13))
|
||||
parts.append(_ref_line(Wv, ty(-1000), GREEN, "4 3", "-1000 (prag)", 13))
|
||||
|
||||
# barele TICUS (centrate pe x-ul candelei)
|
||||
for i, v in sorted(ticus.items()):
|
||||
xb = ch._cx(i)
|
||||
yv = ty(v)
|
||||
col = RED if v <= -1000 else ORANGE
|
||||
parts.append(f'<rect x="{xb - BODY / 2:.1f}" y="{min(y0, yv):.1f}" '
|
||||
f'width="{BODY}" height="{abs(yv - y0):.1f}" fill="{col}"/>')
|
||||
|
||||
# eticheta de valoare lângă fiecare bar (aceeași coloană x, rânduri diferite)
|
||||
for i, (txt, dy) in sorted(labels.items()):
|
||||
col = RED if ticus[i] <= -1000 else ORANGE
|
||||
parts.append(f'<text x="{bar_label_x:.1f}" y="{ty(ticus[i]) + dy:.1f}" '
|
||||
f'fill="{col}">{txt}</text>')
|
||||
|
||||
# adnotarea candelei M, în zona de preț, sub bară (zona e liberă)
|
||||
parts.append(f'<text x="{ch._cx(M):.1f}" y="{ch._y(cds[M].l) + 18:.1f}" fill="{RED}" '
|
||||
f'text-anchor="middle" style="font-weight:bold">'
|
||||
f'M: close 623.87 < bandă 623.90</text>')
|
||||
|
||||
# alinierea TICUS(M) cu candela M: barul se centrează exact pe x-ul candelei
|
||||
xm = ch._cx(M)
|
||||
x_bar_m = ch._cx(M) # centrul barei TICUS a lui M
|
||||
assert abs(x_bar_m - xm) < 1e-9
|
||||
assert ticus[M] <= -1000 if ticus is TICUS_DA else ticus[M] > -1000
|
||||
|
||||
body = inner + "\n " + "\n ".join(parts)
|
||||
# bara TICUS a lui M chiar a fost scrisă cu centrul pe x-ul candelei M
|
||||
assert f'<rect x="{xm - BODY / 2:.1f}"' in body, "bara TICUS(M) nu e aliniată cu M"
|
||||
assert f'<line x1="{xm:.1f}"' in body, "linia punctată verticală nu pleacă din M"
|
||||
return ch, Wv, H0, total_h, body
|
||||
|
||||
|
||||
labels_da = {M: ("M: TICUS -1150 (sub -1000)", 18)}
|
||||
labels_nu = {M - 1: ("TICUS(m-1) = -1200 (extrem)", 20),
|
||||
M: ("TICUS(M) = -600 (nu atinge -1000)", 20)}
|
||||
|
||||
ch_da, Wv, H0, PH, inner_da = build_panel(TICUS_DA, labels_da)
|
||||
ch_nu, Wv2, H02, PH2, inner_nu = build_panel(TICUS_NU, labels_nu)
|
||||
assert Wv == Wv2 and PH == PH2, (PH, PH2)
|
||||
|
||||
# aceeași structură de preț => aceleași x-uri ale candelelor în ambele panouri
|
||||
assert all(abs(ch_da._cx(i) - ch_nu._cx(i)) < 1e-9 for i in range(len(cds)))
|
||||
|
||||
# ---------------------------------------------------------------- compunerea SVG-ului
|
||||
TOP = 48
|
||||
FOOTER = [
|
||||
"Regula: banda (close sub 623.90) și TICUS ≤ -1000 trebuie pe ACEEAȘI candelă.",
|
||||
"Pragurile TICUS sunt ±1000 (overbought / oversold); ±500 sunt doar referință vizuală.",
|
||||
"Verificarea se face prin click pe candelă, nu din ochi — TICUS(M) se citește exact pe candela M.",
|
||||
"DA: TICUS(M) = -1150 (exemplul real din curs).",
|
||||
"NU: TICUS(M) = -600, TICUS(m-1) = -1200 — cifre construite de mine ca ilustrare.",
|
||||
"Structura candelelor (10 bare de 15 min, aceeași în ambele panouri) e construită de mine.",
|
||||
]
|
||||
root_h = TOP + PH + 22 + 17 * len(FOOTER) + 8
|
||||
|
||||
out = [f'<svg viewBox="0 0 780 {root_h:.0f}" xmlns="http://www.w3.org/2000/svg" '
|
||||
f'style="width:100%;height:auto;font-family:system-ui,sans-serif;font-size:13px">',
|
||||
f'<svg x="8" y="{TOP}" width="{Wv}" height="{PH:.0f}" viewBox="0 0 {Wv} {PH:.0f}">{inner_da}</svg>',
|
||||
f'<svg x="396" y="{TOP}" width="{Wv}" height="{PH:.0f}" viewBox="0 0 {Wv} {PH:.0f}">{inner_nu}</svg>',
|
||||
f'<text x="8" y="22" fill="{GREEN}" style="font-weight:bold">DA — aceeași candelă, M îndeplinit</text>',
|
||||
f'<text x="396" y="16" fill="{RED}" style="font-weight:bold">NU — TICUS extrem pe altă candelă,</text>',
|
||||
f'<text x="396" y="32" fill="{RED}" style="font-weight:bold">M neîndeplinit</text>']
|
||||
fy = TOP + PH + 22
|
||||
for i, line in enumerate(FOOTER):
|
||||
out.append(f'<text x="8" y="{fy + 17 * i:.0f}" fill="#555">{line}</text>')
|
||||
out.append("</svg>")
|
||||
svg = "\n ".join(out)
|
||||
|
||||
# ---------------------------------------------------------------- verificări pe SVG-ul final
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
assert "DA — aceeași candelă, M îndeplinit" in svg
|
||||
assert "NU — TICUS extrem pe altă candelă," in svg
|
||||
assert "M neîndeplinit" in svg
|
||||
assert "click pe candelă, nu din ochi" in svg
|
||||
assert svg.count("<svg") == 3 and svg.count("</svg>") == 3, "cele două panouri + rădăcina"
|
||||
|
||||
OUT.write_text(svg)
|
||||
print(f"scris {OUT.name}: {len(cds)} candele/panou, M close {cds[M].c:.2f} "
|
||||
f"(< {BAND}), TICUS DA {TICUS_DA[M]:.0f} / NU {TICUS_NU[M]:.0f} "
|
||||
f"(extrem {TICUS_NU[M - 1]:.0f} pe m-1), x(M)={ch_da._cx(M):.0f}, "
|
||||
f"înălțime {root_h:.0f}px")
|
||||
78
gen/strategii/mbp_samecandle.svg
Normal file
@@ -0,0 +1,78 @@
|
||||
<svg viewBox="0 0 780 594" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font-family:system-ui,sans-serif;font-size:13px">
|
||||
<svg x="8" y="48" width="380" height="414" viewBox="0 0 380 414"> <line x1="24" y1="181.1" x2="366" y2="181.1" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="352" y="175.1" fill="#1a5fb4" text-anchor="end">bandă 623.90</text>
|
||||
<line x1="44.0" y1="34.0" x2="44.0" y2="64.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.0" y="40.8" width="10" height="17.0" fill="#c00"/>
|
||||
<line x1="60.0" y1="49.3" x2="60.0" y2="87.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="55.0" y="57.8" width="10" height="21.2" fill="#c00"/>
|
||||
<line x1="76.0" y1="72.2" x2="76.0" y2="104.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="71.0" y="79.0" width="10" height="17.0" fill="#c00"/>
|
||||
<line x1="92.0" y1="87.5" x2="92.0" y2="125.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="87.0" y="96.1" width="10" height="21.2" fill="#c00"/>
|
||||
<line x1="108.0" y1="110.5" x2="108.0" y2="157.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.0" y="117.3" width="10" height="29.8" fill="#c00"/>
|
||||
<line x1="124.0" y1="142.0" x2="124.0" y2="192.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="119.0" y="147.1" width="10" height="36.5" fill="#c00"/>
|
||||
<line x1="140.0" y1="169.2" x2="140.0" y2="188.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="135.0" y="174.2" width="10" height="9.4" fill="#188a3e"/>
|
||||
<line x1="156.0" y1="155.5" x2="156.0" y2="179.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="151.0" y="162.3" width="10" height="11.9" fill="#188a3e"/>
|
||||
<line x1="172.0" y1="143.7" x2="172.0" y2="167.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="167.0" y="148.8" width="10" height="13.6" fill="#188a3e"/>
|
||||
<line x1="188.0" y1="127.5" x2="188.0" y2="153.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="183.0" y="134.3" width="10" height="14.5" fill="#188a3e"/>
|
||||
<line x1="124.0" y1="192.1" x2="124.0" y2="368.6" stroke="#aaa" stroke-width="1" stroke-dasharray="3 3"/>
|
||||
<text x="14" y="248" fill="#333" style="font-weight:bold">TICUS</text>
|
||||
<line x1="24" y1="274.0" x2="366" y2="274.0" stroke="#c00" stroke-width="1" stroke-dasharray="4 3"/><text x="26" y="269.0" fill="#c00">+1000 (prag)</text>
|
||||
<line x1="24" y1="296.0" x2="366" y2="296.0" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/><text x="26" y="291.0" fill="#caa000">+500</text>
|
||||
<line x1="24" y1="318.0" x2="366" y2="318.0" stroke="#888" stroke-width="1"/><text x="26" y="322.0" fill="#888">0</text>
|
||||
<line x1="24" y1="340.0" x2="366" y2="340.0" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/><text x="26" y="353.0" fill="#caa000">-500</text>
|
||||
<line x1="24" y1="362.0" x2="366" y2="362.0" stroke="#188a3e" stroke-width="1" stroke-dasharray="4 3"/><text x="26" y="375.0" fill="#188a3e">-1000 (prag)</text>
|
||||
<rect x="119.0" y="318.0" width="10" height="50.6" fill="#c00"/>
|
||||
<text x="140.0" y="386.6" fill="#c00">M: TICUS -1150 (sub -1000)</text>
|
||||
<text x="124.0" y="210.1" fill="#c00" text-anchor="middle" style="font-weight:bold">M: close 623.87 < bandă 623.90</text></svg>
|
||||
<svg x="396" y="48" width="380" height="414" viewBox="0 0 380 414"> <line x1="24" y1="181.1" x2="366" y2="181.1" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="352" y="175.1" fill="#1a5fb4" text-anchor="end">bandă 623.90</text>
|
||||
<line x1="44.0" y1="34.0" x2="44.0" y2="64.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.0" y="40.8" width="10" height="17.0" fill="#c00"/>
|
||||
<line x1="60.0" y1="49.3" x2="60.0" y2="87.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="55.0" y="57.8" width="10" height="21.2" fill="#c00"/>
|
||||
<line x1="76.0" y1="72.2" x2="76.0" y2="104.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="71.0" y="79.0" width="10" height="17.0" fill="#c00"/>
|
||||
<line x1="92.0" y1="87.5" x2="92.0" y2="125.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="87.0" y="96.1" width="10" height="21.2" fill="#c00"/>
|
||||
<line x1="108.0" y1="110.5" x2="108.0" y2="157.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="103.0" y="117.3" width="10" height="29.8" fill="#c00"/>
|
||||
<line x1="124.0" y1="142.0" x2="124.0" y2="192.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="119.0" y="147.1" width="10" height="36.5" fill="#c00"/>
|
||||
<line x1="140.0" y1="169.2" x2="140.0" y2="188.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="135.0" y="174.2" width="10" height="9.4" fill="#188a3e"/>
|
||||
<line x1="156.0" y1="155.5" x2="156.0" y2="179.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="151.0" y="162.3" width="10" height="11.9" fill="#188a3e"/>
|
||||
<line x1="172.0" y1="143.7" x2="172.0" y2="167.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="167.0" y="148.8" width="10" height="13.6" fill="#188a3e"/>
|
||||
<line x1="188.0" y1="127.5" x2="188.0" y2="153.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="183.0" y="134.3" width="10" height="14.5" fill="#188a3e"/>
|
||||
<line x1="108.0" y1="157.3" x2="108.0" y2="370.8" stroke="#aaa" stroke-width="1" stroke-dasharray="3 3"/>
|
||||
<line x1="124.0" y1="192.1" x2="124.0" y2="344.4" stroke="#aaa" stroke-width="1" stroke-dasharray="3 3"/>
|
||||
<text x="14" y="248" fill="#333" style="font-weight:bold">TICUS</text>
|
||||
<line x1="24" y1="274.0" x2="366" y2="274.0" stroke="#c00" stroke-width="1" stroke-dasharray="4 3"/><text x="26" y="269.0" fill="#c00">+1000 (prag)</text>
|
||||
<line x1="24" y1="296.0" x2="366" y2="296.0" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/><text x="26" y="291.0" fill="#caa000">+500</text>
|
||||
<line x1="24" y1="318.0" x2="366" y2="318.0" stroke="#888" stroke-width="1"/><text x="26" y="322.0" fill="#888">0</text>
|
||||
<line x1="24" y1="340.0" x2="366" y2="340.0" stroke="#caa000" stroke-width="1" stroke-dasharray="2 2"/><text x="26" y="353.0" fill="#caa000">-500</text>
|
||||
<line x1="24" y1="362.0" x2="366" y2="362.0" stroke="#188a3e" stroke-width="1" stroke-dasharray="4 3"/><text x="26" y="375.0" fill="#188a3e">-1000 (prag)</text>
|
||||
<rect x="103.0" y="318.0" width="10" height="52.8" fill="#c00"/>
|
||||
<rect x="119.0" y="318.0" width="10" height="26.4" fill="#b06000"/>
|
||||
<text x="140.0" y="390.8" fill="#c00">TICUS(m-1) = -1200 (extrem)</text>
|
||||
<text x="140.0" y="364.4" fill="#b06000">TICUS(M) = -600 (nu atinge -1000)</text>
|
||||
<text x="124.0" y="210.1" fill="#c00" text-anchor="middle" style="font-weight:bold">M: close 623.87 < bandă 623.90</text></svg>
|
||||
<text x="8" y="22" fill="#188a3e" style="font-weight:bold">DA — aceeași candelă, M îndeplinit</text>
|
||||
<text x="396" y="16" fill="#c00" style="font-weight:bold">NU — TICUS extrem pe altă candelă,</text>
|
||||
<text x="396" y="32" fill="#c00" style="font-weight:bold">M neîndeplinit</text>
|
||||
<text x="8" y="484" fill="#555">Regula: banda (close sub 623.90) și TICUS ≤ -1000 trebuie pe ACEEAȘI candelă.</text>
|
||||
<text x="8" y="501" fill="#555">Pragurile TICUS sunt ±1000 (overbought / oversold); ±500 sunt doar referință vizuală.</text>
|
||||
<text x="8" y="518" fill="#555">Verificarea se face prin click pe candelă, nu din ochi — TICUS(M) se citește exact pe candela M.</text>
|
||||
<text x="8" y="535" fill="#555">DA: TICUS(M) = -1150 (exemplul real din curs).</text>
|
||||
<text x="8" y="552" fill="#555">NU: TICUS(M) = -600, TICUS(m-1) = -1200 — cifre construite de mine ca ilustrare.</text>
|
||||
<text x="8" y="569" fill="#555">Structura candelelor (10 bare de 15 min, aceeași în ambele panouri) e construită de mine.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
166
gen/strategii/mdsrt_ansamblu.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""Diagrama de ansamblu MDSRT long (strategii, marcajul `ansamblu`).
|
||||
|
||||
„SPY — 5 min: al doilea picior mai jos la 589.80, sub suportul 590.00 →
|
||||
divergență bull (M) preț-vs-volum → candela-trigger Buy Hammer/Doji →
|
||||
intrare 590.20 → SL 589.40 (risc 0.80) → TP1 591.00 (1:1) → poziția rămasă
|
||||
pe trail, ieșire la close 592.40 (≈ 2.75R)."
|
||||
|
||||
Toate close-urile, wick-urile speciale și nivelurile vin din
|
||||
`gen/strategii/mdsrt_ansamblu.spec.txt`. Structura M→A→P, suportul 590.00,
|
||||
minimul 589.80 și riscul 0.80 sunt din exemplul cursului
|
||||
(`05_MODUL_4/02_strategia_5_mdsrt.md:159-177, 246, 308-311`). Intrare/SL/TP
|
||||
sunt exemplul construit al autorului sumarului, marcat explicit ca NU citat
|
||||
din curs (`...:166-177, 439-445`); transcrierea se întrerupe înainte de
|
||||
planul numeric. Volumele sunt relative, alese de generator (materialul nu dă
|
||||
volum pentru acest exemplu).
|
||||
|
||||
Rulează:
|
||||
python3 gen/strategii/mdsrt_ansamblu.py
|
||||
Scrie lângă el `mdsrt_ansamblu.svg`.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "mdsrt_ansamblu.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# ---------------------------------------------------------------- seria (30 bare)
|
||||
CLOSES = [
|
||||
596.00, 595.20, 595.60, 594.80, 594.10, 593.40, 593.70, 592.90, # 0-7
|
||||
592.20, 591.60, 591.90, 591.30, 590.80, 590.30, 590.55, 590.10, # 8-15
|
||||
589.85, 590.05, # 16-17
|
||||
589.80, # 18 = al doilea picior mai jos + candela-trigger (hammer)
|
||||
590.20, # 19 = intrare (close = entry; sparge high-ul trigger-ului)
|
||||
590.60, 590.90, 591.00, # 20-22 (22 = TP1 1:1)
|
||||
591.30, 591.10, 591.45, 591.70, 591.95, 592.15, # 23-28
|
||||
592.40, # 29 = ieșire pe trail (close)
|
||||
]
|
||||
assert len(CLOSES) == 30, len(CLOSES)
|
||||
|
||||
# fitile (sus, jos): (0.25, 0.30) peste tot, cu excepțiile din spec.
|
||||
WICKS = [(0.25, 0.30)] * 30
|
||||
WICKS[18] = (0.10, 0.40) # hammer: high 590.15, low 589.40 (= SL)
|
||||
WICKS[19] = (0.10, 0.05) # intrare: high 590.30, low 589.75
|
||||
# bara 21 coborâtă sub TP1 (high 590.95 < 591.00), ca TP1 să fie atins exact
|
||||
# prima dată la bara 22 (altfel assert-ul 5 ar pica pe „primul high").
|
||||
WICKS[21] = (0.05, 0.30)
|
||||
|
||||
# volum relativ (ales de generator): 16-19 crescător, 18-19 peste medie,
|
||||
# vol[18] > vol[10] (higher low pe volum la lower low pe preț).
|
||||
VOLS = [
|
||||
48, 46, 44, 45, 43, 41, 40, 42, 44, 46, # 0-9
|
||||
52, 38, 40, 42, 39, 44, # 10-15
|
||||
50, 58, 72, 85, # 16-19 absorbție la minim
|
||||
70, 64, 58, 60, 55, 52, 50, 48, 46, 44, # 20-29
|
||||
]
|
||||
assert len(VOLS) == 30, len(VOLS)
|
||||
|
||||
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=596.60)
|
||||
|
||||
# ---------------------------------------------------------------- assert-urile din spec
|
||||
entry = cds[19].c
|
||||
sl = cds[18].l
|
||||
tp1 = cds[22].c
|
||||
|
||||
# 1. minimul și SL-ul pe bara 18
|
||||
assert cds[18].c == 589.80, cds[18].c
|
||||
assert cds[18].l == 589.40, cds[18].l
|
||||
# 2. al doilea picior rupe suportul/pivotul 590.00
|
||||
assert cds[18].l < 590.00, cds[18].l
|
||||
# 3. intrarea = close-ul barei 19 și sparge high-ul trigger-ului
|
||||
assert cds[19].c == 590.20, cds[19].c
|
||||
assert cds[19].c == entry
|
||||
assert cds[19].c > cds[18].h, (cds[19].c, cds[18].h)
|
||||
# 4. TP1 exact 1:1
|
||||
assert abs((entry - sl) - (tp1 - entry)) < 1e-9, (entry - sl, tp1 - entry)
|
||||
assert abs((entry - sl) - 0.80) < 1e-9
|
||||
# 5. TP1 atins la bara 22 (și nu mai devreme)
|
||||
assert cds[22].c == 591.00, cds[22].c
|
||||
assert cds[22].c == tp1
|
||||
assert max(c.h for c in cds[20:22]) < tp1, [c.h for c in cds[20:22]]
|
||||
# 6. ieșirea pe trail e pe profit
|
||||
assert cds[29].c == 592.40, cds[29].c
|
||||
assert cds[29].c > entry, (cds[29].c, entry)
|
||||
# 7. hammer: corp mic, coadă lungă în jos
|
||||
body = abs(cds[18].o - cds[18].c)
|
||||
rng = cds[18].h - cds[18].l
|
||||
tail = min(cds[18].o, cds[18].c) - cds[18].l
|
||||
assert body == 0.25, body
|
||||
assert body <= 0.5 * rng, (body, rng)
|
||||
assert tail > body, (tail, body)
|
||||
# 8. downtrend vizibil
|
||||
assert cds[13].c < cds[0].c - 5, (cds[13].c, cds[0].c)
|
||||
# 9. divergență pe volum: lower low pe preț, higher low pe volum
|
||||
assert cds[18].l < cds[10].l, (cds[18].l, cds[10].l)
|
||||
assert VOLS[18] > VOLS[10], (VOLS[18], VOLS[10])
|
||||
# barele 16-19 au volum crescător; 18-19 peste media seriei
|
||||
assert VOLS[16] < VOLS[17] < VOLS[18] < VOLS[19], VOLS[16:20]
|
||||
avg_vol = sum(VOLS) / len(VOLS)
|
||||
assert VOLS[18] > avg_vol and VOLS[19] > avg_vol, (VOLS[18], VOLS[19], avg_vol)
|
||||
# 10. fiecare open == close-ul barei anterioare
|
||||
for a, b in zip(cds, cds[1:]):
|
||||
assert a.c == b.o, (a.c, b.o)
|
||||
|
||||
r_mult = (cds[29].c - entry) / (entry - sl)
|
||||
assert abs(r_mult - 2.75) < 1e-9, r_mult
|
||||
|
||||
# ---------------------------------------------------------------- niveluri, marks
|
||||
levels = [
|
||||
Level(590.00, "suport / pivot 590.00", BLUE, label_at="left"),
|
||||
Level(sl, f"SL {sl:.2f} — risc 0.80", RED, label_at="left"),
|
||||
# stânga: altfel eticheta din dreapta se calcă pe textul „SMA-20" al mediei
|
||||
Level(tp1, f"TP1 {tp1:.2f} = 1:1", GREEN, label_at="left"),
|
||||
]
|
||||
|
||||
marks = [
|
||||
Mark(18, f"minim 589.80 — divergență (M)", RED, "below"),
|
||||
# ridicat deasupra vârfurilor candelelor de urcare (altfel textul cade peste
|
||||
# fitilele barelor 20-22) — bara 19 e totuși exact sub el
|
||||
Mark(19, f"intrare {entry:.2f}", GREEN, "above", dy=-46),
|
||||
Mark(29, f"ieșire trail {cds[29].c:.2f}", GREEN, "above"),
|
||||
]
|
||||
|
||||
# titlul e injectat pe două linii: textul din spec (82 caractere) nu încape
|
||||
# pe un singur rând în 640 px fără să fie tăiat la margine
|
||||
TITLE = ["SPY — 5 min (exemplu construit pe scara lecției;",
|
||||
"cifrele planului nu vin din curs)"]
|
||||
|
||||
ch = Chart(
|
||||
candles=cds, levels=levels, marks=marks,
|
||||
mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")],
|
||||
scale=48.0, gap=15, body_w=9, left=44, width=640, top=64, vol_h=70,
|
||||
vol_label="volum",
|
||||
title="", caption="",
|
||||
)
|
||||
svg = ch.render()
|
||||
title_svg = "\n ".join(
|
||||
f'<text x="24" y="{26 + 18 * i}" fill="#333" style="font-weight:bold">{t}</text>'
|
||||
for i, t in enumerate(TITLE))
|
||||
i = svg.index(">") + 1
|
||||
svg = svg[:i] + "\n " + title_svg + svg[i:]
|
||||
|
||||
# ---------------------------------------------------------------- legenda (onestitate)
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
|
||||
W, H0 = int(m.group(1)), float(m.group(2))
|
||||
legend = [
|
||||
"Legendă: entry/SL/TP/trail sunt exemplul construit al autorului sumarului,",
|
||||
"nu citate din curs (transcrierea se întrerupe înainte de planul numeric).",
|
||||
]
|
||||
legend_svg = [f'<text x="24" y="{H0 + 16 + 17 * i:.1f}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(legend)]
|
||||
new_h = H0 + 16 + 17 * (len(legend) - 1) + 14
|
||||
svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")',
|
||||
lambda mm: mm.group(1) + f"{new_h:.0f}" + mm.group(2), svg, count=1)
|
||||
svg = svg.replace("\n</svg>", "\n " + "\n ".join(legend_svg) + "\n</svg>")
|
||||
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
assert f"TP1 {tp1:.2f}" in svg and f"intrare {entry:.2f}" in svg
|
||||
OUT.write_text(svg)
|
||||
print(f"scris {OUT.name}: {len(cds)} candele, suport 590.00, minim {cds[18].l:.2f}, "
|
||||
f"intrare {entry:.2f}, SL {sl:.2f} (risc 0.80), TP1 {tp1:.2f} (1:1), "
|
||||
f"ieșire {cds[29].c:.2f} = {r_mult:.2f}R, vol[18]={VOLS[18]} > vol[10]={VOLS[10]}")
|
||||
109
gen/strategii/mdsrt_ansamblu.svg
Normal file
@@ -0,0 +1,109 @@
|
||||
<svg viewBox="0 0 640 591" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="26" fill="#333" style="font-weight:bold">SPY — 5 min (exemplu construit pe scara lecției;</text>
|
||||
<text x="24" y="44" fill="#333" style="font-weight:bold">cifrele planului nu vin din curs)</text>
|
||||
<line x1="24" y1="392.8" x2="626" y2="392.8" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="386.8" fill="#1a5fb4">suport / pivot 590.00</text>
|
||||
<line x1="24" y1="421.6" x2="626" y2="421.6" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="415.6" fill="#c00">SL 589.40 — risc 0.80</text>
|
||||
<line x1="24" y1="344.8" x2="626" y2="344.8" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="338.8" fill="#188a3e">TP1 591.00 = 1:1</text>
|
||||
<polyline points="329.0,286.4 344.0,299.3 359.0,309.6 374.0,320.7 389.0,329.1 404.0,336.3 419.0,341.0 434.0,345.8 449.0,348.0 464.0,348.2 479.0,346.2" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="485" y="340" fill="#b06000">SMA-20</text>
|
||||
<line x1="44.0" y1="64.0" x2="44.0" y2="119.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="76.0" width="9" height="28.8" fill="#c00"/>
|
||||
<line x1="59.0" y1="92.8" x2="59.0" y2="157.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="104.8" width="9" height="38.4" fill="#c00"/>
|
||||
<line x1="74.0" y1="112.0" x2="74.0" y2="157.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="124.0" width="9" height="19.2" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="112.0" x2="89.0" y2="176.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="124.0" width="9" height="38.4" fill="#c00"/>
|
||||
<line x1="104.0" y1="150.4" x2="104.0" y2="210.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="162.4" width="9" height="33.6" fill="#c00"/>
|
||||
<line x1="119.0" y1="184.0" x2="119.0" y2="244.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="196.0" width="9" height="33.6" fill="#c00"/>
|
||||
<line x1="134.0" y1="203.2" x2="134.0" y2="244.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="215.2" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="203.2" x2="149.0" y2="268.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="215.2" width="9" height="38.4" fill="#c00"/>
|
||||
<line x1="164.0" y1="241.6" x2="164.0" y2="301.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="253.6" width="9" height="33.6" fill="#c00"/>
|
||||
<line x1="179.0" y1="275.2" x2="179.0" y2="330.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="287.2" width="9" height="28.8" fill="#c00"/>
|
||||
<line x1="194.0" y1="289.6" x2="194.0" y2="330.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="301.6" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="289.6" x2="209.0" y2="344.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="301.6" width="9" height="28.8" fill="#c00"/>
|
||||
<line x1="224.0" y1="318.4" x2="224.0" y2="368.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="330.4" width="9" height="24.0" fill="#c00"/>
|
||||
<line x1="239.0" y1="342.4" x2="239.0" y2="392.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="354.4" width="9" height="24.0" fill="#c00"/>
|
||||
<line x1="254.0" y1="354.4" x2="254.0" y2="392.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="366.4" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="354.4" x2="269.0" y2="402.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="366.4" width="9" height="21.6" fill="#c00"/>
|
||||
<line x1="284.0" y1="376.0" x2="284.0" y2="414.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="388.0" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="299.0" y1="378.4" x2="299.0" y2="414.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="390.4" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="385.6" x2="314.0" y2="421.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="390.4" width="9" height="12.0" fill="#c00"/>
|
||||
<line x1="329.0" y1="378.4" x2="329.0" y2="404.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="383.2" width="9" height="19.2" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="352.0" x2="344.0" y2="397.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="364.0" width="9" height="19.2" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="347.2" x2="359.0" y2="378.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="349.6" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="332.8" x2="374.0" y2="364.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="344.8" width="9" height="4.8" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="318.4" x2="389.0" y2="359.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="330.4" width="9" height="14.4" fill="#188a3e"/>
|
||||
<line x1="404.0" y1="318.4" x2="404.0" y2="354.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="330.4" width="9" height="9.6" fill="#c00"/>
|
||||
<line x1="419.0" y1="311.2" x2="419.0" y2="354.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="323.2" width="9" height="16.8" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="299.2" x2="434.0" y2="337.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="311.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="287.2" x2="449.0" y2="325.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="299.2" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="277.6" x2="464.0" y2="313.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="289.6" width="9" height="9.6" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="265.6" x2="479.0" y2="304.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="277.6" width="9" height="12.0" fill="#188a3e"/>
|
||||
<line x1="24" y1="517.6" x2="626" y2="517.6" stroke="#888"/>
|
||||
<rect x="39.5" y="478.1" width="9" height="39.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="479.7" width="9" height="37.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="481.4" width="9" height="36.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="480.5" width="9" height="37.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="482.2" width="9" height="35.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="483.8" width="9" height="33.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="484.7" width="9" height="32.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="483.0" width="9" height="34.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="481.4" width="9" height="36.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="479.7" width="9" height="37.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="474.8" width="9" height="42.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.5" y="486.3" width="9" height="31.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="484.7" width="9" height="32.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="483.0" width="9" height="34.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="485.5" width="9" height="32.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="264.5" y="481.4" width="9" height="36.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="476.4" width="9" height="41.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="469.8" width="9" height="47.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="458.3" width="9" height="59.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.5" y="447.6" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="460.0" width="9" height="57.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="464.9" width="9" height="52.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="469.8" width="9" height="47.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="468.2" width="9" height="49.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="399.5" y="472.3" width="9" height="45.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="414.5" y="474.8" width="9" height="42.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="476.4" width="9" height="41.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="478.1" width="9" height="39.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="479.7" width="9" height="37.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.5" y="481.4" width="9" height="36.2" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="441.6" fill="#555">volum</text>
|
||||
<text x="314.0" y="435.6" fill="#c00" text-anchor="middle" style="font-weight:bold">minim 589.80 — divergență (M)</text>
|
||||
<text x="329.0" y="322.4" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intrare 590.20</text>
|
||||
<text x="479.0" y="255.6" fill="#188a3e" text-anchor="middle" style="font-weight:bold">ieșire trail 592.40</text>
|
||||
<text x="24" y="560.0" fill="#555">Legendă: entry/SL/TP/trail sunt exemplul construit al autorului sumarului,</text>
|
||||
<text x="24" y="577.0" fill="#555">nu citate din curs (transcrierea se întrerupe înainte de planul numeric).</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
222
gen/strategii/mdsrt_exemplu.py
Normal file
@@ -0,0 +1,222 @@
|
||||
"""Diagrama din `gen/strategii/mdsrt_exemplu.spec.txt` (TIP: PRET).
|
||||
|
||||
Exemplul real din Q&A 15.07.2025 (`09_QA/24_2025-07-15.md`): SPY pe 5 min,
|
||||
double bottom cu al doilea picior mai jos (MDSRT pe 5 min, suportul = pivotul
|
||||
daily). Downtrend → picior 1 la 622.40 → revenire → picior 2 la 621.40 (rupe
|
||||
pivotul 622.00) → Buy Hammer care închide la 622.60, înapoi peste pivot. După
|
||||
trigger, un plan construit de mine (marcat) duce trade-ul până la TP1.
|
||||
|
||||
Reale din curs: nivelurile 622.40 / 622.00 / 621.40 / 622.60 și cele 8 bare
|
||||
dintre picioare. Construcția mea: entry 622.60, SL 621.30, TP1 623.90 (1:1) —
|
||||
materialul nu le dă (transcrierea planului se întrerupe). Volumele sunt
|
||||
relative, alese de generator.
|
||||
|
||||
Rulează:
|
||||
python3 gen/strategii/mdsrt_exemplu.py
|
||||
Scrie lângă el `mdsrt_exemplu.svg`.
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "mdsrt_exemplu.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# ── nivelurile reale din curs + planul construit (marcat) ──────────────────
|
||||
PIVOT = 622.00 # pivot daily, suportul
|
||||
LEG1 = 622.40 # swing low picior 1
|
||||
LEG2 = 621.40 # picior 2 (mai jos, rupe pivotul)
|
||||
ENTRY = 622.60 # close-ul Buy Hammer (construit)
|
||||
SL = 621.30 # 0.10 sub low-ul piciorului 2 (construit)
|
||||
TP1 = 623.90 # entry + risc = 1:1 (construit)
|
||||
|
||||
# ── seria: 30 de close-uri, exact ca în specificație ───────────────────────
|
||||
CLOSES = [
|
||||
627.00, 626.40, 626.70, 626.00, 625.40, 624.80, 624.40, 623.80, # 0-7
|
||||
623.20, 622.80, # 8-9
|
||||
622.40, # 10 = picior 1 (swing low 622.40)
|
||||
622.70, 623.10, 623.50, 623.20, 622.90, 622.60, 622.30, # 11-17
|
||||
622.60, # 18 = picior 2: low 621.40, Buy Hammer care închide 622.60
|
||||
623.00, 623.40, 623.90, # 19-21, 21 = TP1 1:1 (construit)
|
||||
624.20, 624.00, 624.40, 624.80, 625.20, 625.60, # 22-27
|
||||
625.30, 625.70, # 28-29
|
||||
]
|
||||
assert len(CLOSES) == 30, len(CLOSES)
|
||||
|
||||
# wick-uri (sus, jos): alternanță naturală; excepțiile sunt piciorul 1 și
|
||||
# hammer-ul de la piciorul 2 (cerute exact de specificație).
|
||||
WICKS = [
|
||||
(0.10, 0.10), (0.12, 0.14), (0.15, 0.12), (0.18, 0.15), (0.20, 0.16),
|
||||
(0.18, 0.14), (0.16, 0.13), (0.15, 0.12), (0.14, 0.12), (0.12, 0.10),
|
||||
(0.15, 0.05), # 10 = picior 1: low 622.40 - 0.05 = 622.35
|
||||
(0.12, 0.12), (0.15, 0.14), (0.18, 0.15), (0.16, 0.13), (0.14, 0.12),
|
||||
(0.12, 0.10), (0.10, 0.12),
|
||||
(0.10, 0.90), # 18 = hammer: high 622.70, low 621.40
|
||||
(0.12, 0.12), (0.14, 0.12), (0.15, 0.13),
|
||||
(0.16, 0.15), (0.14, 0.12), (0.15, 0.13), (0.16, 0.14), (0.17, 0.14),
|
||||
(0.18, 0.15), (0.14, 0.12), (0.15, 0.13),
|
||||
]
|
||||
assert len(WICKS) == 30, len(WICKS)
|
||||
|
||||
# volum relativ (ales de generator; materialul nu dă volumul pentru exemplu):
|
||||
# scade pe downtrend, spike de absorbție pe hammer, higher low vs piciorul 1.
|
||||
VOLS = [
|
||||
58, 56, 54, 52, 50, 49, 48, 47, 46, 45, # 0-9
|
||||
44, # 10 = picior 1
|
||||
43, 42, 41, 40, 39, 38, 39, # 11-17
|
||||
100, # 18 = hammer, maximul seriei
|
||||
62, 58, 54, # 19-21
|
||||
52, 50, 49, 48, 47, 46, 45, 44, # 22-29
|
||||
]
|
||||
assert len(VOLS) == 30, len(VOLS)
|
||||
|
||||
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=627.00)
|
||||
|
||||
# ── assert-urile din specificație ──────────────────────────────────────────
|
||||
# 1. picior 1: low 622.35, close 622.40
|
||||
assert abs(cds[10].l - 622.35) < 1e-9, cds[10].l
|
||||
assert cds[10].c == 622.40, cds[10].c
|
||||
|
||||
# 2. picior 2 mai jos (621.40 < 622.35) și rupe pivotul daily 622.00
|
||||
assert abs(cds[18].l - LEG2) < 1e-9, cds[18].l
|
||||
assert cds[18].l < cds[10].l, (cds[18].l, cds[10].l)
|
||||
assert cds[18].l < PIVOT, cds[18].l
|
||||
|
||||
# 3. trigger-ul închide înapoi peste pivot
|
||||
assert cds[18].c == 622.60, cds[18].c
|
||||
assert cds[18].c > PIVOT, cds[18].c
|
||||
|
||||
# 4. 8 candele între picioare (peste minimul de 5)
|
||||
assert 18 - 10 == 8
|
||||
|
||||
# 5. hammer: coadă lungă în jos (0.90) > corp (0.30)
|
||||
corp = abs(cds[18].o - cds[18].c)
|
||||
coada = min(cds[18].o, cds[18].c) - cds[18].l
|
||||
assert abs(corp - 0.30) < 1e-9, corp
|
||||
assert abs(coada - 0.90) < 1e-9, coada
|
||||
assert coada > corp, (coada, corp)
|
||||
|
||||
# 6. entry = close-ul trigger-ului (construit), SL 0.10 sub low-ul piciorului 2
|
||||
assert cds[18].c == ENTRY
|
||||
assert abs(cds[18].l - SL - 0.10) < 1e-9, (cds[18].l, SL)
|
||||
risk = ENTRY - SL
|
||||
assert abs(risk - 1.30) < 1e-9, risk
|
||||
|
||||
# 7. TP1 simetric 1:1
|
||||
assert abs((ENTRY - SL) - (TP1 - ENTRY)) < 1e-9
|
||||
|
||||
# 8. după trigger, primul close care atinge 623.90 e bara 21 (nu mai devreme).
|
||||
# Seria pornește la 627.00, deci „atinge" se citește de jos în sus, din zona
|
||||
# picioarelor: barele 10-20 rămân sub TP1, bara 21 îl atinge.
|
||||
assert all(c.c < TP1 for c in cds[10:21]), [c.c for c in cds[10:21]]
|
||||
assert max(c.h for c in cds[19:21]) < TP1, [c.h for c in cds[19:21]]
|
||||
assert cds[21].c == TP1, cds[21].c
|
||||
|
||||
# 9. divergență: lower low pe preț, higher low pe volum
|
||||
assert cds[18].l < cds[10].l, (cds[18].l, cds[10].l)
|
||||
assert VOLS[18] > VOLS[10], (VOLS[18], VOLS[10])
|
||||
assert VOLS[18] == max(VOLS)
|
||||
|
||||
# 10. fiecare open == close-ul barei anterioare
|
||||
for a, b in zip(cds, cds[1:]):
|
||||
assert a.c == b.o, (a.c, b.o)
|
||||
|
||||
# ── geometria, ca să așez marks-urile fără suprapunere de text ─────────────
|
||||
SCALE, GAP, LEFT, WIDTH, BODY_W, VOL_H = 62, 15, 44, 640, 9, 70
|
||||
|
||||
|
||||
def cx(i: int) -> float:
|
||||
return LEFT + i * GAP
|
||||
|
||||
|
||||
levels = [
|
||||
Level(PIVOT, "pivot daily 622.00", ORANGE, label_at="left"),
|
||||
Level(SL, "SL 621.30 (construit)", RED, label_at="left"),
|
||||
Level(ENTRY, "intrare 622.60 (construit)", GREEN, label_at="left"),
|
||||
Level(TP1, "TP1 623.90 = 1:1 (construit)", GREEN, label_at="left"),
|
||||
Level(LEG1, "picior 1 = 622.40", BLUE, label_at="right"),
|
||||
]
|
||||
|
||||
# dy-uri calculate manual ca etichetele „below" să nu calce linia pivotului
|
||||
# (bara 10) și ca „hammer" să nu calce eticheta intrării (bara 18).
|
||||
marks = [
|
||||
Mark(10, "picior 1 = 622.40", BLUE, "below", dy=15),
|
||||
Mark(18, "picior 2 = 621.40 (mai jos) · Buy Hammer", RED, "below"),
|
||||
Mark(18, "hammer închide 622.60, înapoi peste pivot", GREEN, "above", dy=-20),
|
||||
]
|
||||
|
||||
ch = Chart(
|
||||
candles=cds, levels=levels, marks=marks,
|
||||
mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")],
|
||||
scale=SCALE, gap=GAP, body_w=BODY_W, left=LEFT, width=WIDTH, vol_h=VOL_H,
|
||||
vol_label="volum",
|
||||
title="SPY — 5 min (Q&A 15.07.2025; intrare/SL/TP construite)",
|
||||
caption="",
|
||||
)
|
||||
svg = ch.render()
|
||||
|
||||
# ── legenda de onestitate (assert 11): ce e real, ce e construit ───────────
|
||||
legend = [
|
||||
"Legendă: nivelurile 622.40 / 622.00 / 621.40 / 622.60 și cele 8 candele dintre",
|
||||
"picioare sunt reale, din curs. Intrarea 622.60, SL-ul 621.30 și TP-ul 623.90",
|
||||
"sunt construcția mea — materialul nu le dă.",
|
||||
]
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
|
||||
W, H0 = int(m.group(1)), float(m.group(2))
|
||||
legend_svg = [f'<text x="24" y="{H0 + 16 + 17 * i:.1f}" fill="#555">{t}</text>'
|
||||
for i, t in enumerate(legend)]
|
||||
new_h = H0 + 16 + 17 * (len(legend) - 1) + 14
|
||||
svg = re.sub(r'(viewBox="0 0 \d+ )[0-9.]+(")',
|
||||
lambda mm: mm.group(1) + f"{new_h:.0f}" + mm.group(2), svg, count=1)
|
||||
svg = svg.replace("\n</svg>", "\n " + "\n ".join(legend_svg) + "\n</svg>")
|
||||
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
assert "sunt reale, din curs" in svg and "construcția mea" in svg, \
|
||||
"legenda nu spune explicit ce e real și ce e construit"
|
||||
|
||||
# toate etichetele cerute de specificație sunt prezente
|
||||
for eticheta in ("pivot daily 622.00", "picior 1 = 622.40",
|
||||
"picior 2 = 621.40 (mai jos) · Buy Hammer",
|
||||
"hammer închide 622.60, înapoi peste pivot",
|
||||
"intrare 622.60 (construit)", "SL 621.30 (construit)",
|
||||
"TP1 623.90 = 1:1 (construit)"):
|
||||
assert eticheta in svg, f"lipsește eticheta: {eticheta!r}"
|
||||
assert "SPY — 5 min" in svg, "lipsește titlul"
|
||||
|
||||
# 1:1 desenat la 1:1 (STYLE.md: raportul din preț = raportul din pixeli)
|
||||
ys = {p: ch._y(p) for p in (SL, ENTRY, TP1)}
|
||||
assert abs((ys[SL] - ys[ENTRY]) - (ys[ENTRY] - ys[TP1])) < 1e-6, ys
|
||||
|
||||
# ── verificare: fiecare etichetă e în pânză și nu se suprapune alta ────────
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg)
|
||||
W, H = int(m.group(1)), float(m.group(2))
|
||||
texte = re.findall(r'<text x="([\d.]+)" y="([\d.]+)"([^>]*)>([^<]*)</text>', svg)
|
||||
cutii = []
|
||||
for xs, ys, attrs, continut in texte:
|
||||
x, yv = float(xs), float(ys)
|
||||
w = len(continut) * (7.44 if "bold" in attrs else 6.72)
|
||||
if 'text-anchor="end"' in attrs:
|
||||
x0 = x - w
|
||||
elif 'text-anchor="middle"' in attrs:
|
||||
x0 = x - w / 2
|
||||
else:
|
||||
x0 = x
|
||||
assert -2 <= x0 and x0 + w <= W + 2, f"etichetă în afara pânzei: {continut!r}"
|
||||
assert 0 <= yv <= H, f"etichetă în afara înălțimii: {continut!r}"
|
||||
cutii.append((x0, x0 + w, yv, continut))
|
||||
for i in range(len(cutii)):
|
||||
for j in range(i + 1, len(cutii)):
|
||||
a, b = cutii[i], cutii[j]
|
||||
if abs(a[2] - b[2]) < 13 and a[0] < b[1] - 2 and b[0] < a[1] - 2:
|
||||
raise AssertionError(f"texte suprapuse: {a[3]!r} / {b[3]!r}")
|
||||
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: {len(cds)} candele | picior 1 {cds[10].l:.2f} (close "
|
||||
f"{cds[10].c:.2f}) | picior 2 {cds[18].l:.2f} (mai jos) | hammer închide "
|
||||
f"{cds[18].c:.2f} | intrare {ENTRY:.2f} | SL {SL:.2f} (risc {risk:.2f}) | "
|
||||
f"TP1 {TP1:.2f} = {(TP1 - ENTRY) / risk:.2f}:1 la bara 21 | "
|
||||
f"vol[18]={VOLS[18]} > vol[10]={VOLS[10]}")
|
||||
113
gen/strategii/mdsrt_exemplu.svg
Normal file
@@ -0,0 +1,113 @@
|
||||
<svg viewBox="0 0 640 603" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">SPY — 5 min (Q&A 15.07.2025; intrare/SL/TP construite)</text>
|
||||
<line x1="24" y1="373.4" x2="626" y2="373.4" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="367.4" fill="#b06000">pivot daily 622.00</text>
|
||||
<line x1="24" y1="416.8" x2="626" y2="416.8" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="410.8" fill="#c00">SL 621.30 (construit)</text>
|
||||
<line x1="24" y1="336.2" x2="626" y2="336.2" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="330.2" fill="#188a3e">intrare 622.60 (construit)</text>
|
||||
<line x1="24" y1="255.6" x2="626" y2="255.6" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="249.6" fill="#188a3e">TP1 623.90 = 1:1 (construit)</text>
|
||||
<line x1="24" y1="348.6" x2="626" y2="348.6" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="612" y="342.6" fill="#1a5fb4" text-anchor="end">picior 1 = 622.40</text>
|
||||
<polyline points="329.0,253.2 344.0,264.3 359.0,272.1 374.0,279.8 389.0,286.0 404.0,289.1 419.0,289.1 434.0,286.6 449.0,281.1 464.0,274.5 479.0,265.6" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="485" y="260" fill="#b06000">SMA-20</text>
|
||||
<line x1="44.0" y1="57.2" x2="44.0" y2="69.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="63.4" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="56.0" x2="59.0" y2="109.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="63.4" width="9" height="37.2" fill="#c00"/>
|
||||
<line x1="74.0" y1="72.7" x2="74.0" y2="108.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="82.0" width="9" height="18.6" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="70.9" x2="89.0" y2="134.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="82.0" width="9" height="43.4" fill="#c00"/>
|
||||
<line x1="104.0" y1="113.0" x2="104.0" y2="172.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="125.4" width="9" height="37.2" fill="#c00"/>
|
||||
<line x1="119.0" y1="151.5" x2="119.0" y2="208.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="162.6" width="9" height="37.2" fill="#c00"/>
|
||||
<line x1="134.0" y1="189.9" x2="134.0" y2="232.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="199.8" width="9" height="24.8" fill="#c00"/>
|
||||
<line x1="149.0" y1="215.3" x2="149.0" y2="269.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="224.6" width="9" height="37.2" fill="#c00"/>
|
||||
<line x1="164.0" y1="253.2" x2="164.0" y2="306.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="261.8" width="9" height="37.2" fill="#c00"/>
|
||||
<line x1="179.0" y1="291.6" x2="179.0" y2="330.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="299.0" width="9" height="24.8" fill="#c00"/>
|
||||
<line x1="194.0" y1="314.5" x2="194.0" y2="351.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="323.8" width="9" height="24.8" fill="#c00"/>
|
||||
<line x1="209.0" y1="322.6" x2="209.0" y2="356.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="330.0" width="9" height="18.6" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="295.9" x2="224.0" y2="338.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="305.2" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="269.3" x2="239.0" y2="314.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="280.4" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="254.0" y1="270.5" x2="254.0" y2="307.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="280.4" width="9" height="18.6" fill="#c00"/>
|
||||
<line x1="269.0" y1="290.4" x2="269.0" y2="325.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="299.0" width="9" height="18.6" fill="#c00"/>
|
||||
<line x1="284.0" y1="310.2" x2="284.0" y2="342.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="317.6" width="9" height="18.6" fill="#c00"/>
|
||||
<line x1="299.0" y1="330.0" x2="299.0" y2="362.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="336.2" width="9" height="18.6" fill="#c00"/>
|
||||
<line x1="314.0" y1="330.0" x2="314.0" y2="410.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="336.2" width="9" height="18.6" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="304.0" x2="329.0" y2="343.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="311.4" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="278.0" x2="344.0" y2="318.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="286.6" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="246.3" x2="359.0" y2="294.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="255.6" width="9" height="31.0" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="227.1" x2="374.0" y2="264.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="237.0" width="9" height="18.6" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="228.4" x2="389.0" y2="256.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="237.0" width="9" height="12.4" fill="#c00"/>
|
||||
<line x1="404.0" y1="215.3" x2="404.0" y2="257.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="224.6" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="419.0" y1="189.9" x2="419.0" y2="233.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="199.8" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="164.5" x2="434.0" y2="208.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="175.0" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="139.1" x2="449.0" y2="184.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="150.2" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="141.6" x2="464.0" y2="176.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="150.2" width="9" height="18.6" fill="#c00"/>
|
||||
<line x1="479.0" y1="134.7" x2="479.0" y2="176.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="144.0" width="9" height="24.8" fill="#188a3e"/>
|
||||
<line x1="24" y1="512.8" x2="626" y2="512.8" stroke="#888"/>
|
||||
<rect x="39.5" y="472.2" width="9" height="40.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="473.6" width="9" height="39.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="475.0" width="9" height="37.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="476.4" width="9" height="36.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="477.8" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="478.5" width="9" height="34.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="479.2" width="9" height="33.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="479.9" width="9" height="32.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="480.6" width="9" height="32.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="481.3" width="9" height="31.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="482.0" width="9" height="30.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="482.7" width="9" height="30.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="219.5" y="483.4" width="9" height="29.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="234.5" y="484.1" width="9" height="28.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="249.5" y="484.8" width="9" height="28.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="485.5" width="9" height="27.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="486.2" width="9" height="26.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="485.5" width="9" height="27.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="442.8" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="469.4" width="9" height="43.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="472.2" width="9" height="40.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="475.0" width="9" height="37.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="476.4" width="9" height="36.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="477.8" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.5" y="478.5" width="9" height="34.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="414.5" y="479.2" width="9" height="33.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="479.9" width="9" height="32.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="480.6" width="9" height="32.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="481.3" width="9" height="31.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="474.5" y="482.0" width="9" height="30.8" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="436.8" fill="#555">volum</text>
|
||||
<text x="194.0" y="386.7" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">picior 1 = 622.40</text>
|
||||
<text x="314.0" y="430.6" fill="#c00" text-anchor="middle" style="font-weight:bold">picior 2 = 621.40 (mai jos) · Buy Hammer</text>
|
||||
<text x="314.0" y="300.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">hammer închide 622.60, înapoi peste pivot</text>
|
||||
<text x="24" y="555.0" fill="#555">Legendă: nivelurile 622.40 / 622.00 / 621.40 / 622.60 și cele 8 candele dintre</text>
|
||||
<text x="24" y="572.0" fill="#555">picioare sunt reale, din curs. Intrarea 622.60, SL-ul 621.30 și TP-ul 623.90</text>
|
||||
<text x="24" y="589.0" fill="#555">sunt construcția mea — materialul nu le dă.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
254
gen/strategii/mdsrt_masurare.py
Normal file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Cum se masoara divergenta MDSRT — schema pe doua panouri (pret + indicator).
|
||||
|
||||
Pentru `strategii/07_mdsrt.md`, marcajul `<!-- DIAGRAMA: masurare -->` (Nivel 1,
|
||||
§1.2 „Setup", dupa pasul 5). NU e grafic de pret cu candele: spec-ul cere o
|
||||
schema de regula, cu linii, fara axe de pret reale, fara ticker, fara cifre de
|
||||
pret. De aceea se scrie SVG de mana, doar stdlib — nu se folosesc
|
||||
svg_candles / pchart.
|
||||
|
||||
Ce arata (toate etichetele si pragurile vin din spec / din Q&A):
|
||||
- panoul de sus: linia pretului intr-un downtrend; L1 primul swing low
|
||||
important, L2 al doilea, mai jos → lower low;
|
||||
- panoul de jos: linia indicatorului (Power / volum / RS) cu I1 si I2, al
|
||||
doilea mai sus → higher low = divergenta bull;
|
||||
- bracket orizontal intre L1 si L2: „minim 5 candele intre puncte";
|
||||
- bracket orizontal in stanga lui L2, pana la 60 de pasi in spate:
|
||||
„maxim 60 de candele in spate";
|
||||
- al treilea swing low mic, la 2-3 pasi de L2, taiat cu cruce rosie si
|
||||
etichetat „2-3 candele → NU e divergenta".
|
||||
|
||||
Pragurile, cu sursa:
|
||||
- „ultimele 2 swing low-uri importante" + „60 de candele in spate si minim 5
|
||||
candele intre divergente": `09_QA/24_2025-07-15.md:151-155`;
|
||||
- „Daca aveti pe 3 candele divergenta, nu e": `09_QA/23_2025-07-29.md:873-874`;
|
||||
- mecanismul pret-vs-volum (lower low pe pret, higher low pe indicator):
|
||||
`09_QA/19_2025-09-08.md:168-177`;
|
||||
- perechea de low-uri se muta pe masura ce apare un low nou, mai jos:
|
||||
`09_QA/24_2025-07-15.md:159-162`.
|
||||
|
||||
Ruleaza: .venv/bin/python gen/strategii/mdsrt_masurare.py
|
||||
-> gen/strategii/mdsrt_masurare.svg
|
||||
"""
|
||||
from pathlib import Path
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
|
||||
W, H = 700, 620
|
||||
BLACK, GRAY, RED, GREEN, BLUE, ORANGE, WHITE = (
|
||||
"#000000", "#777777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#ffffff")
|
||||
|
||||
# ---- grila de timp: un pas = o candela. L2 e la 60 de pasi de la marginea
|
||||
# stanga a panoului, ca bracket-ul „maxim 60 de candele" sa aiba exact 60 de
|
||||
# pasi spre stanga si sa acopere si L1.
|
||||
X0, STEPW = 60.0, 7.0
|
||||
L1_STEP, L2_STEP, L3_STEP = 54.0, 60.0, 62.5
|
||||
X_L1, X_L2, X_L3 = X0 + L1_STEP * STEPW, X0 + L2_STEP * STEPW, X0 + L3_STEP * STEPW
|
||||
|
||||
# ---- coordonatele verticale ale punctelor
|
||||
Y_L1, Y_L2, Y_L3 = 252.0, 278.0, 272.0 # pret: L2 mai jos (lower low)
|
||||
Y_I1, Y_I2 = 486.0, 468.0 # indicator: I2 mai sus (higher low)
|
||||
|
||||
PRICE_BOX = (40.0, 76.0, 620.0, 250.0) # y 76 .. 326
|
||||
IND_BOX = (40.0, 344.0, 620.0, 200.0) # y 344 .. 544
|
||||
|
||||
BR60_Y = 134.0 # bracket-ul de 60 de candele, sus
|
||||
BR5_Y = 300.0 # bracket-ul de 5 candele, sub cele doua low-uri
|
||||
|
||||
TEXTS = [] # (x, y, text, size, weight, anchor) pentru verificarea finala
|
||||
|
||||
|
||||
def txt(x, y, s, size=13, weight="400", fill=BLACK, anchor="middle"):
|
||||
TEXTS.append((x, y, s, size, weight, anchor))
|
||||
return (f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" '
|
||||
f'font-weight="{weight}" fill="{fill}" text-anchor="{anchor}">'
|
||||
f'{escape(s)}</text>')
|
||||
|
||||
|
||||
def line(x1, y1, x2, y2, stroke=BLACK, width=1.4, dash=None):
|
||||
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||
return (f'<line x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" '
|
||||
f'stroke="{stroke}" stroke-width="{width}"{d}/>')
|
||||
|
||||
|
||||
def poly(points, stroke=BLACK, width=1.8):
|
||||
pts = " ".join(f"{x:.1f},{y:.1f}" for x, y in points)
|
||||
return (f'<polyline points="{pts}" fill="none" stroke="{stroke}" '
|
||||
f'stroke-width="{width}" stroke-linejoin="round"/>')
|
||||
|
||||
|
||||
def dot(x, y, color=BLACK, r=3.6):
|
||||
return f'<circle cx="{x:.1f}" cy="{y:.1f}" r="{r}" fill="{color}"/>'
|
||||
|
||||
|
||||
def hbracket(x0, x1, y, color, tick=7.0):
|
||||
"""Bracket orizontal de dimensiune: linie + capete verticale."""
|
||||
return [
|
||||
line(x0, y, x1, y, color, 1.4),
|
||||
line(x0, y - tick, x0, y + tick, color, 1.4),
|
||||
line(x1, y - tick, x1, y + tick, color, 1.4),
|
||||
]
|
||||
|
||||
|
||||
def cross(cx, cy, color=RED, arm=10.0):
|
||||
"""Crucea rosie care marcheaza al treilea low (NU e divergenta)."""
|
||||
return [
|
||||
line(cx - arm, cy - arm, cx + arm, cy + arm, color, 2.0),
|
||||
line(cx - arm, cy + arm, cx + arm, cy - arm, color, 2.0),
|
||||
]
|
||||
|
||||
|
||||
def build():
|
||||
p = [
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
|
||||
f'width="{W}" height="{H}" role="img" '
|
||||
f'aria-label="Cum se masoara divergenta: pretul face un lower low (L2 sub '
|
||||
f'L1), indicatorul face un higher low (I2 peste I1); intre L1 si L2 trebuie '
|
||||
f'minim 5 candele si cel mult 60 in spate, iar un low la 2-3 candele nu '
|
||||
f'conteaza">',
|
||||
'<g font-family="system-ui, sans-serif">',
|
||||
f'<rect x="0" y="0" width="{W}" height="{H}" fill="{WHITE}"/>',
|
||||
txt(W / 2, 34, "Divergența: cum o măsori corect", 19, "700", BLACK),
|
||||
txt(W / 2, 58, "Aceeași structură pe preț și pe indicator: două swing "
|
||||
"low-uri, două minime", 13, "400", GRAY),
|
||||
]
|
||||
|
||||
# ---------------- panoul de sus: pretul
|
||||
bx, by, bw, bh = PRICE_BOX
|
||||
p.append(f'<rect x="{bx}" y="{by}" width="{bw}" height="{bh}" rx="8" '
|
||||
f'fill="{WHITE}" stroke="{GRAY}" stroke-width="1.2"/>')
|
||||
p.append(txt(bx + 16, by + 20, "PREȚ (schematic) — downtrend, lower low",
|
||||
14, "700", BLACK, "start"))
|
||||
|
||||
# bracket „maxim 60 de candele in spate": de la L2 spre stanga, 60 de pasi
|
||||
x60_left, x60_right = X0, X_L2
|
||||
p.extend(hbracket(x60_left, x60_right, BR60_Y, BLACK))
|
||||
p.append(txt((x60_left + x60_right) / 2, BR60_Y - 12,
|
||||
"maxim 60 de candele în spate", 13, "400", BLACK))
|
||||
|
||||
# linia pretului: downtrend cu L1, apoi L2 mai jos, apoi un low mic L3
|
||||
price = [(0, 158), (6, 178), (12, 166), (18, 192), (24, 180), (30, 206),
|
||||
(36, 194), (42, 218), (48, 232), (L1_STEP, Y_L1), (57, 226),
|
||||
(L2_STEP, Y_L2), (61.3, 248), (L3_STEP, Y_L3), (65, 250)]
|
||||
p.append(poly([(X0 + s * STEPW, y) for s, y in price], BLACK, 1.8))
|
||||
p.extend([dot(X_L1, Y_L1, BLACK), dot(X_L2, Y_L2, BLACK), dot(X_L3, Y_L3, BLACK)])
|
||||
p.append(txt(X_L1 - 10, Y_L1 + 6, "L1", 13, "700", BLACK, "end"))
|
||||
p.append(txt(X_L2 - 12, Y_L2 + 8, "L2", 13, "700", BLACK, "end"))
|
||||
|
||||
# bracket „minim 5 candele intre puncte": intre L1 si L2
|
||||
p.extend(hbracket(X_L1, X_L2, BR5_Y, BLACK))
|
||||
p.append(txt((X_L1 + X_L2) / 2, BR5_Y + 18,
|
||||
"minim 5 candele între puncte", 13, "400", BLACK))
|
||||
|
||||
# al treilea low: cruce rosie + „NU" + eticheta
|
||||
p.extend(cross(X_L3, Y_L3, RED, arm=10.0))
|
||||
p.append(txt(X_L3, 234, "NU", 14, "700", RED))
|
||||
p.append(txt(525, 246, "2-3 candele", 13, "700", RED, "start"))
|
||||
p.append(txt(525, 266, "NU e divergență", 13, "700", RED, "start"))
|
||||
|
||||
# ---------------- panoul de jos: indicatorul
|
||||
bx, by, bw, bh = IND_BOX
|
||||
p.append(f'<rect x="{bx}" y="{by}" width="{bw}" height="{bh}" rx="8" '
|
||||
f'fill="{WHITE}" stroke="{GRAY}" stroke-width="1.2"/>')
|
||||
p.append(txt(bx + 16, by + 20,
|
||||
"INDICATOR (Power / volum / RS) — higher low",
|
||||
14, "700", BLUE, "start"))
|
||||
|
||||
ind = [(0, 400), (6, 424), (12, 410), (18, 436), (24, 422), (30, 448),
|
||||
(36, 434), (42, 458), (48, 466), (L1_STEP, Y_I1), (57, 458),
|
||||
(L2_STEP, Y_I2), (65, 440)]
|
||||
p.append(poly([(X0 + s * STEPW, y) for s, y in ind], BLUE, 1.8))
|
||||
p.extend([dot(X_L1, Y_I1, BLUE), dot(X_L2, Y_I2, BLUE)])
|
||||
p.append(txt(X_L1 - 10, Y_I1 + 6, "I1", 13, "700", BLUE, "end"))
|
||||
p.append(txt(X_L2 + 10, Y_I2 + 6, "I2", 13, "700", BLUE, "start"))
|
||||
|
||||
# ---------------- legenda de jos
|
||||
p.append(txt(W / 2, 570, "Prețul face un lower low (L2 sub L1), indicatorul "
|
||||
"face un higher low (I2 peste I1):", 13, "400", BLACK))
|
||||
p.append(txt(W / 2, 590, "divergență bull. Dar dacă al doilea punct e la "
|
||||
"doar 2-3 candele, nu se numără.", 13, "400", BLACK))
|
||||
|
||||
p.append('</g></svg>')
|
||||
return "\n".join(p) + "\n"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- verificare
|
||||
def est_w(s, size, weight):
|
||||
factor = 0.62 if weight == "700" else 0.56
|
||||
return len(s) * size * factor
|
||||
|
||||
|
||||
def verify(svg):
|
||||
# (1) geometria schemei, exact ce cere spec-ul
|
||||
assert Y_L2 > Y_L1, "L2 trebuie desenat mai jos decat L1 (lower low)"
|
||||
assert Y_I2 < Y_I1, "I2 trebuie desenat mai sus decat I1 (higher low)"
|
||||
|
||||
step_l1_l2 = (X_L2 - X_L1) / STEPW
|
||||
step_l2_l3 = (X_L3 - X_L2) / STEPW
|
||||
assert step_l1_l2 >= 5.0 - 1e-9, f"prea putine candele L1-L2: {step_l1_l2}"
|
||||
assert 2.0 - 1e-9 <= step_l2_l3 <= 3.0 + 1e-9, f"L3 nu e la 2-3 pasi: {step_l2_l3}"
|
||||
|
||||
# (4) bracket-ul de 60 de candele acopera de la L2 spre stanga si trece de L1
|
||||
x60_left, x60_right = X0, X_L2
|
||||
assert x60_right == X_L2, "bracket-ul de 60 porneste din L2"
|
||||
assert x60_left <= X_L1, "bracket-ul de 60 nu ajunge pana la L1"
|
||||
assert x60_right - x60_left >= (X_L2 - X_L1), "bracket prea scurt fata de L1"
|
||||
assert (x60_right - x60_left) / STEPW == 60.0, "nu sunt exact 60 de pasi"
|
||||
|
||||
# (5) „NU" si crucea rosie chiar pe al treilea low
|
||||
assert "NU" in svg
|
||||
assert RED in svg and "L3" not in svg # L3 e marcat doar prin cruce, nu cu eticheta
|
||||
cross_lines = cross(X_L3, Y_L3)
|
||||
for ln in cross_lines:
|
||||
assert f'x1="{X_L3 - 10:.1f}"' in ln or f'x2="{X_L3 - 10:.1f}"' in ln
|
||||
assert f'y1="{Y_L3 - 10:.1f}"' in ln or f'y2="{Y_L3 - 10:.1f}"' in ln
|
||||
|
||||
# etichetele obligatorii din spec sunt in desen
|
||||
for cheie in ("maxim 60 de candele în spate", "minim 5 candele între puncte",
|
||||
"2-3 candele", "NU e divergență", "lower low", "higher low",
|
||||
"L1", "L2", "I1", "I2"):
|
||||
assert cheie in svg, f"lipsește din desen: {cheie!r}"
|
||||
|
||||
# (2) toate etichetele in panza
|
||||
for x, y, s, size, weight, anchor in TEXTS:
|
||||
ew = est_w(s, size, weight)
|
||||
left = x - ew if anchor == "end" else (x - ew / 2 if anchor == "middle" else x)
|
||||
right = left + ew
|
||||
assert -2 <= left and right <= W + 2, f"text in afara panzei: {s!r}"
|
||||
assert 0 <= y <= H, f"text in afara inaltimii: {s!r} (y={y})"
|
||||
|
||||
# (3) fara doua texte pe aceeasi pozitie
|
||||
for i in range(len(TEXTS)):
|
||||
xi, yi, si, szi, wi, ai = TEXTS[i]
|
||||
we = est_w(si, szi, wi)
|
||||
li = xi - we if ai == "end" else (xi - we / 2 if ai == "middle" else xi)
|
||||
ri = li + we
|
||||
for j in range(i + 1, len(TEXTS)):
|
||||
xj, yj, sj, szj, wj, aj = TEXTS[j]
|
||||
wej = est_w(sj, szj, wj)
|
||||
lj = xj - wej if aj == "end" else (xj - wej / 2 if aj == "middle" else xj)
|
||||
rj = lj + wej
|
||||
if abs(yi - yj) < 13 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
# etichetele panourilor incap in panouri
|
||||
for x, y, s, size, weight, anchor in TEXTS:
|
||||
for (px, py, pw, ph) in (PRICE_BOX, IND_BOX):
|
||||
if py <= y <= py + ph and px - 2 <= x <= px + pw + 2:
|
||||
assert x >= px + 12, f"eticheta lipita de margine: {s!r}"
|
||||
assert est_w(s, size, weight) <= pw - 24, f"prea lata in panou: {s!r}"
|
||||
|
||||
# fara linie goala in SVG
|
||||
assert "" not in svg.split("\n")[:-1], "SVG-ul are linie goala"
|
||||
assert "\n\n" not in svg, "SVG-ul are linie goala"
|
||||
return True
|
||||
|
||||
|
||||
SVG = build()
|
||||
assert verify(SVG), "verificare esuata"
|
||||
OUT.write_text(SVG, encoding="utf-8")
|
||||
print(f"scris {OUT} ({W}x{H}), verificare OK: {len(TEXTS)} etichete, "
|
||||
f"L1-L2={(X_L2 - X_L1) / STEPW:.1f} pasi, "
|
||||
f"L2-L3={(X_L3 - X_L2) / STEPW:.1f} pasi, "
|
||||
f"fereastra 60 = {(X_L2 - X0) / STEPW:.0f} pasi")
|
||||
36
gen/strategii/mdsrt_masurare.svg
Normal file
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 620" width="700" height="620" role="img" aria-label="Cum se masoara divergenta: pretul face un lower low (L2 sub L1), indicatorul face un higher low (I2 peste I1); intre L1 si L2 trebuie minim 5 candele si cel mult 60 in spate, iar un low la 2-3 candele nu conteaza">
|
||||
<g font-family="system-ui, sans-serif">
|
||||
<rect x="0" y="0" width="700" height="620" fill="#ffffff"/>
|
||||
<text x="350.0" y="34.0" font-size="19" font-weight="700" fill="#000000" text-anchor="middle">Divergența: cum o măsori corect</text>
|
||||
<text x="350.0" y="58.0" font-size="13" font-weight="400" fill="#777777" text-anchor="middle">Aceeași structură pe preț și pe indicator: două swing low-uri, două minime</text>
|
||||
<rect x="40.0" y="76.0" width="620.0" height="250.0" rx="8" fill="#ffffff" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="56.0" y="96.0" font-size="14" font-weight="700" fill="#000000" text-anchor="start">PREȚ (schematic) — downtrend, lower low</text>
|
||||
<line x1="60.0" y1="134.0" x2="480.0" y2="134.0" stroke="#000000" stroke-width="1.4"/>
|
||||
<line x1="60.0" y1="127.0" x2="60.0" y2="141.0" stroke="#000000" stroke-width="1.4"/>
|
||||
<line x1="480.0" y1="127.0" x2="480.0" y2="141.0" stroke="#000000" stroke-width="1.4"/>
|
||||
<text x="270.0" y="122.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">maxim 60 de candele în spate</text>
|
||||
<polyline points="60.0,158.0 102.0,178.0 144.0,166.0 186.0,192.0 228.0,180.0 270.0,206.0 312.0,194.0 354.0,218.0 396.0,232.0 438.0,252.0 459.0,226.0 480.0,278.0 489.1,248.0 497.5,272.0 515.0,250.0" fill="none" stroke="#000000" stroke-width="1.8" stroke-linejoin="round"/>
|
||||
<circle cx="438.0" cy="252.0" r="3.6" fill="#000000"/>
|
||||
<circle cx="480.0" cy="278.0" r="3.6" fill="#000000"/>
|
||||
<circle cx="497.5" cy="272.0" r="3.6" fill="#000000"/>
|
||||
<text x="428.0" y="258.0" font-size="13" font-weight="700" fill="#000000" text-anchor="end">L1</text>
|
||||
<text x="468.0" y="286.0" font-size="13" font-weight="700" fill="#000000" text-anchor="end">L2</text>
|
||||
<line x1="438.0" y1="300.0" x2="480.0" y2="300.0" stroke="#000000" stroke-width="1.4"/>
|
||||
<line x1="438.0" y1="293.0" x2="438.0" y2="307.0" stroke="#000000" stroke-width="1.4"/>
|
||||
<line x1="480.0" y1="293.0" x2="480.0" y2="307.0" stroke="#000000" stroke-width="1.4"/>
|
||||
<text x="459.0" y="318.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">minim 5 candele între puncte</text>
|
||||
<line x1="487.5" y1="262.0" x2="507.5" y2="282.0" stroke="#c00" stroke-width="2.0"/>
|
||||
<line x1="487.5" y1="282.0" x2="507.5" y2="262.0" stroke="#c00" stroke-width="2.0"/>
|
||||
<text x="497.5" y="234.0" font-size="14" font-weight="700" fill="#c00" text-anchor="middle">NU</text>
|
||||
<text x="525.0" y="246.0" font-size="13" font-weight="700" fill="#c00" text-anchor="start">2-3 candele</text>
|
||||
<text x="525.0" y="266.0" font-size="13" font-weight="700" fill="#c00" text-anchor="start">NU e divergență</text>
|
||||
<rect x="40.0" y="344.0" width="620.0" height="200.0" rx="8" fill="#ffffff" stroke="#777777" stroke-width="1.2"/>
|
||||
<text x="56.0" y="364.0" font-size="14" font-weight="700" fill="#1a5fb4" text-anchor="start">INDICATOR (Power / volum / RS) — higher low</text>
|
||||
<polyline points="60.0,400.0 102.0,424.0 144.0,410.0 186.0,436.0 228.0,422.0 270.0,448.0 312.0,434.0 354.0,458.0 396.0,466.0 438.0,486.0 459.0,458.0 480.0,468.0 515.0,440.0" fill="none" stroke="#1a5fb4" stroke-width="1.8" stroke-linejoin="round"/>
|
||||
<circle cx="438.0" cy="486.0" r="3.6" fill="#1a5fb4"/>
|
||||
<circle cx="480.0" cy="468.0" r="3.6" fill="#1a5fb4"/>
|
||||
<text x="428.0" y="492.0" font-size="13" font-weight="700" fill="#1a5fb4" text-anchor="end">I1</text>
|
||||
<text x="490.0" y="474.0" font-size="13" font-weight="700" fill="#1a5fb4" text-anchor="start">I2</text>
|
||||
<text x="350.0" y="570.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">Prețul face un lower low (L2 sub L1), indicatorul face un higher low (I2 peste I1):</text>
|
||||
<text x="350.0" y="590.0" font-size="13" font-weight="400" fill="#000000" text-anchor="middle">divergență bull. Dar dacă al doilea punct e la doar 2-3 candele, nu se numără.</text>
|
||||
</g></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
199
gen/strategii/mdsrt_trinity.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
"""MDSRT — minimul obligatoriu (M complet) vs. varianta Trinity.
|
||||
|
||||
Diagramă conceptuală (nu grafic de preț): pentru
|
||||
`summaries/strategii/07_mdsrt.md`, Nivel 2, §2.1 „Ce dă edge", după tabel
|
||||
(marcajul `<!-- DIAGRAMA: trinity -->`). Citește specificația
|
||||
`gen/strategii/mdsrt_trinity.spec.txt` și scrie
|
||||
`gen/strategii/mdsrt_trinity.svg`.
|
||||
|
||||
NU folosește `svg_candles`: SVG-ul e construit ca string, fără dependințe.
|
||||
|
||||
Conținutul, cu sursa fiecărei afirmații:
|
||||
- Power obligatoriu; dacă nu e în divergență, setup-ul nu se califică:
|
||||
`05_MODUL_4/02_strategia_5_mdsrt.md:59-61, 432-434`.
|
||||
- Minim 2 indicatori: Power + (Volume SAU Relative Strength):
|
||||
`05_MODUL_4/02_strategia_5_mdsrt.md:85-88`.
|
||||
- Trinity = toți trei în divergență simultan, „setup de 5 stele":
|
||||
`05_MODUL_4/02_strategia_5_mdsrt.md:6-8, 34-42, 87-88`.
|
||||
- Sensul din selecția simbolului (aliniere în cadranul 1; trei divergențe
|
||||
simultane NU sunt Trinity): `08_ANALIZA_ZILNICA/11_selectia_simbolului.md:612-627`.
|
||||
|
||||
Rulează: .venv/bin/python gen/strategii/mdsrt_trinity.py
|
||||
"""
|
||||
import html
|
||||
from pathlib import Path
|
||||
|
||||
W, H = 700, 550
|
||||
|
||||
BLACK = "#000000"
|
||||
GRAY = "#777777"
|
||||
RED = "#c00"
|
||||
GREEN = "#188a3e"
|
||||
BLUE = "#1a5fb4"
|
||||
|
||||
texts = [] # (x, y, text, size, weight, fill, w_est)
|
||||
boxes = [] # dict-uri: doar casetele, pentru numărătoarea din assert-uri
|
||||
|
||||
|
||||
def add(x, y, s, size=13, weight="normal", fill=BLACK):
|
||||
factor = 0.62 if weight == "bold" else 0.56
|
||||
w = len(s) * size * factor
|
||||
texts.append((x, y, s, size, weight, fill, w))
|
||||
return (x, y, s, size, weight, fill, w)
|
||||
|
||||
|
||||
def box(x, y, w, h, *, fill="#ffffff", stroke=GRAY, sw=1.5, dash=None, row=None):
|
||||
b = {"x": x, "y": y, "w": w, "h": h, "row": row, "lines": []}
|
||||
boxes.append(b)
|
||||
return b
|
||||
|
||||
|
||||
def box_line(b, dx, dy, s, size=14, weight="normal", fill=BLACK):
|
||||
cx = b["x"] + b["w"] / 2.0 + dx if dx is not None else b["x"] + b["w"] / 2.0
|
||||
add(cx, b["y"] + dy, s, size, weight, fill)
|
||||
b["lines"].append(s)
|
||||
|
||||
|
||||
parts = []
|
||||
|
||||
|
||||
def rect(b, fill, stroke, sw=1.5, dash=None):
|
||||
d = f' stroke-dasharray="{dash}"' if dash else ""
|
||||
return (f'<rect x="{b["x"]}" y="{b["y"]}" width="{b["w"]}" '
|
||||
f'height="{b["h"]}" fill="{fill}" stroke="{stroke}" '
|
||||
f'stroke-width="{sw}"{d}/>')
|
||||
|
||||
|
||||
# --------------------------------------------------------------- titlu
|
||||
add(W / 2, 34, "MDSRT — minimul obligatoriu vs. Trinity", 20, "bold")
|
||||
add(W / 2, 58,
|
||||
"Minim Power + unul din ceilalți e obligatoriu; toți trei în divergență = Trinity.",
|
||||
13, "normal", GRAY)
|
||||
|
||||
# ------------------------------------------------- rând 1 — minim obligatoriu
|
||||
add(W / 2, 100, "Minimul obligatoriu (M complet)", 16, "bold")
|
||||
|
||||
B1 = box(40, 112, 250, 56, row=1)
|
||||
B2 = box(310, 112, 350, 56, row=1)
|
||||
parts.append(rect(B1, GREEN, GREEN))
|
||||
parts.append(rect(B2, "#ffffff", GREEN, sw=2, dash="5 3"))
|
||||
|
||||
box_line(B1, None, 31, "Power (obligatoriu)", 15, "bold", "#ffffff")
|
||||
box_line(B2, None, 22, "Volume SAU Relative Strength", 14, "normal", GREEN)
|
||||
box_line(B2, None, 44, "— oricare una", 13, "normal", GREEN)
|
||||
|
||||
add(W / 2, 196, "Power lipsește → setup-ul NU se califică", 14, "bold", RED)
|
||||
|
||||
# ------------------------------------------------- rând 2 — Trinity (opțional)
|
||||
add(W / 2, 250, "Trinity (opțional, semnal mai puternic)", 16, "bold")
|
||||
|
||||
TITLES = ["Power (obligatoriu)", "Volume", "Relative Strength"]
|
||||
B3 = box(30, 262, 200, 76, row=2)
|
||||
B4 = box(250, 262, 200, 76, row=2)
|
||||
B5 = box(470, 262, 200, 76, row=2)
|
||||
for b in (B3, B4, B5):
|
||||
parts.append(rect(b, GREEN, GREEN))
|
||||
for b, t in zip((B3, B4, B5), TITLES):
|
||||
box_line(b, None, 34, t, 14, "bold", "#ffffff")
|
||||
box_line(b, None, 56, "divergență", 14, "bold", "#ffffff")
|
||||
|
||||
add(W / 2, 360, "toți trei în divergență simultan, pe aceeași zonă de preț",
|
||||
14, "normal", BLACK)
|
||||
add(W / 2, 386, "comparat în curs cu un «setup de 5 stele»", 14, "bold", GREEN)
|
||||
|
||||
# --------------------------------------------------------------- subsol
|
||||
parts.append(f'<line x1="40" y1="420" x2="660" y2="420" stroke="{GRAY}" '
|
||||
f'stroke-width="1"/>')
|
||||
add(W / 2, 448,
|
||||
"La MDSRT, „Trinity” se folosește local cu sensul „toți trei în divergență”.",
|
||||
13, "normal", GRAY)
|
||||
add(W / 2, 468,
|
||||
"Lecția de selecție dă aceluiași cuvânt alt sens:", 13, "normal", GRAY)
|
||||
add(W / 2, 486,
|
||||
"aliniere în cadranul 1, nu divergență (contradicția 2.4).",
|
||||
13, "normal", GRAY)
|
||||
add(W / 2, 512,
|
||||
"Sursa: 05_MODUL_4/02_strategia_5_mdsrt.md:6-8, 34-42, 59-61, 85-88;",
|
||||
13, "normal", GRAY)
|
||||
add(W / 2, 530,
|
||||
"08_ANALIZA_ZILNICA/11_selectia_simbolului.md:612-627.",
|
||||
13, "normal", GRAY)
|
||||
|
||||
# --------------------------------------------------------------- emitere
|
||||
head = (f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {W} {H}" '
|
||||
f'width="{W}" height="{H}" font-family="system-ui, sans-serif" role="img" '
|
||||
f'aria-label="MDSRT: minimul obligatoriu este Power (obligatoriu) plus '
|
||||
f'Volume sau Relative Strength; varianta Trinity are toți trei '
|
||||
f'indicatori în divergență simultan, pe aceeași zonă de preț">')
|
||||
parts.insert(0, f'<rect x="0" y="0" width="{W}" height="{H}" fill="#ffffff"/>')
|
||||
parts.insert(0, head)
|
||||
|
||||
body = []
|
||||
for x, y, s, size, weight, fill, _ in texts:
|
||||
body.append(
|
||||
f'<text x="{x:.1f}" y="{y:.1f}" font-size="{size}" '
|
||||
f'font-weight="{weight}" fill="{fill}" text-anchor="middle">'
|
||||
f'{html.escape(s, quote=False)}</text>')
|
||||
svg = "\n".join(parts + body + ["</svg>"]) + "\n"
|
||||
|
||||
# =============================================================== VERIFICARE
|
||||
# 1. Rândul 1 conține exact 2 casete (Power + „Volume SAU Relative Strength"),
|
||||
# nu 3.
|
||||
row1 = [b for b in boxes if b["row"] == 1]
|
||||
assert len(row1) == 2, f"rândul 1 are {len(row1)} casete, trebuie exact 2"
|
||||
|
||||
# 2. Rândul 2 conține exact 3 casete (Power, Volume, Relative Strength).
|
||||
row2 = [b for b in boxes if b["row"] == 2]
|
||||
assert len(row2) == 3, f"rândul 2 are {len(row2)} casete, trebuie exact 3"
|
||||
assert [b["lines"][0] for b in row2] == TITLES, "titlurile din rândul 2 diferă"
|
||||
|
||||
# 3. Textul „Power (obligatoriu)" apare în ambele rânduri.
|
||||
r1_labels = [s for b in row1 for s in b["lines"]]
|
||||
r2_labels = [s for b in row2 for s in b["lines"]]
|
||||
assert "Power (obligatoriu)" in r1_labels, "lipsește din rândul 1"
|
||||
assert "Power (obligatoriu)" in r2_labels, "lipsește din rândul 2"
|
||||
assert svg.count(">Power (obligatoriu)</text>") == 2, \
|
||||
"„Power (obligatoriu)” nu apare exact de două ori"
|
||||
|
||||
# 4. Eticheta „Power lipsește → setup-ul NU se califică" e lângă rândul 1.
|
||||
assert "Power lipsește → setup-ul NU se califică" in svg, "lipsește eticheta rând 1"
|
||||
|
||||
# 5. Nota de subsol despre cele două sensuri ale lui „Trinity" e prezentă.
|
||||
assert "se folosește local cu sensul" in svg and "alt sens" in svg \
|
||||
and "cadranul 1, nu divergență" in svg and "contradicția 2.4" in svg, \
|
||||
"lipsește nota despre cele două sensuri ale lui Trinity"
|
||||
|
||||
# stil: fără umbre/gradienti, text >= 13px, totul în panză, fără suprapuneri
|
||||
assert "filter=" not in svg and "gradient" not in svg, "fără umbre/gradienti"
|
||||
assert 'font-family="system-ui, sans-serif"' in svg, "lipsește fontul cerut"
|
||||
|
||||
rects_collide = []
|
||||
for x, y, s, size, weight, fill, w in texts:
|
||||
assert size >= 13, f"font sub 13px: {s!r} ({size})"
|
||||
left, right = x - w / 2.0, x + w / 2.0
|
||||
top, bottom = y - size * 0.78, y + size * 0.22
|
||||
assert 0 <= left and right <= W, f"etichetă în afara panzei (orizontal): {s!r}"
|
||||
assert 0 <= top and bottom <= H, f"etichetă în afara panzei (vertical): {s!r}"
|
||||
rects_collide.append((left, right, top, bottom, s))
|
||||
|
||||
for i in range(len(rects_collide)):
|
||||
for j in range(i + 1, len(rects_collide)):
|
||||
l1, r1, t1, b1, s1 = rects_collide[i]
|
||||
l2, r2, t2, b2, s2 = rects_collide[j]
|
||||
if r1 > l2 and r2 > l1 and b1 > t2 and b2 > t1:
|
||||
raise AssertionError(f"etichete suprapuse: {s1!r} / {s2!r}")
|
||||
|
||||
for required in ("Minimul obligatoriu (M complet)",
|
||||
"Volume SAU Relative Strength",
|
||||
"oricare una",
|
||||
"Trinity (opțional, semnal mai puternic)",
|
||||
"divergență",
|
||||
"pe aceeași zonă de preț",
|
||||
"setup de 5 stele"):
|
||||
assert required in svg, f"lipsește din desen: {required!r}"
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
print(f"scris {OUT} ({len(svg)} bytes, {len(texts)} etichete, "
|
||||
f"{len(boxes)} casete, {W}x{H}), verificare OK")
|
||||
30
gen/strategii/mdsrt_trinity.svg
Normal file
@@ -0,0 +1,30 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 550" width="700" height="550" font-family="system-ui, sans-serif" role="img" aria-label="MDSRT: minimul obligatoriu este Power (obligatoriu) plus Volume sau Relative Strength; varianta Trinity are toți trei indicatori în divergență simultan, pe aceeași zonă de preț">
|
||||
<rect x="0" y="0" width="700" height="550" fill="#ffffff"/>
|
||||
<rect x="40" y="112" width="250" height="56" fill="#188a3e" stroke="#188a3e" stroke-width="1.5"/>
|
||||
<rect x="310" y="112" width="350" height="56" fill="#ffffff" stroke="#188a3e" stroke-width="2" stroke-dasharray="5 3"/>
|
||||
<rect x="30" y="262" width="200" height="76" fill="#188a3e" stroke="#188a3e" stroke-width="1.5"/>
|
||||
<rect x="250" y="262" width="200" height="76" fill="#188a3e" stroke="#188a3e" stroke-width="1.5"/>
|
||||
<rect x="470" y="262" width="200" height="76" fill="#188a3e" stroke="#188a3e" stroke-width="1.5"/>
|
||||
<line x1="40" y1="420" x2="660" y2="420" stroke="#777777" stroke-width="1"/>
|
||||
<text x="350.0" y="34.0" font-size="20" font-weight="bold" fill="#000000" text-anchor="middle">MDSRT — minimul obligatoriu vs. Trinity</text>
|
||||
<text x="350.0" y="58.0" font-size="13" font-weight="normal" fill="#777777" text-anchor="middle">Minim Power + unul din ceilalți e obligatoriu; toți trei în divergență = Trinity.</text>
|
||||
<text x="350.0" y="100.0" font-size="16" font-weight="bold" fill="#000000" text-anchor="middle">Minimul obligatoriu (M complet)</text>
|
||||
<text x="165.0" y="143.0" font-size="15" font-weight="bold" fill="#ffffff" text-anchor="middle">Power (obligatoriu)</text>
|
||||
<text x="485.0" y="134.0" font-size="14" font-weight="normal" fill="#188a3e" text-anchor="middle">Volume SAU Relative Strength</text>
|
||||
<text x="485.0" y="156.0" font-size="13" font-weight="normal" fill="#188a3e" text-anchor="middle">— oricare una</text>
|
||||
<text x="350.0" y="196.0" font-size="14" font-weight="bold" fill="#c00" text-anchor="middle">Power lipsește → setup-ul NU se califică</text>
|
||||
<text x="350.0" y="250.0" font-size="16" font-weight="bold" fill="#000000" text-anchor="middle">Trinity (opțional, semnal mai puternic)</text>
|
||||
<text x="130.0" y="296.0" font-size="14" font-weight="bold" fill="#ffffff" text-anchor="middle">Power (obligatoriu)</text>
|
||||
<text x="130.0" y="318.0" font-size="14" font-weight="bold" fill="#ffffff" text-anchor="middle">divergență</text>
|
||||
<text x="350.0" y="296.0" font-size="14" font-weight="bold" fill="#ffffff" text-anchor="middle">Volume</text>
|
||||
<text x="350.0" y="318.0" font-size="14" font-weight="bold" fill="#ffffff" text-anchor="middle">divergență</text>
|
||||
<text x="570.0" y="296.0" font-size="14" font-weight="bold" fill="#ffffff" text-anchor="middle">Relative Strength</text>
|
||||
<text x="570.0" y="318.0" font-size="14" font-weight="bold" fill="#ffffff" text-anchor="middle">divergență</text>
|
||||
<text x="350.0" y="360.0" font-size="14" font-weight="normal" fill="#000000" text-anchor="middle">toți trei în divergență simultan, pe aceeași zonă de preț</text>
|
||||
<text x="350.0" y="386.0" font-size="14" font-weight="bold" fill="#188a3e" text-anchor="middle">comparat în curs cu un «setup de 5 stele»</text>
|
||||
<text x="350.0" y="448.0" font-size="13" font-weight="normal" fill="#777777" text-anchor="middle">La MDSRT, „Trinity” se folosește local cu sensul „toți trei în divergență”.</text>
|
||||
<text x="350.0" y="468.0" font-size="13" font-weight="normal" fill="#777777" text-anchor="middle">Lecția de selecție dă aceluiași cuvânt alt sens:</text>
|
||||
<text x="350.0" y="486.0" font-size="13" font-weight="normal" fill="#777777" text-anchor="middle">aliniere în cadranul 1, nu divergență (contradicția 2.4).</text>
|
||||
<text x="350.0" y="512.0" font-size="13" font-weight="normal" fill="#777777" text-anchor="middle">Sursa: 05_MODUL_4/02_strategia_5_mdsrt.md:6-8, 34-42, 59-61, 85-88;</text>
|
||||
<text x="350.0" y="530.0" font-size="13" font-weight="normal" fill="#777777" text-anchor="middle">08_ANALIZA_ZILNICA/11_selectia_simbolului.md:612-627.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
139
gen/strategii/mp2_ansamblu.py
Normal file
@@ -0,0 +1,139 @@
|
||||
"""Diagrama de ansamblu MP² — bottom major NVDA (strategii/02_mp2.md, Nivel 0).
|
||||
|
||||
„NVDA — weekly (pre-split 1:10)": downtrend masiv → bază/bottom oct. 2022 →
|
||||
crossing-ul pivotului weekly (mic) peste cel monthly (mare) → candela de
|
||||
trigger care închide peste pivotul mic (M+A) → intrare 13.80 → SL la swing
|
||||
low-ul absolut 10.63 (−23 %) → urcare spre TP 17.25 (+25 %, ~9 săptămâni).
|
||||
|
||||
Cifrele (13.80 / 10.63 / 17.25 / −23 % / +25 %) sunt din
|
||||
`summaries/04_MODUL_3/04_strategia_2_mp_patrat.md:341-357,435`.
|
||||
Traseul de preț și cele două linii de pivot sunt schematice: cursul nu dă
|
||||
valorile pivoților, doar faptul că micul trece deasupra marelui la crossing
|
||||
(`...:326-335`).
|
||||
|
||||
Rulează:
|
||||
python3 gen/strategii/mp2_ansamblu.py
|
||||
Scrie lângă el `mp2_ansamblu.svg`.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "mp2_ansamblu.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE = "#777", "#c00", "#188a3e", "#1a5fb4"
|
||||
|
||||
# ------------------------------------------------------------------ seria (38 bare)
|
||||
# 16 bare de downtrend 32.00 -> 11.40 (minimul absolut 10.63 e fitilul barei 15),
|
||||
# 10 bare de bază/recuperare 11.80 -> 13.80 (bara 25 = intrarea, după M+A),
|
||||
# 12 bare de urcare 14.10 -> 17.25 (ținta). open-ul = close-ul precedentului.
|
||||
DOWN = [32.00, 30.80, 29.60, 28.70, 27.40, 26.50, 25.30, 24.20, 23.60,
|
||||
22.30, 21.10, 19.90, 18.50, 16.90, 15.20, 11.40] # 0-15
|
||||
BASE = [11.80, 12.10, 12.35, 12.20, 12.55, 12.85, 13.05, 13.35, 13.55,
|
||||
13.80] # 16-25
|
||||
UP = [14.10, 14.45, 14.80, 15.10, 15.45, 15.80, 16.10, 15.90, 16.30,
|
||||
16.60, 16.95, 17.25] # 26-37
|
||||
CLOSES = DOWN + BASE + UP
|
||||
assert len(CLOSES) == 38, len(CLOSES)
|
||||
|
||||
CAPIT, ENTRY, LAST = 15, 25, 37
|
||||
|
||||
# fitile (sus, jos) per bară; bara 15 are fitilul de jos până la minimul absolut
|
||||
WICKS = ([(0.35, 0.35)] * 14
|
||||
+ [(0.30, 0.30)] # 14
|
||||
+ [(0.20, 0.77)] # 15: low = 11.40 - 0.77 = 10.63
|
||||
+ [(0.30, 0.30)] * 9 # 16-24
|
||||
+ [(0.25, 0.20)] # 25: candela de intrare
|
||||
+ [(0.30, 0.30)] * 12) # 26-37
|
||||
assert len(WICKS) == len(CLOSES)
|
||||
|
||||
VOLS = ([52, 55, 50, 58, 54, 49, 56, 52, 60, 57, 63, 68, 72, 80, 90, 120] # 0-15
|
||||
+ [100, 72, 60, 55, 58, 62, 66, 70, 78, 95] # 16-25
|
||||
+ [88, 80, 74, 70, 68, 72, 76, 70, 74, 78, 84, 92]) # 26-37
|
||||
assert len(VOLS) == len(CLOSES)
|
||||
|
||||
cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=32.60)
|
||||
|
||||
# ------------------------------------------------- liniile de pivot (trepte)
|
||||
# Stepped, schematice (cursul nu dă valorile): monthly „mare" se recalculează
|
||||
# rar, weekly „mic" mai des. Crossing: micul trece deasupra marelui exact pe
|
||||
# candela de intrare.
|
||||
PMARE = [31.00] * 10 + [21.00] * 10 + [13.55] * 8 + [13.20] * 10 # monthly
|
||||
PMIC = ([30.50] * 5 + [29.00] * 5 + [20.00] * 4 + [17.00] * 4
|
||||
+ [12.60] * 3 + [12.95] * 2 + [13.20] * 2 + [13.70] * 3
|
||||
+ [14.20] * 4 + [14.60] * 6) # weekly
|
||||
assert len(PMARE) == len(PMIC) == len(CLOSES)
|
||||
|
||||
# ---------------------------------------------------------- assert-urile din spec
|
||||
entry = cds[ENTRY].c
|
||||
sl, tp = 10.63, cds[LAST].c
|
||||
|
||||
assert abs(entry - 13.80) < 0.05 # close-ul intrării == 13.80
|
||||
assert abs(sl - 10.63) < 0.05 and sl < entry # SL = swing low absolut, sub entry
|
||||
assert abs(tp - 17.25) < 0.10 and tp > entry # TP = 17.25, peste entry
|
||||
# spec: `abs((entry/sl - 1)*100 - 23)`; formula din spec e inversată (dă +29.8,
|
||||
# nu −23). Pierderea corectă din entry până la SL e (sl/entry - 1), deci o scriu așa:
|
||||
loss_pct = (sl / entry - 1) * 100
|
||||
assert abs(loss_pct + 23) < 1.5, loss_pct # SL ≈ −23 %
|
||||
assert abs((tp / entry - 1) * 100 - 25) < 1.5 # TP ≈ +25 %
|
||||
assert abs(min(c.l for c in cds) - 10.63) < 0.01 # minimul absolut al seriei = SL
|
||||
assert min(c.l for c in cds[-5:-1]) > 10.63 # stopul de 4 bare ar fi mult mai sus
|
||||
# crossing-ul pivoților, înainte/după candela de intrare
|
||||
assert all(PMIC[i] < PMARE[i] for i in range(ENTRY - 4, ENTRY)), "mic < mare înainte de intrare"
|
||||
assert all(PMIC[i] > PMARE[i] for i in range(ENTRY, ENTRY + 4)), "mic > mare după intrare"
|
||||
assert cds[ENTRY].c > PMIC[ENTRY], "candela de trigger nu închide peste pivotul mic"
|
||||
assert abs(cds[CAPIT].l - sl) < 1e-9, "fitilul barei 15 nu atinge exact SL-ul"
|
||||
|
||||
levels = [
|
||||
Level(13.80, "entry ~13.80 (după M+A)", GREEN, label_at="left"),
|
||||
Level(sl, "SL swing low absolut 10.63, −23%", RED, label_at="left"),
|
||||
Level(tp, "TP ~+25% (17.25), ~9 săpt.", BLUE, label_at="left"),
|
||||
]
|
||||
marks = [
|
||||
Mark(CAPIT, f"minim absolut {sl:.2f}", RED, "below", dy=-6),
|
||||
Mark(ENTRY, "M+A", BLUE, "above"),
|
||||
]
|
||||
|
||||
ch = Chart(candles=cds, levels=levels, marks=marks,
|
||||
scale=17.0, gap=15, body_w=9, left=44, width=720, top=64, vol_h=70,
|
||||
vol_label="volum", title="NVDA — weekly (pre-split 1:10)",
|
||||
caption="Bottom oct. 2022: entry ~13.80, −23%, ~+25% în ~9 săpt. "
|
||||
"Traseu și pivoți construiți (cursul nu dă valorile lor).")
|
||||
|
||||
svg = ch.render()
|
||||
|
||||
|
||||
def step_points(vals: list[float]) -> str:
|
||||
"""Polilinie în trepte: orizontal pe fiecare palier, vertical la schimbare."""
|
||||
pts: list[tuple[float, float]] = []
|
||||
for i, v in enumerate(vals):
|
||||
x, y = ch._cx(i), ch._y(v)
|
||||
if i == 0:
|
||||
pts.append((x, y))
|
||||
elif v != vals[i - 1]:
|
||||
pts.append((x, ch._y(vals[i - 1]))) # orizontal până la bara nouă
|
||||
pts.append((x, y)) # salt vertical la nivelul nou
|
||||
else:
|
||||
pts.append((x, y))
|
||||
return " ".join(f"{x:.1f},{y:.1f}" for x, y in pts)
|
||||
|
||||
|
||||
# injectate imediat după tag-ul de deschidere => desenate în spatele candelelor
|
||||
inject = [
|
||||
f'<polyline points="{step_points(PMARE)}" fill="none" stroke="{GRAY}" stroke-width="1.6"/>',
|
||||
f'<polyline points="{step_points(PMIC)}" fill="none" stroke="{BLUE}" stroke-width="1.6"/>',
|
||||
f'<text x="712" y="{ch._y(PMIC[-1]) - 6:.1f}" fill="{BLUE}" text-anchor="end">'
|
||||
f'pivot weekly (mic)</text>',
|
||||
f'<text x="712" y="{ch._y(PMARE[-1]) + 14:.1f}" fill="{GRAY}" text-anchor="end">'
|
||||
f'pivot monthly (mare)</text>',
|
||||
]
|
||||
i = svg.index(">") + 1
|
||||
svg = svg[:i] + "\n " + "\n ".join(inject) + svg[i:]
|
||||
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown"
|
||||
OUT.write_text(svg)
|
||||
print(f"scris {OUT.name}: {len(cds)} candele, entry {entry:.2f}, "
|
||||
f"SL {sl:.2f} ({loss_pct:+.1f}%), TP {tp:.2f} "
|
||||
f"({(tp/entry - 1) * 100:+.1f}%), crossing la bara {ENTRY}")
|
||||
132
gen/strategii/mp2_ansamblu.svg
Normal file
@@ -0,0 +1,132 @@
|
||||
<svg viewBox="0 0 720 603" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<polyline points="44.0,119.2 59.0,119.2 74.0,119.2 89.0,119.2 104.0,119.2 119.0,119.2 134.0,119.2 149.0,119.2 164.0,119.2 179.0,119.2 194.0,119.2 194.0,289.2 209.0,289.2 224.0,289.2 239.0,289.2 254.0,289.2 269.0,289.2 284.0,289.2 299.0,289.2 314.0,289.2 329.0,289.2 344.0,289.2 344.0,415.8 359.0,415.8 374.0,415.8 389.0,415.8 404.0,415.8 419.0,415.8 434.0,415.8 449.0,415.8 464.0,415.8 464.0,421.8 479.0,421.8 494.0,421.8 509.0,421.8 524.0,421.8 539.0,421.8 554.0,421.8 569.0,421.8 584.0,421.8 599.0,421.8" fill="none" stroke="#777" stroke-width="1.6"/>
|
||||
<polyline points="44.0,127.7 59.0,127.7 74.0,127.7 89.0,127.7 104.0,127.7 119.0,127.7 119.0,153.2 134.0,153.2 149.0,153.2 164.0,153.2 179.0,153.2 194.0,153.2 194.0,306.2 209.0,306.2 224.0,306.2 239.0,306.2 254.0,306.2 254.0,357.2 269.0,357.2 284.0,357.2 299.0,357.2 314.0,357.2 314.0,432.0 329.0,432.0 344.0,432.0 359.0,432.0 359.0,426.0 374.0,426.0 389.0,426.0 389.0,421.8 404.0,421.8 419.0,421.8 419.0,413.3 434.0,413.3 449.0,413.3 464.0,413.3 464.0,404.8 479.0,404.8 494.0,404.8 509.0,404.8 524.0,404.8 524.0,398.0 539.0,398.0 554.0,398.0 569.0,398.0 584.0,398.0 599.0,398.0" fill="none" stroke="#1a5fb4" stroke-width="1.6"/>
|
||||
<text x="712" y="392.0" fill="#1a5fb4" text-anchor="end">pivot weekly (mic)</text>
|
||||
<text x="712" y="435.8" fill="#777" text-anchor="end">pivot monthly (mare)</text>
|
||||
<text x="24" y="78" fill="#333" style="font-weight:bold">NVDA — weekly (pre-split 1:10)</text>
|
||||
<line x1="24" y1="411.6" x2="706" y2="411.6" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="405.6" fill="#188a3e">entry ~13.80 (după M+A)</text>
|
||||
<line x1="24" y1="465.4" x2="706" y2="465.4" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="459.4" fill="#c00">SL swing low absolut 10.63, −23%</text>
|
||||
<line x1="24" y1="352.9" x2="706" y2="352.9" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="346.9" fill="#1a5fb4">TP ~+25% (17.25), ~9 săpt.</text>
|
||||
<line x1="44.0" y1="86.0" x2="44.0" y2="108.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="92.0" width="9" height="10.2" fill="#c00"/>
|
||||
<line x1="59.0" y1="96.2" x2="59.0" y2="128.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="102.2" width="9" height="20.4" fill="#c00"/>
|
||||
<line x1="74.0" y1="116.6" x2="74.0" y2="148.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="122.6" width="9" height="20.4" fill="#c00"/>
|
||||
<line x1="89.0" y1="137.0" x2="89.0" y2="164.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="143.0" width="9" height="15.3" fill="#c00"/>
|
||||
<line x1="104.0" y1="152.3" x2="104.0" y2="186.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="158.3" width="9" height="22.1" fill="#c00"/>
|
||||
<line x1="119.0" y1="174.4" x2="119.0" y2="201.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="180.4" width="9" height="15.3" fill="#c00"/>
|
||||
<line x1="134.0" y1="189.7" x2="134.0" y2="222.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="195.7" width="9" height="20.4" fill="#c00"/>
|
||||
<line x1="149.0" y1="210.1" x2="149.0" y2="240.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="216.1" width="9" height="18.7" fill="#c00"/>
|
||||
<line x1="164.0" y1="228.8" x2="164.0" y2="250.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="234.8" width="9" height="10.2" fill="#c00"/>
|
||||
<line x1="179.0" y1="239.0" x2="179.0" y2="273.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="245.0" width="9" height="22.1" fill="#c00"/>
|
||||
<line x1="194.0" y1="261.1" x2="194.0" y2="293.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="267.1" width="9" height="20.4" fill="#c00"/>
|
||||
<line x1="209.0" y1="281.5" x2="209.0" y2="313.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="287.5" width="9" height="20.4" fill="#c00"/>
|
||||
<line x1="224.0" y1="301.9" x2="224.0" y2="337.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="307.9" width="9" height="23.8" fill="#c00"/>
|
||||
<line x1="239.0" y1="325.7" x2="239.0" y2="364.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="331.7" width="9" height="27.2" fill="#c00"/>
|
||||
<line x1="254.0" y1="353.8" x2="254.0" y2="392.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="358.9" width="9" height="28.9" fill="#c00"/>
|
||||
<line x1="269.0" y1="384.4" x2="269.0" y2="465.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="387.8" width="9" height="64.6" fill="#c00"/>
|
||||
<line x1="284.0" y1="440.5" x2="284.0" y2="457.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="445.6" width="9" height="6.8" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="435.4" x2="299.0" y2="450.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="440.5" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="431.1" x2="314.0" y2="445.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="436.2" width="9" height="4.2" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="431.1" x2="329.0" y2="443.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="436.2" width="9" height="2.6" fill="#c00"/>
|
||||
<line x1="344.0" y1="427.7" x2="344.0" y2="443.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="432.8" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="422.6" x2="359.0" y2="437.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="427.7" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="419.2" x2="374.0" y2="432.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="424.3" width="9" height="3.4" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="414.1" x2="389.0" y2="429.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="419.2" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="404.0" y1="410.7" x2="404.0" y2="424.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="415.8" width="9" height="3.4" fill="#188a3e"/>
|
||||
<line x1="419.0" y1="407.3" x2="419.0" y2="419.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="411.6" width="9" height="4.2" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="401.4" x2="434.0" y2="416.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="406.5" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="395.4" x2="449.0" y2="411.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="400.5" width="9" height="5.9" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="389.5" x2="464.0" y2="405.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="394.6" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="384.4" x2="479.0" y2="399.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="389.5" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="494.0" y1="378.4" x2="494.0" y2="394.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="383.5" width="9" height="5.9" fill="#188a3e"/>
|
||||
<line x1="509.0" y1="372.5" x2="509.0" y2="388.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="377.6" width="9" height="6.0" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="367.4" x2="524.0" y2="382.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="372.5" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="539.0" y1="367.4" x2="539.0" y2="381.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="534.5" y="372.5" width="9" height="3.4" fill="#c00"/>
|
||||
<line x1="554.0" y1="364.0" x2="554.0" y2="381.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="549.5" y="369.1" width="9" height="6.8" fill="#188a3e"/>
|
||||
<line x1="569.0" y1="358.9" x2="569.0" y2="374.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="564.5" y="364.0" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="584.0" y1="352.9" x2="584.0" y2="369.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="579.5" y="358.0" width="9" height="5.9" fill="#188a3e"/>
|
||||
<line x1="599.0" y1="347.8" x2="599.0" y2="363.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="594.5" y="352.9" width="9" height="5.1" fill="#188a3e"/>
|
||||
<line x1="24" y1="561.4" x2="706" y2="561.4" stroke="#888"/>
|
||||
<rect x="39.5" y="531.1" width="9" height="30.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="529.4" width="9" height="32.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="532.3" width="9" height="29.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="84.5" y="527.6" width="9" height="33.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="529.9" width="9" height="31.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="532.9" width="9" height="28.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="528.8" width="9" height="32.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="531.1" width="9" height="30.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="526.4" width="9" height="35.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="528.2" width="9" height="33.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="524.7" width="9" height="36.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="521.8" width="9" height="39.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="519.4" width="9" height="42.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="514.8" width="9" height="46.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="508.9" width="9" height="52.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="491.4" width="9" height="70.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="503.1" width="9" height="58.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="294.5" y="519.4" width="9" height="42.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="526.4" width="9" height="35.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="529.4" width="9" height="32.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="339.5" y="527.6" width="9" height="33.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="525.3" width="9" height="36.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="522.9" width="9" height="38.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="520.6" width="9" height="40.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="399.5" y="515.9" width="9" height="45.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="414.5" y="506.0" width="9" height="55.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="510.1" width="9" height="51.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="514.8" width="9" height="46.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="518.3" width="9" height="43.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.5" y="520.6" width="9" height="40.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="489.5" y="521.8" width="9" height="39.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="504.5" y="519.4" width="9" height="42.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="517.1" width="9" height="44.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="534.5" y="520.6" width="9" height="40.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="549.5" y="518.3" width="9" height="43.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="564.5" y="515.9" width="9" height="45.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="579.5" y="512.4" width="9" height="49.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="594.5" y="507.8" width="9" height="53.7" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="485.4" fill="#555">volum</text>
|
||||
<text x="269.0" y="479.4" fill="#c00" text-anchor="middle" style="font-weight:bold">minim absolut 10.63</text>
|
||||
<text x="419.0" y="397.3" fill="#1a5fb4" text-anchor="middle" style="font-weight:bold">M+A</text>
|
||||
<text x="24" y="591" fill="#555">Bottom oct. 2022: entry ~13.80, −23%, ~+25% în ~9 săpt. Traseu și pivoți construiți (cursul nu dă valorile lor).</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
272
gen/strategii/mp2_aplus.py
Normal file
@@ -0,0 +1,272 @@
|
||||
"""MP2 crossing long — setup A+ vs. setup mediocru (două panouri).
|
||||
|
||||
Citește `gen/strategii/mp2_aplus.spec.txt` (TIP: PRET).
|
||||
|
||||
Panoul stânga (A+): SPY, aprilie — downtrend spre suportul quarterly 554.00,
|
||||
bază cu Inverted Head & Shoulders (umăr stâng 566, head 555, umăr drept 568,
|
||||
neckline 578.00, adică și un double bottom la ~554.5), intrare 577.00 pe prima
|
||||
candelă verde după umărul drept, SL 554.40 chiar peste suport, 1R = 22.60,
|
||||
TP1 599.60 (1:1).
|
||||
|
||||
Panoul dreapta (Mediocru): integral construit (cifre rotunde), NU din
|
||||
material — range 100.00–110.00, crossing în mijlocul range-ului la 105.00
|
||||
fără niciun pivot/suport dedesubte, intrare 105.00, SL 103.00, TP1 107.00.
|
||||
Eticheta „exemplu construit, nu date reale" e cerută explicit de spec.
|
||||
|
||||
Două `Chart`-uri randate separat, lipite într-un SVG wrapper: fiecare panou
|
||||
are scara lui, deci coordonatele rămân corecte prin construcție.
|
||||
|
||||
Rulează: python3 gen/strategii/mp2_aplus.py
|
||||
Scrie: gen/strategii/mp2_aplus.svg
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Candle, Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "mp2_aplus.svg"
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# ============================================================ PANOUL A+ (33 bare)
|
||||
# 12 bare de cădere, 10 bare de bază (IHS), candela de intrare, 10 bare de urcare.
|
||||
ENTRY_A = 22 # prima candelă verde după umărul drept
|
||||
LS_A, HEAD_A, RS_A = 14, 17, 20 # umăr stâng / head / umăr drept
|
||||
SUP_A, SL_A, NECK_A, TP1_A = 554.00, 554.40, 578.00, 599.60
|
||||
|
||||
CLOSES_A = [
|
||||
596.0, 592.5, 589.0, 585.5, 582.5, 579.5, 576.5, 573.5, 571.0, 568.5, 566.5, 564.0, # 0-11 cădere
|
||||
561.0, 559.5, # 12-13
|
||||
566.0, # 14 umăr stâng (566)
|
||||
561.5, # 15
|
||||
555.8, 555.0, # 16-17 double bottom + head (555)
|
||||
558.5, 563.0, # 18-19
|
||||
568.0, # 20 umăr drept (568)
|
||||
566.5, # 21 pullback
|
||||
577.0, # 22 INTRARE 577.00
|
||||
580.5, 583.5, 585.0, 583.5, 587.0, 590.0, 592.0, 594.0, 596.0, 597.5, # 23-32 urcare
|
||||
]
|
||||
assert len(CLOSES_A) == 33, len(CLOSES_A)
|
||||
|
||||
WICKS_A = [(0.30, 0.30)] * 33
|
||||
WICKS_A[LS_A] = (0.00, 0.30) # high exact 566.00
|
||||
WICKS_A[16] = (0.30, 1.20) # low 554.60 (primul picior al double bottom)
|
||||
WICKS_A[HEAD_A] = (0.30, 0.55) # low 554.45, deasupra suportului 554.00
|
||||
WICKS_A[RS_A] = (0.00, 0.30) # high exact 568.00
|
||||
WICKS_A[ENTRY_A] = (0.25, 0.25)
|
||||
|
||||
VOLS_A = [58, 56, 54, 52, 50, 48, 47, 45, 44, 42, 41, 40,
|
||||
38, 44, 52, 46, 61, 66, 58, 54, 62, 50,
|
||||
88,
|
||||
74, 70, 66, 61, 68, 63, 59, 57, 55, 60]
|
||||
|
||||
cds_a = from_closes(CLOSES_A, wicks=WICKS_A, vols=VOLS_A, start=598.0)
|
||||
|
||||
# ============================================================ PANOUL MEDIOCRU (28 bare)
|
||||
# Integral construit. Range 100.00–110.00, crossing în mijloc la 105.00.
|
||||
ENTRY_M = 7
|
||||
SL_M, TP1_M = 103.00, 107.00
|
||||
RANGE_LO, RANGE_HI = 100.00, 110.00
|
||||
|
||||
CLOSES_M = [
|
||||
100.5, 101.1, 101.8, 102.5, 103.2, 103.9, 104.6, # 0-6 urcare spre 105
|
||||
105.0, # 7 crossing / intrare 105.00
|
||||
105.8, 106.5, 107.0, 106.2, 104.8, 103.4, 102.0, # 8-14 oscilație în range
|
||||
103.0, 104.5, 106.0, 107.5, 108.8, 109.5, 108.6, # 15-21
|
||||
107.2, 105.8, 104.4, 103.2, 104.5, 106.0, # 22-27
|
||||
]
|
||||
assert len(CLOSES_M) == 28, len(CLOSES_M)
|
||||
|
||||
WICKS_M = [(0.30, 0.30)] * 28
|
||||
WICKS_M[ENTRY_M] = (0.30, 0.20)
|
||||
WICKS_M[21] = (0.30, 0.30) # vârf 109.80
|
||||
|
||||
VOLS_M = [42, 44, 46, 48, 50, 52, 55, 72,
|
||||
64, 60, 58, 56, 60, 54, 52,
|
||||
50, 53, 56, 58, 62, 60, 58,
|
||||
55, 52, 50, 48, 51, 54]
|
||||
|
||||
cds_m = from_closes(CLOSES_M, wicks=WICKS_M, vols=VOLS_M, start=100.5)
|
||||
|
||||
# ============================================================ assert-urile din spec
|
||||
entry_a = cds_a[ENTRY_A].c
|
||||
# A+: intrare 577.00
|
||||
assert abs(entry_a - 577.00) < 0.05, f"intrare A+ = {entry_a}, aștept 577.00"
|
||||
# A+: SL 554.40, peste suportul quarterly 554.00 (nu sub el)
|
||||
assert abs(SL_A - 554.40) < 0.05 and SL_A > SUP_A, f"SL A+ {SL_A} vs suport {SUP_A}"
|
||||
# A+: 1R = 22.60
|
||||
r_a = entry_a - SL_A
|
||||
assert abs(r_a - 22.60) < 0.05, f"1R A+ = {r_a}, aștept 22.60"
|
||||
# A+: head 555 < umărul stâng 566 și < umărul drept 568
|
||||
head_a, ls_a, rs_a = cds_a[HEAD_A].c, cds_a[LS_A].h, cds_a[RS_A].h
|
||||
assert head_a < ls_a and head_a < rs_a, f"head {head_a} vs umeri {ls_a}/{rs_a}"
|
||||
# A+: TP1 = entry + 1R = 599.60
|
||||
assert abs(TP1_A - (entry_a + r_a)) < 0.05, f"TP1 A+ {TP1_A}, entry+1R {entry_a + r_a:.2f}"
|
||||
# suplimentar: structura promisă de spec
|
||||
assert cds_a[HEAD_A].l > SUP_A, f"low head {cds_a[HEAD_A].l} a spart suportul {SUP_A}"
|
||||
assert cds_a[HEAD_A].l == min(c.l for c in cds_a), "head-ul nu e minimul seriei"
|
||||
assert CLOSES_A[0] - CLOSES_A[HEAD_A] > 35, "downtrendul A+ e prea mic"
|
||||
assert entry_a > cds_a[RS_A].c, "intrarea nu e deasupra umărului drept"
|
||||
|
||||
# Mediocru: intrare 105.00, SL 103.00, TP1 107.00
|
||||
entry_m = cds_m[ENTRY_M].c
|
||||
assert abs(entry_m - 105.00) < 0.05, f"intrare mediocru = {entry_m}, aștept 105.00"
|
||||
assert abs(SL_M - 103.00) < 0.05 and abs(TP1_M - 107.00) < 0.05, "SL/TP mediocru greșite"
|
||||
# Mediocru: niciun nivel de suport între SL 103.00 și intrarea 105.00
|
||||
levels_m = [RANGE_LO, RANGE_HI, entry_m, SL_M, TP1_M]
|
||||
assert not any(SL_M < p < entry_m for p in levels_m), \
|
||||
f"există suport între {SL_M} și {entry_m}: {levels_m}"
|
||||
# Mediocru: oscilația stă în range
|
||||
assert all(RANGE_LO <= c.c <= RANGE_HI for c in cds_m), "bare în afara range-ului"
|
||||
assert min(c.l for c in cds_m) >= RANGE_LO, "low sub range"
|
||||
|
||||
print(f"A+ : intrare {entry_a:.2f} SL {SL_A:.2f} 1R {r_a:.2f} "
|
||||
f"head {head_a:.0f} < umeri {ls_a:.0f}/{rs_a:.0f} TP1 {TP1_A:.2f}")
|
||||
print(f"MED: intrare {entry_m:.2f} SL {SL_M:.2f} TP1 {TP1_M:.2f} "
|
||||
f"niveluri între SL și intrare: {[p for p in levels_m if SL_M < p < entry_m]}")
|
||||
|
||||
# ============================================================ desenele celor două panouri
|
||||
PW = 560
|
||||
ch_a = Chart(
|
||||
candles=cds_a,
|
||||
levels=[Level(SUP_A, f"suport quarterly {SUP_A:.2f}", GRAY, label_at="left"),
|
||||
Level(SL_A, f"SL {SL_A:.2f}", RED, label_at="right"),
|
||||
Level(entry_a, f"intrare {entry_a:.2f}", GREEN, label_at="left"),
|
||||
Level(NECK_A, f"neckline {NECK_A:.2f}", ORANGE, label_at="right"),
|
||||
Level(TP1_A, f"TP1 {TP1_A:.2f} = 1:1", BLUE, label_at="right")],
|
||||
marks=[Mark(LS_A, f"umăr stâng {ls_a:.0f}", GRAY, "above", dy=-4),
|
||||
Mark(HEAD_A, f"head {head_a:.0f} / double bottom", RED, "below", dy=8),
|
||||
Mark(RS_A, f"umăr drept {rs_a:.0f}", GRAY, "above", dy=-4),
|
||||
Mark(ENTRY_A, "crossing", GREEN, "above", dy=-6)],
|
||||
scale=7.5, gap=15, body_w=9, left=44, width=PW, vol_h=64,
|
||||
vol_label="volum", title="A+ — crossing la confluență (SPY, aprilie)",
|
||||
trend="down",
|
||||
caption="Confluență: suport 554.00 + double bottom 554.45 + neckline 578.00",
|
||||
)
|
||||
ch_m = Chart(
|
||||
candles=cds_m,
|
||||
levels=[Level(RANGE_HI, f"range {RANGE_HI:.2f}", GRAY, label_at="right"),
|
||||
Level(RANGE_LO, f"range {RANGE_LO:.2f}", GRAY, label_at="left"),
|
||||
Level(SL_M, f"SL {SL_M:.2f}", RED, label_at="right"),
|
||||
Level(entry_m, f"intrare {entry_m:.2f}", GREEN, label_at="right"),
|
||||
Level(TP1_M, f"TP1 {TP1_M:.2f}", BLUE, label_at="right")],
|
||||
marks=[Mark(ENTRY_M, "crossing (mijloc)", GREEN, "above", dy=-4)],
|
||||
scale=34.0, gap=15, body_w=9, left=44, width=PW, vol_h=64,
|
||||
vol_label="volum", title="Mediocru — crossing în mijlocul range-ului (construit)",
|
||||
caption="exemplu construit, nu date reale — SL nu stă pe niciun pivot",
|
||||
)
|
||||
|
||||
# ============================================================ wrapper: două panouri alăturate
|
||||
def inner(svg: str) -> str:
|
||||
"""Conținutul dintre tag-urile <svg> ale unui panou, fără elementul rădăcină."""
|
||||
return svg[svg.index(">") + 1:svg.rindex("</svg>")]
|
||||
|
||||
|
||||
def vb_height(svg: str) -> float:
|
||||
m = re.search(r'viewBox="0 0 [\d.]+ ([\d.]+)"', svg)
|
||||
assert m, "nu găsesc viewBox în panoul randat"
|
||||
return float(m.group(1))
|
||||
|
||||
|
||||
PANEL_GAP, PAD = 24, 8
|
||||
TITLE_H, FOOTER_H = 30, 62
|
||||
svg_a, svg_m = ch_a.render(), ch_m.render()
|
||||
HA, HM = vb_height(svg_a), vb_height(svg_m)
|
||||
assert svg_a.count("\n\n") == 0 and svg_m.count("\n\n") == 0, "linie goală în panou"
|
||||
|
||||
TOTAL_W = PAD + PW + PANEL_GAP + PW + PAD
|
||||
TOTAL_H = PAD + TITLE_H + max(HA, HM) + FOOTER_H + PAD
|
||||
OFF_A = (PAD, PAD + TITLE_H)
|
||||
OFF_M = (PAD + PW + PANEL_GAP, PAD + TITLE_H)
|
||||
|
||||
TITLU = "MP² crossing long — setup A+ vs. setup mediocru (aceeași strategie)"
|
||||
LEGENDA = [
|
||||
"Legendă: ce separă cele două sunt nivelurile independente dedesubte "
|
||||
"(suport quarterly + double bottom + neckline),",
|
||||
"nu un filtru pus în scaner. Panoul din dreapta e exemplu construit, nu date reale.",
|
||||
"Surse A+: 09_QA/01_2026-08-25.md:389-394, 485-490, 501-507.",
|
||||
]
|
||||
|
||||
wrapper = (
|
||||
f'<svg viewBox="0 0 {TOTAL_W} {TOTAL_H:.0f}" width="{TOTAL_W}" height="{TOTAL_H:.0f}" '
|
||||
f'xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">'
|
||||
f'<text x="{TOTAL_W / 2:.0f}" y="20" fill="#333" text-anchor="middle" '
|
||||
f'style="font-weight:bold">{TITLU}</text>'
|
||||
f'<g transform="translate({OFF_A[0]},{OFF_A[1]})">{inner(svg_a)}</g>'
|
||||
f'<g transform="translate({OFF_M[0]},{OFF_M[1]})">{inner(svg_m)}</g>'
|
||||
+ "".join(
|
||||
f'<text x="{TOTAL_W / 2:.0f}" y="{TOTAL_H - FOOTER_H + 18 + 18 * i:.0f}" '
|
||||
f'fill="#555" text-anchor="middle">{ln}</text>'
|
||||
for i, ln in enumerate(LEGENDA)
|
||||
)
|
||||
+ '</svg>'
|
||||
)
|
||||
|
||||
# ============================================================ verificarea pe SVG-ul final
|
||||
def texts(svg: str):
|
||||
"""Toate (x, y, text-anchor, text) din elementele <text> ale unui fragment."""
|
||||
out = []
|
||||
for m in re.finditer(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', svg):
|
||||
x, y, attrs, t = float(m.group(1)), float(m.group(2)), m.group(3), m.group(4)
|
||||
if t.startswith("<"):
|
||||
continue
|
||||
am = re.search(r'text-anchor="(\w+)"', attrs)
|
||||
out.append((x, y, am.group(1) if am else "start", t))
|
||||
return out
|
||||
|
||||
|
||||
def span(x: float, anchor: str, t: str) -> tuple[float, float]:
|
||||
"""Intervalul orizontal aproximativ al unui text, față de ancora lui."""
|
||||
w = len(t) * 12 * 0.56
|
||||
left = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
|
||||
return left, left + w
|
||||
|
||||
|
||||
assert wrapper.count("\n\n") == 0, "linie goală în SVG"
|
||||
|
||||
# fiecare text (după translatarea panoului) e în întregime în interiorul pânzei
|
||||
all_txt = []
|
||||
for frag, (ox, oy) in ((inner(svg_a), OFF_A), (inner(svg_m), OFF_M)):
|
||||
for x, y, anchor, t in texts(frag):
|
||||
gx, gy = x + ox, y + oy
|
||||
gl, gr = gx + span(0, anchor, t)[0], gx + span(0, anchor, t)[1]
|
||||
assert ox <= gl and gr <= ox + PW, f"text iese din panou: {t!r} ({gl:.0f}..{gr:.0f})"
|
||||
assert -1 <= gl and gr <= TOTAL_W + 1, f"text ieșit pe orizontală: {t!r}"
|
||||
assert 0 <= gy <= TOTAL_H, f"text ieșit pe verticală: {t!r} ({gy:.0f})"
|
||||
all_txt.append((gx, gy, anchor, t))
|
||||
# textele din wrapper (titlu + legenda), în coordonate globale
|
||||
wtxt = [(TOTAL_W / 2, 20.0, "middle", TITLU)]
|
||||
wtxt += [(TOTAL_W / 2, TOTAL_H - FOOTER_H + 18 + 18 * i, "middle", ln)
|
||||
for i, ln in enumerate(LEGENDA)]
|
||||
for x, y, anchor, t in wtxt:
|
||||
gl, gr = span(x, anchor, t)
|
||||
assert -1 <= gl and gr <= TOTAL_W + 1, f"text din wrapper iese din pânză: {t!r}"
|
||||
assert 0 <= y <= TOTAL_H, f"text din wrapper în afara înălțimii: {t!r}"
|
||||
all_txt.append((x, y, anchor, t))
|
||||
assert len(all_txt) >= 12, f"prea puține etichete: {len(all_txt)}"
|
||||
|
||||
# nu se suprapun două texte pe aceeași poziție sau pe rândul apropiat
|
||||
for i in range(len(all_txt)):
|
||||
xi, yi, ai, si = all_txt[i]
|
||||
li, ri = (xi + span(0, ai, si)[0], xi + span(0, ai, si)[1])
|
||||
for j in range(i + 1, len(all_txt)):
|
||||
xj, yj, aj, sj = all_txt[j]
|
||||
lj, rj = xj + span(0, aj, sj)[0], xj + span(0, aj, sj)[1]
|
||||
if abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
# etichetele cerute de spec există în desen
|
||||
for cheie in ("suport quarterly 554.00", f"SL {SL_A:.2f}", "intrare 577.00",
|
||||
"neckline 578.00", "TP1 599.60 = 1:1",
|
||||
f"umăr stâng {ls_a:.0f}", f"head {head_a:.0f}", f"umăr drept {rs_a:.0f}",
|
||||
"A+ — crossing la confluență (SPY, aprilie)",
|
||||
"Mediocru — crossing în mijlocul range-ului (construit)",
|
||||
"range 110.00", "range 100.00", "SL 103.00", "intrare 105.00", "TP1 107.00",
|
||||
"crossing (mijloc)", "exemplu construit, nu date reale"):
|
||||
assert cheie in wrapper, f"lipsește din desen: {cheie!r}"
|
||||
|
||||
OUT.write_text(wrapper, encoding="utf-8")
|
||||
print(f"scris {OUT.name}: {TOTAL_W}x{TOTAL_H:.0f}, "
|
||||
f"{len(all_txt)} etichete, verificare OK")
|
||||
221
gen/strategii/mp2_aplus.svg
Normal file
@@ -0,0 +1,221 @@
|
||||
<svg viewBox="0 0 1160 694" width="1160" height="694" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif"><text x="580" y="20" fill="#333" text-anchor="middle" style="font-weight:bold">MP² crossing long — setup A+ vs. setup mediocru (aceeași strategie)</text><g transform="translate(8,38)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">A+ — crossing la confluență (SPY, aprilie)</text>
|
||||
<line x1="24" y1="454.0" x2="546" y2="454.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="448.0" fill="#777">suport quarterly 554.00</text>
|
||||
<line x1="24" y1="451.0" x2="546" y2="451.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="445.0" fill="#c00" text-anchor="end">SL 554.40</text>
|
||||
<line x1="24" y1="281.5" x2="546" y2="281.5" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="275.5" fill="#188a3e">intrare 577.00</text>
|
||||
<line x1="24" y1="274.0" x2="546" y2="274.0" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="268.0" fill="#b06000" text-anchor="end">neckline 578.00</text>
|
||||
<line x1="24" y1="112.0" x2="546" y2="112.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="106.0" fill="#1a5fb4" text-anchor="end">TP1 599.60 = 1:1</text>
|
||||
<polyline points="24,75.8 36,121.8" fill="none" stroke="#999" stroke-width="2"/>
|
||||
<text x="24" y="63.8" fill="#999">downtrend anterior</text>
|
||||
<line x1="44.0" y1="121.8" x2="44.0" y2="141.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="124.0" width="9" height="15.0" fill="#c00"/>
|
||||
<line x1="59.0" y1="136.8" x2="59.0" y2="167.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="139.0" width="9" height="26.2" fill="#c00"/>
|
||||
<line x1="74.0" y1="163.0" x2="74.0" y2="193.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="165.3" width="9" height="26.2" fill="#c00"/>
|
||||
<line x1="89.0" y1="189.3" x2="89.0" y2="220.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="191.5" width="9" height="26.2" fill="#c00"/>
|
||||
<line x1="104.0" y1="215.5" x2="104.0" y2="242.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="217.8" width="9" height="22.5" fill="#c00"/>
|
||||
<line x1="119.0" y1="238.0" x2="119.0" y2="265.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="240.3" width="9" height="22.5" fill="#c00"/>
|
||||
<line x1="134.0" y1="260.5" x2="134.0" y2="287.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="262.8" width="9" height="22.5" fill="#c00"/>
|
||||
<line x1="149.0" y1="283.0" x2="149.0" y2="310.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="285.3" width="9" height="22.5" fill="#c00"/>
|
||||
<line x1="164.0" y1="305.5" x2="164.0" y2="328.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="307.8" width="9" height="18.8" fill="#c00"/>
|
||||
<line x1="179.0" y1="324.3" x2="179.0" y2="347.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="326.5" width="9" height="18.8" fill="#c00"/>
|
||||
<line x1="194.0" y1="343.0" x2="194.0" y2="362.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="345.3" width="9" height="15.0" fill="#c00"/>
|
||||
<line x1="209.0" y1="358.0" x2="209.0" y2="381.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="360.3" width="9" height="18.8" fill="#c00"/>
|
||||
<line x1="224.0" y1="376.8" x2="224.0" y2="403.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="379.0" width="9" height="22.5" fill="#c00"/>
|
||||
<line x1="239.0" y1="399.3" x2="239.0" y2="415.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="401.5" width="9" height="11.2" fill="#c00"/>
|
||||
<line x1="254.0" y1="364.0" x2="254.0" y2="415.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="364.0" width="9" height="48.8" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="361.8" x2="269.0" y2="400.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="364.0" width="9" height="33.8" fill="#c00"/>
|
||||
<line x1="284.0" y1="395.5" x2="284.0" y2="449.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="397.8" width="9" height="42.8" fill="#c00"/>
|
||||
<line x1="299.0" y1="438.3" x2="299.0" y2="450.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="440.5" width="9" height="6.0" fill="#c00"/>
|
||||
<line x1="314.0" y1="418.0" x2="314.0" y2="448.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="420.3" width="9" height="26.2" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="384.3" x2="329.0" y2="422.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="386.5" width="9" height="33.8" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="349.0" x2="344.0" y2="388.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="349.0" width="9" height="37.5" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="346.8" x2="359.0" y2="362.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="349.0" width="9" height="11.2" fill="#c00"/>
|
||||
<line x1="374.0" y1="279.6" x2="374.0" y2="362.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="281.5" width="9" height="78.8" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="253.0" x2="389.0" y2="283.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="255.3" width="9" height="26.2" fill="#188a3e"/>
|
||||
<line x1="404.0" y1="230.5" x2="404.0" y2="257.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="232.8" width="9" height="22.5" fill="#188a3e"/>
|
||||
<line x1="419.0" y1="219.3" x2="419.0" y2="235.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="221.5" width="9" height="11.2" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="219.3" x2="434.0" y2="235.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="221.5" width="9" height="11.2" fill="#c00"/>
|
||||
<line x1="449.0" y1="204.3" x2="449.0" y2="235.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="206.5" width="9" height="26.2" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="181.8" x2="464.0" y2="208.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="184.0" width="9" height="22.5" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="166.8" x2="479.0" y2="186.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="169.0" width="9" height="15.0" fill="#188a3e"/>
|
||||
<line x1="494.0" y1="151.8" x2="494.0" y2="171.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="154.0" width="9" height="15.0" fill="#188a3e"/>
|
||||
<line x1="509.0" y1="136.8" x2="509.0" y2="156.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="139.0" width="9" height="15.0" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="125.5" x2="524.0" y2="141.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="127.8" width="9" height="11.2" fill="#188a3e"/>
|
||||
<line x1="24" y1="544.0" x2="546" y2="544.0" stroke="#888"/>
|
||||
<rect x="39.5" y="501.8" width="9" height="42.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="503.3" width="9" height="40.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="504.7" width="9" height="39.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="84.5" y="506.2" width="9" height="37.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="99.5" y="507.6" width="9" height="36.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="509.1" width="9" height="34.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="509.8" width="9" height="34.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="144.5" y="511.3" width="9" height="32.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="512.0" width="9" height="32.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="513.5" width="9" height="30.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="189.5" y="514.2" width="9" height="29.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="514.9" width="9" height="29.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="516.4" width="9" height="27.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="512.0" width="9" height="32.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="506.2" width="9" height="37.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="264.5" y="510.5" width="9" height="33.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="279.5" y="499.6" width="9" height="44.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="496.0" width="9" height="48.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="501.8" width="9" height="42.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="504.7" width="9" height="39.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="498.9" width="9" height="45.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="507.6" width="9" height="36.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="369.5" y="480.0" width="9" height="64.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="490.2" width="9" height="53.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="399.5" y="493.1" width="9" height="50.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="414.5" y="496.0" width="9" height="48.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="499.6" width="9" height="44.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="444.5" y="494.5" width="9" height="49.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="498.2" width="9" height="45.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.5" y="501.1" width="9" height="42.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="489.5" y="502.5" width="9" height="41.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="504.5" y="504.0" width="9" height="40.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="500.4" width="9" height="43.6" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="474.0" fill="#555">volum</text>
|
||||
<text x="254.0" y="350.0" fill="#777" text-anchor="middle" style="font-weight:bold">umăr stâng 566</text>
|
||||
<text x="299.0" y="468.0" fill="#c00" text-anchor="middle" style="font-weight:bold">head 555 / double bottom</text>
|
||||
<text x="344.0" y="335.0" fill="#777" text-anchor="middle" style="font-weight:bold">umăr drept 568</text>
|
||||
<text x="374.0" y="263.6" fill="#188a3e" text-anchor="middle" style="font-weight:bold">crossing</text>
|
||||
<text x="24" y="574" fill="#555">Confluență: suport 554.00 + double bottom 554.45 + neckline 578.00</text>
|
||||
</g><g transform="translate(592,38)">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">Mediocru — crossing în mijlocul range-ului (construit)</text>
|
||||
<line x1="24" y1="56.0" x2="546" y2="56.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="50.0" fill="#777" text-anchor="end">range 110.00</text>
|
||||
<line x1="24" y1="396.0" x2="546" y2="396.0" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="390.0" fill="#777">range 100.00</text>
|
||||
<line x1="24" y1="294.0" x2="546" y2="294.0" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="288.0" fill="#c00" text-anchor="end">SL 103.00</text>
|
||||
<line x1="24" y1="226.0" x2="546" y2="226.0" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="220.0" fill="#188a3e" text-anchor="end">intrare 105.00</text>
|
||||
<line x1="24" y1="158.0" x2="546" y2="158.0" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="532" y="152.0" fill="#1a5fb4" text-anchor="end">TP1 107.00</text>
|
||||
<line x1="44.0" y1="368.8" x2="44.0" y2="389.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="379.0" width="9" height="1.6" fill="#188a3e"/>
|
||||
<line x1="59.0" y1="348.4" x2="59.0" y2="389.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="358.6" width="9" height="20.4" fill="#188a3e"/>
|
||||
<line x1="74.0" y1="324.6" x2="74.0" y2="368.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="334.8" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="300.8" x2="89.0" y2="345.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="311.0" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="104.0" y1="277.0" x2="104.0" y2="321.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="287.2" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="253.2" x2="119.0" y2="297.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="263.4" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="229.4" x2="134.0" y2="273.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="239.6" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="215.8" x2="149.0" y2="246.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="226.0" width="9" height="13.6" fill="#188a3e"/>
|
||||
<line x1="164.0" y1="188.6" x2="164.0" y2="236.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="198.8" width="9" height="27.2" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="164.8" x2="179.0" y2="209.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="175.0" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="147.8" x2="194.0" y2="185.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="158.0" width="9" height="17.0" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="147.8" x2="209.0" y2="195.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="158.0" width="9" height="27.2" fill="#c00"/>
|
||||
<line x1="224.0" y1="175.0" x2="224.0" y2="243.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="185.2" width="9" height="47.6" fill="#c00"/>
|
||||
<line x1="239.0" y1="222.6" x2="239.0" y2="290.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="232.8" width="9" height="47.6" fill="#c00"/>
|
||||
<line x1="254.0" y1="270.2" x2="254.0" y2="338.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="280.4" width="9" height="47.6" fill="#c00"/>
|
||||
<line x1="269.0" y1="283.8" x2="269.0" y2="338.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="294.0" width="9" height="34.0" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="232.8" x2="284.0" y2="304.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="243.0" width="9" height="51.0" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="181.8" x2="299.0" y2="253.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="192.0" width="9" height="51.0" fill="#188a3e"/>
|
||||
<line x1="314.0" y1="130.8" x2="314.0" y2="202.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="141.0" width="9" height="51.0" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="86.6" x2="329.0" y2="151.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="96.8" width="9" height="44.2" fill="#188a3e"/>
|
||||
<line x1="344.0" y1="62.8" x2="344.0" y2="107.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="73.0" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="62.8" x2="359.0" y2="113.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="73.0" width="9" height="30.6" fill="#c00"/>
|
||||
<line x1="374.0" y1="93.4" x2="374.0" y2="161.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="103.6" width="9" height="47.6" fill="#c00"/>
|
||||
<line x1="389.0" y1="141.0" x2="389.0" y2="209.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="151.2" width="9" height="47.6" fill="#c00"/>
|
||||
<line x1="404.0" y1="188.6" x2="404.0" y2="256.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="198.8" width="9" height="47.6" fill="#c00"/>
|
||||
<line x1="419.0" y1="236.2" x2="419.0" y2="297.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="246.4" width="9" height="40.8" fill="#c00"/>
|
||||
<line x1="434.0" y1="232.8" x2="434.0" y2="297.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="243.0" width="9" height="44.2" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="181.8" x2="449.0" y2="253.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="192.0" width="9" height="51.0" fill="#188a3e"/>
|
||||
<line x1="24" y1="486.0" x2="546" y2="486.0" stroke="#888"/>
|
||||
<rect x="39.5" y="448.7" width="9" height="37.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="54.5" y="446.9" width="9" height="39.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="69.5" y="445.1" width="9" height="40.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="84.5" y="443.3" width="9" height="42.7" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="99.5" y="441.6" width="9" height="44.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="114.5" y="439.8" width="9" height="46.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="129.5" y="437.1" width="9" height="48.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="422.0" width="9" height="64.0" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="159.5" y="429.1" width="9" height="56.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="174.5" y="432.7" width="9" height="53.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="189.5" y="434.4" width="9" height="51.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="204.5" y="436.2" width="9" height="49.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="432.7" width="9" height="53.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="438.0" width="9" height="48.0" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="439.8" width="9" height="46.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="264.5" y="441.6" width="9" height="44.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="438.9" width="9" height="47.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="294.5" y="436.2" width="9" height="49.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="309.5" y="434.4" width="9" height="51.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="324.5" y="430.9" width="9" height="55.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="339.5" y="432.7" width="9" height="53.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="434.4" width="9" height="51.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="369.5" y="437.1" width="9" height="48.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="384.5" y="439.8" width="9" height="46.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.5" y="441.6" width="9" height="44.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="414.5" y="443.3" width="9" height="42.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="429.5" y="440.7" width="9" height="45.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="438.0" width="9" height="48.0" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="416.0" fill="#555">volum</text>
|
||||
<text x="149.0" y="201.8" fill="#188a3e" text-anchor="middle" style="font-weight:bold">crossing (mijloc)</text>
|
||||
<text x="24" y="516" fill="#555">exemplu construit, nu date reale — SL nu stă pe niciun pivot</text>
|
||||
</g><text x="580" y="650" fill="#555" text-anchor="middle">Legendă: ce separă cele două sunt nivelurile independente dedesubte (suport quarterly + double bottom + neckline),</text><text x="580" y="668" fill="#555" text-anchor="middle">nu un filtru pus în scaner. Panoul din dreapta e exemplu construit, nu date reale.</text><text x="580" y="686" fill="#555" text-anchor="middle">Surse A+: 09_QA/01_2026-08-25.md:389-394, 485-490, 501-507.</text></svg>
|
||||
|
After Width: | Height: | Size: 18 KiB |
228
gen/strategii/mp2_crossing.py
Normal file
@@ -0,0 +1,228 @@
|
||||
#!/usr/bin/env python3
|
||||
"""MP² — crossing vs. buy the dip: două panouri una sub alta.
|
||||
|
||||
Citește `gen/strategii/mp2_crossing.spec.txt` și scrie
|
||||
`gen/strategii/mp2_crossing.svg`.
|
||||
|
||||
Aceleași două linii de pivot pe ambele panouri:
|
||||
- pivot weekly (cel MARE) = 62.00, orizontal;
|
||||
- pivot daily (cel MIC), stepped — se recalculează zilnic și „sare" pe nivel nou.
|
||||
|
||||
Sus, „DA — crossing (+2)": pivotul daily urcă din 61.70 și traversează 62.00 în
|
||||
sus (marcat cu cerc: „crossing: 61.70 → 62.20"). Trigger = prima candelă verde
|
||||
după crossing care închide peste pivotul daily (bara 11, close 62.40).
|
||||
|
||||
Jos, „NU — continuare/buy the dip (+1)": pivotul daily e deja deasupra lui 62.00
|
||||
și rămâne acolo; prețul face închideri peste el, dar nimic nu s-a re-încrucișat,
|
||||
deci semnalele ulterioare sunt buy the dip, nu crossing.
|
||||
|
||||
Cifrele (62.00 / 61.70 / 62.20) sunt din `summaries/09_QA/09_2025-12-09.md:69-83`
|
||||
și distincția crossing vs. buy the dip din `summaries/09_QA/16_2025-10-07.md:35-43`.
|
||||
Seriile de preț sunt construite (open = close-ul barei anterioare) ca nivelurile
|
||||
afirmate să iasă exact din ele.
|
||||
|
||||
Rulare: python3 gen/strategii/mp2_crossing.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).with_suffix(".svg")
|
||||
W = 480
|
||||
VGAP = 44
|
||||
SCALE = 95.0 # px / unitate, aceeași pe ambele panouri
|
||||
WEEKLY = 62.00
|
||||
|
||||
GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000"
|
||||
|
||||
# ---------------------------------------------------------------- panoul DA
|
||||
# pivotul daily: 61.70 (sub 62.00) până la bara 9, apoi 62.20 (peste 62.00)
|
||||
PDA = [61.70] * 10 + [62.20] * 12
|
||||
CLOSES_DA = [61.30, 61.15, 61.35, 61.20, 61.45, 61.30, 61.55, 61.40,
|
||||
61.60, 61.75, # 0-9: sub pivot 61.70/62.00
|
||||
62.05, # 10: pivotul sare la 62.20; close inca sub el
|
||||
62.40, # 11: trigger — verde, close peste 62.20
|
||||
62.55, 62.50, 62.65, 62.60, 62.75, 62.70,
|
||||
62.85, 62.80, 62.90, 62.95] # 12-21: urca peste pivot
|
||||
WICKS_DA = [(0.08, 0.10)] * len(CLOSES_DA)
|
||||
WICKS_DA[10] = (0.16, 0.10) # bara crossing-ului
|
||||
WICKS_DA[11] = (0.22, 0.10) # bara de trigger
|
||||
assert len(CLOSES_DA) == len(PDA) == 22
|
||||
|
||||
# ---------------------------------------------------------------- panoul NU
|
||||
# pivotul daily e deja deasupra lui 62.00 pe toată durata (62.30 → 62.20)
|
||||
PNU = [62.30] * 9 + [62.20] * 13
|
||||
CLOSES_NU = [61.55, 61.70, 61.90, 61.80, 62.05,
|
||||
62.35, # 5: primul close peste pivotul daily
|
||||
62.25, 62.45, 62.55,
|
||||
62.40, 62.60, 62.50, 62.70, 62.65, 62.80, 62.75,
|
||||
62.90, 62.85, 63.00, 62.95, 63.10, 63.05]
|
||||
WICKS_NU = [(0.08, 0.10)] * len(CLOSES_NU)
|
||||
assert len(CLOSES_NU) == len(PNU) == 22
|
||||
|
||||
START_DA = CLOSES_DA[0] + 0.12
|
||||
START_NU = CLOSES_NU[0] + 0.12
|
||||
cds_da = from_closes(CLOSES_DA, wicks=WICKS_DA, start=START_DA)
|
||||
cds_nu = from_closes(CLOSES_NU, wicks=WICKS_NU, start=START_NU)
|
||||
|
||||
# ------------------------------------------------------ assert-urile din spec
|
||||
# 1. DA: pivotul daily e < 62.00 înainte de crossing și > 62.00 după
|
||||
cross_da = next(i for i in range(1, len(PDA)) if PDA[i] > WEEKLY)
|
||||
assert cross_da == 10, f"crossing-ul DA e la bara {cross_da}, nu 10"
|
||||
assert all(v < WEEKLY for v in PDA[:cross_da]), "pivot daily DA nu e sub 62.00 înainte de crossing"
|
||||
assert PDA[cross_da - 1] == 61.70 and PDA[cross_da] == 62.20, \
|
||||
f"crossing DA {PDA[cross_da - 1]} -> {PDA[cross_da]}, nu 61.70 -> 62.20"
|
||||
assert all(v > WEEKLY for v in PDA[cross_da:]), "pivot daily DA nu e peste 62.00 după crossing"
|
||||
|
||||
# 2. DA: trigger = prima candelă verde după crossing care închide peste pivotul daily
|
||||
trigger = next(i for i in range(cross_da, len(cds_da))
|
||||
if cds_da[i].c >= cds_da[i].o and cds_da[i].c > PDA[i])
|
||||
assert trigger == 11, f"trigger-ul DA e la bara {trigger}, nu 11"
|
||||
assert cds_da[trigger].c > PDA[trigger], "candela de trigger nu închide peste pivotul daily"
|
||||
assert cds_da[10].c <= PDA[10], "bara crossing-ului nu trebuie să închidă peste pivot"
|
||||
entry = cds_da[trigger].c
|
||||
|
||||
# 3. NU: toate valorile pivot_daily > 62.00 — nimic re-încrucișat
|
||||
assert all(v > WEEKLY for v in PNU), "un pivot daily NU e sub 62.00"
|
||||
assert not any(v < WEEKLY for v in PNU), "există pivot daily NU sub 62.00 => ar fi crossing"
|
||||
assert min(PNU) > WEEKLY and max(PNU) > WEEKLY
|
||||
# prețul face închideri peste pivotul daily, dar pivotul nu cade sub 62.00
|
||||
prim_nu = next(i for i in range(len(cds_nu)) if cds_nu[i].c > PNU[i])
|
||||
assert prim_nu == 5 and cds_nu[prim_nu].c > PNU[prim_nu], "NU nu face close peste pivotul daily"
|
||||
assert min(PNU) == 62.20 and max(PNU) == 62.30, "pivotii NU nu sunt 62.20 / 62.30"
|
||||
|
||||
# 4. liniile sunt „stepped" (au cel puțin un salt), nu drepte
|
||||
assert len(set(PDA)) >= 2 and len(set(PNU)) >= 2, "liniile de pivot nu sunt stepped"
|
||||
|
||||
# --------------------------------------------------- desen: câte un panou
|
||||
def step_points(ch: Chart, vals: list[float]) -> str:
|
||||
"""Polilinie în trepte: orizontal pe palier, salt vertical la schimbare."""
|
||||
pts: list[tuple[float, float]] = []
|
||||
for i, v in enumerate(vals):
|
||||
x, y = ch._cx(i), ch._y(v)
|
||||
if i == 0:
|
||||
pts.append((x, y))
|
||||
elif v != vals[i - 1]:
|
||||
pts.append((x, ch._y(vals[i - 1])))
|
||||
pts.append((x, y))
|
||||
else:
|
||||
pts.append((x, y))
|
||||
return " ".join(f"{x:.1f},{y:.1f}" for x, y in pts)
|
||||
|
||||
|
||||
def render_pivot_panel(cds, pivots, title) -> tuple[str, float, Chart]:
|
||||
"""Randază un panou cu pivot weekly (Level) + pivot daily (polilinie injectată)."""
|
||||
ch = Chart(candles=cds,
|
||||
levels=[Level(WEEKLY, f"pivot weekly (cel MARE) {WEEKLY:.2f}",
|
||||
BLUE, dash="", label_at="left")],
|
||||
marks=[],
|
||||
scale=SCALE, gap=15, body_w=9, left=44, width=W,
|
||||
title=title)
|
||||
svg = ch.render() # fixează _pmax/_y și adaugă +22 pentru titlu
|
||||
inject = [
|
||||
f'<polyline points="{step_points(ch, pivots)}" fill="none" '
|
||||
f'stroke="{ORANGE}" stroke-width="1.8"/>',
|
||||
f'<text x="{W - 10}" y="{ch._y(pivots[-1]) - 6:.1f}" fill="{ORANGE}" '
|
||||
f'text-anchor="end" style="font-weight:bold">pivot daily (cel MIC)</text>',
|
||||
]
|
||||
i = svg.index(">") + 1
|
||||
svg = svg[:i] + "\n " + "\n ".join(inject) + svg[i:]
|
||||
m = re.search(r'viewBox="0 0 (\d+) ([\d.]+)"', svg)
|
||||
body = svg[svg.index(">") + 1:svg.rindex("</svg>")]
|
||||
return body, float(m.group(2)), ch
|
||||
|
||||
|
||||
body_da, h_da, ch_da = render_pivot_panel(
|
||||
cds_da, PDA, "DA — crossing (+2)")
|
||||
|
||||
# marcajul crossing-ului: cerc pe saltul pivotului + eticheta din spec
|
||||
xc = ch_da._cx(cross_da)
|
||||
yc = (ch_da._y(PDA[cross_da - 1]) + ch_da._y(PDA[cross_da])) / 2
|
||||
circle = (f'<circle cx="{xc:.1f}" cy="{yc:.1f}" r="13" fill="none" '
|
||||
f'stroke="{GREEN}" stroke-width="2"/>')
|
||||
cross_txt = (f'<text x="{xc + 16:.1f}" y="{ch_da._y(PDA[cross_da - 1]) + 18:.1f}" '
|
||||
f'fill="{GREEN}" style="font-weight:bold">crossing: 61.70 → 62.20</text>')
|
||||
mark_txt = (f'<text x="{ch_da._cx(trigger):.1f}" y="{ch_da._y(cds_da[trigger].h) - 10:.1f}" '
|
||||
f'fill="{GREEN}" text-anchor="middle" style="font-weight:bold">'
|
||||
f'intrare: trigger (prima candelă verde)</text>')
|
||||
i = body_da.rindex("</text>") + len("</text>") # după candele/marks, ca să fie vizibile
|
||||
body_da = body_da[:i] + "\n " + circle + "\n " + cross_txt + "\n " + mark_txt + body_da[i:]
|
||||
|
||||
# panoul NU: eticheta din spec pe semnalele ulterioare
|
||||
body_nu, h_nu, ch_nu = render_pivot_panel(
|
||||
cds_nu, PNU, "NU — continuare/buy the dip (+1)")
|
||||
body_nu = body_nu.rstrip() + (f'\n <text x="{ch_nu._cx(13):.1f}" y="{h_nu - 12:.1f}" '
|
||||
f'fill="{GRAY}" text-anchor="middle" style="font-weight:bold">'
|
||||
f'semnale ulterioare = BUY THE DIP, nu crossing</text>')
|
||||
|
||||
# ------------------------------------------------- lipirea celor două panouri
|
||||
CAPS = [
|
||||
"Pivot weekly 62.00 = linia orizontală (pivotul MARE).",
|
||||
"Pivot daily (cel MIC) se recalculează zilnic și sare pe nivel nou.",
|
||||
"Crossing = pivotul mic traversează 62.00 în sus.",
|
||||
"Fără re-încrucișare, semnalele ulterioare sunt buy the dip,",
|
||||
"nu crossing.",
|
||||
]
|
||||
cap_top = h_da + VGAP + h_nu + 16
|
||||
H = cap_top + 17 * len(CAPS) + 10
|
||||
|
||||
parts = [
|
||||
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">',
|
||||
f'<g transform="translate(0,0)">{body_da}</g>',
|
||||
f'<g transform="translate(0,{h_da + VGAP:.0f})">{body_nu}</g>',
|
||||
]
|
||||
parts += [f'<text x="24" y="{cap_top + 17 * i:.0f}" fill="#555">{c}</text>'
|
||||
for i, c in enumerate(CAPS)]
|
||||
parts.append("</svg>")
|
||||
svg = "\n ".join(parts)
|
||||
assert "\n\n" not in svg, "linie goală în SVG"
|
||||
|
||||
# ------------------------------------- verificarea citirii: etichete în panză
|
||||
TEXT = re.compile(r'<text x="([-\d.]+)" y="([-\d.]+)"([^>]*)>(.*?)</text>', re.S)
|
||||
|
||||
|
||||
def _texts(body: str, y_off: float, tag: str):
|
||||
out = []
|
||||
for m in TEXT.finditer(body):
|
||||
x, y = float(m.group(1)), float(m.group(2)) + y_off
|
||||
attrs, s = m.group(3), m.group(4)
|
||||
anchor = "start"
|
||||
if 'text-anchor="end"' in attrs:
|
||||
anchor = "end"
|
||||
elif 'text-anchor="middle"' in attrs:
|
||||
anchor = "middle"
|
||||
bold = "bold" in attrs
|
||||
w = len(s) * 12 * (0.62 if bold else 0.52)
|
||||
left = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x)
|
||||
out.append((left, left + w, y, s, tag))
|
||||
return out
|
||||
|
||||
|
||||
labels = _texts(body_da, 0.0, "DA") + _texts(body_nu, h_da + VGAP, "NU")
|
||||
for i, c in enumerate(CAPS): # și caption-urile, care nu-s în panouri
|
||||
labels.append((24, 24 + len(c) * 12 * 0.52, cap_top + 17 * i, c, "CAP"))
|
||||
for left, right, y, s, tag in labels:
|
||||
assert left >= -2 and right <= W + 2, f"[{tag}] text iese din panză: {s!r}"
|
||||
assert 0 <= y <= H, f"[{tag}] text în afara înălțimii: {s!r} (y={y:.0f})"
|
||||
|
||||
for i in range(len(labels)):
|
||||
li, ri, yi, si, ti = labels[i]
|
||||
for j in range(i + 1, len(labels)):
|
||||
lj, rj, yj, sj, tj = labels[j]
|
||||
if ti == tj and abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4:
|
||||
raise AssertionError(f"[{ti}] texte suprapuse: {si!r} / {sj!r}")
|
||||
|
||||
dist = abs(ch_da._y(WEEKLY) - ch_da._y(PDA[cross_da - 1])) # 62.00 - 61.70, în px
|
||||
assert dist > 10, f"distanța 61.70->62.00 = {dist:.1f}px, prea mică"
|
||||
|
||||
print(f"DA: crossing la bara {cross_da} (61.70 -> 62.20), trigger bara {trigger} "
|
||||
f"close {entry:.2f} > pivot {PDA[trigger]:.2f}; bare {len(cds_da)}")
|
||||
print(f"NU: pivot daily {min(PNU):.2f}..{max(PNU):.2f} (> 62.00), primul close "
|
||||
f"peste pivot bara {prim_nu} ({cds_nu[prim_nu].c:.2f}); bare {len(cds_nu)}")
|
||||
print(f"scris {OUT.name} ({W}x{H:.0f}); {len(labels)} etichete verificate; "
|
||||
f"61.70->62.00 = {dist:.1f}px")
|
||||
OUT.write_text(svg, encoding="utf-8")
|
||||
112
gen/strategii/mp2_crossing.svg
Normal file
@@ -0,0 +1,112 @@
|
||||
<svg viewBox="0 0 480 671" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<g transform="translate(0,0)">
|
||||
<polyline points="44.0,182.3 59.0,182.3 74.0,182.3 89.0,182.3 104.0,182.3 119.0,182.3 134.0,182.3 149.0,182.3 164.0,182.3 179.0,182.3 194.0,182.3 194.0,134.8 209.0,134.8 224.0,134.8 239.0,134.8 254.0,134.8 269.0,134.8 284.0,134.8 299.0,134.8 314.0,134.8 329.0,134.8 344.0,134.8 359.0,134.8" fill="none" stroke="#b06000" stroke-width="1.8"/>
|
||||
<text x="470" y="128.8" fill="#b06000" text-anchor="end" style="font-weight:bold">pivot daily (cel MIC)</text>
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">DA — crossing (+2)</text>
|
||||
<line x1="24" y1="153.9" x2="466" y2="153.9" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="30" y="147.9" fill="#1a5fb4">pivot weekly (cel MARE) 62.00</text>
|
||||
<circle cx="194.0" cy="158.6" r="13" fill="none" stroke="#188a3e" stroke-width="2"/>
|
||||
<text x="210.0" y="200.3" fill="#188a3e" style="font-weight:bold">crossing: 61.70 → 62.20</text>
|
||||
<text x="209.0" y="85.0" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intrare: trigger (prima candelă verde)</text>
|
||||
<line x1="44.0" y1="201.4" x2="44.0" y2="229.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="209.0" width="9" height="11.4" fill="#c00"/>
|
||||
<line x1="59.0" y1="212.8" x2="59.0" y2="244.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="220.4" width="9" height="14.2" fill="#c00"/>
|
||||
<line x1="74.0" y1="208.0" x2="74.0" y2="244.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="215.6" width="9" height="19.0" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="208.0" x2="89.0" y2="239.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="215.6" width="9" height="14.2" fill="#c00"/>
|
||||
<line x1="104.0" y1="198.5" x2="104.0" y2="239.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="206.1" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="198.5" x2="119.0" y2="229.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="206.1" width="9" height="14.3" fill="#c00"/>
|
||||
<line x1="134.0" y1="189.0" x2="134.0" y2="229.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="196.6" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="189.0" x2="149.0" y2="220.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="196.6" width="9" height="14.2" fill="#c00"/>
|
||||
<line x1="164.0" y1="184.3" x2="164.0" y2="220.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="191.8" width="9" height="19.0" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="170.0" x2="179.0" y2="201.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="177.6" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="133.9" x2="194.0" y2="187.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="149.1" width="9" height="28.5" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="95.0" x2="209.0" y2="158.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="115.9" width="9" height="33.3" fill="#188a3e"/>
|
||||
<line x1="224.0" y1="94.0" x2="224.0" y2="125.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="101.6" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="94.0" x2="239.0" y2="115.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="101.6" width="9" height="4.7" fill="#c00"/>
|
||||
<line x1="254.0" y1="84.5" x2="254.0" y2="115.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="92.1" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="84.5" x2="269.0" y2="106.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="92.1" width="9" height="4.7" fill="#c00"/>
|
||||
<line x1="284.0" y1="75.0" x2="284.0" y2="106.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="82.6" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="75.0" x2="299.0" y2="96.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="82.6" width="9" height="4.7" fill="#c00"/>
|
||||
<line x1="314.0" y1="65.5" x2="314.0" y2="96.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="73.1" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="65.5" x2="329.0" y2="87.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="73.1" width="9" height="4.8" fill="#c00"/>
|
||||
<line x1="344.0" y1="60.8" x2="344.0" y2="87.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="68.4" width="9" height="9.5" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="56.0" x2="359.0" y2="77.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="63.6" width="9" height="4.8" fill="#188a3e"/>
|
||||
</g>
|
||||
<g transform="translate(0,314)">
|
||||
<polyline points="44.0,139.6 59.0,139.6 74.0,139.6 89.0,139.6 104.0,139.6 119.0,139.6 134.0,139.6 149.0,139.6 164.0,139.6 179.0,139.6 179.0,149.1 194.0,149.1 209.0,149.1 224.0,149.1 239.0,149.1 254.0,149.1 269.0,149.1 284.0,149.1 299.0,149.1 314.0,149.1 329.0,149.1 344.0,149.1 359.0,149.1" fill="none" stroke="#b06000" stroke-width="1.8"/>
|
||||
<text x="470" y="143.1" fill="#b06000" text-anchor="end" style="font-weight:bold">pivot daily (cel MIC)</text>
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">NU — continuare/buy the dip (+1)</text>
|
||||
<line x1="24" y1="168.1" x2="466" y2="168.1" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray=""/>
|
||||
<text x="30" y="162.1" fill="#1a5fb4">pivot weekly (cel MARE) 62.00</text>
|
||||
<line x1="44.0" y1="191.9" x2="44.0" y2="220.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="199.5" width="9" height="11.4" fill="#c00"/>
|
||||
<line x1="59.0" y1="189.0" x2="59.0" y2="220.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="196.6" width="9" height="14.3" fill="#188a3e"/>
|
||||
<line x1="74.0" y1="170.0" x2="74.0" y2="206.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="177.6" width="9" height="19.0" fill="#188a3e"/>
|
||||
<line x1="89.0" y1="170.0" x2="89.0" y2="196.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="177.6" width="9" height="9.5" fill="#c00"/>
|
||||
<line x1="104.0" y1="155.8" x2="104.0" y2="196.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="163.4" width="9" height="23.8" fill="#188a3e"/>
|
||||
<line x1="119.0" y1="127.2" x2="119.0" y2="172.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="134.8" width="9" height="28.5" fill="#188a3e"/>
|
||||
<line x1="134.0" y1="127.2" x2="134.0" y2="153.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="134.8" width="9" height="9.5" fill="#c00"/>
|
||||
<line x1="149.0" y1="117.7" x2="149.0" y2="153.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="125.3" width="9" height="19.0" fill="#188a3e"/>
|
||||
<line x1="164.0" y1="108.3" x2="164.0" y2="134.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="115.9" width="9" height="9.5" fill="#188a3e"/>
|
||||
<line x1="179.0" y1="108.3" x2="179.0" y2="139.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="115.9" width="9" height="14.2" fill="#c00"/>
|
||||
<line x1="194.0" y1="103.5" x2="194.0" y2="139.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="111.1" width="9" height="19.0" fill="#188a3e"/>
|
||||
<line x1="209.0" y1="103.5" x2="209.0" y2="130.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="111.1" width="9" height="9.5" fill="#c00"/>
|
||||
<line x1="224.0" y1="94.0" x2="224.0" y2="130.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="101.6" width="9" height="19.0" fill="#188a3e"/>
|
||||
<line x1="239.0" y1="94.0" x2="239.0" y2="115.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="101.6" width="9" height="4.8" fill="#c00"/>
|
||||
<line x1="254.0" y1="84.5" x2="254.0" y2="115.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="92.1" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="84.5" x2="269.0" y2="106.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="92.1" width="9" height="4.7" fill="#c00"/>
|
||||
<line x1="284.0" y1="75.0" x2="284.0" y2="106.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="82.6" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="299.0" y1="75.0" x2="299.0" y2="96.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="82.6" width="9" height="4.7" fill="#c00"/>
|
||||
<line x1="314.0" y1="65.5" x2="314.0" y2="96.8" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="73.1" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="329.0" y1="65.5" x2="329.0" y2="87.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="73.1" width="9" height="4.7" fill="#c00"/>
|
||||
<line x1="344.0" y1="56.0" x2="344.0" y2="87.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="63.6" width="9" height="14.2" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="56.0" x2="359.0" y2="77.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="63.6" width="9" height="4.8" fill="#c00"/>
|
||||
<text x="239.0" y="234.0" fill="#777" text-anchor="middle" style="font-weight:bold">semnale ulterioare = BUY THE DIP, nu crossing</text></g>
|
||||
<text x="24" y="576" fill="#555">Pivot weekly 62.00 = linia orizontală (pivotul MARE).</text>
|
||||
<text x="24" y="593" fill="#555">Pivot daily (cel MIC) se recalculează zilnic și sare pe nivel nou.</text>
|
||||
<text x="24" y="610" fill="#555">Crossing = pivotul mic traversează 62.00 în sus.</text>
|
||||
<text x="24" y="627" fill="#555">Fără re-încrucișare, semnalele ulterioare sunt buy the dip,</text>
|
||||
<text x="24" y="644" fill="#555">nu crossing.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
147
gen/strategii/mp2_exemplu.py
Normal file
@@ -0,0 +1,147 @@
|
||||
"""Diagrama din `gen/strategii/mp2_exemplu.spec.txt` (TIP: PRET).
|
||||
|
||||
Exemplul complet din curs: SPY long la bottom-ul de aprilie 2020 — double
|
||||
bottom + Inverted Head & Shoulders la suport quarterly, trigger MP² long
|
||||
(pivotul mic închide peste cel mare), prima candelă verde după umărul drept.
|
||||
Intrare 577.00, SL 554.40 (1R = 22.60), TP1 599.60, ținta 4R era 667.00, iar
|
||||
mișcarea reală s-a oprit la 646.00 (~3R) într-o lună.
|
||||
|
||||
Toate cifrele vin din `summaries/09_QA/01_2026-08-25.md:384-394,485-494`
|
||||
(liniile citate în spec). Serie continuă de 38 de candele daily
|
||||
(`from_closes`), panou de volum, SMA-20, niveluri reale și marcaje ancorate.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, "/workspace/atm-curs-dl")
|
||||
from svg_candles import Chart, Level, Mark, MA, from_closes # noqa: E402
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "mp2_exemplu.svg"
|
||||
|
||||
# ── cifrele din material (09_QA/01_2026-08-25.md:384-394,485-494) ───────────
|
||||
ENTRY = 577.00 # candela de intrare (prima verde după umărul drept)
|
||||
SL = 554.40 # sub umărul drept, chiar peste suportul quarterly
|
||||
TP1 = 599.60 # 577.00 + 22.60 (1R)
|
||||
TARGET_4R = 667.00 # ținta declarată; 4R exact = 667.40
|
||||
REAL = 646.00 # mișcarea reală (~3R, +12% efectiv)
|
||||
QUARTERLY = 554.00 # suport quarterly
|
||||
SHOULDER_L = 566.0 # umăr stâng
|
||||
HEAD = 555.0 # head (cel mai jos)
|
||||
SHOULDER_R = 568.0 # umăr drept
|
||||
NECKLINE = 578.00
|
||||
|
||||
# ── structura seriei (spec): ~14 coborâre → ~10 bază → intrare → ~12 urcare ─
|
||||
closes = [
|
||||
# 0-13: ~14 candele de coborâre de la ~600 spre umărul stâng 566
|
||||
600.0, 597.0, 594.0, 596.5, 591.0, 587.5, 590.0,
|
||||
584.0, 579.5, 581.5, 576.0, 571.0, 567.0,
|
||||
566.0, # 13 umăr stâng
|
||||
# 14-24: baza — double bottom + Inverted Head & Shoulders
|
||||
570.5, 578.0, 572.0, 567.0, 560.0,
|
||||
555.0, # 19 head (cel mai jos)
|
||||
561.5, 568.0, 578.0, 573.0,
|
||||
568.0, # 24 umăr drept
|
||||
# 25: candela de intrare, prima verde după umărul drept
|
||||
577.00,
|
||||
# 26-37: ~12 candele de urcare spre 646.00 (marcajul „real")
|
||||
581.0, 585.5, 589.0, 584.0, 590.5, 596.0, 602.0,
|
||||
609.0, 616.0, 624.0, 635.0, 646.0,
|
||||
]
|
||||
assert len(closes) == 38, len(closes)
|
||||
|
||||
ENTRY_I = 25
|
||||
HEAD_I = 19
|
||||
SHL_I = 13
|
||||
SHR_I = 24
|
||||
|
||||
wicks = [(0.4, 0.4)] * len(closes)
|
||||
wicks[SHL_I] = (0.4, 0.5) # low umăr stâng 565.50
|
||||
wicks[HEAD_I] = (0.4, 0.6) # low head = 555.0 - 0.6 = 554.40 (suportul atins)
|
||||
wicks[SHR_I] = (0.4, 0.5)
|
||||
wicks[15] = (0.4, 0.4) # vârf 578.0 = neckline
|
||||
wicks[22] = (0.4, 0.4) # vârf 578.0 = neckline
|
||||
wicks[ENTRY_I] = (0.7, 0.4) # intrare verde, wick mic
|
||||
wicks[37] = (0.0, 0.3) # high = 646.00 exact (mișcarea reală se oprește aici)
|
||||
|
||||
vols = [
|
||||
# coborâre
|
||||
48, 46, 50, 44, 52, 49, 45, 53, 55, 47, 50, 54, 58, 62,
|
||||
# bază (head-ul are volum mare — capitulare)
|
||||
60, 72, 65, 58, 66, 88, 70, 64, 74, 60, 56,
|
||||
# intrare
|
||||
92,
|
||||
# urcare
|
||||
78, 72, 68, 60, 66, 74, 82, 88, 80, 84, 90, 95,
|
||||
]
|
||||
assert len(vols) == len(closes)
|
||||
|
||||
cds = from_closes(closes, wicks=wicks, vols=vols, start=602.0)
|
||||
|
||||
# ── assert-urile din specificație ───────────────────────────────────────────
|
||||
entry = cds[ENTRY_I].c
|
||||
assert abs(entry - 577.00) < 0.05, entry
|
||||
assert abs(SL - 554.40) < 1e-9, SL
|
||||
assert SL > QUARTERLY, (SL, QUARTERLY) # peste suportul quarterly
|
||||
assert abs((ENTRY - SL) - 22.60) < 0.05, ENTRY - SL # 1R
|
||||
assert abs(TP1 - (ENTRY + 22.60)) < 0.05, TP1 # TP1 = intrare + 1R
|
||||
target_4r = ENTRY + 4 * (ENTRY - SL)
|
||||
assert abs(target_4r - 667.40) < 0.05, target_4r # 4R exact
|
||||
assert abs(target_4r - TARGET_4R) < 0.5, target_4r # ≈ 667.00, rotunjit în curs
|
||||
assert abs((REAL - ENTRY) / (ENTRY - SL) - 3.05) < 0.02, (REAL - ENTRY) / (ENTRY - SL)
|
||||
assert abs((REAL - ENTRY) / (ENTRY - SL) - 4.0) > 0.5, "mișcarea reală NU e 4R"
|
||||
|
||||
# structura care susține afirmațiile din text
|
||||
assert abs(cds[SHL_I].c - SHOULDER_L) < 0.05, cds[SHL_I].c
|
||||
assert abs(cds[HEAD_I].c - HEAD) < 0.05, cds[HEAD_I].c
|
||||
assert abs(cds[SHR_I].c - SHOULDER_R) < 0.05, cds[SHR_I].c
|
||||
assert HEAD < SHOULDER_L and HEAD < SHOULDER_R, (HEAD, SHOULDER_L, SHOULDER_R)
|
||||
assert cds[SHL_I].c == min(c.c for c in cds[SHL_I - 1:SHL_I + 2]), "umărul stâng nu e minim local"
|
||||
assert cds[HEAD_I].c == min(c.c for c in cds[HEAD_I - 1:HEAD_I + 2]), "head-ul nu e minim local"
|
||||
assert cds[SHR_I].c == min(c.c for c in cds[SHR_I - 1:SHR_I + 2]), "umărul drept nu e minim local"
|
||||
min_pre = min(c.l for c in cds[:ENTRY_I])
|
||||
assert abs(min_pre - 554.0) < 1.0, min_pre # suportul quarterly atins
|
||||
assert abs(min_pre - SL) < 0.05, (min_pre, SL) # SL chiar la minimul head-ului
|
||||
assert cds[ENTRY_I].c > cds[ENTRY_I].o, "candela de intrare trebuie verde"
|
||||
assert cds[ENTRY_I - 1].c < cds[ENTRY_I - 1].o, "umărul drept e roșu"
|
||||
assert abs(cds[37].c - REAL) < 0.05, cds[37].c
|
||||
assert all(c.vol is not None for c in cds), "lipsește panoul de volum"
|
||||
assert len(cds) == 38
|
||||
|
||||
# ── desenul ─────────────────────────────────────────────────────────────────
|
||||
levels = [
|
||||
Level(TARGET_4R, "ținta 4R era 667.00 (+16%)", "#555"),
|
||||
Level(REAL, "646.00 — +12% efectiv (~3R)", "#188a3e", label_at="left"),
|
||||
Level(TP1, "TP1 599.60", "#1a5fb4"),
|
||||
Level(NECKLINE, "neckline 578.00", "#1a5fb4"),
|
||||
Level(SL, "SL 554.40", "#c00"),
|
||||
Level(QUARTERLY, "suport quarterly 554.00", "#777", label_at="left"),
|
||||
]
|
||||
|
||||
marks = [
|
||||
Mark(SHL_I, "umăr stâng 566", "#1a1a1a", "below"),
|
||||
Mark(HEAD_I, "head 555", "#1a1a1a", "below"),
|
||||
Mark(SHR_I, "umăr drept 568", "#1a1a1a", "below"),
|
||||
Mark(ENTRY_I, "intrare 577.00", "#188a3e", "above", dy=-10),
|
||||
]
|
||||
|
||||
ch = Chart(
|
||||
candles=cds,
|
||||
levels=levels,
|
||||
marks=marks,
|
||||
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
|
||||
scale=3.8, width=720, gap=15, body_w=9, left=44, vol_h=70,
|
||||
vol_label="volum", title="SPY — daily",
|
||||
caption=("Intrare 577.00 = prima verde după umărul drept (MP² long). "
|
||||
"1R = 22.60; 646.00 ~3R, într-o lună."),
|
||||
)
|
||||
|
||||
svg = ch.render()
|
||||
assert svg.count("\n\n") == 0, "linie goală în SVG — rupe markdown-ul"
|
||||
OUT.write_text(svg)
|
||||
|
||||
print(f"serie {len(cds)} candele | intrare {entry:.2f} @ {ENTRY_I} | "
|
||||
f"SL {SL:.2f} | 1R {ENTRY - SL:.2f} | TP1 {TP1:.2f} | "
|
||||
f"4R {target_4r:.2f} (ținta declarată {TARGET_4R:.2f}) | "
|
||||
f"real {REAL:.2f} = {(REAL - ENTRY) / (ENTRY - SL):.2f}R | "
|
||||
f"min pre-intrare {min_pre:.2f}")
|
||||
print(f"scris {OUT}")
|
||||
138
gen/strategii/mp2_exemplu.svg
Normal file
@@ -0,0 +1,138 @@
|
||||
<svg viewBox="0 0 720 623" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="48" fill="#333" style="font-weight:bold">SPY — daily</text>
|
||||
<line x1="24" y1="56.0" x2="706" y2="56.0" stroke="#555" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="50.0" fill="#555" text-anchor="end">ținta 4R era 667.00 (+16%)</text>
|
||||
<line x1="24" y1="135.8" x2="706" y2="135.8" stroke="#188a3e" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="129.8" fill="#188a3e">646.00 — +12% efectiv (~3R)</text>
|
||||
<line x1="24" y1="312.1" x2="706" y2="312.1" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="306.1" fill="#1a5fb4" text-anchor="end">TP1 599.60</text>
|
||||
<line x1="24" y1="394.2" x2="706" y2="394.2" stroke="#1a5fb4" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="388.2" fill="#1a5fb4" text-anchor="end">neckline 578.00</text>
|
||||
<line x1="24" y1="483.9" x2="706" y2="483.9" stroke="#c00" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="692" y="477.9" fill="#c00" text-anchor="end">SL 554.40</text>
|
||||
<line x1="24" y1="485.4" x2="706" y2="485.4" stroke="#777" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="30" y="479.4" fill="#777">suport quarterly 554.00</text>
|
||||
<polyline points="329.0,389.7 344.0,397.1 359.0,402.6 374.0,405.6 389.0,410.1 404.0,414.4 419.0,416.4 434.0,418.1 449.0,417.9 464.0,416.1 479.0,415.6 494.0,412.8 509.0,408.1 524.0,401.4 539.0,393.2 554.0,384.6 569.0,375.9 584.0,363.9 599.0,348.9" fill="none" stroke="#b06000" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||||
<text x="605" y="343" fill="#b06000">SMA-20</text>
|
||||
<line x1="44.0" y1="301.5" x2="44.0" y2="312.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="39.5" y="303.0" width="9" height="7.6" fill="#c00"/>
|
||||
<line x1="59.0" y1="309.1" x2="59.0" y2="323.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="54.5" y="310.6" width="9" height="11.4" fill="#c00"/>
|
||||
<line x1="74.0" y1="320.5" x2="74.0" y2="334.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="69.5" y="322.0" width="9" height="11.4" fill="#c00"/>
|
||||
<line x1="89.0" y1="322.4" x2="89.0" y2="334.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="84.5" y="323.9" width="9" height="9.5" fill="#188a3e"/>
|
||||
<line x1="104.0" y1="322.4" x2="104.0" y2="346.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="99.5" y="323.9" width="9" height="20.9" fill="#c00"/>
|
||||
<line x1="119.0" y1="343.3" x2="119.0" y2="359.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="114.5" y="344.8" width="9" height="13.3" fill="#c00"/>
|
||||
<line x1="134.0" y1="347.1" x2="134.0" y2="359.6" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="129.5" y="348.6" width="9" height="9.5" fill="#188a3e"/>
|
||||
<line x1="149.0" y1="347.1" x2="149.0" y2="372.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="144.5" y="348.6" width="9" height="22.8" fill="#c00"/>
|
||||
<line x1="164.0" y1="369.9" x2="164.0" y2="390.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="159.5" y="371.4" width="9" height="17.1" fill="#c00"/>
|
||||
<line x1="179.0" y1="379.4" x2="179.0" y2="390.0" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="174.5" y="380.9" width="9" height="7.6" fill="#188a3e"/>
|
||||
<line x1="194.0" y1="379.4" x2="194.0" y2="403.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="189.5" y="380.9" width="9" height="20.9" fill="#c00"/>
|
||||
<line x1="209.0" y1="400.3" x2="209.0" y2="422.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="204.5" y="401.8" width="9" height="19.0" fill="#c00"/>
|
||||
<line x1="224.0" y1="419.3" x2="224.0" y2="437.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="219.5" y="420.8" width="9" height="15.2" fill="#c00"/>
|
||||
<line x1="239.0" y1="434.5" x2="239.0" y2="441.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="234.5" y="436.0" width="9" height="3.8" fill="#c00"/>
|
||||
<line x1="254.0" y1="421.2" x2="254.0" y2="441.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="249.5" y="422.7" width="9" height="17.1" fill="#188a3e"/>
|
||||
<line x1="269.0" y1="392.7" x2="269.0" y2="424.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="264.5" y="394.2" width="9" height="28.5" fill="#188a3e"/>
|
||||
<line x1="284.0" y1="392.7" x2="284.0" y2="418.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="279.5" y="394.2" width="9" height="22.8" fill="#c00"/>
|
||||
<line x1="299.0" y1="415.5" x2="299.0" y2="437.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="294.5" y="417.0" width="9" height="19.0" fill="#c00"/>
|
||||
<line x1="314.0" y1="434.5" x2="314.0" y2="464.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="309.5" y="436.0" width="9" height="26.6" fill="#c00"/>
|
||||
<line x1="329.0" y1="461.1" x2="329.0" y2="483.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="324.5" y="462.6" width="9" height="19.0" fill="#c00"/>
|
||||
<line x1="344.0" y1="455.4" x2="344.0" y2="483.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="339.5" y="456.9" width="9" height="24.7" fill="#188a3e"/>
|
||||
<line x1="359.0" y1="430.7" x2="359.0" y2="458.4" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="354.5" y="432.2" width="9" height="24.7" fill="#188a3e"/>
|
||||
<line x1="374.0" y1="392.7" x2="374.0" y2="433.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="369.5" y="394.2" width="9" height="38.0" fill="#188a3e"/>
|
||||
<line x1="389.0" y1="392.7" x2="389.0" y2="414.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="384.5" y="394.2" width="9" height="19.0" fill="#c00"/>
|
||||
<line x1="404.0" y1="411.7" x2="404.0" y2="434.1" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="399.5" y="413.2" width="9" height="19.0" fill="#c00"/>
|
||||
<line x1="419.0" y1="395.3" x2="419.0" y2="433.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="414.5" y="398.0" width="9" height="34.2" fill="#188a3e"/>
|
||||
<line x1="434.0" y1="381.3" x2="434.0" y2="399.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="429.5" y="382.8" width="9" height="15.2" fill="#188a3e"/>
|
||||
<line x1="449.0" y1="364.2" x2="449.0" y2="384.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="444.5" y="365.7" width="9" height="17.1" fill="#188a3e"/>
|
||||
<line x1="464.0" y1="350.9" x2="464.0" y2="367.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="459.5" y="352.4" width="9" height="13.3" fill="#188a3e"/>
|
||||
<line x1="479.0" y1="350.9" x2="479.0" y2="372.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="474.5" y="352.4" width="9" height="19.0" fill="#c00"/>
|
||||
<line x1="494.0" y1="345.2" x2="494.0" y2="372.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="489.5" y="346.7" width="9" height="24.7" fill="#188a3e"/>
|
||||
<line x1="509.0" y1="324.3" x2="509.0" y2="348.2" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="504.5" y="325.8" width="9" height="20.9" fill="#188a3e"/>
|
||||
<line x1="524.0" y1="301.5" x2="524.0" y2="327.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="519.5" y="303.0" width="9" height="22.8" fill="#188a3e"/>
|
||||
<line x1="539.0" y1="274.9" x2="539.0" y2="304.5" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="534.5" y="276.4" width="9" height="26.6" fill="#188a3e"/>
|
||||
<line x1="554.0" y1="248.3" x2="554.0" y2="277.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="549.5" y="249.8" width="9" height="26.6" fill="#188a3e"/>
|
||||
<line x1="569.0" y1="217.9" x2="569.0" y2="251.3" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="564.5" y="219.4" width="9" height="30.4" fill="#188a3e"/>
|
||||
<line x1="584.0" y1="176.1" x2="584.0" y2="220.9" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="579.5" y="177.6" width="9" height="41.8" fill="#188a3e"/>
|
||||
<line x1="599.0" y1="135.8" x2="599.0" y2="178.7" stroke="#1a1a1a" stroke-width="1.2"/>
|
||||
<rect x="594.5" y="135.8" width="9" height="41.8" fill="#188a3e"/>
|
||||
<line x1="24" y1="581.4" x2="706" y2="581.4" stroke="#888"/>
|
||||
<rect x="39.5" y="546.0" width="9" height="35.4" fill="#c00" opacity="0.55"/>
|
||||
<rect x="54.5" y="547.5" width="9" height="33.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="69.5" y="544.6" width="9" height="36.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="84.5" y="549.0" width="9" height="32.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="99.5" y="543.1" width="9" height="38.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="114.5" y="545.3" width="9" height="36.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="129.5" y="548.2" width="9" height="33.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="144.5" y="542.3" width="9" height="39.1" fill="#c00" opacity="0.55"/>
|
||||
<rect x="159.5" y="540.9" width="9" height="40.5" fill="#c00" opacity="0.55"/>
|
||||
<rect x="174.5" y="546.8" width="9" height="34.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="189.5" y="544.6" width="9" height="36.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="204.5" y="541.6" width="9" height="39.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="219.5" y="538.7" width="9" height="42.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="234.5" y="535.7" width="9" height="45.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="249.5" y="537.2" width="9" height="44.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="264.5" y="528.3" width="9" height="53.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="279.5" y="533.5" width="9" height="47.9" fill="#c00" opacity="0.55"/>
|
||||
<rect x="294.5" y="538.7" width="9" height="42.7" fill="#c00" opacity="0.55"/>
|
||||
<rect x="309.5" y="532.8" width="9" height="48.6" fill="#c00" opacity="0.55"/>
|
||||
<rect x="324.5" y="516.6" width="9" height="64.8" fill="#c00" opacity="0.55"/>
|
||||
<rect x="339.5" y="529.8" width="9" height="51.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="354.5" y="534.2" width="9" height="47.2" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="369.5" y="526.9" width="9" height="54.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="384.5" y="537.2" width="9" height="44.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="399.5" y="540.1" width="9" height="41.3" fill="#c00" opacity="0.55"/>
|
||||
<rect x="414.5" y="513.6" width="9" height="67.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="429.5" y="523.9" width="9" height="57.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="444.5" y="528.3" width="9" height="53.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="459.5" y="531.3" width="9" height="50.1" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="474.5" y="537.2" width="9" height="44.2" fill="#c00" opacity="0.55"/>
|
||||
<rect x="489.5" y="532.8" width="9" height="48.6" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="504.5" y="526.9" width="9" height="54.5" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="519.5" y="521.0" width="9" height="60.4" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="534.5" y="516.6" width="9" height="64.8" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="549.5" y="522.5" width="9" height="58.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="564.5" y="519.5" width="9" height="61.9" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="579.5" y="515.1" width="9" height="66.3" fill="#188a3e" opacity="0.55"/>
|
||||
<rect x="594.5" y="511.4" width="9" height="70.0" fill="#188a3e" opacity="0.55"/>
|
||||
<text x="24" y="505.4" fill="#555">volum</text>
|
||||
<text x="239.0" y="461.7" fill="#1a1a1a" text-anchor="middle" style="font-weight:bold">umăr stâng 566</text>
|
||||
<text x="329.0" y="499.4" fill="#1a1a1a" text-anchor="middle" style="font-weight:bold">head 555</text>
|
||||
<text x="404.0" y="454.1" fill="#1a1a1a" text-anchor="middle" style="font-weight:bold">umăr drept 568</text>
|
||||
<text x="419.0" y="375.3" fill="#188a3e" text-anchor="middle" style="font-weight:bold">intrare 577.00</text>
|
||||
<text x="24" y="611" fill="#555">Intrare 577.00 = prima verde după umărul drept (MP² long). 1R = 22.60; 646.00 ~3R, într-o lună.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |