"""Diagramele pentru summaries/06_MODUL_5/04_strategia_7_micis.md. Toate graficele de preț se construiesc cu svg_candles (o singură scară px/unitate pe desen), fiecare cifră afirmată în etichetă e verificată prin assert, apoi se substituie marcatorii din fișierul .md. Regula de cifre, cerută explicit: pe etichete intră DOAR prețuri rostite în transcriere (157.47, 155.02, 86, 94, 133, 182, 48, 28, 217.02, 214.25, 108, 180) sau prețuri dintr-un exemplu construit de mine, declarat ca atare în titlu. Valorile derivate (preț mediu, 1R) apar doar pe diagramele construite, unde R-urile sunt ale mele. Cele două schițe fără candele (`divergenta`, `flux`) sunt scrise de mână, pentru că nu conțin candele, intrare, stop sau țintă. Rulează: .venv/bin/python gen/strategia_7_micis.py """ import os import re import sys sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Chart, Level, Mark, MA, Zone, Arrow, from_closes # noqa: E402 MD = ("/workspace/atm-curs-dl/summaries/06_MODUL_5/" "04_strategia_7_micis.md") SVG = {} # ----------------------------------------------------------------- unelte mici def ramp(a, b, n): """n close-uri de la a (exclusiv) la b (inclusiv), în pași egali.""" return [round(a + (b - a) * k / n, 2) for k in range(1, n + 1)] def vols(n, base=44, span=12, **over): v = [float(base + ((i * 7) % span) - span // 2) for i in range(n)] for k, x in over.items(): v[int(k[1:])] = float(x) return v def lo(cds, a, b): return round(min(c.l for c in cds[a:b + 1]), 2) def hi(cds, a, b): return round(max(c.h for c in cds[a:b + 1]), 2) def run_desc(cds, end): """Câte bare consecutive, terminând cu `end`, au high-uri descrescătoare. Regula strategiei e „5-8 candele"; cititorul numără pe desen după high-uri (`04_6_...txt:142`), deci eticheta zonei se calculează de aici, nu se scrie de mână. Altfel desenul ajunge să încalce propria regulă. """ k = 1 while end - k >= 0 and cds[end - k].h > cds[end - k + 1].h: k += 1 return k def zona_downtrend(cds, end, color="#c00"): """Zone(…) peste run-ul descrescător care se termină la bara `end`.""" n = run_desc(cds, end) assert 5 <= n <= 8, \ f"downtrend-ul desenat are {n} candele cu high-uri descrescătoare, " \ f"iar strategia cere 5-8" return Zone(end - n + 1, end, f"downtrend {n} candele", color) def deco(ch, name, distante=(), intregi=()): """Randează graficul, verifică etichetele, salvează. `distante` = numere cu două zecimale care au voie în etichete fără să fie niveluri desenate (R-uri, înălțimi). `intregi` = numere întregi rostite care au voie în etichete (86, 94, 133, 182, 28, 48, 108, 180). """ svg = ch.render() assert "\n\n" not in svg, f"{name}: linie goală în svg" assert len(ch.caption) <= 112, f"{name}: caption de {len(ch.caption)} caractere" desenate = ({f"{lv.price:.2f}" for lv in ch.levels} | {f"{a.price:.2f}" for a in ch.arrows if a.price is not None} | {f"{d:.2f}" for d in distante}) permise_int = ({str(i) for i in intregi} | {f"{lv.price:.0f}" for lv in ch.levels}) for t in re.findall(r"]*>(.*?)", svg, re.S): plain = re.sub(r"<[^>]+>", "", t) for m in re.finditer(r"\d+\.\d\d", plain): assert m.group(0) in desenate, \ f"{name}: eticheta numește {m.group(0)}, care nu e un nivel desenat" # întregii din etichete: doar niveluri desenate sau numere rostite. # unitățile de numărătoare (bare, săptămâni) și SMA-ul nu sunt prețuri. for m in re.finditer( r"(? sus - 0.06 * (jos - sus), \ f"{name}: nivelul {lv.price} e lipit de marginea de sus" SVG[name] = svg print(f" {name}: {len(ch.candles)} bare, {len(ch.levels)} niveluri") CONSTRUIT = "Exemplu construit (fără ticker), la scară. R-urile sunt ale mele." # =========================================================== seria construită # Un singur trade construit, folosit de trei diagrame (harta, pasul 1, țintele), # ca să nu se schimbe cifrele de la o secțiune la alta. CL = ([64.00, 65.80, 64.30, 65.90, 64.20, 62.80, 64.00] # 0-6 consolidare + [60.50, 57.00, 53.50, 50.00, 46.50, 43.00, 39.90] # 7-13 downtrend, 7 bare + [40.00] # 14 doji + [45.00] # 15 marubozu de trigger + [43.20, 42.60] # 16-17 pullback-ul + [43.60, 44.80, 46.00, 47.90, 49.20, 50.70] # 18-23 urcarea + [52.00, 51.30, 53.00, 54.50]) # 24-27 continuare WK = ([(0.40, 0.40)] * 14 + [(2.00, 1.90)] # 14 fitile de doji + [(0.00, 0.00)] # 15 marubozu curat + [(0.30, 0.20), (0.20, 0.30)] + [(0.35, 0.35)] * 10) VL = vols(28, 46, 12, i11=58, i12=62, i13=66, i14=70, i15=96, i16=58, i17=52, i18=60, i19=66, i20=72, i21=78, i22=70, i23=80) CDS = from_closes(CL, wicks=WK, vols=VL, start=65.00) DOJI_LO, DOJI_HI = lo(CDS, 14, 14), hi(CDS, 14, 14) H_DOJI = round(DOJI_HI - DOJI_LO, 2) # 4.00 assert (DOJI_LO, DOJI_HI, H_DOJI) == (38.00, 42.00, 4.00), (DOJI_LO, DOJI_HI) assert abs(CDS[14].c - CDS[14].o) <= 0.10 * H_DOJI, "candela 14 nu e doji" assert CDS[15].c > DOJI_HI, "trigger-ul nu închide peste high-ul doji-ului" assert lo(CDS, 7, 13) < lo(CDS, 0, 6), "downtrend-ul numărat nu coboară" assert run_desc(CDS, 13) == 7, \ f"înaintea doji-ului trebuie 7 bare cu high descrescător, sunt {run_desc(CDS, 13)}" TRIG_HI, TRIG_LO = hi(CDS, 15, 15), lo(CDS, 15, 15) H_TRIG = round(TRIG_HI - TRIG_LO, 2) # 5.00 E1 = CDS[15].c # 45.00, la close E2 = round(TRIG_HI - H_TRIG / 3, 2) # 43.33, 1/3 din marubozu E3 = round(TRIG_HI - H_TRIG / 2, 2) # 42.50, 1/2 din marubozu SL = round(DOJI_LO + H_DOJI / 3, 2) # 39.33, 2/3 din doji MED = round((E1 + E2 + E3) / 3, 2) # 43.61, prețul mediu R1 = round(E1 - SL, 2) R2 = round(MED - SL, 2) TP1 = round(E1 + R1, 2) TP2 = round(MED + R2, 2) assert lo(CDS, 16, 16) <= E2 and lo(CDS, 17, 17) <= E3, "ladder-ul nu se umple" assert lo(CDS, 16, 17) > SL, "pullback-ul atinge stopul" assert hi(CDS, 21, 21) >= TP2 and hi(CDS, 23, 23) >= TP1, "țintele nu sunt atinse" assert TP2 < TP1, "pe prețul mediu ținta de 1R vine mai jos, nu mai sus" print(f"construit: doji {DOJI_LO}-{DOJI_HI} (h={H_DOJI}) intrări {E1}/{E2}/{E3}" f" medie {MED} SL {SL} TP1 {TP1} ({R1}) TP2 {TP2} ({R2})") # ---------------------------------------------------------------- 1. harta deco(Chart( candles=CDS, levels=[Level(SL, f"SL {SL:.2f} = 2/3 din doji", "#c00", label_at="left"), Level(E1, f"intrare 1/3 la close {E1:.2f}", "#188a3e"), Level(E2, f"limit 1/3 la {E2:.2f}", "#188a3e", label_at="left"), Level(E3, f"limit 1/3 la {E3:.2f}", "#188a3e", label_at="left"), # prețul mediu există abia după ce s-a umplut a treia tranșă: # linia pornește de acolo, deci nu mai taie eticheta limitului Level(MED, f"preț mediu {MED:.2f}", "#6a3d9a", i0=17), Level(TP2, f"TP2 {TP2:.2f} — lectura mea", "#1a5fb4", i0=17), Level(TP1, f"TP1 {TP1:.2f}", "#1a5fb4")], marks=[Mark(14, "doji", "#b06000", "below", dy=14), Mark(15, "buy doji", "#188a3e", dy=-6), Mark(5, "consolidare", "#888", "below"), Mark(17, "se umplu limiturile", "#188a3e", "below", dy=0), Mark(23, "ieși, nu aștepți", "#1a5fb4", dy=-8)], zones=[zona_downtrend(CDS, 13), Zone(15, 17, "fereastra de intrare", "#188a3e")], mas=[MA(20, "#b06000", dash="4 3")], scale=18.0, gap=17, body_w=9, left=52, width=700, top=76, vol_h=70, vol_label="volum", title="MICIS, tot trade-ul — weekly (exemplu construit)", caption=CONSTRUIT), "harta", distante=(R1, R2, H_DOJI)) # ------------------------------------------------- 2. pasul 1 — setup-ul DA PIV = 36.00 cl2 = ([78.00, 76.50, 77.00, 75.00, 73.50, 74.00, 72.00, 70.00, 68.50, 67.00, 66.00] + CL[:15]) wk2 = [(0.50, 0.50)] * 11 + WK[:15] vl2 = vols(26, 44, 12, i21=58, i22=62, i23=66, i24=70, i25=88) cds2 = from_closes(cl2, wicks=wk2, vols=vl2, start=79.00) assert lo(cds2, 25, 25) == DOJI_LO and hi(cds2, 25, 25) == DOJI_HI, "doji schimbat" assert lo(cds2, 25, 25) > PIV, "doji-ul trebuie să se oprească pe pivot, nu sub el" assert vl2[23] < vl2[24] < vl2[25], "volumul nu crește pe ultimele trei bare" deco(Chart( candles=cds2, levels=[Level(PIV, f"pivot quarterly {PIV:.2f}", "#b06000", label_at="left"), Level(DOJI_LO, f"low-ul doji-ului {DOJI_LO:.2f}", "#6a3d9a")], marks=[Mark(25, "doji", "#b06000", dy=-18)], zones=[zona_downtrend(cds2, 24)], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=8.0, gap=17, body_w=9, left=52, width=700, vol_h=70, vol_label="volum", title="Pasul 1, DA — doji weekly pe pivotul quarterly", caption="Exemplu construit (fără ticker). Volumul crește pe ultimele trei " "bare: asta e acumularea."), "pasul1_da") # ------------------------------------------------- 3. pasul 1 — setup-ul NU cl3 = (ramp(41.00, 47.00, 6) + [46.50, 47.50] + ramp(47.50, 53.00, 6) + [52.40, 53.20] + ramp(53.20, 59.00, 6) + [58.60, 59.40, 58.80] + [59.00] + [58.40, 57.00, 55.00]) wk3 = [(0.35, 0.35)] * 25 + [(1.60, 1.50)] + [(0.40, 0.40)] * 3 # volumul trebuie să scadă pe TOT uptrend-ul, nu doar pe ultimele bare: # eticheta „volum în scădere" se citește peste zona verde, nu peste 4 bare vl3 = ([50.0, 52.0, 48.0, 54.0, 50.0, 52.0] + [62.0, 60.0, 58.0, 56.0, 54.0, 52.0, 50.0, 48.0, 46.0, 44.0, 42.0, 40.0, 38.0, 36.0, 33.0, 30.0, 28.0, 26.0, 25.0, 24.0] + [40.0, 56.0, 68.0]) cds3 = from_closes(cl3, wicks=wk3, vols=vl3, start=40.00) assert abs(cds3[25].c - cds3[25].o) <= 0.10 * (cds3[25].h - cds3[25].l), \ "candela 25 nu e doji" assert cds3[25].c > cds3[0].c and hi(cds3, 25, 25) == hi(cds3, 0, 28), \ "doji-ul trebuie să cadă în vârful unui uptrend" assert vl3[25] == min(vl3), "volumul trebuie să fie cel mai mic la doji" assert all(vl3[i] > vl3[i + 1] for i in range(6, 25)), \ "barele de volum trebuie să scadă pe toată zona de uptrend, cât ține eticheta" assert vl3[26] < vl3[27] < vl3[28], "volumul trebuie să revină pe corecție" deco(Chart( candles=cds3, marks=[Mark(25, "doji aici: după uptrend", "#c00", dy=-10), Mark(16, "volum în scădere pe tot uptrend-ul", "#c00", "below", dy=24), Mark(28, "urmează corecția, cu volum", "#c00", "below", dy=14), Mark(12, "uptrend", "#188a3e", "below", dy=38)], zones=[Zone(6, 24, "", "#188a3e")], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=11.0, gap=17, body_w=9, left=52, width=700, top=64, vol_h=70, vol_label="volum", title="Pasul 1, NU — același doji, context greșit", caption="Exemplu construit (fără ticker), la scară. Doji-ul arată identic; " "ce îl descalifică e ce e în stânga lui."), "pasul1_nu") # ------------------------------------- 4. pasul 2 — ladder-ul umplut (JNJ) J_CL = ([166.00, 164.50, 165.50, 163.00, 161.00, 162.00, 159.50, 157.00, 155.00, 153.00, 151.50, 150.00] # 0-11 downtrend + [150.12] # 12 doji + [157.47] # 13 marubozu, close = high + [155.60, 154.00] # 14-15 pullback + [155.50, 157.00, 158.60, 160.50, 162.00, 161.00, 163.50, 165.00, 166.50, 168.00]) J_WK = ([(0.60, 0.60)] * 12 + [(2.40, 2.60)] + [(0.00, 0.00)] + [(0.70, 0.70), (0.40, 0.40)] + [(0.60, 0.60)] * 10) J_VL = vols(26, 44, 12, i12=58, i13=92, i14=54, i15=50, i16=58, i17=64, i18=70) JC = from_closes(J_CL, wicks=J_WK, vols=J_VL, start=167.00) J_E1 = JC[13].c J_HI, J_LO = hi(JC, 13, 13), lo(JC, 13, 13) J_E2 = round(J_HI - (J_HI - J_LO) / 3, 2) J_E3 = round(J_HI - (J_HI - J_LO) / 2, 2) assert J_E1 == 157.47 and J_HI == 157.47, "close-ul rostit e 157.47, pe high" assert J_E2 == 155.02, f"1/3 din marubozu trebuie să cadă pe 155.02, nu {J_E2}" assert JC[13].c > hi(JC, 12, 12), "trigger-ul nu închide peste high-ul doji-ului" assert lo(JC, 14, 14) <= J_E2 and lo(JC, 15, 15) <= J_E3, "ladder-ul nu se umple" deco(Chart( candles=JC, levels=[Level(J_E1, f"intrare 1/3 la close {J_E1:.2f}", "#188a3e"), # limiturile există abia după close-ul candelei de trigger, deci # liniile pornesc de la ea: altfel taie eticheta de dedesubt Level(J_E2, f"limit 1/3 la {J_E2:.2f} = 1/3 din marubozu", "#188a3e", i0=13), Level(J_E3, "limit 1/3 la 1/2 din marubozu", "#6a3d9a", label_at="left")], marks=[Mark(12, "doji", "#b06000", "below", dy=14), Mark(13, "marubozu de trigger", "#188a3e", dy=-26), Mark(15, "pullback-ul umple ambele limituri", "#188a3e", "below", dy=38)], scale=10.0, gap=17, body_w=9, left=58, width=700, vol_h=70, vol_label="volum", title="Pasul 2, ladder umplut — JNJ weekly", caption="Ale lui: close-ul 157.47 și limit-ul de 155.02; barele din jur " "sunt reconstruite de mine."), "pasul2_jnj", distante=(J_E3,)) # ------------------------------- 5. pasul 2 — ladder-ul neumplut (AMD 108→180) A_CL = ([206.00, 198.00, 190.00, 182.00, 176.00, 168.00] # 0-5 + [152.00, 146.00, 138.00, 130.00, 120.00, 112.00, 104.00, 98.00] # 6-13 + [97.50] # 14 doji + [108.00] # 15 marubozu, close rostit + [116.00, 124.00, 121.00, 132.00, 141.00, 138.00, 150.00, 158.00, 166.00, 174.00, 180.00]) # 16-26 pleacă fără tine A_WK = ([(2.00, 2.00)] * 6 + [(1.60, 1.60)] * 8 + [(7.50, 8.00)] + [(0.50, 0.50)] + [(1.60, 1.60)] * 11) A_VL = vols(27, 44, 12, i14=58, i15=96, i16=78, i17=74, i18=62, i19=76, i22=80) AC = from_closes(A_CL, wicks=A_WK, vols=A_VL, start=210.00) A_E1 = AC[15].c A_HI, A_LO = hi(AC, 15, 15), lo(AC, 15, 15) A_E2 = round(A_HI - (A_HI - A_LO) / 3, 2) assert A_E1 == 108.00, "close-ul rostit e 108" assert hi(AC, 26, 26) >= 180.00, "seria trebuie să ajungă la 180, cât a rostit" assert lo(AC, 16, 26) > A_E2, "ladder-ul NU trebuie să se umple pe acest desen" deco(Chart( candles=AC, levels=[Level(A_E1, f"intrare 1/3 la close {A_E1:.2f}", "#188a3e"), # înălțimea candelei nu e rostită pe AMD, deci nivelul de 1/3 nu # primește cifră — aceeași regulă ca pe JNJ Level(A_E2, "limit 1/3 — niciodată atins", "#c00", label_at="left"), Level(180.00, "unde a ajuns: 180", "#1a5fb4")], marks=[Mark(14, "doji", "#b06000", "below", dy=14), Mark(15, "intri cu 1/3 și rămâi cu 1/3", "#188a3e", dy=-30)], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=2.3, gap=19, body_w=10, left=58, width=700, vol_h=70, vol_label="volum", title="Pasul 2, ladder neumplut — AMD monthly", caption="Ale lui: 108 (de unde a plecat) și 180 (unde a ajuns); restul e " "reconstruit de mine."), "pasul2_amd", intregi=(108, 180)) # -------------------------------------------- 6. pasul 3 — stopul (NVDA monthly) N_CL = ([20.00, 23.50, 27.00, 25.00, 31.00, 38.00, 45.00, 42.00, 50.00, 55.00, 62.00, 75.00, 90.00, 108.00, 125.00, 138.00] # 0-15 uptrend lung + [150.00, 138.00, 126.00, 115.00, 101.00] # 16-20 downtrend, 5 bare + [100.50] # 21 doji + [133.00] # 22 marubozu de trigger + [141.00, 152.00, 164.00, 175.00, 182.00]) # 23-27 spre 182 # bara 16 e vârful: fitil de respingere deasupra, ca high-ul ei să fie # cel mai sus din downtrend și numărătoarea după high-uri să dea 5, cât # spune eticheta N_WK = ([(1.50, 1.50)] * 16 + [(5.00, 3.00)] + [(3.00, 3.00)] * 4 + [(13.00, 14.50)] + [(0.50, 1.50)] + [(2.00, 2.00)] * 5) N_VL = vols(28, 46, 12, i19=66, i20=78, i21=84, i22=104, i23=86, i24=80) NC = from_closes(N_CL, wicks=N_WK, vols=N_VL, start=19.00) N_LO, N_HI = lo(NC, 21, 21), hi(NC, 21, 21) assert (N_LO, round(N_HI - N_LO, 2)) == (86.00, 28.00), (N_LO, N_HI) assert abs(NC[21].c - NC[21].o) <= 0.10 * 28.00, "candela 21 nu e doji" assert NC[22].c == 133.00 and NC[22].c > N_HI, "trigger-ul închide la 133, peste doji" assert hi(NC, 27, 27) >= 182.00, "seria trebuie să ajungă la 182" assert round(133.00 + 48.00, 2) == 181.00, "133 + 48 = 181, el a rostit 182" SL_23 = round(N_LO + 28.00 / 3, 2) # 95.33 strict; el a citit „pe la 94" print(f" NVDA: 1/3 de la low = {SL_23} strict, rostit 94; " f"distanța 133→86 = {(133 - 86) / 86 * 100:.1f}%") deco(Chart( candles=NC, levels=[Level(86.00, "sub low-ul doji-ului: 86", "#c00", label_at="left"), Level(94.00, "SL 2/3 din doji: 94", "#c00", label_at="left"), Level(133.00, "intrare 1/3 la close: 133", "#188a3e"), Level(182.00, "ținta rostită: 182", "#1a5fb4")], marks=[Mark(21, "doji monthly, înălțime 28", "#b06000", "below", dy=16), Mark(22, "marubozu de trigger", "#188a3e", dy=-70)], zones=[zona_downtrend(NC, 20)], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=2.4, gap=19, body_w=10, left=58, width=700, top=60, vol_h=70, vol_label="volum", title="Pasul 3, stopul — NVDA monthly", caption="Ale lui: 86, 94, 133, 182, înălțimea de 28 și urcarea de 48; " "barele din jur sunt ale mele."), "pasul3_nvda", intregi=(86, 94, 133, 182, 28, 48)) # ------------------------------------------ 7. pasul 4 — cele două ținte, zoom cl4 = CL[9:] wk4 = WK[9:] vl4 = VL[9:] cds4 = from_closes(cl4, wicks=wk4, vols=vl4, start=CL[8]) assert lo(cds4, 5, 5) == DOJI_LO and cds4[6].c == E1, "zoom-ul mută seria" deco(Chart( candles=cds4, levels=[Level(SL, f"SL {SL:.2f}", "#c00", label_at="left"), Level(MED, f"preț mediu {MED:.2f}", "#6a3d9a"), Level(E1, f"intrare 1/3 la {E1:.2f}", "#188a3e"), Level(TP2, f"TP2 {TP2:.2f}", "#1a5fb4"), Level(TP1, f"TP1 {TP1:.2f}", "#1a5fb4")], arrows=[Arrow(10, f"TP2 = 1R pe medie ({R2:.2f})", price=TP2, where="left", color="#1a5fb4", dx=-6, dy=-16), Arrow(12, f"TP1 = 1R pe prima tranșă ({R1:.2f})", price=TP1, where="above", color="#1a5fb4", dy=-10)], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=13.0, gap=28, body_w=14, left=60, width=700, top=56, title="Pasul 4, cele două ținte — weekly (exemplu construit)", caption="Lectura mea: mediind în jos, TP2 vine înaintea lui TP1. " "Fraza-sursă e neinteligibilă aici."), "pasul4_tinte", distante=(R1, R2)) # --------------------------------------------------- 8. pasul 5 — trail-ul cl5 = (ramp(44.00, 50.00, 5) + [49.20, 48.60, 49.40] # 0-7 trail 1 + ramp(49.40, 56.00, 5) + [55.00, 54.20, 55.40] # 8-15 trail 2 + ramp(55.40, 62.00, 5) + [61.00, 60.20, 61.40] # 16-23 trail 3 + ramp(61.40, 67.00, 4) + [66.20, 65.60]) # 24-29 trail 4 wk5 = [(0.40, 0.40)] * 30 vl5 = vols(30, 50, 12) cds5 = from_closes(cl5, wicks=wk5, vols=vl5, start=43.00) # fiecare trail e declanșat de un swing low anume; linia lui pornește de la # bara aceea, nu din marginea desenului SW = [6, 14, 22, 29] t1, t2, t3, t4 = (lo(cds5, 5, 7), lo(cds5, 13, 15), lo(cds5, 21, 23), lo(cds5, 28, 29)) assert t1 < t2 < t3 < t4, (t1, t2, t3, t4) # regresia de prins: o linie de trail nu are voie să treacă pe deasupra # prețului la dreapta ei — altfel desenul arată o poziție deja stopată for _i0, _t in zip(SW, (t1, t2, t3, t4)): assert all(c.l >= _t - 1e-9 for c in cds5[_i0:]), \ f"trail-ul {_t} de la bara {_i0} trece peste low-ul unei bare de după" assert lo(cds5, max(0, _i0 - 1), min(_i0 + 1, len(cds5) - 1)) == _t, \ f"trail-ul {_t} nu e ancorat de swing low-ul de la bara {_i0}" deco(Chart( candles=cds5, # etichetele la dreapta: la stânga ar cădea peste candele, pentru că # fiecare linie începe acum în mijlocul desenului levels=[Level(t1, f"trail 1: {t1:.2f}", "#c00", i0=SW[0]), Level(t2, f"trail 2: {t2:.2f}", "#c00", i0=SW[1]), Level(t3, f"trail 3: {t3:.2f}", "#c00", i0=SW[2]), Level(t4, f"trail 4: {t4:.2f}", "#c00", i0=SW[3])], marks=[Mark(6, "swing low 1", "#c00", "below", dy=14), Mark(14, "swing low 2", "#c00", "below", dy=14), Mark(22, "swing low 3", "#c00", "below", dy=14), Mark(29, "swing low 4", "#c00", "below", dy=14)], mas=[MA(20, "#b06000", dash="4 3")], scale=7.0, gap=17, body_w=9, left=52, width=700, top=64, vol_h=70, vol_label="volum", title="Pasul 5, trail pe swing low — weekly (decizia a fost pe monthly)", caption="Fiecare linie de trail pornește de la swing low-ul care o " "declanșează; stopul nu coboară niciodată."), "pasul5_trail") # ------------------------------------- 9. când nu merge (Tesla, 217.02/214.25) T_CL = ([320.00, 312.00, 304.00, 298.00] # 0-3 + [290.00, 282.00, 272.00, 262.00, 252.00, 244.00, 236.00, 230.00] # 4-11 + [232.00] # 12 buy hammer + [228.00, 222.00, 216.00] # 13-15 scoate stopul + [225.00] # 16 al doilea semnal + [240.00, 252.00, 262.00, 272.00, 282.00, 290.00, 298.00]) # 17-23 T_WK = ([(3.00, 3.00)] * 12 + [(2.00, 12.98)] + [(2.00, 2.00), (2.00, 2.00), (1.00, 1.75)] + [(2.00, 2.00)] * 8) T_VL = vols(24, 46, 12, i12=72, i15=78, i16=84, i17=88, i18=80) TC = from_closes(T_CL, wicks=T_WK, vols=T_VL, start=326.00) assert lo(TC, 12, 12) == 217.02, f"low-ul hammer-ului trebuie 217.02, e {lo(TC, 12, 12)}" assert lo(TC, 13, 15) == 214.25, f"minimul următor trebuie 214.25, e {lo(TC, 13, 15)}" assert 214.25 < 217.02, "al doilea minim e sub low-ul hammer-ului" assert len(TC[17:24]) == 7, "urcarea rostită e de 7 săptămâni" assert len(TC) == 24, "după al doilea semnal se desenează exact cele 7 bare rostite" assert all(TC[i].c < TC[i + 1].c for i in range(17, 23)), \ "cele 7 bare etichetate ca urcare trebuie să urce toate" deco(Chart( candles=TC, levels=[Level(217.02, "low-ul hammer-ului: 217.02", "#b06000", label_at="left"), # cele două minime sunt la 2.77 dolari: eticheta celui de jos # trece sub propria linie, altfel o taie linia de deasupra Level(214.25, "minimul de după: 214.25", "#c00", label_dy=18)], marks=[Mark(12, "buy hammer — primul semnal", "#b06000", dy=-10), Mark(17, "al doilea semnal, acesta merge", "#188a3e", dy=-10), Mark(23, "7 săptămâni de urcare", "#188a3e", dy=-10)], arrows=[Arrow(15, "stopul pus sub 217.02\na fost luat aici", price=214.25, where="below", color="#c00", dy=4)], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=2.6, gap=19, body_w=10, left=58, width=700, vol_h=70, vol_label="volum", title="Când nu merge — Tesla weekly", caption="Ale lui: 217.02, 214.25 și cele 7 săptămâni de după al doilea " "semnal; restul barelor e al meu."), "nu_merge", intregi=()) # ------------------------------------------- 10. filtrul de volum (schiță) SVG["divergenta"] = """ Filtrul de volum: divergență bull pe histograma instituțională Sus prețul, jos histograma. Cifrele de volum sunt citirile lui pe Ford; forma preț o desenez eu. PREȚ (weekly) picior 1 picior 2, mai jos și totuși urcă VOLUM INSTITUȚIONAL 0 minus 265 mil. minus 243 mil. — mai sus Prețul face minim mai jos, histograma nu: cumpărătorii nu mai cedează. Asta e divergența bull care validează filtrul. """ # ---------------------------------------- 11. rutina de scanare (schiță) SVG["flux"] = """ Rutina: scanezi doji, aștepți cu radar screen-ul 1. Scanner, pe candelă închisă doar DOJI, nu buy doji 2. Lista, 200-300 de simboluri ținută 4 luni, într-un excel 3. Radar screen lista stă acolo și clipește 4. Clipește = buy doji îl vezi în timp ce se formează De ce în ordinea asta: scanner-ul vede doar candela închisă, deci buy doji-ul l-ai afla vineri la 23 sau în ultima zi a lunii, prea târziu. Radar screen-ul citește candela în formare, deci îl prinzi cu 2-3 zile înainte să se închidă săptămâna sau luna. Când scanezi Vineri seara, după închidere, pentru weekly. Cu 2-3 zile înainte de finalul lunii, pentru monthly. Doji-ul rămâne valabil ca sursă de buy doji 1-4 perioade, de aceea lista se ține patru luni. """ # ---------------------------------------------------------------- substituire if os.path.exists(MD): with open(MD, encoding="utf-8") as f: md = f.read() lipsa = [n for n in SVG if f"" not in md] assert not lipsa, f"marcatori lipsă în .md: {lipsa}" for n, svg in SVG.items(): md = re.sub(rf"(\s*)?", lambda _m: f"\n{svg}", md, count=1) with open(MD, "w", encoding="utf-8") as f: f.write(md) print(f"\n{len(SVG)} diagrame substituite in {MD}") else: print(f"\n{len(SVG)} diagrame generate; {MD} nu exista inca")