"""Diagramele pentru summaries/06_MODUL_5/05_strategia_8_dhrt.md. Toate graficele cu candele / intrare / stop / ținte se construiesc cu svg_candles (o singură scară px/unitate pe desen), fiecare cifră din etichete e verificată prin assert, apoi se substituie marcatorii din .md. Regula de cifre: pe etichete intră DOAR prețuri rostite în transcriere (51, la XLF) sau prețuri din exemplul construit de mine, declarat ca atare în titlu și în caption. Valorile derivate din geometria cursului (SL la 1/3, TP1 = R, TP2 = proiecția gâtului) apar doar pe desenele construite, unde seria e a mea. Cele două schițe fără candele (`hs_pe_volum`, `cadrane`) sunt scrise de mână. Rulează: .venv/bin/python gen/strategia_8_dhrt.py """ import os import re import sys sys.path.insert(0, "/workspace/atm-curs-dl") from svg_candles import Chart, Level, Mark, MA, Zone, from_closes # noqa: E402 MD = ("/workspace/atm-curs-dl/summaries/06_MODUL_5/" "05_strategia_8_dhrt.md") SVG = {} # ----------------------------------------------------------------- unelte mici 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 floor_lows(cds, a, b, price): """Scurtează fitilele de jos ale barelor a..b până la `price`. Nu atinge corpurile: dacă un open sau close ar fi sub nivel, crapă. Lungimea fitilului e oricum un parametru liber al desenului; aici o fixez ca mai multe bare să se oprească exact pe nivelul pe care îl afirmă eticheta. """ for c in cds[a:b + 1]: assert min(c.o, c.c) >= price, f"corpul barei trece sub {price}" if c.l < price: c.l = price return price 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 deco(ch, name, distante=(), intregi=()): """Randează, verifică etichetele, salvează. `distante` = numere cu două zecimale permise în etichete fără să fie niveluri desenate (R-uri, înălțimi de pattern). `intregi` = întregi rostiți în transcriere, permiși în etichete. """ svg = ch.render() assert "\n\n" not in svg, f"{name}: linie goală în svg" assert len(ch.caption) <= 118, f"{name}: caption de {len(ch.caption)} caractere" desenate = ({f"{lv.price:.2f}" for lv in ch.levels} | {f"{z.p_top:.2f}" for z in ch.zones if z.p_top is not None} | {f"{z.p_bot:.2f}" for z in ch.zones if z.p_bot 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" for m in re.finditer( r"(? sus - 0.06 * (jos - sus), \ f"{name}: nivelul {lv.price} e lipit de marginea de sus" assert ch._y(lv.price) < jos + 0.06 * (jos - sus), \ f"{name}: nivelul {lv.price} e lipit de marginea de jos" SVG[name] = svg print(f" {name}: {len(ch.candles)} bare, {len(ch.levels)} niveluri") CONSTRUIT = ("Exemplu construit (fără ticker), la scară. Geometria e a cursului; " "prețurile și R-urile sunt ale mele.") # ===================================================== seria principală (HS răsturnat) # O singură serie construită, folosită de harta, pasul 3 (stopul) și pasul 4 # (țintele), ca să nu se schimbe cifrele de la o secțiune la alta. CL = ( [71.00, 69.00, 67.00, 65.00, 63.00, 61.00] # 0-5 downtrend anterior + [59.50, 58.80, 60.50, 62.00] # 6-9 umărul stâng + [62.80, 62.00, 63.00] # 10-12 gâtul, stânga (high 63.00) + [61.00, 58.00, 56.00, 54.00, 53.00, 52.30] # 13-18 căderea în cap + [54.00, 56.50, 58.50, 60.50, 62.00, 63.00] # 19-24 urcarea din cap + [62.30, 61.00, 59.30, 58.80, 59.60, 59.00] # 25-30 umărul drept + [59.10] # 31 doji + [65.00] # 32 breakout = intrare + [63.90, 64.60, 66.50, 69.00, 71.00] # 33-37 retest, apoi urcă + [73.00, 71.80, 74.00, 76.10, 77.50]) # 38-42 TP2, TP1, trail WK = ( [(0.40, 0.40)] * 6 + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.30), (0.30, 0.30)] + [(0.20, 0.30), (0.30, 0.30), (0.00, 0.30)] # 12: high-ul gâtului stâng + [(0.30, 0.30)] * 5 + [(0.30, 0.30)] + [(0.30, 0.30)] * 5 + [(0.00, 0.30)] # 24: high-ul gâtului drept + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.30), (0.30, 0.30), (0.30, 0.30), (0.40, 0.30)] + [(0.90, 0.50)] # 31: fitile de doji + [(0.20, 0.20)] # 32: candela de breakout + [(0.40, 0.40)] * 5 + [(0.40, 0.40)] * 5) VL = vols(43, 46, 12, i27=56, i28=60, i29=64, i30=68, i31=74, i32=112, i33=62, i34=66, i35=78, i36=84, i37=80, i39=86, i40=92) CDS = from_closes(CL, wicks=WK, vols=VL, start=73.00) CAP = lo(CDS, 13, 18) # low-ul capului UMD = lo(CDS, 25, 31) # nivelul umărului drept UMS = lo(CDS, 6, 9) # nivelul umărului stâng H = round(UMD - CAP, 2) # înălțimea cap -> umăr drept SL = round(CAP + H / 3, 2) # 1/3 de la cap: marginea de sus a treimii E = CDS[32].c # intrarea, la close-ul candelei de breakout R = round(E - SL, 2) TP1 = round(E + R, 2) # „măsoară din interiorul gâtului pe aici, iau un average" (`05_7_...txt:1304`): # nivelul se ia din interiorul gâtului, NU de la cele mai de sus vârfuri. Pe # desen vârfurile sunt mai sus (barele 13 și 25 ating 63.30), de aceea eticheta # nu are voie să spună „media celor două vârfuri". GAT = round((hi(CDS, 12, 12) + hi(CDS, 24, 24)) / 2, 2) H_HS = round(GAT - CAP, 2) TP2 = round(GAT + H_HS, 2) assert (CAP, UMD, UMS) == (52.00, 58.50, 58.50), (CAP, UMD, UMS) assert H == 6.50, H assert SL == 54.17 and CAP < SL < UMD, (CAP, SL, UMD) assert abs(CDS[31].c - CDS[31].o) <= 0.15 * (CDS[31].h - CDS[31].l), \ "candela 31 nu e doji" assert CDS[32].c > hi(CDS, 31, 31) and CDS[32].c > GAT, \ "candela de breakout trebuie să închidă peste doji ȘI peste gât" assert lo(CDS, 33, 37) > SL, "retestul nu trebuie să atingă stopul" assert hi(CDS, 38, 42) >= TP1 > TP2, \ "aici TP1 (=R) iese peste TP2 (proiecția gâtului) — exact cazul din lecție" assert lo(CDS, 13, 18) < lo(CDS, 6, 9) and lo(CDS, 13, 18) < lo(CDS, 25, 31), \ "capul trebuie să fie mai jos decât ambii umeri" assert VL[32] > max(VL[25:32]), "candela de breakout nu are volumul cel mai mare" # regresia de prins: eticheta gâtului spune „din interiorul gâtului, nu de la # vârfuri", deci nivelul trebuie să stea strict sub cel mai de sus vârf al zonei assert GAT < max(hi(CDS, 10, 13), hi(CDS, 24, 25)), \ f"gâtul {GAT} nu e sub vârfurile zonei: eticheta ar minți" print(f"construit: cap {CAP} umeri {UMS}/{UMD} H {H} SL {SL} intrare {E}" f" R {R} TP1 {TP1} gât {GAT} (h {H_HS}) TP2 {TP2}") # ------------------------------------------------------------------- 1. harta deco(Chart( candles=CDS, levels=[Level(SL, f"SL {SL:.2f}", "#c00", label_at="left"), Level(E, f"intrare {E:.2f}", "#188a3e"), Level(GAT, f"linia gâtului {GAT:.2f}", "#6a3d9a"), Level(TP2, f"TP2 {TP2:.2f} = proiecția gâtului", "#1a5fb4", label_at="left"), Level(TP1, f"TP1 {TP1:.2f} = distanța stopului", "#1a5fb4")], marks=[Mark(7, "umărul stâng", "#6a3d9a", "below", dy=14), Mark(17, "capul", "#6a3d9a", "below", dy=16), Mark(31, "doji", "#b06000", "below", dy=8), Mark(32, "buy doji = trigger", "#188a3e", dy=-40), Mark(34, "pullback la nivelul de breakout", "#b06000", "below", dy=22), Mark(41, "apoi trail", "#1a5fb4", dy=-8)], zones=[Zone(25, 31, "umărul drept", "#188a3e")], mas=[MA(20, "#b06000", dash="4 3")], scale=11.0, gap=15, body_w=9, left=58, width=700, top=46, vol_h=70, vol_label="volum", title="DHRT, tot trade-ul — HS răsturnat (exemplu construit)", caption=CONSTRUIT), "harta", distante=(R, H, H_HS)) # ===================================== 2. pasul 1, DA — capul în suport, umărul pe pivot S_CL = ( [48.00, 46.50, 45.00, 43.50, 42.00] # 0-4 downtrend + [40.60, 40.10, 41.50, 43.00] # 5-8 umărul stâng + [43.60, 42.80, 43.50] # 9-11 gâtul + [41.50, 39.00, 37.50, 36.30] # 12-15 căderea în cap + [37.80, 39.50, 41.00, 42.60, 43.40] # 16-20 urcarea + [42.40, 41.20, 40.30, 40.10, 40.80, 40.40] # 21-26 umărul drept + [40.50, 44.20, 45.60, 47.00, 48.50]) # 27-31 doji, breakout, urcă S_WK = ([(0.40, 0.40)] * 5 + [(0.30, 0.30), (0.30, 0.10), (0.30, 0.10), (0.30, 0.30)] + [(0.30, 0.30), (0.30, 0.30), (0.50, 0.30)] + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.30), (0.30, 0.30)] + [(0.30, 0.30)] * 4 + [(0.40, 0.30)] + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.30), (0.30, 0.10), (0.30, 0.10), (0.30, 0.30)] + [(0.70, 0.40)] + [(0.20, 0.20)] + [(0.40, 0.40)] * 3) S_VL = vols(32, 44, 12, i23=54, i24=58, i25=62, i26=66, i27=70, i28=104, i29=84) SC = from_closes(S_CL, wicks=S_WK, vols=S_VL, start=49.00) SUP, PIV = lo(SC, 12, 15), lo(SC, 21, 27) assert SUP == 36.00 and PIV == 40.00, (SUP, PIV) assert PIV > SUP, "umărul drept trebuie să stea peste suportul capului" assert abs(lo(SC, 5, 8) - PIV) <= 0.20, "umerii trebuie să fie aproximativ simetrici" assert abs(SC[27].c - SC[27].o) <= 0.15 * (SC[27].h - SC[27].l), "bara 27 nu e doji" assert SC[28].c > hi(SC, 27, 27) and SC[28].c > hi(SC, 9, 11), \ "breakout-ul trebuie să treacă peste doji și peste gât" deco(Chart( candles=SC, levels=[Level(SUP, f"suport monthly {SUP:.2f}", "#b06000", label_at="left"), Level(PIV, f"pivot monthly {PIV:.2f}", "#6a3d9a", label_at="left")], marks=[Mark(6, "umărul stâng", "#6a3d9a", dy=-22), Mark(14, "capul, în suport", "#b06000", "below", dy=16), Mark(27, "doji", "#b06000", "below", dy=28), Mark(28, "buy doji = trigger", "#188a3e", dy=-44)], zones=[Zone(21, 27, "umărul drept, pullback pe pivot", "#188a3e")], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=14.0, gap=17, body_w=9, left=58, width=700, top=46, vol_h=70, vol_label="volum", title="Pasul 1, DA — capul în suport, umărul drept pe pivot", caption="Exemplu construit (fără ticker), la scară. Volumul crește pe " "ultimele bare ale umărului și explodează pe breakout."), "pas1_da") # ======================================== 3. pasul 1, NU — triple bottom la al 4-lea test N_CL = ( [38.00, 36.50, 35.00, 33.60, 32.40] # 0-4 downtrend + [31.10, 32.80, 34.00] # 5-7 piciorul 1 + [33.00, 31.90, 31.20, 32.60, 33.80] # 8-12 piciorul 2 + [32.90, 31.80, 31.10, 32.20, 33.20] # 13-17 piciorul 3 + [32.40, 31.60, 31.00, 31.40, 30.20, 28.60, 27.00]) # 18-24 al 4-lea: rupe N_WK = ([(0.40, 0.40)] * 5 + [(0.30, 0.10), (0.30, 0.30), (0.30, 0.30)] + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.10), (0.30, 0.30), (0.30, 0.30)] + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.10), (0.30, 0.30), (0.30, 0.30)] + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.20), (0.30, 0.30), (0.30, 0.30), (0.30, 0.30), (0.30, 0.30)]) N_VL = vols(25, 54, 14, i5=74, i10=58, i15=46, i20=36, i21=40, i22=62, i23=70, i24=76) NC = from_closes(N_CL, wicks=N_WK, vols=N_VL, start=39.00) BAZA = floor_lows(NC, 5, 17, 31.00) assert lo(NC, 5, 17) == 31.00, lo(NC, 5, 17) _pic = [min(c.c for c in NC[a:b + 1]) for a, b in ((5, 7), (8, 12), (13, 17))] assert max(_pic) - min(_pic) <= 0.30, \ f"cele trei picioare trebuie să închidă aproximativ la același nivel: {_pic}" assert lo(NC, 18, 24) < BAZA, "al 4-lea test trebuie să rupă baza" assert N_VL[5] > N_VL[10] > N_VL[15] > N_VL[20], \ "volumul trebuie să scadă de la un picior la altul" deco(Chart( candles=NC, levels=[Level(BAZA, f"baza testată de 3 ori: {BAZA:.2f}", "#c00")], marks=[Mark(5, "1", "#6a3d9a", "below", dy=14), Mark(10, "2", "#6a3d9a", "below", dy=14), Mark(15, "3", "#6a3d9a", "below", dy=14), Mark(20, "al 4-lea test: nu mai ține", "#c00", "below", dy=96), Mark(8, "volumul scade la fiecare picior", "#c00", dy=-8)], zones=[Zone(18, 24, "breakdown, nu breakout", "#c00")], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=20.0, gap=19, body_w=9, left=58, width=700, top=46, vol_h=70, label_gap=60, vol_label="volum", title="Pasul 1, NU — triple bottom la al 4-lea test", caption="Exemplu construit (fără ticker), la scară. Cu cât baza e retestată " "mai des și volumul scade, cresc șansele să cadă."), "pas1_nu") # ========================================= 4. pasul 2, DA — trigger-ul din umărul drept T_CL = ([55.00, 56.50, 58.00, 59.50, 61.00, 62.20, 63.00] # 0-6 urcarea din cap + [62.40, 61.30, 60.20, 59.40, 59.90, 59.20, 59.70, 59.10, 59.80, 59.30] # 7-16 consolidarea + [59.40] # 17 doji + [65.00] # 18 buy doji/breakout + [64.20, 65.80, 67.50, 69.00]) # 19-22 continuă T_WK = ([(0.40, 0.40)] * 7 + [(0.30, 0.30)] * 10 + [(0.90, 0.70)] # 17 doji + [(0.20, 0.20)] # 18 breakout + [(0.40, 0.40)] * 4) T_VL = vols(23, 44, 10, i4=78, i8=46, i9=42, i10=40, i11=44, i12=48, i13=52, i14=58, i15=64, i16=70, i17=76, i18=118, i19=70, i20=84, i21=90) TC = from_closes(T_CL, wicks=T_WK, vols=T_VL, start=54.00) CONS_HI = hi(TC, 7, 17) assert abs(TC[17].c - TC[17].o) <= 0.15 * (TC[17].h - TC[17].l), "bara 17 nu e doji" assert TC[18].c > CONS_HI, "breakout-ul trebuie să închidă peste vârful consolidării" assert T_VL[18] > T_VL[4], "volumul de breakout trebuie să treacă peste swing high-ul lui" assert T_VL[10] < T_VL[13] < T_VL[16] < T_VL[17], "volumul nu e în acumulare" deco(Chart( candles=TC, levels=[Level(CONS_HI, f"vârful consolidării {CONS_HI:.2f}", "#6a3d9a", label_at="left")], marks=[Mark(17, "doji: setup-ul", "#b06000", "below", dy=26), Mark(18, "buy doji = trigger", "#188a3e", dy=-14), Mark(11, "↓ volum în acumulare: bare tot mai mari", "#188a3e", "below", dy=26), Mark(4, "↓ swing high de volum", "#1a5fb4", "below", dy=70)], zones=[Zone(7, 17, "umărul drept: 11 bare de consolidare", "#188a3e")], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=16.0, gap=26, body_w=11, left=58, width=700, top=46, vol_h=80, vol_label="volum", title="Pasul 2, DA — trigger valid din umărul drept", caption="Exemplu construit (fără ticker). Volumul de breakout trece peste " "swing high-ul anterior de volum."), "pas2_da") # ========================================== 5. pasul 2, NU — volumul rupe în jos X_CL = ([55.00, 56.50, 58.00, 59.50, 61.00, 62.20, 63.00] + [62.40, 61.30, 60.20, 59.40, 59.90, 59.20, 59.70, 59.10, 59.80, 59.30] + [59.40] # 17 același doji + [64.40] # 18 breakout, dar fără volum + [62.00, 60.00, 57.50, 55.00]) # 19-22 breakdown X_WK = T_WK # Regula de „nu intra" din sursă e despre o histogramă CU SEMN care cade sub # swing low-ul ei („histograma asta cade sub swing low-ul de aici […] iar prețul # încă nu e acolo"), nu despre bare brute pornite de la o bază: pe bare # nesemnate „minime tot mai joase" nu înseamnă nimic vizual. De aceea panoul de # jos al acestei diagrame e desenat în jurul unui zero (`vol_zero`), cu trei # minime tot mai joase, iar bara candelei de breakout fals e mică și sub zero. ZERO = 50.0 X_VL = [60.0, 64.0, 68.0, 74.0, 80.0, 72.0, 66.0, # 0-6 urcarea 56.0, 50.0, 42.0, # 7-9 minimul 1 48.0, 46.0, 38.0, # 10-12 minimul 2 44.0, 42.0, 34.0, 40.0, # 13-16 minimul 3 36.0, # 17 doji 44.0, # 18 breakout fals 30.0, 24.0, 20.0, 16.0] # 19-22 breakdown XC = from_closes(X_CL, wicks=X_WK, vols=X_VL, start=54.00) assert abs(XC[17].c - XC[17].o) <= 0.15 * (XC[17].h - XC[17].l), "bara 17 nu e doji" assert XC[18].c > hi(XC, 7, 17), "breakout-ul de preț există și aici" assert X_VL[18] < X_VL[4], "volumul de breakout NU trebuie să treacă swing high-ul" assert X_VL[18] < ZERO, \ "pe candela de breakout histograma trebuie să fie deja sub zero" _SW = (9, 12, 15) for _i in _SW: assert X_VL[_i - 1] > X_VL[_i] < X_VL[_i + 1], \ f"bara {_i} nu e un minim local al histogramei" assert X_VL[_i] < ZERO, f"minimul de la bara {_i} trebuie să fie sub zero" assert X_VL[_SW[0]] > X_VL[_SW[1]] > X_VL[_SW[2]], \ f"minimele histogramei nu coboară: {[X_VL[i] for i in _SW]}" assert XC[22].c < lo(XC, 7, 17), "după breakout-ul fals prețul rupe consolidarea" deco(Chart( candles=XC, levels=[Level(lo(XC, 7, 17), f"baza {lo(XC, 7, 17):.2f}", "#c00", label_at="left")], marks=[Mark(17, "doji", "#b06000", "below", dy=26), Mark(18, "închide peste consolidare, dar volumul e deja sub zero", "#c00", dy=-10), Mark(11, "histograma rupe sub propriul swing low, de trei ori", "#c00", "below", dy=46), Mark(22, "breakdown", "#c00", "below", dy=14)], zones=[Zone(7, 17, "aceeași consolidare de 11 bare", "#c00")], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=14.0, gap=26, body_w=11, left=58, width=700, top=46, vol_h=80, vol_zero=ZERO, vol_label="histogramă de volum, cu semn (stil on balance volume)", title="Pasul 2, NU — breakout de preț fără breakout de volum", caption="Exemplu construit. Prețul e identic cu al diagramei de deasupra; " "panoul de jos e histograma cu semn, nu volumul brut."), "pas2_nu") # ================================================== 6. pasul 3 — stopul, treimea de jos deco(Chart( candles=CDS[6:36], levels=[Level(UMD, f"umăr drept {UMD:.2f}", "#6a3d9a"), Level(CAP, f"cap {CAP:.2f} — baza lui H", "#6a3d9a", label_at="left"), Level(SL, f"SL cel mai sus permis: {SL:.2f} = cap + H/3", "#c00"), Level(E, f"intrare {E:.2f}; distanța până la SL e R", "#188a3e", label_at="left")], marks=[Mark(12, "capul", "#6a3d9a", "below", dy=16), Mark(22, "banda roșie = treimea de jos din H", "#c00", "below", dy=104)], zones=[Zone(8, 27, "", "#c00", p_top=SL, p_bot=CAP)], scale=13.0, gap=20, body_w=10, left=58, width=700, top=46, title="Pasul 3, stopul — H de la cap la umărul drept, apoi o treime", caption=CONSTRUIT), "pas3_stop", distante=(H, R)) # ========================================================= 7. pasul 4 — cele două ținte deco(Chart( candles=CDS[18:], levels=[Level(CAP, f"capul {CAP:.2f} = baza înălțimii HS", "#6a3d9a"), Level(SL, f"SL {SL:.2f}", "#c00", label_at="left"), Level(E, f"intrare {E:.2f}", "#188a3e", label_at="left"), Level(GAT, f"linia gâtului {GAT:.2f}, din interiorul gâtului " f"(nu de la vârfuri)", "#6a3d9a", label_at="left"), Level(TP2, f"TP2 {TP2:.2f} = gât + înălțimea HS " f"({GAT:.2f} − {CAP:.2f} = {H_HS:.2f})", "#1a5fb4", label_at="left"), Level(TP1, f"TP1 {TP1:.2f} = intrare + R", "#1a5fb4", label_at="left")], marks=[Mark(14, "buy doji", "#188a3e", dy=-10)], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=12.0, gap=24, body_w=10, left=58, width=700, top=46, title="Pasul 4, țintele — TP1 = R, TP2 = proiecția gâtului", caption="Exemplu construit (fără ticker), la scară. Aici TP1 iese PESTE TP2; " "ordinea lor nu e fixă."), "pas4_tinte", distante=(R, H_HS)) # ================================================ 8. retestul „locului crimei", DA Y_CL = ([50.00, 51.20, 52.40, 53.00, 52.20, 53.40, 52.60, 53.20, 52.40, 53.00] # 0-9 consolidarea + [56.00] # 10 breakout + [54.60, 53.00, 52.70] # 11-13 retest în consolidare + [52.80] # 14 doji + [57.20, 58.80, 60.50, 62.00]) # 15-18 pleacă Y_WK = ([(0.30, 0.30)] * 10 + [(0.20, 0.20)] + [(0.30, 0.30), (0.30, 0.30), (0.30, 0.30)] + [(0.60, 0.50)] + [(0.30, 0.30)] * 4) # barele de retest trebuie să fie mai mici decât ale consolidării: legenda # spune „volum mic pe retest, mare pe repornire" Y_VL = vols(19, 44, 10, i10=96, i11=30, i12=26, i13=24, i14=28, i15=88, i16=82) YC = from_closes(Y_CL, wicks=Y_WK, vols=Y_VL, start=49.00) C_HI, C_LO = hi(YC, 0, 9), lo(YC, 0, 9) JUM = round((C_HI + C_LO) / 2, 2) assert YC[10].c > C_HI, "bara 10 trebuie să facă breakout peste consolidare" assert JUM < lo(YC, 11, 14) < C_HI, \ "retestul trebuie să intre în consolidare, dar să se oprească peste jumătate" assert YC[18].c > YC[10].c, "după retest prețul trebuie să plece mai sus" assert max(Y_VL[11:15]) < min(Y_VL[0:10]), \ "barele de retest trebuie să fie sub toate barele consolidării" assert Y_VL[15] > max(Y_VL[0:15]) * 0.9, "repornirea trebuie să vină cu volum" # dreptunghiul consolidării e ancorat exact de cele două cifre din etichete, # fără cei 8 px de padding impliciți: altfel capacul nu cade pe 53.70 assert JUM == round((C_HI + C_LO) / 2, 2), "jumătatea nu e a intervalului declarat" deco(Chart( candles=YC, levels=[Level(C_HI, f"vârful consolidării {C_HI:.2f} = locul crimei", "#6a3d9a"), Level(JUM, f"jumătatea consolidării {JUM:.2f} " f"= ({C_HI:.2f} + {C_LO:.2f}) / 2", "#b06000"), Level(C_LO, f"baza consolidării {C_LO:.2f}", "#6a3d9a")], marks=[Mark(10, "breakout", "#188a3e", dy=-10), Mark(13, "retest, se oprește peste jumătate", "#188a3e", "below", dy=20), Mark(14, "doji aici: a doua intrare", "#188a3e", "below", dy=38)], zones=[Zone(0, 9, "consolidarea", "#6a3d9a", p_top=C_HI, p_bot=C_LO)], scale=22.0, gap=30, body_w=12, left=58, width=700, top=46, vol_h=70, vol_label="volum", title="Retestul, DA — se oprește în jumătatea de sus", caption="Exemplu construit (fără ticker). Volum mic pe retest, mare pe " "repornire: nu vin vânzători noi."), "retest_da") # ================================================ 9. retestul „locului crimei", NU Z_CL = ([50.00, 51.20, 52.40, 53.00, 52.20, 53.40, 52.60, 53.20, 52.40, 53.00] + [56.00] + [54.80, 53.20, 51.80, 50.40] # 11-14 trece tot jos + [49.00, 47.60, 46.00, 44.50]) # 15-18 breakdown Z_WK = ([(0.30, 0.30)] * 10 + [(0.20, 0.20)] + [(0.30, 0.30)] * 8) Z_VL = vols(19, 44, 10, i10=96, i11=58, i12=64, i13=72, i14=80, i15=88, i16=94) ZC = from_closes(Z_CL, wicks=Z_WK, vols=Z_VL, start=49.00) assert hi(ZC, 0, 9) == C_HI and lo(ZC, 0, 9) == C_LO, \ "consolidarea trebuie să fie identică cu cea din diagrama DA" assert ZC[10].c > C_HI, "și aici există breakout" assert ZC[14].c < JUM and ZC[18].c < C_LO, "retestul trece sub bază: breakdown" assert Z_VL[13] > Z_VL[11], "volumul crește pe coborâre — vin vânzători" deco(Chart( candles=ZC, levels=[Level(C_HI, f"vârful consolidării {C_HI:.2f}", "#6a3d9a"), Level(JUM, f"jumătatea consolidării {JUM:.2f}", "#b06000"), Level(C_LO, f"baza {C_LO:.2f}", "#c00")], marks=[Mark(10, "același breakout", "#188a3e", dy=-10), Mark(13, "retestul trece sub jumătate", "#c00", "below", dy=52), Mark(17, "volum în creștere pe coborâre", "#c00", "below", dy=16)], zones=[Zone(0, 9, "aceeași consolidare", "#6a3d9a", p_top=C_HI, p_bot=C_LO)], scale=15.0, gap=30, body_w=12, left=58, width=700, top=46, vol_h=70, vol_label="volum", title="Retestul, NU — trece sub jumătate și rupe baza", caption="Exemplu construit (fără ticker). Același breakout; diferă cât de " "adânc cade retestul și ce face volumul."), "retest_nu") # ================================================= 10. varianta short — ascending wedge W_CL = ([52.10, 52.50, 52.90] # 0-2 uptrend anterior + [53.40, 52.80, 52.10] # 3-5 vârf 1, picior 1 + [52.80, 53.40, 53.90, 53.20, 52.70] # 6-10 vârf 2, picior 2 + [53.20, 53.70, 54.10, 53.60, 53.20] # 11-15 vârf 3, picior 3 + [53.60, 53.95] # 16-17 ultimul test + [53.90] # 18 sell doji + [53.10, 52.50, 52.00, 51.50, 51.00, 50.90, 51.20]) # 19-25 cade spre 51 W_WK = ([(0.25, 0.25)] * 18 + [(0.55, 0.50)] + [(0.25, 0.25)] * 7) W_VL = vols(26, 48, 10, i3=62, i8=50, i10=40, i12=36, i13=34, i15=30, i16=28, i17=26, i18=30, i19=78, i20=86, i21=92, i22=96, i23=88) WC = from_closes(W_CL, wicks=W_WK, vols=W_VL, start=51.80) TINTA = 51.00 L1, L2, L3 = lo(WC, 4, 6), lo(WC, 9, 11), lo(WC, 14, 16) H1, H2, H3 = hi(WC, 2, 4), hi(WC, 7, 9), hi(WC, 12, 14) assert L1 < L2 < L3, f"minimele wedge-ului trebuie să urce: {L1} {L2} {L3}" assert H1 < H2 < H3, f"maximele trebuie să urce și ele: {H1} {H2} {H3}" assert (H1 - L1) > (H2 - L2) > (H3 - L3), \ f"wedge înseamnă convergență: {H1-L1:.2f} {H2-L2:.2f} {H3-L3:.2f}" assert abs(WC[18].c - WC[18].o) <= 0.20 * (WC[18].h - WC[18].l), "bara 18 nu e doji" assert WC[19].c < lo(WC, 17, 18), "sell doji-ul trebuie să rupă în jos" assert lo(WC, 21, 25) <= TINTA, "seria trebuie să atingă ținta rostită, 51" assert W_VL[17] == min(W_VL), "volumul trebuie să se stingă în vârful wedge-ului" assert max(W_VL[19:23]) > max(W_VL[:19]), "volumul trebuie să explodeze pe coborâre" print(f" wedge: picioare {L1}/{L2}/{L3} vârfuri {H1}/{H2}/{H3} " f"deschideri {H1-L1:.2f}/{H2-L2:.2f}/{H3-L3:.2f}") deco(Chart( candles=WC, levels=[Level(TINTA, "ținta rostită în lecție: 51", "#1a5fb4", label_at="left")], marks=[Mark(16, "volumul se stinge", "#c00", "below", dy=30), Mark(18, "sell doji = trigger de short", "#c00", dy=-10), Mark(23, "ținta atinsă", "#1a5fb4", "below", dy=16), Mark(10, "minimele urcă mai repede decât maximele", "#c00", "below", dy=40)], zones=[Zone(2, 18, "", "#c00")], mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")], scale=65.0, gap=23, body_w=10, left=58, width=700, top=46, vol_h=70, vol_label="volum", title="Varianta short — XLF, ascending wedge", caption="Al lui: ținta de 51 și patternul pe XLF. Barele sunt ale mele; " "wedge-ul se îngustează, nu urcă paralel."), "short_wedge", intregi=(51,)) # ============================================ 11. HS răsturnat și pe volum (schiță) SVG["hs_pe_volum"] = """ Secretul: același HS răsturnat, de două ori — pe preț și pe volum Sus prețul, jos linia de trend a volumului. Când ambele desenează aceeași formă, setup-ul e mai tare. PREȚ linia gâtului umăr stâng cap umăr drept breakout VOLUM (linia de trend) umăr stâng cap umăr drept, mai sus decât cel stâng Umărul drept de pe volum mai sus decât cel stâng: cumpărătorii revin cu mai multă forță decât prima dată. Asta cauți. """ # ================================================ 12. cadranele de filtru (schiță) SVG["cadrane"] = """ „Cadran long" — cele două cadrane în care ai voie să iei trigger-ul improving se îmbunătățește, încă sub piață DA outperforming bate piața DA lagging rămâne în urmă NU weakening se deteriorează NU slab ← → puternic Ordinea de importanță, după autor 1. volumul — oricare dintre cele două 2. relative strength în cadran long 3. power în cadran long Toate trei aliniate = Trinity Nu e obligatoriu: „nu poți să le ai chiar pe toate". Volumul singur e suficient ca filtru minim. Relative strength se citește față de SPY, deci are sens doar pe equities (acțiuni, ETF-uri de sector). Pe commodities schimbi reperul pe o clasă similară: petrol față de gaze, aur față de argint, grâu față de porumb. """ # ---------------------------------------------------------------- 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")