From c08c857274465bf15bf9c8f8113c846d6104cccd Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Sun, 20 Sep 2026 12:46:38 +0000 Subject: [PATCH] atmi: diagramele refacute, cinci noi, si fisa de surse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noi: pivoti (PPQ/SQ calculati din trimestrul precedent, cu formula pe desen), perechi_tf (weekly -> daily ca descompunere reala, cu caseta, ghidaje si PPQ pe ambele panouri), trigger (BIM apoi verde, fata de verde inainte de BIM — cutitul in cadere), risc (ce inseamna "fara stop": 24 de bare sub apa, -20 % pe pozitie), reinvestire (ciclul banilor pe trei cicluri). aplus devine corectie: diagrama arata un prag de corectie, nu un setup A+, si numele vechi trimitea la altceva. Trei bug-uri reale: exemplul era etichetat weekly cu 20 de bare, dar textul spunea "un an mai tarziu" — trecut pe monthly cu assert pe numarul de luni; ansamblul avea o trimitere fisier:linie desenata in SVG; si benzile PPQ/SQ erau inversate ca sens, cu SQ fix pe intrare. PPQ 95.80 si SQ 90.80 sunt completare proprie — cursul spune explicit ca nu da valorile; derivate cu formula standard, cu assert. Textul capitolului e neatins — agentul a atins pragul de context. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ --- gen/strategii/atmi_ansamblu.py | 84 ++--- gen/strategii/atmi_ansamblu.svg | 231 +++++++------- gen/strategii/atmi_corectie.py | 33 +- gen/strategii/atmi_corectie.svg | 119 +++++++ gen/strategii/atmi_exemplu.py | 42 +-- gen/strategii/atmi_exemplu.svg | 102 +++--- gen/strategii/atmi_perechi_tf.py | 260 ++++++++++++++++ gen/strategii/atmi_perechi_tf.svg | 88 ++++++ gen/strategii/atmi_pivoti.py | 117 +++++++ gen/strategii/atmi_pivoti.svg | 125 ++++++++ gen/strategii/atmi_reinvestire.py | 134 ++++++++ gen/strategii/atmi_reinvestire.svg | 38 +++ gen/strategii/atmi_risc.py | 100 ++++++ gen/strategii/atmi_risc.svg | 126 ++++++++ gen/strategii/atmi_trigger.py | 187 +++++++++++ gen/strategii/atmi_trigger.svg | 62 ++++ gen/strategii/hammer_mbt10_bh_ansamblu.py | 204 ++++++------ gen/strategii/mdsrt_ansamblu.py | 309 +++++++++++-------- gen/strategii/mdsrt_ansamblu.svg | 235 +++++++------- gen/strategii/mdsrt_exemplu.py | 37 ++- gen/strategii/mdsrt_exemplu.svg | 15 +- gen/strategii/mdsrt_trinity.py | 8 +- gen/strategii/mdsrt_trinity.svg | 2 - summaries/17_STRATEGII/05_atmi.md | 4 +- summaries/17_STRATEGII/fise/05_atmi_surse.md | 94 ++++++ 25 files changed, 2133 insertions(+), 623 deletions(-) create mode 100644 gen/strategii/atmi_corectie.svg create mode 100644 gen/strategii/atmi_perechi_tf.py create mode 100644 gen/strategii/atmi_perechi_tf.svg create mode 100644 gen/strategii/atmi_pivoti.py create mode 100644 gen/strategii/atmi_pivoti.svg create mode 100644 gen/strategii/atmi_reinvestire.py create mode 100644 gen/strategii/atmi_reinvestire.svg create mode 100644 gen/strategii/atmi_risc.py create mode 100644 gen/strategii/atmi_risc.svg create mode 100644 gen/strategii/atmi_trigger.py create mode 100644 gen/strategii/atmi_trigger.svg create mode 100644 summaries/17_STRATEGII/fise/05_atmi_surse.md diff --git a/gen/strategii/atmi_ansamblu.py b/gen/strategii/atmi_ansamblu.py index f045744..5e7621a 100644 --- a/gen/strategii/atmi_ansamblu.py +++ b/gen/strategii/atmi_ansamblu.py @@ -1,6 +1,6 @@ """Diagrama de ansamblu ATMI (strategii/05_atmi.md, marcajul `ansamblu`). -„SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 %". +„SPY — daily: corecție ≥4 % → zona 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 @@ -27,29 +27,32 @@ 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 +UP_A = [96.60, 97.10, 97.60, 98.00, 98.50, 98.20, 98.70, 99.20, + 99.60, 99.30, 99.70, 100.00] # 0-11 +CORR = [99.30, 98.50, 97.80, 97.20, 96.70, 96.30, 95.90, 96.00] # 12-19 (18 = BIM) +UP_B = [96.60, 97.20, 97.90, 98.60, 99.40, 100.30, 101.40, 102.60, + 103.40, 104.20, 104.90, 105.60] # 20-31 CLOSES = UP_A + CORR + UP_B -assert len(CLOSES) == 33, len(CLOSES) -PEAK, BIM, ENTRY, LAST = 14, 21, 22, 32 +assert len(CLOSES) == 32, len(CLOSES) +PEAK, BIM, ENTRY, LAST = 11, 18, 19, 31 # 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) +WICKS = ([(0.25, 0.25)] * 11 + + [(0.00, 0.25)] # 11: vârful atinge exact 100.00 + + [(0.00, 0.20), (0.05, 0.20), (0.05, 0.20), (0.05, 0.20), + (0.05, 0.20), (0.05, 0.20), + (0.05, 0.35), # 18: bara de BIM, fundul la 95.55 + (0.15, 0.05)] # 19: candela verde de intrare + + [(0.25, 0.00)] + [(0.25, 0.25)] * 10 + + [(0.00, 0.20)]) # 31: închide/atinge exact 105.60 +assert len(WICKS) == len(CLOSES), (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) +VOLS = ([44, 47, 42, 50, 45, 41, 48, 43, 46, 51, 44, 58] # 0-11 urcare + + [64, 70, 66, 61, 58, 74, 96, 88] # 12-19 corecție + BIM/intrare + + [55, 58, 52, 60, 57, 63, 59, 66, 70, 74, 78, 84]) # 20-31 urcare spre TP +assert len(VOLS) == len(CLOSES), (len(VOLS), len(CLOSES)) -cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=88.0) +cds = from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=96.20) # ---------------------------------------------------------------- assert-urile din spec high = 100.00 @@ -58,9 +61,9 @@ 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 abs(tp - max(c.h for c in cds[-12:])) < 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 abs(min(c.l for c in cds[12:20]) - 95.55) < 1e-9 # fundul corecției = 95.55 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 @@ -75,16 +78,17 @@ 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), + Mark(13, "corecție 4,0 %", ORANGE, "below", dy=26), + Mark(BIM, "BIM", "#0a7f9e", "above"), + Mark(ENTRY, "prima candelă verde → intrare", GREEN, "below", dy=30), + Mark(LAST, "ieși cu tot", BLUE, "below", dy=22), ] 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, + scale=22.0, gap=17, body_w=9, left=44, width=720, vol_h=70, vol_gap=72, top=88, vol_label="volum", - caption="Niveluri (100.00 / 96.00 / 105.60) din curs; PPQ/SQ schematice, fără cifre în material.") + caption="100.00 / 96.00 / 105.60 sunt cifrele exemplului din curs; PPQ 95.80 e calculat " + "în diagrama pasului 1 (cursul nu dă valorile pivoților).") svg = ch.render() @@ -98,26 +102,30 @@ def band(price: float, half: float, color: str, label: str) -> str: y_top, y_bot = ch._y(price + half), ch._y(price - half) return (f'' - f'{label}') + f'{label}') -# 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") +# PPQ 95.80 e pivotul trimestrial calculat în diagrama pasului 1; banda e +# zona de cumpărare din jurul lui. SQ 90.80 e sub scara acestui desen, deci +# nu se desenează aici — apare pe diagrama pasului 1. +PPQ = 95.80 +assert abs(min(c.l for c in cds) - 95.55) < 1e-9 +assert PPQ - 0.30 <= min(c.l for c in cds) <= PPQ + 0.30, "fundul nu cade în banda PPQ" +assert entry > PPQ, "candela verde trebuie să închidă peste pivot" +ppq = band(PPQ, 0.30, PURPLE, f"zona PPQ {PPQ:.2f} — aici cumperi") +sq = "" # „fără stop-loss standard" stă sub zona de intrare, unde nu are candele -no_sl = (f'fără stop-loss standard' - f'(03_strategia_2_atmi.md:164-169)') +no_sl = (f'fără stop-loss standard — nicio linie sub intrare') # 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 = ('' - 'SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 %') + 'SPY — daily: corecție ≥4 % → zona PPQ → BIM + verde → intrare → TP +10 %') i = svg.index(">") + 1 -svg = svg[:i] + "\n " + "\n ".join([sq, ppq, no_sl, titlu]) + svg[i:] +svg = svg[:i] + "\n " + "\n ".join([ppq, no_sl, titlu]) + svg[i:] assert svg.count("\n\n") == 0, "linie goală în SVG rupe blocul din markdown" +assert ".md:" not in svg, "trimitere fișier:linie în SVG" OUT.write_text(svg) print(f"scris {OUT.name}: {len(cds)} candele, intrare {entry:.2f}, TP {tp:.2f}") diff --git a/gen/strategii/atmi_ansamblu.svg b/gen/strategii/atmi_ansamblu.svg index fec12fe..15eddb3 100644 --- a/gen/strategii/atmi_ansamblu.svg +++ b/gen/strategii/atmi_ansamblu.svg @@ -1,119 +1,114 @@ - - SQ — zonă quarterly - PPQ — zonă quarterly - fără stop-loss standard(03_strategia_2_atmi.md:164-169) - SPY — daily: corecție ≥4 % → SQ/PPQ → BIM + verde → intrare → TP +10 % - - 100.00 — swing high recent - - intrare 96.00 - - TP +10 % = 105.60 - - SMA-20 (ilustrativ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - volum - corecție 4.0 % - BIM - BIM + candelă verde → intrare - Niveluri (100.00 / 96.00 / 105.60) din curs; PPQ/SQ schematice, fără cifre în material. + + zona PPQ 95.80 — aici cumperi + fără stop-loss standard — nicio linie sub intrare + SPY — daily: corecție ≥4 % → zona PPQ → BIM + verde → intrare → TP +10 % + + 100.00 — swing high recent + + intrare 96.00 + + TP +10 % = 105.60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + volum + corecție 4,0 % + BIM + prima candelă verde → intrare + ieși cu tot + 100.00 / 96.00 / 105.60 sunt cifrele exemplului din curs; PPQ 95.80 e calculat în diagrama pasului 1 (cursul nu dă valorile pivoților). \ No newline at end of file diff --git a/gen/strategii/atmi_corectie.py b/gen/strategii/atmi_corectie.py index 4ddcd31..390edd8 100644 --- a/gen/strategii/atmi_corectie.py +++ b/gen/strategii/atmi_corectie.py @@ -1,6 +1,6 @@ """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`. +Citește specificația `gen/strategii/atmi_corectie.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 @@ -19,6 +19,7 @@ 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" +PURPLE = "#7b2fbf" # --- seria: 32 de close-uri, structura din specificație ---------------------- # 0-11 urcare 90.00 -> 100.00 (swing high la bara 11) @@ -38,7 +39,7 @@ 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[18] = (0.20, 0.43) # BIM: codiță lungă în jos, fundul la 95.55 wicks[19] = (0.20, 0.15) # candela de intrare wicks[31] = (0.00, 0.30) # TP: high = close = 105.60 @@ -115,7 +116,7 @@ ch = Chart( 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, + scale=21.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.", ) @@ -123,9 +124,14 @@ 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) +PPQ = 95.80 # pivotul trimestrial, calculat în atmi_pivoti.py +FUND = round(min(c.l for c in cds[12:20]), 2) +assert FUND == 95.55, FUND +assert PPQ - 0.30 <= FUND <= PPQ + 0.30, "fundul căii DA nu cade în banda PPQ" +assert NU_STOP >= PPQ + 0.30, "calea NU ar coborî în zona de cumpărare" +band_top, band_bot = ch._y(PPQ + 0.30), ch._y(PPQ - 0.30) band = (f'') + f'fill="{PURPLE}" opacity="0.12"/>') nu_line = ('') @@ -135,15 +141,15 @@ nu_hline = (f'') # eticheta traseului NU, sub linia 96.10 (nu se suprapune cu eticheta verde de 96.00) -nu_lab = (f'' - f'96.10 = 3.9 % — NU, sub prag') +nu_lab = (f'' + f'{NU_STOP:.2f} = 3.9 % — NU, sub prag') # eticheta zonei quarterly, sub banda (fără cifre: cursul nu le dă) -zone_lab = (f'' - f'zonă quarterly') +zone_lab = (f'' + f'zona PPQ {PPQ:.2f} — aici cumperi') # legende scurte ale celor două trasee -da_lab = (f'calea DA (candelele)') -nu_leg = (f'calea NU (continuă în jos)') overlay = [nu_line, nu_hline, stop_dot, nu_lab, zone_lab, da_lab, nu_leg] @@ -151,8 +157,9 @@ svg = svg.replace(">", ">\n " + band, 1) # ban svg = svg.replace("\n", "\n " + "\n ".join(overlay) + "\n") # traseele, peste assert "\n\n" not in svg, "linie goală în SVG" +assert ".md:" not in svg, "trimitere fișier:linie în SVG" assert "" 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: +with open("/workspace/atm-curs-dl/gen/strategii/atmi_corectie.svg", "w") as f: f.write(svg) -print("scris gen/strategii/atmi_aplus.svg") +print("scris gen/strategii/atmi_corectie.svg") diff --git a/gen/strategii/atmi_corectie.svg b/gen/strategii/atmi_corectie.svg new file mode 100644 index 0000000..546ccf3 --- /dev/null +++ b/gen/strategii/atmi_corectie.svg @@ -0,0 +1,119 @@ + + + SPY — daily: aceeași corecție, 4.0 % (DA) vs. 3.9 % (NU) + + 100.00 — swing high + + 96.00 = 4.0 % — DA, intri + + TP +10 % = 105.60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + volum + BIM + candelă verde → trigger + Regula e pe procent, nu pe cât de dramatică arată linia: calea NU pare o cădere mare, dar e 3.9 %. + Calea DA (candele): 4.0 % la 96.00. Calea NU (punctat): 3.9 % („3.93 %” în curs) — fără intrare. + + + + 96.10 = 3.9 % — NU, sub prag + zona PPQ 95.80 — aici cumperi + calea DA (candelele) + calea NU (continuă în jos) + \ No newline at end of file diff --git a/gen/strategii/atmi_exemplu.py b/gen/strategii/atmi_exemplu.py index 806b23b..fb7cd39 100644 --- a/gen/strategii/atmi_exemplu.py +++ b/gen/strategii/atmi_exemplu.py @@ -24,10 +24,10 @@ 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 +TP_I = 30 # 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 ─────────── +# ── structura seriei: 12 sus → 5 corecție → intrare → 13 luni spre TP ───── closes = [ # ~12 candele de urcare spre swing high ~123.00 115.80, 116.50, 117.20, 117.90, 117.60, 118.30, @@ -35,13 +35,11 @@ closes = [ # ~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, + # 13 candele lunare de urcare de la 117.43 spre 129.17 — „aproximativ un an" + 118.20, 119.10, 119.90, 120.60, 121.50, 122.40, 123.30, + 124.20, 125.30, 126.40, 127.40, 128.30, 129.17, ] -assert len(closes) == 38, len(closes) +assert len(closes) == 31, len(closes) wicks = [(0.25, 0.30)] * len(closes) wicks[0] = (0.35, 0.35) @@ -53,8 +51,8 @@ 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] + 58, 55, 53, 57, 54, 56, 59, 62, 64, 68, 72, 78, 88] +assert len(vols) == len(closes), (len(vols), len(closes)) cds = from_closes(closes, wicks=wicks, vols=vols, start=115.30) @@ -68,7 +66,7 @@ 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:]) +assert abs(max(c.h for c in cds[-13:]) - tp) < 0.05, max(c.h for c in cds[-13:]) # structura care susține afirmațiile din text assert abs(swing_high - SWING_HIGH) < 0.05, swing_high @@ -76,14 +74,15 @@ assert cds[ENTRY_I].c > cds[ENTRY_I].o, "candela de intrare trebuie să fie verd 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 +LUNI = TP_I - ENTRY_I # câte candele lunare de la intrare la TP1 +assert 12 <= LUNI <= 14, LUNI # „aproximativ un an" din material -TP_LABEL = "TP1 atins ~1 an mai târziu (data exactă neclară în transcriere — ASR)" -assert "~1 an" in TP_LABEL +TP_LABEL = f"TP1 atins {LUNI} luni mai târziu — „aproximativ un an\" în material "\ + "(ziua exactă e neclară în transcriere)" +assert "aproximativ un an" in TP_LABEL levels = [ - Level(ENTRY, "intrare 117.43", "#188a3e", label_at="left"), + Level(ENTRY, "intrare 117.43", "#188a3e", label_at="right"), Level(TP, "TP1 129.17 (+10 %)", "#1a5fb4"), ] @@ -101,6 +100,8 @@ assert len(levels) == 2, "doar intrare și TP; strategic nu există stop standar # 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 - 2].note = ("trigger-ul (BIM + candela verde daily) se petrece în interiorul " + "lunii de intrare — vezi diagrama pasului 4") cds[TP_I - 1].note = "după TP: capital + profit → cash până la următorul trigger valid" cds[TP_I].note = TP_LABEL @@ -110,16 +111,17 @@ ch = Chart( 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.", + vol_label="volum", title="SPY — monthly: intrare ianuarie 2005 la 117.43 → TP1 +10 % = 129.17", + caption="Grafic lunar, ca să încapă orizontul poziției. Swing high-ul e al meu; " + "intrarea și ținta 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} | " +print(f"serie {len(cds)} candele lunare | 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)") + f"entry→TP {LUNI} luni") print(f"scris {OUT}") diff --git a/gen/strategii/atmi_exemplu.svg b/gen/strategii/atmi_exemplu.svg index 56917a0..4e12c44 100644 --- a/gen/strategii/atmi_exemplu.svg +++ b/gen/strategii/atmi_exemplu.svg @@ -1,10 +1,10 @@ - - SPY — weekly: intrare 117.43 (27.01.2005) → TP +10 % = 129.17 + + SPY — monthly: intrare ianuarie 2005 la 117.43 → TP1 +10 % = 129.17 - intrare 117.43 + intrare 117.43 TP1 129.17 (+10 %) - + @@ -41,46 +41,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -107,25 +93,19 @@ - - - - - - - - - - - - - + + + + + + volum swing high ~123.00 BIM 27.01.2005 - TP1 - după TP: capital + profit → cash până la următorul trigger valid - TP1 atins ~1 an mai târziu (data exactă neclară în transcriere — ASR) - Swing high ~123.00 construit de mine; cifrele de intrare/TP sunt din material. + TP1 + trigger-ul (BIM + candela verde daily) se petrece în interiorul lunii de intrare — vezi diagrama pasului 4 + după TP: capital + profit → cash până la următorul trigger valid + TP1 atins 13 luni mai târziu — „aproximativ un an" în material (ziua exactă e neclară în transcriere) + Grafic lunar, ca să încapă orizontul poziției. Swing high-ul e al meu; intrarea și ținta sunt din material. \ No newline at end of file diff --git a/gen/strategii/atmi_perechi_tf.py b/gen/strategii/atmi_perechi_tf.py new file mode 100644 index 0000000..a05d295 --- /dev/null +++ b/gen/strategii/atmi_perechi_tf.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +"""ATMI — Pasul 3: nivelul trăiește pe weekly, declanșatorul pe daily. + +Panoul din stânga (weekly) arată de unde pleacă corecția și unde stă zona de +cumpărare. Panoul din dreapta NU e o serie care vine după el: e +**descompunerea reală** a ultimelor 3 candele weekly — fiecare candelă +weekly = 5 bare daily, cu open, close, max(high) și min(low) care coincid, +verificat bară cu bară. + +Sursa de adevăr e seria daily (`FINE`); candelele weekly din casetă se +calculează din ea prin agregare, deci corespondența nu poate rămâne în urmă. + +Nivelul comun e PPQ 95.80 (calculat în `atmi_pivoti.py`): e desenat pe +ambele panouri, cu același număr, și legat printr-un conector — scările au +pante diferite, e o lupă, deci același preț cade la înălțimi diferite. + +Rulare: python3 gen/strategii/atmi_perechi_tf.py +""" +from pathlib import Path + +OUT = Path(__file__).with_suffix(".svg") +W = 720 +K = 5 # 5 zile de bursă într-o săptămână +NZ = 3 # câte candele weekly se sparg în dreapta +PPQ, SQ = 95.80, 90.80 # din atmi_pivoti.py +SWING = 100.00 + +GREEN, RED, GRAY, INK, BLUE = "#188a3e", "#c00", "#888", "#333", "#1a5fb4" +PURPLE, CYAN, LINK = "#7b2fbf", "#0a7f9e", "#aaa" + +# --- seria daily: 15 bare = 3 săptămâni ------------------------------------ +D_CLOSES = [98.60, 98.20, 97.90, 97.60, 97.90, # săptămâna 1 + 97.50, 97.10, 96.80, 97.00, 96.60, # săptămâna 2 + 96.30, 96.00, 95.90, 95.70, 96.00] # săptămâna 3 (13 = BIM, 14 = intrare) +D_START = 98.90 +BIM_I, ENTRY_I = 13, 14 + +wick = [(0.15, 0.15)] * 15 +wick[BIM_I] = (0.10, 0.15) # bara de BIM coboară sub PPQ: 95.70 - 0.15 = 95.55 +wick[ENTRY_I] = (0.15, 0.05) + +FINE, o = [], D_START +for i, c in enumerate(D_CLOSES): + wu, wd = wick[i] + FINE.append((o, max(o, c) + wu, min(o, c) - wd, c)) + o = c + +# --- barele weekly dinaintea lupei ---------------------------------------- +# (open, high, low, close); ultima închide exact pe open-ul primei bare daily +BASE = [(93.60, 94.80, 93.30, 94.50), (94.50, 95.90, 94.20, 95.60), + (95.60, 97.10, 95.30, 96.80), (96.80, 98.30, 96.50, 98.00), + (98.00, 99.50, 97.70, 99.20), (99.20, 100.00, 98.90, 100.00), + (100.00, 100.00, 98.60, 98.90)] +SWING_I = 5 + + +def aggregate(fine, k, n): + out_ = [] + for j in range(n): + part = fine[j * k:(j + 1) * k] + out_.append((part[0][0], max(b[1] for b in part), + min(b[2] for b in part), part[-1][3])) + return out_ + + +ZOOM = aggregate(FINE, K, NZ) +BIG = BASE + ZOOM + +# --- ce trebuie să fie adevărat ------------------------------------------- +eq = lambda a, b: abs(a - b) < 0.005 # noqa: E731 +assert len(FINE) == K * NZ +for name, bars in (("weekly", BIG), ("daily", FINE)): + for i, (o_, h, l, c) in enumerate(bars): + assert h >= max(o_, c) and l <= min(o_, c), f"{name}[{i}] h/l incoerent" + if i: + assert eq(o_, bars[i - 1][3]), f"{name}[{i}] open != close precedent" +# lupa se leagă de restul graficului mare +assert eq(FINE[0][0], BASE[-1][3]), "open-ul primei bare daily != close-ul ultimei weekly" +# ce pretinde textul: corecția se întinde peste mai multe candele weekly, iar +# trigger-ul cade în ultima +assert eq(max(b[1] for b in BASE), SWING), max(b[1] for b in BASE) +assert BIM_I // K == ENTRY_I // K == NZ - 1, "BIM și intrarea nu cad în aceeași săptămână" +assert (BIM_I // K) > 0, "trigger-ul ar încăpea în prima candelă weekly a lupei" +ENTRY = FINE[ENTRY_I][3] +assert eq(ENTRY, 96.00) and eq((SWING - ENTRY) / SWING * 100, 4.0), ENTRY +assert FINE[ENTRY_I][3] > FINE[ENTRY_I][0], "candela de intrare nu e verde" +assert FINE[BIM_I][2] < PPQ < ENTRY, "prețul nu intră în zona PPQ înainte de intrare" +assert eq(ZOOM[-1][3], ENTRY), "close-ul săptămânii = close-ul barei daily de intrare" +assert eq(ZOOM[-1][2], FINE[BIM_I][2]), "low-ul săptămânii != low-ul barei de BIM" +assert min(b[2] for b in BIG) > SQ, "seria desenată atinge deja SQ" +# cât de aproape e fundul de PPQ, în procente — asta face zona „atinsă" +DIST = abs(FINE[BIM_I][2] - PPQ) / PPQ * 100 +assert DIST < 0.5, DIST + +# --- desen ----------------------------------------------------------------- +PLOT_H = 190 +BX0, BGAP = 132, 16 +SX0, SGAP = 392, 14 +BODY_W = 9 +FRAME_L = SX0 - 12 +COL_X = 608 +MIDX = FRAME_L - 20 +PAD = 0.07 + +out, LABELS = [], [] + + +def text(x, y, s, fill=INK, anchor="start", bold=False, size=12): + st = f"font-size:{size}px" + (";font-weight:bold" if bold else "") + a = f' text-anchor="{anchor}"' if anchor != "start" else "" + w = len(s) * (size * 0.5 if size < 12 else 5.7) + x0 = x if anchor == "start" else (x - w / 2 if anchor == "middle" else x - w) + assert 4 <= x0 and x0 + w <= W - 4, f"text iese din viewBox: {s!r} ({x0:.0f}..{x0 + w:.0f})" + LABELS.append((x0, x0 + w, y, s)) + out.append(f'{s}') + + +def hline(x1, x2, y, color, dash, width=1.5): + out.append(f'') + + +def line(x1, y1, x2, y2, color, dash="", width=1.2, marker=""): + d = f' stroke-dasharray="{dash}"' if dash else "" + m = f' marker-end="url(#{marker})"' if marker else "" + out.append(f'') + + +def rect(x0_, y0_, x1, y1, color, fill_op=0.10, dash=""): + d = f' stroke-dasharray="{dash}"' if dash else "" + out.append(f'') + + +def candles(bars, x0, gap, yf): + for i, (o_, h, l, c) in enumerate(bars): + cx = x0 + i * gap + line(cx, yf(h), cx, yf(l), "#1a1a1a", width=1.2) + top_ = yf(max(o_, c)) + hh = max(yf(min(o_, c)) - top_, 1.6) + out.append(f'') + + +out.append('' + '') +text(20, 22, "Nivelul trăiește pe weekly, declanșatorul pe daily", bold=True, size=13) +text(20, 38, f"Fiecare candelă weekly din casetă = {K} bare daily (o săptămână de bursă):", + fill="#555", size=11) +text(20, 52, "același open, același close, max(high) și min(low) identice.", + fill="#555", size=11) + +top = 104 +text(20, top - 12, "weekly: de unde pleacă corecția", fill="#555", size=11) +text(FRAME_L, top - 12, f"daily: ultimele {NZ} candele weekly, desfăcute", fill="#555", size=11) + +zlo_, zhi_ = min(b[2] for b in FINE), max(b[1] for b in FINE) +pad = PAD * (zhi_ - zlo_) +lo, hi = zlo_ - pad, zhi_ + pad + +bmax, bmin = max(b[1] for b in BIG), min(min(b[2] for b in BIG), SQ, lo) +bs, ss = PLOT_H / (bmax - bmin), PLOT_H / (hi - lo) +yb = lambda p: top + (bmax - p) * bs # noqa: E731 +ys = lambda p: top + (hi - p) * ss # noqa: E731 + +# ---- panoul weekly --------------------------------------------------------- +lx0, lx1 = BX0 + len(BASE) * BGAP - 7, BX0 + (len(BIG) - 1) * BGAP + 9 +ly0, ly1 = yb(hi), yb(lo) +rect(lx0, ly0, lx1, ly1, BLUE, fill_op=0.06, dash="4 3") +hline(BX0 - 22, lx1, yb(PPQ), PURPLE, "4 3") +hline(BX0 - 22, lx1, yb(SQ), GREEN, "4 3") +hline(BX0 - 22, lx1, yb(SWING), "#555", "3 3", width=1.0) +text(24, yb(SWING) + 13, f"swing high {SWING:.2f}", fill="#555", size=11) +text(24, yb(PPQ) - 6, f"PPQ {PPQ:.2f}", fill=PURPLE, size=11, bold=True) +text(24, yb(SQ) - 6, f"SQ {SQ:.2f}", fill=GREEN, size=11, bold=True) +text(24, yb(SQ) + 13, "zona 2, dacă", fill=GREEN, size=10) +text(24, yb(SQ) + 25, "panica merge", fill=GREEN, size=10) +text(24, yb(SQ) + 37, "mai jos", fill=GREEN, size=10) +candles(BIG, BX0, BGAP, yb) +# paranteza corecției: de la swing high până la intrare, pe panoul mare +brx = BX0 - 16 +out.append(f'') +text(brx - 3, (yb(SWING) + yb(ENTRY)) / 2 + 4, + f"-{(SWING - ENTRY) / SWING * 100:.1f} %".replace(".", ","), + fill=RED, anchor="end", bold=True, size=11) +text(BX0 + 2 * BGAP, top + PLOT_H + 20, + "corecția de minim 4 % se măsoară aici, pe weekly", fill=RED, size=11) + +# ---- ghidajele de lupă ----------------------------------------------------- +guides = [((lx1, ly0), (FRAME_L, top)), ((lx1, ly1), (FRAME_L, top + PLOT_H))] +for a_, b_ in guides: + line(a_[0], a_[1], b_[0], b_[1], BLUE, dash="5 3", width=1.1) + +# ---- conectorul nivelului comun ------------------------------------------- +ycb, ycs = yb(PPQ), ys(PPQ) +mid = (lx1 + FRAME_L) / 2 +out.append(f'') +text(mid + 4, (ycb + ycs) / 2 + 4, "același", fill="#999", size=10) +text(mid + 4, (ycb + ycs) / 2 + 16, "preț", fill="#999", size=10) + +# ---- panoul daily ---------------------------------------------------------- +frame_r = SX0 + (len(FINE) - 1) * SGAP + 10 +rect(FRAME_L, top, frame_r, top + PLOT_H, BLUE, fill_op=0.03) +y_ppq, y_e = ys(PPQ), ys(ENTRY) +assert top + PLOT_H - ys(zlo_) > 6, "minimul e lipit de marginea de jos a cadrului" +assert ys(zhi_) - top > 6, "maximul e lipit de marginea de sus a cadrului" +# zona de decizie: bara de BIM + candela verde +sx0, sx1 = SX0 + BIM_I * SGAP - 10, SX0 + ENTRY_I * SGAP + 10 +rect(sx0, ys(FINE[ENTRY_I][1]) - 6, sx1, ys(min(b[2] for b in FINE)) + 6, GREEN) +hline(FRAME_L, frame_r, y_ppq, PURPLE, "4 3") +hline(FRAME_L, frame_r, y_e, GREEN, "6 3") +candles(FINE, SX0, SGAP, ys) +text(COL_X, y_ppq + 4, f"PPQ {PPQ:.2f}", fill=PURPLE, size=10, bold=True) +text(COL_X, y_ppq + 16, "același nivel,", fill=PURPLE, size=10) +text(COL_X, y_ppq + 28, "văzut de aproape", fill=PURPLE, size=10) +text(COL_X, y_e - 8, f"intrare {ENTRY:.2f}", fill=GREEN, size=10, bold=True) +line(SX0 + BIM_I * SGAP, ys(FINE[BIM_I][2]) + 4, SX0 + BIM_I * SGAP, + top + PLOT_H + 14, CYAN, width=1) +text(SX0 + BIM_I * SGAP - 6, top + PLOT_H + 28, "BIM: aici se oprește vânzarea", + fill=CYAN, anchor="middle", size=11, bold=True) +text(SX0 + BIM_I * SGAP - 6, top + PLOT_H + 42, + f"(fundul e la {DIST:.2f} % de PPQ)".replace(".", ","), + fill=CYAN, anchor="middle", size=10) +text(20, top + PLOT_H + 58, + "Pe weekly nu vezi unde s-a oprit căderea; pe daily, da — de aceea intri de aici.", + fill="#555", size=11) + +# --- prețurile rămân fiecare pe panoul lui, cu excepția nivelului comun ---- +COMUN = {f"{PPQ:.2f}"} +MARE = {f"{SWING:.2f}", f"{SQ:.2f}"} - COMUN +MIC = {f"{ENTRY:.2f}"} - COMUN +for x0_, _x1, _y, s_ in LABELS: + rau = MARE if x0_ >= MIDX else MIC + for v in rau: + assert v not in s_, f"eticheta {s_!r} poartă prețul {v} al celuilalt panou" +st_ = [s for x0_, _x1, _y, s in LABELS if f"{PPQ:.2f}" in s and x0_ < MIDX] +dr_ = [s for x0_, _x1, _y, s in LABELS if f"{PPQ:.2f}" in s and x0_ >= MIDX] +assert st_ and dr_, f"nivelul comun {PPQ} lipsește de pe un panou ({st_} / {dr_})" + +H = top + PLOT_H + 76 +for i in range(len(LABELS)): + for j in range(i + 1, len(LABELS)): + xa0, xa1, ya, sa = LABELS[i] + xb0, xb1, yb_, sb = LABELS[j] + assert not (abs(ya - yb_) < 12 and xa0 < xb1 - 4 and xb0 < xa1 - 4), \ + f"etichete suprapuse: {sa!r} / {sb!r}" + assert 0 < LABELS[i][2] < H, f"etichetă în afara înălțimii: {LABELS[i][3]!r}" + +svg = (f'\n ' + "\n ".join(out) + "\n") +assert "\n\n" not in svg and ".md:" not in svg +OUT.write_text(svg, encoding="utf-8") +print(f"weekly {len(BIG)} bare (ultimele {NZ} = {len(FINE)} daily) | swing {SWING:.2f} " + f"| PPQ {PPQ} | intrare {ENTRY:.2f} | fund {min(b[2] for b in FINE):.2f} " + f"({DIST:.2f} % sub PPQ) | scris {OUT.name} ({W}x{H:.0f})") diff --git a/gen/strategii/atmi_perechi_tf.svg b/gen/strategii/atmi_perechi_tf.svg new file mode 100644 index 0000000..c094257 --- /dev/null +++ b/gen/strategii/atmi_perechi_tf.svg @@ -0,0 +1,88 @@ + + + Nivelul trăiește pe weekly, declanșatorul pe daily + Fiecare candelă weekly din casetă = 5 bare daily (o săptămână de bursă): + același open, același close, max(high) și min(low) identice. + weekly: de unde pleacă corecția + daily: ultimele 3 candele weekly, desfăcute + + + + + swing high 100.00 + PPQ 95.80 + SQ 90.80 + zona 2, dacă + panica merge + mai jos + + + + + + + + + + + + + + + + + + + + + + -4,0 % + corecția de minim 4 % se măsoară aici, pe weekly + + + + același + preț + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PPQ 95.80 + același nivel, + văzut de aproape + intrare 96.00 + + BIM: aici se oprește vânzarea + (fundul e la 0,26 % de PPQ) + Pe weekly nu vezi unde s-a oprit căderea; pe daily, da — de aceea intri de aici. + \ No newline at end of file diff --git a/gen/strategii/atmi_pivoti.py b/gen/strategii/atmi_pivoti.py new file mode 100644 index 0000000..c6b9d15 --- /dev/null +++ b/gen/strategii/atmi_pivoti.py @@ -0,0 +1,117 @@ +"""ATMI — Pasul 1: PPQ și SQ, calculați din trimestrul precedent. + +Un singur grafic daily care acoperă sfârșitul trimestrului precedent (zona +gri) și începutul celui nou. Din H/L/C-ul trimestrului precedent ies, prin +formula standard de pivot point, cele două niveluri pe care ATMI cumpără: +PPQ (pivotul trimestrial) și SQ (suportul trimestrial, S1). Liniile se +prelungesc peste trimestrul nou — acolo le folosești. + +Cifrele PPQ/SQ sunt COMPLETARE PROPRIE: cursul spune explicit că nu dă +valorile lor. Formula e cea confirmată „1 la 1" în material. Aici sunt +calculate în cod din seria desenată, nu scrise de mână. + +Rulează: python3 gen/strategii/atmi_pivoti.py +""" +import sys +from pathlib import Path + +sys.path.insert(0, "/workspace/atm-curs-dl") +from svg_candles import Chart, Level, Mark, Zone, Arrow, from_closes # noqa: E402 + +OUT = Path(__file__).resolve().parent / "atmi_pivoti.svg" +GRAY, RED, GREEN, BLUE, ORANGE, INK = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000", "#333" +PURPLE = "#7b2fbf" + +# --- seria: 22 de bare din trimestrul precedent + 12 din cel nou ----------- +# Trimestrul precedent trebuie să aibă H = 100.80, L = 90.40, C = 96.20. +Q_PREV = [95.40, 94.10, 93.00, 92.10, 90.90, 90.70, 92.20, 93.60, 95.00, 96.40, + 97.90, 99.20, 100.40, 99.80, 98.60, 97.40, 96.20, 95.10, 95.90, 96.80, + 97.30, 96.20] +Q_NOU = [97.00, 97.70, 98.40, 99.00, 99.60, 100.00, 99.20, 98.30, 97.40, 96.90, + 96.50, 96.20] +CLOSES = Q_PREV + Q_NOU +NP = len(Q_PREV) +assert len(CLOSES) == 34 + +wicks = [(0.25, 0.25)] * len(CLOSES) +wicks[4] = (0.20, 0.50) # minimul trimestrului precedent: 90.90 - 0.50 = 90.40 +wicks[12] = (0.40, 0.20) # maximul trimestrului precedent: 100.40 + 0.40 = 100.80 +wicks[NP - 1] = (0.20, 0.20) +wicks[NP] = (0.25, 0.00) # prima bară a trimestrului nou pleacă de pe close-ul C +wicks[NP + 5] = (0.00, 0.25) # swing high-ul trimestrului nou: exact 100.00 +wicks[NP + 6] = (0.00, 0.25) # nicio bară de după nu depășește swing high-ul +wicks[-3] = (0.20, 0.00) +wicks[-2] = (0.20, 0.00) +wicks[-1] = (0.15, 0.20) # low-ul ultimei bare: 96.20 - 0.20 = 96.00 + +cds = from_closes(CLOSES, wicks=wicks, + vols=[40 + (i * 7) % 33 for i in range(len(CLOSES))], start=96.00) + +# --- pivoții, calculați din seria desenată --------------------------------- +H = round(max(c.h for c in cds[:NP]), 2) +L = round(min(c.l for c in cds[:NP]), 2) +C = round(cds[NP - 1].c, 2) +PP = round((H + L + C) / 3, 2) +S1 = round(2 * PP - H, 2) +R1 = round(2 * PP - L, 2) + +assert (H, L, C) == (100.80, 90.40, 96.20), (H, L, C) +assert PP == 95.80, PP +assert S1 == 90.80, S1 +assert R1 == 101.20, R1 +assert S1 < PP < R1 +assert abs(S1 - L) > 0.3, "S1 a ieșit chiar pe low — ar părea o coincidență, nu un calcul" + +# ce afirmă textul capitolului despre trimestrul NOU +SWING = round(max(c.h for c in cds[NP:]), 2) +FUND = round(min(c.l for c in cds[NP:]), 2) +assert abs(SWING - 100.00) < 1e-9, SWING +ZONA = (PP - 0.30, PP + 0.30) # banda de cumpărare din jurul pivotului +assert abs(FUND - 96.00) < 1e-9, FUND +assert ZONA[0] <= FUND <= ZONA[1], (FUND, ZONA) # ultima bară intră în zona PPQ +assert abs((SWING - 96.00) / SWING * 100 - 4.0) < 0.05 # corecție de exact 4,0 % +assert all(c.l > S1 for c in cds[NP:]), "trimestrul nou nu trebuie să atingă SQ" + +levels = [ + Level(PP, f"PPQ {PP:.2f} — zona 1", PURPLE), + Level(S1, f"SQ {S1:.2f} — zona 2", GREEN), +] +marks = [ + Mark(12, f"H {H:.2f}", INK, "above"), + Mark(4, f"L {L:.2f}", INK, "below"), + Mark(NP - 1, f"C {C:.2f}", INK, "above", dy=-16), + Mark(NP + 5, f"swing high {SWING:.2f}", RED, "above"), + Mark(len(cds) - 4, "corecție 4,0 % — prețul intră în zona PPQ: treci la pasul 2", + PURPLE, "below", dy=34), +] +arrows = [] +zones = [Zone(0, NP - 1, "", GRAY), Zone(NP, len(cds) - 1, "", BLUE)] + +ch = Chart(candles=cds, levels=levels, marks=marks, zones=zones, arrows=arrows, + scale=13.0, gap=16, body_w=9, left=52, width=720, vol_h=60, top=124, + label_gap=54, + caption="Cifrele PPQ/SQ sunt completarea mea: cursul dă regula, nu valorile. " + "Formula e cea standard, confirmată în material pentru pivoți.") +svg = ch.render() + +# formula, scrisă cu cifrele desenului, ca să nu rămână o regulă abstractă +form = [f"PP = (H + L + C) / 3 = ({H:.2f} + {L:.2f} + {C:.2f}) / 3 = {PP:.2f} → PPQ", + f"S1 = 2 x PP - H = 2 x {PP:.2f} - {H:.2f} = {S1:.2f} → SQ", + f"R1 = 2 x PP - L = 2 x {PP:.2f} - {L:.2f} = {R1:.2f}"] +box = [f'' + f'SPY — daily: pivoții trimestrului nou, calculați din trimestrul precedent', + f'', + f'' + f'trimestrul precedent', + f'' + f'trimestrul nou: aici tranzacționezi'] +for i, s in enumerate(form): + box.append(f'{s}') + +i = svg.index(">") + 1 +svg = svg[:i] + "\n " + "\n ".join(box) + svg[i:] +assert "\n\n" not in svg +assert ".md:" not in svg +OUT.write_text(svg) +print(f"scris {OUT.name}: H {H} L {L} C {C} -> PPQ {PP}, SQ {S1}, R1 {R1}") diff --git a/gen/strategii/atmi_pivoti.svg b/gen/strategii/atmi_pivoti.svg new file mode 100644 index 0000000..5aeb1cd --- /dev/null +++ b/gen/strategii/atmi_pivoti.svg @@ -0,0 +1,125 @@ + + SPY — daily: pivoții trimestrului nou, calculați din trimestrul precedent + + trimestrul precedent + trimestrul nou: aici tranzacționezi + PP = (H + L + C) / 3 = (100.80 + 90.40 + 96.20) / 3 = 95.80 → PPQ + S1 = 2 x PP - H = 2 x 95.80 - 100.80 = 90.80 → SQ + R1 = 2 x PP - L = 2 x 95.80 - 90.40 = 101.20 + + + + PPQ 95.80 — zona 1 + + SQ 90.80 — zona 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + volum + H 100.80 + L 90.40 + C 96.20 + swing high 100.00 + corecție 4,0 % — prețul intră în zona PPQ: treci la pasul 2 + Cifrele PPQ/SQ sunt completarea mea: cursul dă regula, nu valorile. Formula e cea standard, confirmată în material pentru pivoți. + \ No newline at end of file diff --git a/gen/strategii/atmi_reinvestire.py b/gen/strategii/atmi_reinvestire.py new file mode 100644 index 0000000..048036e --- /dev/null +++ b/gen/strategii/atmi_reinvestire.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""ATMI — Pasul 6: ieșirea la +10 %, cash, apoi reinvestirea integrală. + +Nu e un grafic de preț: e ciclul banilor. Trei cicluri, fiecare cu aceeași +regulă — ieși cu TOATĂ poziția la +10 %, banii stau în cash până la +următorul trigger valid, iar acolo intră înapoi integral, plus contribuția +nouă. Coloana din dreapta arată ce iese dacă reinvestești doar contribuțiile +și lași profitul deoparte: aceeași sumă pusă, mai puțin la final. + +Contribuția de 600 $/trimestru e cifra cursului („1 unitate SPY"); ținta de ++10 % e regula lui. Suma finală e calculată aici, nu preluată din backtest — +backtest-ul cursului (~16.300 $ → ~52-53.000 $ în 20 de ani) are alt +orizont și e citat în text, nu desenat. + +Rulează: python3 gen/strategii/atmi_reinvestire.py +""" +from pathlib import Path + +OUT = Path(__file__).with_suffix(".svg") +W = 700 +CONTRIB, TINTA = 600.0, 0.10 +N = 3 + +GREEN, RED, BLUE, INK, GRAY, PURPLE = "#188a3e", "#c00", "#1a5fb4", "#333", "#777", "#7b2fbf" + +# --- cele două scenarii ---------------------------------------------------- +cicluri, capital = [], 0.0 +for i in range(N): + intrat = round(capital + CONTRIB, 2) + iesit = round(intrat * (1 + TINTA), 2) + cicluri.append((i + 1, intrat, iesit)) + capital = iesit + +fara = round(N * CONTRIB * (1 + TINTA), 2) # fiecare contribuție câștigă 10 % o dată +pus = round(N * CONTRIB, 2) + +assert cicluri[0] == (1, 600.00, 660.00), cicluri[0] +assert cicluri[1] == (2, 1260.00, 1386.00), cicluri[1] +assert cicluri[2] == (3, 1986.00, 2184.60), cicluri[2] +assert fara == 1980.00 and pus == 1800.00 +CASTIG = round(cicluri[-1][2] - fara, 2) +assert CASTIG == 204.60, CASTIG +# fiecare ciclu intră cu exact ce a ieșit din precedentul plus contribuția +for (_, intr, _), (_, _, ies) in zip(cicluri[1:], cicluri): + assert round(intr - ies, 2) == CONTRIB +assert all(round(ies / intr, 3) == 1.100 for _, intr, ies in cicluri) + +# --- desen ----------------------------------------------------------------- +out, LABELS = [], [] +BASE_Y = 250 # linia de zero a coloanelor +PXPU = 0.072 # px per dolar +COL_W, X0, STEP = 46, 60, 190 +assert cicluri[-1][2] * PXPU < BASE_Y - 80, "coloana cea mai înaltă iese din desen" + + +def text(x, y, s, fill=INK, anchor="start", bold=False, size=12): + st = f"font-size:{size}px" + (";font-weight:bold" if bold else "") + a = f' text-anchor="{anchor}"' if anchor != "start" else "" + w = len(s) * (size * 0.52) + x0 = x if anchor == "start" else (x - w / 2 if anchor == "middle" else x - w) + assert 2 <= x0 and x0 + w <= W - 2, f"text iese din panză: {s!r} ({x0:.0f}..{x0 + w:.0f})" + LABELS.append((x0, x0 + w, y, s)) + out.append(f'{s}') + + +def bar(x, val, color, label, sub): + h = val * PXPU + out.append(f'') + text(x + COL_W / 2, BASE_Y - h - 8, label, fill=color, anchor="middle", bold=True, size=11) + text(x + COL_W / 2, BASE_Y + 15, sub, fill=GRAY, anchor="middle", size=10) + + +out.append('' + '') +text(20, 22, "Ciclul ATMI: ieși cu tot la +10 %, stai în cash, reintri cu tot plus contribuția", + bold=True, size=13) +text(20, 40, f"Contribuție {CONTRIB:.0f} $ la fiecare trigger valid; ținta +10 % pe poziție.", + fill="#555", size=11) + +for i, (nr, intrat, iesit) in enumerate(cicluri): + xa = X0 + i * STEP + xb = xa + COL_W + 26 + text(xa, 72, f"ciclul {nr}", fill=INK, bold=True, size=12) + bar(xa, intrat, BLUE, f"{intrat:,.0f} $".replace(",", "."), "intri cu tot") + bar(xb, iesit, GREEN, f"{iesit:,.2f} $".replace(",", "x").replace(".", ",").replace("x", "."), + "ieși la +10 %") + # săgeata dintre intrare și ieșire (ce face trade-ul) + out.append(f'') + if i + 1 < len(cicluri): + # săgeata spre ciclul următor: cash + contribuție nouă + xn = X0 + (i + 1) * STEP + y = BASE_Y + 46 + out.append(f'') + text((xb + xn) / 2 + COL_W / 2, y + 16, + f"cash, apoi + {CONTRIB:.0f} $ nou", fill=BLUE, + anchor="middle", size=11, bold=True) + +out.append(f'') + +# comparația de la final +yc = BASE_Y + 92 +out.append(f'') +text(34, yc, f"Ai pus în total {pus:,.0f} $".replace(",", ".") + ", în trei contribuții egale.", + fill=INK, size=11, bold=True) +text(34, yc + 17, f"Cu reinvestire integrală: " + f"{cicluri[-1][2]:,.2f} $".replace(",", "x").replace(".", ",").replace("x", ".") + + f" · dacă lași profitul deoparte: {fara:,.0f} $".replace(",", ".") + + f" · diferența: {CASTIG:,.2f} $".replace(",", "x").replace(".", ",").replace("x", "."), + fill=INK, size=11) +text(34, yc + 34, "Cifrele astea sunt calculul meu pe trei cicluri, nu backtest-ul cursului.", + fill=GRAY, size=10) + +H = yc + 56 +for i in range(len(LABELS)): + for j in range(i + 1, len(LABELS)): + xa0, xa1, ya, sa = LABELS[i] + xb0, xb1, yb_, sb = LABELS[j] + assert not (abs(ya - yb_) < 12 and xa0 < xb1 - 4 and xb0 < xa1 - 4), \ + f"etichete suprapuse: {sa!r} / {sb!r}" + assert 0 < LABELS[i][2] < H, f"etichetă în afara înălțimii: {LABELS[i][3]!r}" + +svg = (f'\n ' + "\n ".join(out) + "\n") +assert "\n\n" not in svg and ".md:" not in svg +OUT.write_text(svg, encoding="utf-8") +print(" | ".join(f"c{n}: {a:.0f} -> {b:.2f}" for n, a, b in cicluri) + + f" | fără reinvestire {fara:.0f} | diferență {CASTIG:.2f} | scris {OUT.name} ({W}x{H:.0f})") diff --git a/gen/strategii/atmi_reinvestire.svg b/gen/strategii/atmi_reinvestire.svg new file mode 100644 index 0000000..4ac9e90 --- /dev/null +++ b/gen/strategii/atmi_reinvestire.svg @@ -0,0 +1,38 @@ + + + Ciclul ATMI: ieși cu tot la +10 %, stai în cash, reintri cu tot plus contribuția + Contribuție 600 $ la fiecare trigger valid; ținta +10 % pe poziție. + ciclul 1 + + 600 $ + intri cu tot + + 660,00 $ + ieși la +10 % + + + cash, apoi + 600 $ nou + ciclul 2 + + 1.260 $ + intri cu tot + + 1.386,00 $ + ieși la +10 % + + + cash, apoi + 600 $ nou + ciclul 3 + + 1.986 $ + intri cu tot + + 2.184,60 $ + ieși la +10 % + + + + Ai pus în total 1.800 $, în trei contribuții egale. + Cu reinvestire integrală: 2.184,60 $ · dacă lași profitul deoparte: 1.980 $ · diferența: 204,60 $ + Cifrele astea sunt calculul meu pe trei cicluri, nu backtest-ul cursului. + \ No newline at end of file diff --git a/gen/strategii/atmi_risc.py b/gen/strategii/atmi_risc.py new file mode 100644 index 0000000..b3d9375 --- /dev/null +++ b/gen/strategii/atmi_risc.py @@ -0,0 +1,100 @@ +"""ATMI — Pasul 5: ce înseamnă „fără stop-loss", desenat. + +Același trade ca în restul capitolului (intrare 96.00, țintă +10 % = 105.60), +dar arătând drumul, nu doar capetele: între intrare și țintă poziția trece +printr-un drawdown — pierdere flotantă — de 20 % pe poziție, fără ca nimic +să te scoată din ea. Cifra de 20-30 % pe poziție fără levier e a cursului +(COVID 2020, 2018); traseul barelor e construit de mine. + +Coloana din dreapta traduce aceeași cădere de 20 % a indicelui în pierdere +pe poziție, pe trei variante de instrument — calculul e în cod. + +Rulează: python3 gen/strategii/atmi_risc.py +""" +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 / "atmi_risc.svg" +GRAY, RED, GREEN, BLUE, INK, PURPLE = "#777", "#c00", "#188a3e", "#1a5fb4", "#333", "#7b2fbf" + +ENTRY, TP, PPQ = 96.00, 105.60, 95.80 +DD_PCT = 20.0 # drawdown pe poziție, fără levier +FUND = round(ENTRY * (1 - DD_PCT / 100), 2) # 76.80 + +# 3 bare de context + intrarea + 14 bare în jos + 17 bare de revenire +CLOSES = ([98.20, 97.10, 96.40, ENTRY] + + [94.60, 92.80, 90.90, 89.40, 90.70, 88.60, 86.30, 84.40, + 85.60, 83.10, 81.00, 79.20, 78.10, FUND] + + [78.40, 80.60, 82.90, 84.50, 86.80, 88.40, 90.10, 92.30, + 94.00, ENTRY, 97.80, 99.40, 100.90, 102.20, 103.40, 104.50, TP]) +ENTRY_I, FUND_I, BE_I, TP_I = 3, 17, 27, len(CLOSES) - 1 +assert CLOSES[ENTRY_I] == ENTRY and CLOSES[FUND_I] == FUND +assert CLOSES[BE_I] == ENTRY and CLOSES[TP_I] == TP + +wicks = [(0.35, 0.35)] * len(CLOSES) +wicks[ENTRY_I] = (0.30, 0.10) +wicks[FUND_I] = (0.30, 0.00) # fundul e exact -20 % +wicks[FUND_I + 1] = (0.35, 0.00) # nicio bară de după nu coboară sub fund +wicks[TP_I] = (0.00, 0.35) # ținta e exact maximul seriei +cds = from_closes(CLOSES, wicks=wicks, vols=[44 + (i * 11) % 41 for i in range(len(CLOSES))], + start=98.90) + +# --- ce susține textul ----------------------------------------------------- +fund = round(min(c.l for c in cds), 2) +assert fund == FUND, fund +assert round((ENTRY - fund) / ENTRY * 100, 1) == DD_PCT +assert round(max(c.h for c in cds), 2) == TP +assert round(TP / ENTRY, 3) == 1.100 +assert cds[FUND_I].l == fund and all(c.l >= fund for c in cds) +assert FUND < PPQ, "fundul trebuie să fie sub pivotul pe care ai cumpărat" +assert BE_I > FUND_I and CLOSES[BE_I] == ENTRY, "lipsește revenirea pe zero" +DURATA_JOS = BE_I - ENTRY_I # câte bare stai sub prețul de intrare +assert DURATA_JOS == 24, DURATA_JOS + +# aceeași cădere de 20 % pe indice, tradusă pe trei instrumente +VARIANTE = [("ETF simplu (SPY)", 1), ("ETF cu levier 2x", 2), ("ETF cu levier 3x (SPXL)", 3)] +PIERDERI = [(n, round(DD_PCT * k, 1)) for n, k in VARIANTE] +assert PIERDERI[-1][1] == 60.0, PIERDERI +COVID = 34.0 # SPY în 2020, cifra cursului +assert round(COVID * 3, 1) > 100.0, "3x pe -34 % trebuie să treacă de -100 %" + +levels = [ + Level(TP, f"TP +10 % = {TP:.2f}", BLUE), + Level(ENTRY, f"intrare {ENTRY:.2f}", GREEN, label_at="right"), + Level(fund, f"fund {fund:.2f} = -{DD_PCT:.0f} % pe poziție", RED, label_at="left"), +] +marks = [ + Mark(FUND_I, f"-{DD_PCT:.0f} %: nimic nu te scoate", RED, "below"), + Mark(BE_I, "abia aici ești pe zero", GRAY, "above", dy=-22), + Mark(TP_I, "TP", BLUE, "below"), +] + +ch = Chart(candles=cds, levels=levels, marks=marks, + scale=5.6, gap=16, body_w=9, left=56, width=720, vol_h=60, top=152, + label_gap=46, + caption="Cifra de 20-30 % pe poziție fără levier e a cursului; traseul barelor " + "e construit de mine, ca să se vadă cât durează.") +svg = ch.render() + +titlu = ('' + 'SPY — daily: fără stop, între intrare și țintă treci prin toată căderea') +sub = (f'' + f'{DURATA_JOS} de bare sub prețul de intrare, fără niciun ordin care să te scoată ' + f'— ăsta e costul regulii „fără stop-loss".') +box = [f'', + f'' + f'aceeași cădere de {DD_PCT:.0f} % pe indice, pe poziție:'] +for i, (nume, pct) in enumerate(PIERDERI): + box.append(f'' + f'{nume}: -{pct:.0f} %') + +i = svg.index(">") + 1 +svg = svg[:i] + "\n " + "\n ".join([titlu, sub] + box) + svg[i:] +assert "\n\n" not in svg and ".md:" not in svg +OUT.write_text(svg) +print(f"scris {OUT.name}: intrare {ENTRY} fund {fund} (-{DD_PCT} %) TP {TP}, " + f"{DURATA_JOS} bare sub apă") diff --git a/gen/strategii/atmi_risc.svg b/gen/strategii/atmi_risc.svg new file mode 100644 index 0000000..f0b7714 --- /dev/null +++ b/gen/strategii/atmi_risc.svg @@ -0,0 +1,126 @@ + + SPY — daily: fără stop, între intrare și țintă treci prin toată căderea + 24 de bare sub prețul de intrare, fără niciun ordin care să te scoată — ăsta e costul regulii „fără stop-loss". + + aceeași cădere de 20 % pe indice, pe poziție: + ETF simplu (SPY): -20 % + ETF cu levier 2x: -40 % + ETF cu levier 3x (SPXL): -60 % + + TP +10 % = 105.60 + + intrare 96.00 + + fund 76.80 = -20 % pe poziție + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + volum + -20 %: nimic nu te scoate + abia aici ești pe zero + TP + Cifra de 20-30 % pe poziție fără levier e a cursului; traseul barelor e construit de mine, ca să se vadă cât durează. + \ No newline at end of file diff --git a/gen/strategii/atmi_trigger.py b/gen/strategii/atmi_trigger.py new file mode 100644 index 0000000..778236e --- /dev/null +++ b/gen/strategii/atmi_trigger.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +"""ATMI — Pasul 4: trigger-ul. DA (BIM, apoi candela verde) vs. NU (verde fără BIM). + +Două panouri, unul sub altul, cu exact aceleași patru bare de corecție. Sus: +prețul intră în zona PPQ, apare săgeata BIM, iar prima candelă verde daily de +după ea închide la 96.00 — acolo intri. Jos: candela verde vine ÎNAINTE de +orice BIM, deci nu e trigger; prețul continuă să cadă spre SQ — „cuțitul în +cădere" din curs. + +Numerele sunt cele ale capitolului: PPQ 95.80, SQ 90.80 (calculate în +`atmi_pivoti.py`), intrare 96.00 la close-ul candelei verzi. + +Rulare: python3 gen/strategii/atmi_trigger.py +""" +import re +import sys +from pathlib import Path + +sys.path.insert(0, "/workspace/atm-curs-dl") +from svg_candles import Arrow, Chart, from_closes # noqa: E402 + +OUT = Path(__file__).with_suffix(".svg") +W = 660 +SCALE = 46.0 +LEFT, GAP = 66, 44 +RED, GREEN, BLUE, GREY, CYAN, PURPLE = "#c00", "#188a3e", "#1a5fb4", "#555", "#0a7f9e", "#7b2fbf" + +PPQ, SQ, ENTRY = 95.80, 90.80, 96.00 + +COMUN = [98.60, 97.80, 97.00, 96.40] # corecția, identică pe ambele panouri +CLOSES_DA = COMUN + [95.70, 96.00, 96.60] # 4: BIM 5: prima verde = intrare +CLOSES_NU = COMUN + [96.80, 96.10, 95.10, 94.00, 92.90] # 4: verde fără BIM + +w_da = [(0.15, 0.15)] * len(CLOSES_DA) +w_da[4] = (0.10, 0.15) # bara BIM coboară sub PPQ: 95.55 +w_da[5] = (0.10, 0.10) +w_nu = [(0.15, 0.15)] * len(CLOSES_NU) +w_nu[4] = (0.10, 0.10) + +START = COMUN[0] + 0.30 +cds_da = from_closes(CLOSES_DA, wicks=w_da, start=START) +cds_nu = from_closes(CLOSES_NU, wicks=w_nu, start=START) + +BIM_I, VERDE_DA, VERDE_NU = 4, 5, 4 + +# --- ce trebuie să fie adevărat ca desenul să susțină textul --------------- +ser = lambda c: (round(c.o, 2), round(c.h, 2), round(c.l, 2), round(c.c, 2)) # noqa: E731 +assert [ser(c) for c in cds_da[:4]] == [ser(c) for c in cds_nu[:4]], \ + "panourile nu pleacă din aceeași corecție" +# DA: BIM înainte de verde; verdele e chiar verde; close-ul lui = intrarea +assert BIM_I < VERDE_DA +assert cds_da[BIM_I].c < cds_da[BIM_I].o, "bara de BIM e bara de epuizare, roșie" +assert cds_da[VERDE_DA].c > cds_da[VERDE_DA].o, "candela de intrare nu e verde" +assert abs(cds_da[VERDE_DA].c - ENTRY) < 1e-9, cds_da[VERDE_DA].c +# criteriul A+: candela de intrare închide DEASUPRA pivotului +assert cds_da[VERDE_DA].c > PPQ, "candela de intrare nu închide peste PPQ" +# prețul a intrat efectiv în zona PPQ (a coborât sub pivot și a fost respins) +assert cds_da[BIM_I].l < PPQ < cds_da[VERDE_DA].c, (cds_da[BIM_I].l, PPQ) +assert min(c.l for c in cds_da) == cds_da[BIM_I].l, "fundul nu e pe bara de BIM" +# NU: verdele vine primul, și după el prețul face minime tot mai joase +assert cds_nu[VERDE_NU].c > cds_nu[VERDE_NU].o, "bara 4 de pe panoul NU nu e verde" +lows = [c.l for c in cds_nu[VERDE_NU + 1:]] +assert all(b < a for a, b in zip(lows, lows[1:])), "cuțitul nu cade continuu" +assert cds_nu[-1].c < PPQ - 2.5, "căderea de pe panoul NU e prea mică ca să se vadă" +assert min(c.l for c in cds_nu) > SQ, "căderea desenată trece deja de SQ" +# cele două panouri au aceeași scară, deci căderile se compară cu ochiul +PIERDERE = round((cds_nu[-1].c - cds_nu[VERDE_NU].c) / cds_nu[VERDE_NU].c * 100, 1) +assert PIERDERE < -3.0, PIERDERE + +ch_da = Chart( + candles=cds_da, + marks=[], + arrows=[ + Arrow(BIM_I, "BIM: săgeata indicatorului\ncyan = momentul în care\ntrendul se schimbă", + where="below", color=CYAN), + Arrow(VERDE_DA, f"AICI INTRI, la close {ENTRY:.2f}\nînchide peste pivot, deci\n" + f"cumpărătorii au ținut zona", + price=ENTRY, where="right", color=GREEN, dx=18, dy=40), + ], + scale=SCALE, gap=GAP, body_w=13, left=LEFT, width=W, + title="DA — BIM, apoi prima candelă verde daily", +) +ch_nu = Chart( + candles=cds_nu, + marks=[], + arrows=[ + Arrow(VERDE_NU, "aici NU intri: verdele a venit\nînainte de BIM, deci nimic\n" + "nu confirmă că vânzarea s-a oprit", + where="below", color=RED, dx=40), + Arrow(len(cds_nu) - 1, f"căderea continuă: {abs(PIERDERE):.1f} % mai jos\nde la candela verde —\n" + f"„cuțitul prins în cădere”", + where="below", color=RED, dx=-30), + ], + scale=SCALE, gap=GAP, body_w=13, left=LEFT, width=W, + title="NU — candelă verde înainte de BIM", +) + + +def panel(ch): + svg = ch._render() + m = re.search(r'viewBox="0 0 ([\d.]+) ([\d.]+)"', svg) + assert float(m.group(1)) == W, f"o etichetă iese din {W}px" + return svg[svg.index(">") + 1:svg.rindex("")], float(m.group(2)) + + +def level(ch, price, text, color, below=False): + y = ch._y(price) + out = [f''] + for k, s in enumerate(text): + ty = (y + 15 + 14 * k) if below else (y - 6 - 14 * (len(text) - 1 - k)) + out.append(f'{s}') + return out + + +def zone(ch, i0, i1, label, color, lift=0.0): + cds = ch.candles[i0:i1 + 1] + x0, x1 = ch._cx(i0) - 17, ch._cx(i1) + 17 + y0 = ch._y(max(c.h for c in cds)) - 8 - lift + y1 = ch._y(min(c.l for c in cds)) + 8 + return [f'', + f'{label}'] + + +body_da, h_da = panel(ch_da) +body_nu, h_nu = panel(ch_nu) + +extra_da = (zone(ch_da, BIM_I, VERDE_DA, "zona de decizie: BIM + verde", BLUE, lift=16) + + level(ch_da, PPQ, [f"PPQ {PPQ:.2f} — pivotul trimestrial"], PURPLE, below=True)) +extra_nu = (zone(ch_nu, VERDE_NU, len(cds_nu) - 1, "fără BIM, nimic nu confirmă", RED, lift=16) + + level(ch_nu, PPQ, [f"PPQ {PPQ:.2f} — pivotul trimestrial"], PURPLE, below=True)) + +CAP = ["Aceleași patru bare de corecție pe ambele panouri, aceeași scară.", + "PPQ 95.80 e calculat în diagrama pasului 1; intrarea 96.00 e close-ul candelei verzi."] +assert all(len(s) * 6.3 < W - 40 for s in CAP), "caption prea lung" +cap_top = h_da + h_nu + 20 +H = cap_top + 24 + +svg = "\n ".join([ + f'', + f'{"".join(extra_da)}{body_da}', + f'{"".join(extra_nu)}{body_nu}', + f'{CAP[0]}', + f'{CAP[1]}', + ""]) +assert "\n\n" not in svg and ".md:" not in svg + +TEXT = re.compile(r']*)>(.*?)', re.S) + + +def labels(body, y_off, tag): + out = [] + for m in TEXT.finditer(body): + x, y, attrs, s = float(m.group(1)), float(m.group(2)) + y_off, m.group(3), m.group(4) + anchor = "end" if 'text-anchor="end"' in attrs else ( + "middle" if 'text-anchor="middle"' in attrs else "start") + w = len(s) * 12 * (0.62 if "bold" in attrs else 0.52) + lf = x - w if anchor == "end" else (x - w / 2 if anchor == "middle" else x) + out.append((lf, lf + w, y, s, tag)) + return out + + +LB = (labels(body_da + "".join(extra_da), 0.0, "DA") + + labels(body_nu + "".join(extra_nu), h_da, "NU")) +for lf, rt, y, s, tag in LB: + assert -2 <= lf and rt <= W + 2, f"[{tag}] text iese din panză: {s!r}" + assert 0 <= y <= H, f"[{tag}] text în afara înălțimii: {s!r}" +for i in range(len(LB)): + for j in range(i + 1, len(LB)): + li, ri, yi, si, ti = LB[i] + lj, rj, yj, sj, tj = LB[j] + assert not (ti == tj and abs(yi - yj) < 12 and li < rj - 4 and lj < ri - 4), \ + f"[{ti}] texte suprapuse: {si!r} / {sj!r}" + +# niciun preț scris într-o etichetă care nu e un nivel desenat pe panoul lui +for lf, rt, y, s, tag in LB: + for p in re.findall(r"\d+\.\d\d", s): + assert float(p) in (PPQ, ENTRY), f"[{tag}] prețul {p} din {s!r} nu e un nivel desenat" + +OUT.write_text(svg) +print(f"DA: BIM la {BIM_I} (low {cds_da[BIM_I].l:.2f} < PPQ {PPQ}), intrare {ENTRY:.2f}") +print(f"NU: verde la {VERDE_NU} fără BIM, apoi {PIERDERE:.1f} % în jos " + f"(ultimul close {cds_nu[-1].c:.2f}, peste SQ {SQ})") +print(f"scris {OUT.name} ({W}x{H:.0f})") diff --git a/gen/strategii/atmi_trigger.svg b/gen/strategii/atmi_trigger.svg new file mode 100644 index 0000000..f59e163 --- /dev/null +++ b/gen/strategii/atmi_trigger.svg @@ -0,0 +1,62 @@ + + zona de decizie: BIM + verdePPQ 95.80 — pivotul trimestrial + DA — BIM, apoi prima candelă verde daily + + + + + + + + + + + + + + + + + BIM: săgeata indicatorului + cyan = momentul în care + trendul se schimbă + + + AICI INTRI, la close 96.00 + închide peste pivot, deci + cumpărătorii au ținut zona + + fără BIM, nimic nu confirmăPPQ 95.80 — pivotul trimestrial + NU — candelă verde înainte de BIM + + + + + + + + + + + + + + + + + + + + + aici NU intri: verdele a venit + înainte de BIM, deci nimic + nu confirmă că vânzarea s-a oprit + + + căderea continuă: 4.0 % mai jos + de la candela verde — + „cuțitul prins în cădere” + + Aceleași patru bare de corecție pe ambele panouri, aceeași scară. + PPQ 95.80 e calculat în diagrama pasului 1; intrarea 96.00 e close-ul candelei verzi. + \ No newline at end of file diff --git a/gen/strategii/hammer_mbt10_bh_ansamblu.py b/gen/strategii/hammer_mbt10_bh_ansamblu.py index 2971154..52b2abd 100644 --- a/gen/strategii/hammer_mbt10_bh_ansamblu.py +++ b/gen/strategii/hammer_mbt10_bh_ansamblu.py @@ -1,5 +1,9 @@ """Buy hammer — diagrama de ansamblu (daily, ticker ilustrativ XYZ). +Seria de aici e SURSA UNICĂ a trade-ului din Partea A: `hammer_mbt10_bh_stop` +și `hammer_mbt10_bh_iesire` importă `serie()` și pornesc din aceleași bare +(hammer 63.30 / 62.10, intrare 63.35, SL 62.05, TP 65.95 = 2R). + 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 @@ -10,109 +14,117 @@ 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 +def serie(): + """Cele 34 de bare ale trade-ului XYZ, cu verificările specificației.""" + # --- 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) + # 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" + # 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) + 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 + 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" + # --- 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})") + 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})") + return cds, hammer, conf -# --- 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 "", "".join( - f'{t}' - for i, t in enumerate(legenda)) + "") -assert "\n\n" not in svg, "linie goală în SVG după legendă" + # --- 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ă + ) -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") + svg = ch.render() + assert "", "".join( + f'{t}' + for i, t in enumerate(legenda)) + "") + 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") \ No newline at end of file diff --git a/gen/strategii/mdsrt_ansamblu.py b/gen/strategii/mdsrt_ansamblu.py index 4114e65..a811ec2 100644 --- a/gen/strategii/mdsrt_ansamblu.py +++ b/gen/strategii/mdsrt_ansamblu.py @@ -1,166 +1,203 @@ -"""Diagrama de ansamblu MDSRT long (strategii, marcajul `ansamblu`). +"""MDSRT — diagrama de ansamblu (Nivelul 1) și seria canonică a capitolului. -„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)." +Aici trăiește singurul exemplu construit al capitolului: SPY pe 5 minute, +suport/pivot daily 590.00, piciorul 1 la 590.10 (ține nivelul), piciorul 2 la +589.50 (îl rupe, cu divergență pe volum), candela-trigger = Buy Hammer, +intrare 590.20, SL 589.40, risc 0.80, TP1 591.00 (1:1), ieșire pe trail la +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). +Celelalte generatoare de preț ale capitolului (picior, trigger, stop, iesire) +IMPORTĂ seria de aici. Așa o regulă numerică — marja stopului, distanța până +la țintă — nu poate să difere de la o diagramă la alta. -Rulează: - python3 gen/strategii/mdsrt_ansamblu.py -Scrie lângă el `mdsrt_ansamblu.svg`. +Regulile numerice ale capitolului, definite o singură dată, aici: + MARJA = 0.10 — stopul stă cu atât sub low-ul piciorului 2 + intrarea = close-ul candelei care sparge high-ul candelei-trigger + TP1 = intrare + (intrare − SL) + +Ce e din curs și ce nu: structura M→A→P e a cursului; suportul 590.00 și +minimul piciorului 2 vin din exemplul construit al autorului sumarului +(`05_MODUL_4/02_strategia_5_mdsrt.md:166-177, 439-445`), nu din transcriere — +transcrierea se întrerupe înainte de planul numeric. + +Rulează: python3 gen/strategii/mdsrt_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, MA, from_closes # noqa: E402 +from svg_candles import Chart, Level, Mark, MA, Zone, Arrow, from_closes # noqa: E402 -OUT = Path(__file__).resolve().parent / "mdsrt_ansamblu.svg" +OUT = Path(__file__).with_suffix(".svg") GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000" -# ---------------------------------------------------------------- seria (30 bare) +# ------------------------------------------------- regulile numerice ale capitolului +MARJA = 0.10 # stopul, sub low-ul piciorului 2 +SUPORT = 590.00 # pivotul daily (nivelul de pe TF-ul mare) +LEG1, TRIG, CONF = 10, 18, 19 # indicii: piciorul 1, candela-trigger, candela de intrare +STOP_BARS = 4 # low-ul se caută pe ultimele 4 candele, ca la Buy Doji + 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) + 592.20, 591.60, + 590.40, # 10 = piciorul 1 + 590.90, 591.40, 591.10, 591.30, 590.90, 590.50, 590.05, # 11-17 + 589.80, # 18 = piciorul 2 (Buy Hammer) + 590.20, # 19 = intrarea + 590.60, 590.90, 591.00, # 20-22 (22 = TP1) 591.30, 591.10, 591.45, 591.70, 591.95, 592.15, # 23-28 - 592.40, # 29 = ieșire pe trail (close) + 592.80, 593.10, 592.90, # 29-31 = ultimul impuls + 592.40, # 32 = ieșire pe trail ] -assert len(CLOSES) == 30, len(CLOSES) +WICKS = [(0.25, 0.30)] * 33 +WICKS[LEG1] = (0.25, 0.30) # piciorul 1: low 590.10, deasupra suportului +WICKS[16] = (0.20, 0.15) +WICKS[17] = (0.15, 0.10) # low 589.95 — prețul intră în zonă, fără să facă minim nou +WICKS[TRIG] = (0.10, 0.30) # hammer: high 590.15, low 589.50 +WICKS[CONF] = (0.10, 0.05) +WICKS[20] = (0.25, 0.15) # retestul nivelului ține: low 590.05, peste pivot +WICKS[21] = (0.05, 0.15) # high 590.95 < TP1: ținta e atinsă prima dată la bara 22 -# 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 + 48, 46, 44, 45, 43, 41, 40, 42, 44, 46, # 0-9 + 52, # 10 = volumul piciorului 1 + 38, 40, 42, 39, 44, 50, 58, # 11-17 + 88, 80, # 18-19 absorbție la minim + 70, 64, 58, 60, 55, 52, 50, 48, 46, # 20-28 + 52, 48, 42, 46, # 29-32 ] -assert len(VOLS) == 30, len(VOLS) +assert len(CLOSES) == len(WICKS) == len(VOLS) == 33 -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 +EXIT_I = 32 # bara pe care se închide poziția, sub linia de trail -# 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 +def bull_trail(cds, start): + """Bull Trail, definiția cursului: low-ul fiecărei candele care se închide + peste high-ul candelei dinainte; linia urcă, nu coboară niciodată. + Întoarce lista [(index, nivel)] de la bara `start` încolo.""" + out, cur = [], None + for i in range(start, len(cds)): + if cds[i].c > cds[i - 1].h: + cur = round(cds[i].l, 2) if cur is None else max(cur, round(cds[i].l, 2)) + if cur is not None: + out.append((i, cur)) + return out -# ---------------------------------------------------------------- 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"), -] +def serie(): + """Seria canonică a capitolului, 33 de candele de 5 minute.""" + return from_closes(CLOSES, wicks=WICKS, vols=VOLS, start=596.60) -# 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'{t}' - for i, t in enumerate(TITLE)) -i = svg.index(">") + 1 -svg = svg[:i] + "\n " + title_svg + svg[i:] +cds = serie() +leg1_l = cds[LEG1].l +leg2_l = cds[TRIG].l +ENTRY = cds[CONF].c +SL = round(leg2_l - MARJA, 2) +R = round(ENTRY - SL, 2) +TP1 = round(ENTRY + R, 2) -# ---------------------------------------------------------------- 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'{t}' - 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", "\n " + "\n ".join(legend_svg) + "\n") +# ------------------------------------------------------------------ assert-urile +for i, c in enumerate(cds): + assert c.h >= max(c.o, c.c) and c.l <= min(c.o, c.c), f"bara {i}: h/l incoerent" + if i: + assert abs(c.o - cds[i - 1].c) < 1e-9, f"open[{i}] != close[{i-1}]" -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]}") +# 1. piciorul 1 ține nivelul, piciorul 2 îl rupe — asta e povestea capitolului +assert leg1_l == 590.10 and leg1_l > SUPORT, leg1_l +assert leg2_l == 589.50 and leg2_l < SUPORT, leg2_l +assert leg2_l < leg1_l, (leg2_l, leg1_l) +# 2. cele două picioare sunt minimele locale, nu doar două bare oarecare +assert leg1_l == min(c.l for c in cds[:LEG1 + 6]), "piciorul 1 nu e minimul zonei lui" +assert leg2_l == min(c.l for c in cds), "piciorul 2 nu e minimul seriei" +# 3. distanța dintre picioare depășește minimul de 5 candele cerut de curs +assert TRIG - LEG1 == 8, TRIG - LEG1 +# 4. hammer: corp mic sus, coadă lungă în jos +body = abs(cds[TRIG].o - cds[TRIG].c) +rng = cds[TRIG].h - cds[TRIG].l +tail = min(cds[TRIG].o, cds[TRIG].c) - cds[TRIG].l +assert body <= 0.5 * rng and tail > body, (body, rng, tail) +# 5. intrarea sparge high-ul candelei-trigger (regula de intrare a capitolului) +assert ENTRY == 590.20 and ENTRY > cds[TRIG].h, (ENTRY, cds[TRIG].h) +# 6. stopul: marja sub cel mai jos low din ultimele 4 candele +low4 = min(c.l for c in cds[CONF - STOP_BARS + 1:CONF + 1]) +assert low4 == leg2_l, (low4, leg2_l) +assert SL == 589.40 and R == 0.80, (SL, R) +# 7. TP1 e 1:1 exact și e atins prima dată la bara 22 +assert abs((ENTRY - SL) - (TP1 - ENTRY)) < 1e-9 +assert TP1 == 591.00 +assert next(i for i in range(CONF + 1, len(cds)) if cds[i].h >= TP1 - 1e-9) == 22 +# 8. downtrend vizibil înainte de piciorul 1 +assert cds[LEG1].c < cds[0].c - 5, (cds[LEG1].c, cds[0].c) +# 9. divergența: lower low pe preț, higher low pe volum +assert cds[TRIG].l < cds[LEG1].l and VOLS[TRIG] > VOLS[LEG1] +assert VOLS[16] < VOLS[17] < VOLS[TRIG] == max(VOLS) +# 10. după intrare, retestul ține: nicio bară nu mai închide sub pivot +assert all(c.c > SUPORT for c in cds[CONF + 1:]), "prețul recade sub pivot după intrare" +assert min(c.l for c in cds[CONF + 1:]) > SUPORT, "o bară de după intrare rupe iar pivotul" +# 11. ieșirea pe trail: ultimul swing low înainte de întoarcere e linia de trail, +# iar bara de ieșire e prima care se închide sub ea +_trail = bull_trail(cds, CONF) +TRAIL = dict(_trail)[EXIT_I] # nivelul liniei de trail când vine bara de ieșire +assert TRAIL == 592.50, TRAIL +assert [p for _, p in _trail] == sorted(p for _, p in _trail), "linia de trail coboară" +assert all(cds[i].c > p for i, p in _trail if i < EXIT_I), "trail-ul era rupt mai devreme" +assert cds[EXIT_I].c == 592.40 < TRAIL, (cds[EXIT_I].c, TRAIL) +r_mult = round((cds[EXIT_I].c - ENTRY) / R, 2) +assert r_mult == 2.75, r_mult + +if __name__ == "__main__": + levels = [ + Level(SUPORT, f"pivot daily {SUPORT:.2f} — zona (M)", BLUE, label_at="left"), + Level(SL, f"SL {SL:.2f} = 0.10 sub piciorul 2 (risc {R:.2f})", RED), + Level(TP1, f"TP1 {TP1:.2f} = 1:1", GREEN, label_at="left"), + ] + marks = [ + Mark(LEG1, f"piciorul 1: low {leg1_l:.2f}", GRAY, "below"), + Mark(TRIG, f"piciorul 2: low {leg2_l:.2f}", RED, "below", dy=16), + + ] + arrows = [ + Arrow(TRIG, "Buy Hammer: coadă lungă în jos\nvânzătorii au dus prețul sub nivel\nși nu l-au putut ține acolo", + where="below", price=leg2_l, color=RED, dx=210, dy=58), + Arrow(CONF, f"intrare {ENTRY:.2f}: intri abia aici,\nla close peste high-ul candelei-trigger", + where="above", color=GREEN, dx=40, dy=-44), + Arrow(EXIT_I, f"ieșire pe trail {cds[EXIT_I].c:.2f} = {r_mult:.2f}R\nprima închidere sub linia de trail", + where="right", color=GREEN, dy=-6), + ] + ch = Chart( + candles=cds, levels=levels, marks=marks, zones=[ + Zone(LEG1, CONF, "M (zonă + divergență) → A (trigger) → P (plan)", BLUE)], + arrows=arrows, + mas=[MA(20, ORANGE, dash="4 3", text="SMA-20")], + scale=48.0, gap=15, body_w=9, left=44, width=660, top=74, vol_h=70, + vol_label="volum: la piciorul 2, volum mai mare decât la piciorul 1 = divergență", + ) + svg = ch.render() + TITLE = ["SPY — 5 min (exemplu construit pe scara lecției;", + "cifrele planului nu vin din curs)"] + title_svg = "\n ".join( + f'{t}' + for i, t in enumerate(TITLE)) + i = svg.index(">") + 1 + svg = svg[:i] + "\n " + title_svg + svg[i:] + + m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg) + W, H0 = int(m.group(1)), float(m.group(2)) + legend = [ + "Legendă: structura M → A → P e a cursului; nivelurile (590.00, 589.50) și planul", + "(intrare, SL, TP) sunt exemplul construit al autorului sumarului, nu citate din curs.", + ] + legend_svg = [f'{t}' + 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", "\n " + "\n ".join(legend_svg) + "\n") + assert "\n\n" not in svg, "linie goală în SVG" + OUT.write_text(svg) + print(f"scris {OUT.name}: picior 1 {leg1_l:.2f} / picior 2 {leg2_l:.2f} sub {SUPORT:.2f}, " + f"intrare {ENTRY:.2f}, SL {SL:.2f} (risc {R:.2f}), TP1 {TP1:.2f}, ieșire {r_mult:.2f}R") diff --git a/gen/strategii/mdsrt_ansamblu.svg b/gen/strategii/mdsrt_ansamblu.svg index 0718c56..9b46a44 100644 --- a/gen/strategii/mdsrt_ansamblu.svg +++ b/gen/strategii/mdsrt_ansamblu.svg @@ -1,109 +1,132 @@ - + SPY — 5 min (exemplu construit pe scara lecției; cifrele planului nu vin din curs) - - suport / pivot 590.00 - - SL 589.40 — risc 0.80 - - TP1 591.00 = 1:1 - - SMA-20 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - volum - minim 589.80 — divergență (M) - intrare 590.20 - ieșire trail 592.40 - Legendă: entry/SL/TP/trail sunt exemplul construit al autorului sumarului, - nu citate din curs (transcrierea se întrerupe înainte de planul numeric). + + M (zonă + divergență) → A (trigger) → P (plan) + + pivot daily 590.00 — zona (M) + + SL 589.40 = 0.10 sub piciorul 2 (risc 0.80) + + TP1 591.00 = 1:1 + + SMA-20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + volum: la piciorul 2, volum mai mare decât la piciorul 1 = divergență + piciorul 1: low 590.10 + piciorul 2: low 589.50 + + + Buy Hammer: coadă lungă în jos + vânzătorii au dus prețul sub nivel + și nu l-au putut ține acolo + + + intrare 590.20: intri abia aici, + la close peste high-ul candelei-trigger + + + ieșire pe trail 592.40 = 2.75R + prima închidere sub linia de trail + Legendă: structura M → A → P e a cursului; nivelurile (590.00, 589.50) și planul + (intrare, SL, TP) sunt exemplul construit al autorului sumarului, nu citate din curs. \ No newline at end of file diff --git a/gen/strategii/mdsrt_exemplu.py b/gen/strategii/mdsrt_exemplu.py index 8423bf2..0e49000 100644 --- a/gen/strategii/mdsrt_exemplu.py +++ b/gen/strategii/mdsrt_exemplu.py @@ -30,9 +30,10 @@ GRAY, RED, GREEN, BLUE, ORANGE = "#777", "#c00", "#188a3e", "#1a5fb4", "#b06000" 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) +HAMMER_C = 622.60 # close-ul Buy Hammer — cifră reală, din sesiunea Q&A +ENTRY = 623.00 # close-ul candelei care sparge high-ul hammer-ului (construit) SL = 621.30 # 0.10 sub low-ul piciorului 2 (construit) -TP1 = 623.90 # entry + risc = 1:1 (construit) +TP1 = 624.70 # entry + risc = 1:1 (construit) # ── seria: 30 de close-uri, exact ca în specificație ─────────────────────── CLOSES = [ @@ -100,11 +101,13 @@ 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 +# 6. regula de intrare a capitolului: close-ul candelei care sparge high-ul +# candelei-trigger (aici bara 19), nu close-ul hammer-ului însuși +assert cds[18].c == HAMMER_C +assert cds[19].c == ENTRY and cds[19].c > cds[18].h, (cds[19].c, cds[18].h) 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 +risk = round(ENTRY - SL, 2) +assert risk == 1.70, risk # 7. TP1 simetric 1:1 assert abs((ENTRY - SL) - (TP1 - ENTRY)) < 1e-9 @@ -112,9 +115,9 @@ 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 +assert max(c.h for c in cds[19:25]) < TP1, [c.h for c in cds[19:25]] +assert cds[25].h >= TP1 > cds[25].o, (cds[25].h, cds[25].o) +TP_BAR = 25 # 9. divergență: lower low pe preț, higher low pe volum assert cds[18].l < cds[10].l, (cds[18].l, cds[10].l) @@ -136,9 +139,8 @@ def cx(i: int) -> float: 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"), + Level(ENTRY, "intrare 623.00 (construit)", GREEN, label_at="right"), + Level(TP1, "TP1 624.70 = 1:1 (construit)", GREEN, label_at="left"), ] # dy-uri calculate manual ca etichetele „below" să nu calce linia pivotului @@ -146,7 +148,8 @@ levels = [ 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), + Mark(18, "hammer închide 622.60, înapoi peste pivot", GREEN, "above", dy=-42), + Mark(19, "intri aici: close peste high-ul hammer-ului", GREEN, "above", dy=-62), ] ch = Chart( @@ -162,7 +165,7 @@ 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", + "picioare sunt reale, din curs. Intrarea 623.00, SL-ul 621.30 și TP-ul 624.70", "sunt construcția mea — materialul nu le dă.", ] m = re.search(r'viewBox="0 0 (\d+) ([0-9.]+)"', svg) @@ -182,8 +185,8 @@ assert "sunt reale, din curs" in svg and "construcția mea" in svg, \ 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)"): + "intrare 623.00 (construit)", "SL 621.30 (construit)", + "TP1 624.70 = 1:1 (construit)"): assert eticheta in svg, f"lipsește eticheta: {eticheta!r}" assert "SPY — 5 min" in svg, "lipsește titlul" @@ -218,5 +221,5 @@ 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"TP1 {TP1:.2f} = {(TP1 - ENTRY) / risk:.2f}:1 la bara {TP_BAR} | " f"vol[18]={VOLS[18]} > vol[10]={VOLS[10]}") diff --git a/gen/strategii/mdsrt_exemplu.svg b/gen/strategii/mdsrt_exemplu.svg index 5e14419..e56e0fe 100644 --- a/gen/strategii/mdsrt_exemplu.svg +++ b/gen/strategii/mdsrt_exemplu.svg @@ -4,12 +4,10 @@ pivot daily 622.00 SL 621.30 (construit) - - intrare 622.60 (construit) - - TP1 623.90 = 1:1 (construit) - - picior 1 = 622.40 + + intrare 623.00 (construit) + + TP1 624.70 = 1:1 (construit) SMA-20 @@ -106,8 +104,9 @@ volum picior 1 = 622.40 picior 2 = 621.40 (mai jos) · Buy Hammer - hammer închide 622.60, înapoi peste pivot + hammer închide 622.60, înapoi peste pivot + intri aici: close peste high-ul hammer-ului 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 + picioare sunt reale, din curs. Intrarea 623.00, SL-ul 621.30 și TP-ul 624.70 sunt construcția mea — materialul nu le dă. \ No newline at end of file diff --git a/gen/strategii/mdsrt_trinity.py b/gen/strategii/mdsrt_trinity.py index 5987e46..47605ad 100644 --- a/gen/strategii/mdsrt_trinity.py +++ b/gen/strategii/mdsrt_trinity.py @@ -113,12 +113,7 @@ add(W / 2, 468, 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) +# trimiterile la material stau în fișa de surse a capitolului, nu pe desen # --------------------------------------------------------------- emitere head = (f'') parts.insert(0, f'') +assert ".md:" not in "".join(parts), "trimiteri fișier:linie pe desen" parts.insert(0, head) body = [] diff --git a/gen/strategii/mdsrt_trinity.svg b/gen/strategii/mdsrt_trinity.svg index 5268a12..1e9ea1b 100644 --- a/gen/strategii/mdsrt_trinity.svg +++ b/gen/strategii/mdsrt_trinity.svg @@ -25,6 +25,4 @@ La MDSRT, „Trinity” se folosește local cu sensul „toți trei în divergență”. Lecția de selecție dă aceluiași cuvânt alt sens: aliniere în cadranul 1, nu divergență (contradicția 2.4). -Sursa: 05_MODUL_4/02_strategia_5_mdsrt.md:6-8, 34-42, 59-61, 85-88; -08_ANALIZA_ZILNICA/11_selectia_simbolului.md:612-627. diff --git a/summaries/17_STRATEGII/05_atmi.md b/summaries/17_STRATEGII/05_atmi.md index e87ad66..d063abc 100644 --- a/summaries/17_STRATEGII/05_atmi.md +++ b/summaries/17_STRATEGII/05_atmi.md @@ -389,7 +389,7 @@ formulă de tip „risc % din cont"** în material pentru ATMI. Aceeași corecție, două deznodăminte. Diagrama le pune alături. - + SPY — daily: aceeași corecție, 4.0 % (DA) vs. 3.9 % (NU) @@ -509,7 +509,7 @@ Aceeași corecție, două deznodăminte. Diagrama le pune alături. calea DA (candelele) calea NU (continuă în jos) - + | | **A+** | **Mediocru / NU** | |---|---|---| diff --git a/summaries/17_STRATEGII/fise/05_atmi_surse.md b/summaries/17_STRATEGII/fise/05_atmi_surse.md new file mode 100644 index 0000000..e9206c5 --- /dev/null +++ b/summaries/17_STRATEGII/fise/05_atmi_surse.md @@ -0,0 +1,94 @@ +# Surse — 5. ATMI + +Trimiterile la material pentru [`05_atmi.md`](../05_atmi.md). Stau aici, nu în +capitol: în corpul tutorialului îngreunează cititul, dar fără ele nu se mai +poate verifica nimic. Cifrele sunt linii din fișierele de sumarizare, nu din +transcrieri. + +Textul fără trimitere e explicația mea și nu adaugă reguli; ce completez eu +începe cu **Completare proprie**. Toate diagramele sunt exemple construite: +din curs vin regulile (corecție minim 4 %, zona quarterly, BIM înainte de +candela verde, ținta +10 %, fără stop) și cifrele exemplului SPY 2005 +(117.43 → 129.17). Valorile pivoților — **PPQ 95.80, SQ 90.80** — sunt +calculate de mine cu formula standard, pentru că materialul spune explicit că +nu le dă. + +**Prescurtări:** +`main` = [`03_strategia_2_atmi.md`](../../04_MODUL_3/03_strategia_2_atmi.md) +(lecția principală, integral) · +`bim` = [`10_buysell_momentum_trigger.md`](../../03_MODUL_2/10_buysell_momentum_trigger.md) +(definiția indicatorului BIM) · +`tf` = [`03_timeframes_set_up.md`](../../03_MODUL_2/03_timeframes_set_up.md) +(pivoți quarterly) · +`filtre` = [`01_filtre_scaner_bull_bear_reversal_pp.md`](../../04_MODUL_3/01_filtre_scaner_bull_bear_reversal_pp.md) +(câmpul de scaner „PP or Support") · +`DECIZII` = [`triaj/DECIZII.md`](../../../triaj/DECIZII.md) (registrul de triaj +al materialului) · +fișierele datate sunt sesiuni Q&A din `../../09_QA/`. + +**Nivelul 1 — ce face strategia** — `main`: 4-5, 5-6, 26-29, 31-45, 39-45, +164-169, 170-179, 505-508 · `27_2025-05-28`: 466-467. + +**Pasul 1 — instrumentul, trimestrul și pivoții** — `main`: 6-8, 26-29, +47-54 (formula nu e explicitată cifric), 143 (cursul nu dă cifrele PPQ/SQ), +146-154 · `tf`: 73-79 · `filtre`: 29-34 · `14_2025-10-21`: 501-535 +(indici precalculați în TradeStation) · `27_2025-05-28`: 466-467 +(~2 tranzacții pe trimestru) · `09_2025-12-09`: 1177-1180 (pivoții clasici, +confirmați „1 la 1"). + +**Pasul 2 — corecția de minim 4 %** — `main`: 153-154, 249-319 (cazul de +3,93 % / 3,9 %), 502-504. + +**Pasul 3 — zona SQ / PPQ** — `main`: 146-149 (intrarea sub SQ, rară, doar +în crize reale), 146-154, 151-179 (harta MAPP) · `12_2025-11-18`: 294-296 · +`25_2025-07-08`: 840-843 · `13_2025-10-28`: 104 · `DECIZII`: 234, 324. + +**Pasul 4 — BIM, apoi prima candelă verde** — `main`: 156-159 (verdele de +dinainte de BIM nu e trigger), 156-162, 159-162 (ideal închide deasupra +pivotului), 16-21 (ATMI trimite la BIM fără să-l definească), 497-501 +(„cuțitul prins în cădere") · `bim`: 3-12 (definiția: `jbv2`, două moving +averages, săgeți cyan/crimson). + +**Pasul 5 — fără stop, mărimea contribuției** — `main`: 10-11, 164-169 +(absența stopului e regulă), 166-169 (opțional -100 % pe poziție), 173-175 +(1 unitate SPY ≈ 600 $, ajustabil 0.2-0.5), 470-489 (levierul), 491-494 +(drawdown 20-30 % pe poziție, ~3 momente în 20 de ani), 509-512 · +`11_2025-11-25`: 489-500, 498-500 (dimensionezi ca și cum poți pierde 100 %) · +`04_2026-08-04`: 194-195 (lichiditatea ETF-urilor cu levier), 217-220 +(riscul e emoțional, nu matematic) · `13_2025-10-28`: 69-72 (CFD-urile, doar +1-3 zile). + +**Pasul 6 — ieșirea la +10 % și reinvestirea** — `main`: 170-171 (ieși cu +toată poziția), 176-177 (cash până la următorul trigger), 178-179 +(reinvestești tot + contribuția nouă), 321-356 (dobânda compusă), 349-356 +(poziții în paralel, cu maturități diferite), 465-468 (backtest: ~16.300 $ în +20 de ani → ~52-53.000 $) · `27_2025-05-28`: 456-467, 462-467 (aceleași cifre, +altă formulare; estimarea de 16.000 → 100.000 după a doua reinvestire). + +**A+ vs. mediocru** — `main`: 31-45 (mecanismul Buffett), 146-154, 156-162, +159-162, 185-247 (exemplul „DA"), 249-319 (exemplul „NU"), 39-45, 505-508 · +`DECIZII`: 234, 324. + +**Exemplul real (SPY 2005)** — `main`: 358-363 (intrare 117.43, TP1 +10 % = +129.17, atins „aproximativ un an mai târziu"; ziua exactă e neclară în +transcriere), 164-171, 465-468. + +**Transfer pe prop** — `26_2025-06-11`: 214-222 (limita zilnică se calculează +și pe pozițiile deschise; cazul participantului căruia i s-au închis +tranzacții aflate în profit), 216-222, 225-227 (varianta „swing, 2 săptămâni", +pentru calificare — vezi contradicția 1) · `main`: 470-494. + +**Transfer pe BVB** — `09_2025-12-09`: 1177-1180 (pivoții clasici, „1 la 1") · +`28_2025-05-27`: 374-377 (pragurile US de lichiditate: volum > 500k, preț +> 30-40 $) · `main`: 39-45, 505-508 (premisa cade pe acțiuni individuale), +497-501 (fără BIM, trigger-ul devine cuțitul în cădere). + +**Contradicții și puncte neclare** — (1) orizontul: `main`: 26-29, 358-363 +vs. `26_2025-06-11`: 225-227; (2) aritmetica contribuției: `main`: 173-175 vs. +465-468; (3) formula PPQ/SQ, nedată cifric: `main`: 47-54, 143; (4) trailing / +tranșe / ieșire anticipată, neacoperite: `main`: 170-179; (5) data exactă a +atingerii TP1 (ASR): `main`: 358-363; (6) extinderea acronimului „ATMI" nu +apare nicăieri în material. + +**Legături** — `26_2025-06-11`: 915-917 (autorul recomandă combinarea ATMI cu +MP² la suport/pivot important).