MODUL 5, lectia 3: patternuri complexe partea a 4-a (wedge si flag)

11 diagrame generate cu svg_candles, audit curat. Nou fata de partile 2 si 3:
wedge descendent/ascendent, bull si bear flag ca ABCD, criteriul geometric
wedge vs flag (converg / paralele), cele doua tinte ale flag-ului (canalul si
batul steagului), si marubozul retestat la jumatate cu stopul la open.

Lectia nu rosteste nicio cifra de pret: toate preturile si R-urile din diagrame
sunt construite si marcate ca atare. Cifrele cursului sunt doar numaratori si
Pareto 20/80.

Ramase, cosmetice: eticheta "canal 3.00" sta peste latura punctata si
"TP2 42.40" atinge o candela la bear flag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThN7ixZrowca8aTGrK1MUJ
This commit is contained in:
Claude Agent
2026-10-01 22:12:21 +00:00
parent 4effcdabf1
commit 0344e93028
3 changed files with 3854 additions and 0 deletions

View File

@@ -0,0 +1,650 @@
"""Diagramele pentru summaries/06_MODUL_5/03_patternuri_complexe_partea_a_patra.md.
Toate graficele se construiesc cu svg_candles (o singură scară px/unitate pe
desen), fiecare cifră afirmată în text e verificată prin assert, apoi se
substituie marcatorii <!--SVG:nume--> din fișierul .md.
Liniile de trend (laturile wedge-ului, canalul flag-ului) nu există ca
primitivă în svg_candles, așa că le injectez după randare, folosind exact
_cx/_y ale graficului randat — deci aceeași scară ca restul desenului.
Rulează: .venv/bin/python gen/patternuri_complexe_4.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/"
"03_patternuri_complexe_partea_a_patra.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=10, fade=None, **over):
"""Volume deterministe; `fade=(i0,i1)` le subțiază liniar în consolidare."""
v = [float(base + ((i * 7) % span) - span // 2) for i in range(n)]
if fade:
i0, i1 = fade
for i in range(i0, i1 + 1):
k = (i - i0) / max(1, i1 - i0)
v[i] = round(v[i] * (1.0 - 0.45 * k))
for k, x in over.items():
v[int(k[1:])] = 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 seg(i0, p0, i1, p1, color="#8b5cf6", dash="", w=1.6):
return dict(i0=i0, p0=p0, i1=i1, p1=p1, color=color, dash=dash, w=w)
def txt(i, p, s, color="#8b5cf6", anchor="middle", dx=0.0, dy=0.0, bold=True):
return dict(i=i, p=p, s=s, color=color, anchor=anchor, dx=dx, dy=dy, bold=bold)
def deco(ch, name, segs=(), texts=(), distante=()):
"""Randează graficul, injectează liniile/textele, verifică, salvează."""
saved = ch.top, ch.left, ch.width
try:
svg = ch._render()
extra = []
for s in segs:
extra.append(
f'<line x1="{ch._cx(s["i0"]):.1f}" y1="{ch._y(s["p0"]):.1f}" '
f'x2="{ch._cx(s["i1"]):.1f}" y2="{ch._y(s["p1"]):.1f}" '
f'stroke="{s["color"]}" stroke-width="{s["w"]}"'
+ (f' stroke-dasharray="{s["dash"]}"' if s["dash"] else "") + "/>")
for t in texts:
style = "font-size:11px" + (";font-weight:bold" if t["bold"] else "")
extra.append(
f'<text x="{ch._cx(t["i"]) + t["dx"]:.1f}" '
f'y="{ch._y(t["p"]) + t["dy"]:.1f}" fill="{t["color"]}" '
f'text-anchor="{t["anchor"]}" style="{style}">{t["s"]}</text>')
finally:
ch.top, ch.left, ch.width = saved
if extra:
svg = svg.replace("\n</svg>", "\n " + "\n ".join(extra) + "\n</svg>")
assert "\n\n" not in svg, f"{name}: linie goală în svg"
assert len(ch.caption) <= 110, f"{name}: caption de {len(ch.caption)} caractere"
desenate = ({f"{lv.price:.2f}" for lv in ch.levels}
| {f"{d:.2f}" for d in distante})
for t in re.findall(r"<text\b[^>]*>(.*?)</text>", svg, re.S):
for m in re.finditer(r"\d+\.\d\d", re.sub(r"<[^>]+>", "", t)):
assert m.group(0) in desenate, \
f"{name}: eticheta numește {m.group(0)}, care nu e un nivel desenat"
sus = min(ch._y(c.h) for c in ch.candles)
jos = max(ch._y(c.l) for c in ch.candles)
for lv in ch.levels:
assert ch._y(lv.price) > 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")
CAP = ("Exemplu construit (fără ticker), la scară: lecția nu rostește nicio "
"cifră. Regula de măsurare e a cursului.")
# ============================================================ 1. wedge descendent
U0, L0 = 52.00, 48.00 # laturile la deschiderea triunghiului
BAZA_D = round(U0 - L0, 2) # 4.00
def up_d(i):
return round(U0 - 0.30 * (i - 10), 2)
def lo_d(i):
return round(L0 - 0.15 * (i - 10), 2)
cl = (ramp(42.6, 51.30, 10) # 0-9 trend ascendent anterior
+ [51.80] # 10 swing high, latura de sus
+ ramp(51.80, 47.75, 3) # 11-13 picior 1
+ ramp(47.75, 50.00, 3) # 14-16 swing high 2
+ ramp(50.00, 46.85, 3) # 17-19 picior 2
+ ramp(46.85, 48.20, 3) # 20-22 swing high 3
+ ramp(48.20, 46.80, 2) # 23-24 spre capul triunghiului
+ [46.60] # 25 hammer în capul wedge-ului
+ [47.00] # 26 încă în triunghi
+ [48.10] # 27 candela de breakout
+ [48.60, 49.30, 48.90, 50.10, 51.00, 52.20, 51.80]) # 28-34 spre TP1
wk = ([(0.18, 0.18)] * 10 + [(0.20, 0.15)]
+ [(0.05, 0.15), (0.05, 0.15), (0.05, 0.20)]
+ [(0.15, 0.05), (0.15, 0.05), (0.20, 0.05)]
+ [(0.05, 0.15), (0.05, 0.15), (0.05, 0.20)]
+ [(0.15, 0.05), (0.15, 0.05), (0.20, 0.05)]
+ [(0.05, 0.15), (0.05, 0.15)]
+ [(0.05, 0.85)] + [(0.12, 0.12)] + [(0.10, 0.15)]
+ [(0.15, 0.15), (0.20, 0.12), (0.12, 0.20), (0.18, 0.15),
(0.15, 0.15), (0.15, 0.15), (0.12, 0.22)])
cds = from_closes(cl, wicks=wk, start=42.60,
vols=vols(35, 46, 10, fade=(11, 25), b27=98, b28=84, b33=76))
assert hi(cds, 10, 10) == U0 and lo(cds, 13, 13) == lo_d(13)
assert hi(cds, 16, 16) == up_d(16) and lo(cds, 19, 19) == lo_d(19)
assert hi(cds, 22, 22) == up_d(22) and lo(cds, 25, 25) == lo_d(25)
assert lo(cds, 19, 19) < lo(cds, 13, 13), "picioarele trebuie să coboare"
assert hi(cds, 22, 22) < hi(cds, 16, 16) < hi(cds, 10, 10), "vârfurile coboară mai repede"
# convergența: deschiderea triunghiului se strânge spre cap
assert (up_d(25) - lo_d(25)) < BAZA_D / 2
E_D = cds[27].c # 48.10 — close-ul candelei de breakout
SL_D = 46.70 # sub low-ul candelei de breakout (46.85)
TP_D = round(E_D + BAZA_D, 2) # 52.10
R_D = round((TP_D - E_D) / (E_D - SL_D), 1)
assert cds[27].c > up_d(27) and cds[26].c < up_d(26), "breakout exact pe bara 27"
assert SL_D < lo(cds, 27, 27) < E_D
assert hi(cds, 28, 34) >= TP_D, "seria trebuie să atingă TP1"
assert abs(TP_D - (E_D + BAZA_D)) < 1e-9
print(f"wedge desc: baza {BAZA_D} intrare {E_D} SL {SL_D} TP1 {TP_D} = {R_D}R")
deco(Chart(
candles=cds,
levels=[Level(SL_D, f"SL {SL_D:.2f}", "#c00", label_at="left"),
Level(E_D, f"intrare {E_D:.2f}", "#188a3e"),
Level(TP_D, f"TP1 {TP_D:.2f} = {R_D}R", "#1a5fb4")],
marks=[Mark(2, "trend ascendent anterior", "#188a3e", "below", dy=8),
Mark(13, "picior 1", "#c00", "below"),
Mark(19, "picior 2 (mai jos)", "#c00", "below"),
Mark(25, "hammer in capul triunghiului", "#b06000", "below"),
Mark(27, "breakout cu volum", "#188a3e")],
zones=[Zone(10, 26, "wedge descendent = consolidare contra-trend (17 bare)",
"#8b5cf6")],
arrows=[Arrow(29, "stopul sta sub low-ul candelei de breakout:\n"
"daca pretul se intoarce in triunghi, ideea a murit",
price=SL_D, where="below", color="#c00", dy=18)],
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
scale=24, gap=16, body_w=9, left=46, width=730, top=62, vol_h=70,
title="Wedge descendent — bull",
caption=CAP),
"wedge_desc",
segs=[seg(10, U0, 28, up_d(28)),
seg(10, L0, 13, lo_d(13), "#8b5cf6", "4 3", 1.2),
seg(13, lo_d(13), 27, lo_d(27)),
seg(10.0, L0, 10.0, U0, "#8b5cf6", "", 2.2),
seg(30.6, E_D, 30.6, TP_D, "#1a5fb4", "", 2.2)],
texts=[txt(10.1, (U0 + L0) / 2, f"baza = {BAZA_D:.2f}", anchor="start", dx=4),
txt(30.4, (E_D + TP_D) / 2, f"aceeasi {BAZA_D:.2f}", "#1a5fb4",
anchor="end", dx=-4)],
distante=(BAZA_D,))
# ================================================ 2. wedge vs flag (forma)
cl = (ramp(29.6, 34.00, 6) # 0-5 urcare
+ [33.40, 32.10, 33.10, 32.60, 31.60, 32.30, 31.90, 31.70] # 6-13 convergent
+ ramp(31.70, 37.00, 5) # 14-18 urcare
+ [36.40, 35.00, 36.30, 34.90, 36.20, 34.80, 36.10, 34.70] # 19-26 paralel
+ ramp(34.70, 39.00, 5)) # 27-31 urcare
wk = [(0.15, 0.15)] * 32
cds = from_closes(cl, wicks=wk, start=29.40, vols=vols(32, 44, 10))
deco(Chart(
candles=cds,
zones=[Zone(6, 13, "laturile converg = WEDGE (triunghi)", "#999"),
Zone(19, 26, "laturile sunt paralele = FLAG (dreptunghi)", "#999")],
scale=26, gap=17, body_w=9, left=46, width=660, top=58,
title="Wedge sau flag?",
caption="Exemplu construit (fără ticker). Dacă prelungite laturile se "
"întretaie, e wedge; dacă rămân paralele, e flag."),
"wedge_vs_flag",
segs=[seg(6, 34.00, 14, 32.20), seg(6, 31.90, 14, 32.00),
seg(14, 32.26, 15.6, 32.11, "#8b5cf6", "3 3", 1.0),
seg(19, 36.55, 27, 36.25, "#1a5fb4"), seg(19, 34.85, 27, 34.55, "#1a5fb4"),
seg(27, 36.25, 29, 36.18, "#1a5fb4", "3 3", 1.0),
seg(27, 34.55, 29, 34.48, "#1a5fb4", "3 3", 1.0)],
texts=[txt(15.8, 32.11, "se intretaie", anchor="start", dx=2, dy=4),
txt(29.2, 35.35, "nu se intretaie", "#1a5fb4", anchor="start", dx=2)])
# ========================================== 3. wedge ascendent — schimbare de trend
U1, L1 = 48.00, 45.00
BAZA_A = round(U1 - L1, 2) # 3.00
def up_a(i):
return round(U1 + 0.15 * (i - 10), 2)
def lo_a(i):
return round(L1 + 0.30 * (i - 10), 2)
cl = (ramp(41.0, 47.60, 10) # 0-9 trend ascendent anterior
+ [47.80] # 10 swing high pe latura de sus
+ ramp(47.80, 46.10, 3) # 11-13 swing low pe latura de jos
+ ramp(46.10, 48.70, 3) # 14-16 swing high 2
+ ramp(48.70, 47.90, 3) # 17-19 swing low 2 (mai sus)
+ ramp(47.90, 49.60, 3) # 20-22 swing high 3
+ [49.20, 49.40] # 23-24 ultima bara in triunghi
+ [48.30] # 25 marubozu de breakdown
+ [48.70] # 26 retest la jumatatea lui
+ [48.20, 47.60, 47.90, 47.10, 46.70, 45.20, 45.60]) # 27-33 spre TP1
wk = ([(0.18, 0.18)] * 10 + [(0.20, 0.10)]
+ [(0.05, 0.10), (0.05, 0.10), (0.05, 0.20)]
+ [(0.10, 0.05), (0.10, 0.05), (0.20, 0.05)]
+ [(0.05, 0.10), (0.05, 0.10), (0.05, 0.20)]
+ [(0.10, 0.05), (0.10, 0.05), (0.20, 0.05)]
+ [(0.10, 0.10), (0.10, 0.10)]
+ [(0.04, 0.06)] + [(0.15, 0.10)]
+ [(0.10, 0.15), (0.10, 0.15), (0.15, 0.10), (0.10, 0.15),
(0.10, 0.15), (0.10, 0.20), (0.15, 0.10)])
cds = from_closes(cl, wicks=wk, start=40.80,
vols=vols(34, 46, 10, fade=(11, 24), b25=96, b26=52, b31=78))
assert hi(cds, 10, 10) == U1 and lo(cds, 13, 13) == lo_a(13)
assert hi(cds, 16, 16) == up_a(16) and lo(cds, 19, 19) == lo_a(19)
assert hi(cds, 22, 22) == up_a(22)
assert lo(cds, 19, 19) > lo(cds, 13, 13), "minimele urcă mai repede"
assert hi(cds, 22, 22) > hi(cds, 16, 16) > hi(cds, 10, 10), "maximele urcă mai încet"
assert (up_a(22) - lo_a(22)) < BAZA_A / 2
MB_O, MB_C = cds[25].o, cds[25].c # marubozul de breakdown: 49.40 -> 48.30
JUM = round((MB_O + MB_C) / 2, 2) # 48.85
E_A, SL_A = JUM, MB_O
TP_A = round(MB_C - BAZA_A, 2) # 45.30
R_A = round((E_A - TP_A) / (SL_A - E_A), 1)
assert cds[25].c < lo_a(25) and cds[24].c > lo_a(24), "breakdown exact pe bara 25"
assert abs(MB_C - MB_O) > 1.0 and cds[25].h - MB_O <= 0.06, "marubozu: corp mare, fitil mic"
assert hi(cds, 26, 26) == JUM, f"retestul trebuie să atingă exact {JUM}"
assert lo(cds, 27, 33) <= TP_A, "seria trebuie să atingă TP1"
E_BRK = MB_C # varianta agresivă: short pe breakdown
R_BRK = round((E_BRK - TP_A) / (SL_A - E_BRK), 1)
print(f"wedge asc rev: baza {BAZA_A} marubozu {MB_O}->{MB_C} jum {JUM} "
f"SL {SL_A} TP1 {TP_A} = {R_A}R (pe breakdown: {R_BRK}R)")
deco(Chart(
candles=cds,
levels=[Level(SL_A, f"SL {SL_A:.2f} = open-ul marubozului", "#c00"),
Level(E_A, f"intrare short {E_A:.2f}", "#188a3e", label_at="left"),
Level(TP_A, f"TP1 {TP_A:.2f} = {R_A}R", "#1a5fb4")],
marks=[Mark(2, "trend ascendent anterior", "#188a3e", "below", dy=8),
Mark(13, "minim 1", "#c00", "below", dy=22),
Mark(19, "minim 2 (mai sus)", "#c00", "below"),
Mark(25, "breakdown", "#c00", "below", dy=14),
],
zones=[Zone(10, 24, "wedge ascendent in uptrend = SCHIMBARE de trend (15 bare)",
"#8b5cf6")],
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
scale=42, gap=16, body_w=9, left=46, width=730, top=62, vol_h=70,
title="Wedge ascendent — bear",
caption=CAP),
"wedge_asc_rev",
segs=[seg(10, U1, 26, up_a(26)),
seg(10, L1, 13, lo_a(13), "#8b5cf6", "4 3", 1.2),
seg(13, lo_a(13), 25, lo_a(25)),
seg(10.0, L1, 10.0, U1, "#8b5cf6", "", 2.2)],
texts=[txt(10.1, (U1 + L1) / 2, f"baza = {BAZA_A:.2f}", anchor="start", dx=4)],
distante=(BAZA_A,))
# ===================================== 4. wedge ascendent — continuare de trend
U2, L2 = 51.50, 49.00
BAZA_C = round(U2 - L2, 2) # 2.50
def up_c(i):
return round(U2 + 0.10 * (i - 10), 2)
def lo_c(i):
return round(L2 + 0.22 * (i - 10), 2)
cl = (ramp(57.0, 51.30, 10) # 0-9 trend DESCENDENT anterior
+ [51.30] # 10 swing high pe latura de sus
+ ramp(51.30, 49.76, 3) # 11-13 swing low
+ ramp(49.76, 52.00, 3) # 14-16 swing high 2
+ ramp(52.00, 51.08, 3) # 17-19 swing low 2 (mai sus)
+ ramp(51.08, 52.60, 3) # 20-22 swing high 3
+ [52.20] # 23 ultima bara in triunghi
+ [51.70] # 24 breakdown
+ [51.10, 50.60, 50.90, 50.10, 49.40, 48.90, 49.30]) # 25-31 spre TP1
wk = ([(0.18, 0.18)] * 10 + [(0.20, 0.10)]
+ [(0.05, 0.10), (0.05, 0.10), (0.05, 0.10)]
+ [(0.10, 0.05), (0.10, 0.05), (0.10, 0.05)]
+ [(0.05, 0.10), (0.05, 0.10), (0.05, 0.10)]
+ [(0.10, 0.05), (0.10, 0.05), (0.10, 0.05)]
+ [(0.10, 0.10), (0.06, 0.12)]
+ [(0.10, 0.15), (0.10, 0.15), (0.15, 0.10), (0.10, 0.15),
(0.10, 0.15), (0.10, 0.18), (0.15, 0.10)])
cds = from_closes(cl, wicks=wk, start=57.30,
vols=vols(32, 44, 10, fade=(11, 23), b24=88, b29=74))
assert hi(cds, 10, 10) == U2 and lo(cds, 13, 13) == lo_c(13)
assert hi(cds, 16, 16) == up_c(16) and lo(cds, 19, 19) == lo_c(19)
assert hi(cds, 22, 22) == up_c(22)
assert lo(cds, 19, 19) > lo(cds, 13, 13) and hi(cds, 22, 22) > hi(cds, 10, 10)
assert cds[0].c > cds[9].c, "înainte de pattern trebuie downtrend"
TP_C = round(cds[24].c - BAZA_C, 2) # 49.20
assert cds[24].c < lo_c(24) and cds[23].c > lo_c(23), "breakdown exact pe bara 24"
assert lo(cds, 25, 31) <= TP_C
print(f"wedge asc cont: baza {BAZA_C} breakdown {cds[24].c} TP1 {TP_C}")
deco(Chart(
candles=cds,
levels=[Level(TP_C, f"TP1 {TP_C:.2f}", "#1a5fb4")],
marks=[Mark(2, "trend DESCENDENT anterior", "#c00", dy=-4),
Mark(13, "minim 1", "#c00", "below"),
Mark(19, "minim 2 (mai sus)", "#c00", "below"),
Mark(24, "breakdown", "#c00", "below", dy=14)],
zones=[Zone(10, 23, "acelasi wedge ascendent, dar in downtrend = CONTINUARE",
"#8b5cf6")],
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
scale=26, gap=17, body_w=9, left=46, width=730, top=62, vol_h=70,
title="Wedge ascendent, continuare",
caption=CAP),
"wedge_asc_cont",
segs=[seg(10, U2, 25, up_c(25)),
seg(10, L2, 13, lo_c(13), "#8b5cf6", "4 3", 1.2),
seg(13, lo_c(13), 24, lo_c(24)),
seg(10.0, L2, 10.0, U2, "#8b5cf6", "", 2.2),
seg(28.6, TP_C, 28.6, cds[24].c, "#1a5fb4", "", 2.2)],
texts=[txt(10.1, (U2 + L2) / 2, f"baza = {BAZA_C:.2f}", anchor="start", dx=4),
txt(28.6, (TP_C + cds[24].c) / 2, f"aceeasi {BAZA_C:.2f}", "#1a5fb4",
anchor="start", dx=4)],
distante=(BAZA_C,))
# ======================================================== 5. bull flag / ABCD long
POLE_A, POLE_B = 30.00, 40.00 # bățul steagului
BAT = round(POLE_B - POLE_A, 2) # 10.00
CANAL = 3.00
def up_f(i):
return round(40.00 - 0.25 * (i - 11), 2)
def lo_f(i):
return round(37.00 - 0.25 * (i - 11), 2)
cl = ([30.60, 30.30, 30.80, 30.40] # 0-3 baza bățului
+ ramp(30.40, 39.80, 7) # 4-10 bățul steagului
+ [39.85] # 11 varful bățului (A->B)
+ ramp(39.85, 36.45, 3) # 12-14 pe latura de jos
+ ramp(36.45, 38.35, 3) # 15-17 pe latura de sus
+ ramp(38.35, 34.95, 3) # 18-20 pe latura de jos (C)
+ [35.60, 36.30] # 21-22 inca in canal
+ [37.80] # 23 breakout
+ [38.60, 39.50, 40.90, 40.40, 41.80, 43.00,
42.40, 44.00, 45.50, 46.80, 48.00]) # 24-34 spre TP1 si TP2
wk = ([(0.12, 0.12), (0.10, 0.30), (0.15, 0.10), (0.10, 0.25)]
+ [(0.20, 0.15)] * 7 + [(0.15, 0.12)]
+ [(0.05, 0.15), (0.05, 0.15), (0.05, 0.20)]
+ [(0.10, 0.05), (0.10, 0.05), (0.15, 0.05)]
+ [(0.05, 0.15), (0.05, 0.15), (0.05, 0.20)]
+ [(0.12, 0.10), (0.12, 0.10)] + [(0.10, 0.15)]
+ [(0.18, 0.15)] * 11)
cds = from_closes(cl, wicks=wk, start=30.40,
vols=vols(35, 46, 10, fade=(12, 22), b7=86, b9=90, b23=98, b29=80))
assert lo(cds, 0, 4) == POLE_A, f"baza bățului {lo(cds, 0, 4)}"
assert hi(cds, 11, 11) == POLE_B, f"vârful bățului {hi(cds, 11, 11)}"
assert lo(cds, 14, 14) == lo_f(14) and hi(cds, 17, 17) == up_f(17)
assert lo(cds, 20, 20) == lo_f(20)
# canal paralel: aceeași lățime la început și la sfârșit
assert abs((up_f(11) - lo_f(11)) - CANAL) < 1e-9
assert abs((up_f(23) - lo_f(23)) - CANAL) < 1e-9
E_F = cds[23].c # 37.80
SL_F = 36.00 # sub low-ul candelei de breakout (36.15)
TP1_F = round(E_F + CANAL, 2) # 40.80
TP2_F = round(E_F + BAT, 2) # 47.80
R1_F = round((TP1_F - E_F) / (E_F - SL_F), 1)
R2_F = round((TP2_F - E_F) / (E_F - SL_F), 1)
assert cds[23].c > up_f(23) and cds[22].c < up_f(22), "breakout exact pe bara 23"
assert SL_F < lo(cds, 23, 23) < E_F
assert hi(cds, 24, 34) >= TP2_F, "seria trebuie să atingă și TP2"
print(f"bull flag: canal {CANAL} bat {BAT} intrare {E_F} SL {SL_F} "
f"TP1 {TP1_F}={R1_F}R TP2 {TP2_F}={R2_F}R")
deco(Chart(
candles=cds,
levels=[Level(SL_F, f"SL {SL_F:.2f}", "#c00", label_at="left"),
Level(E_F, f"intrare {E_F:.2f}", "#188a3e", label_at="left"),
Level(TP1_F, f"TP1 {TP1_F:.2f} = canalul = {R1_F}R", "#1a5fb4",
label_at="left"),
Level(TP2_F, f"TP2 {TP2_F:.2f} = batul steagului = {R2_F}R", "#1a5fb4")],
marks=[Mark(2, "A", "#555", "below"), Mark(11, "B", "#555", dy=-20),
Mark(20, "C = aici era by the dip", "#b06000", "below"),
Mark(23, "breakout cu volum", "#188a3e", dy=-16)],
zones=[Zone(11, 22, "flamura (12 bare)", "#999")],
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
scale=13, gap=16, body_w=9, left=46, width=730, top=62, vol_h=70,
title="Bull flag = ABCD long",
caption=CAP),
"bull_flag",
segs=[seg(11, 40.00, 24, up_f(24), "#1a5fb4"),
seg(11, 37.00, 14, lo_f(14), "#1a5fb4", "4 3", 1.2),
seg(14, lo_f(14), 23, lo_f(23), "#1a5fb4"),
seg(11.0, 37.00, 11.0, 40.00, "#1a5fb4", "", 2.2),
seg(3.3, POLE_A, 3.3, POLE_B, "#188a3e", "", 2.2)],
texts=[txt(11.1, 38.50, f"canal {CANAL:.2f}", "#1a5fb4", anchor="start", dx=4),
txt(3.2, 33.00, f"batul = {BAT:.2f}", "#188a3e", anchor="end", dx=-4)],
distante=(CANAL, BAT))
# ======================================================= 6. bear flag / ABCD short
POLE_A2, POLE_B2 = 60.00, 50.00
BAT2 = round(POLE_A2 - POLE_B2, 2) # 10.00
CANAL2 = 3.00
def up_b(i):
return round(53.00 + 0.25 * (i - 11), 2)
def lo_b(i):
return round(50.00 + 0.25 * (i - 11), 2)
cl = ([59.40, 59.70, 59.20, 59.60] # 0-3 varful bățului
+ ramp(59.60, 50.20, 7) # 4-10 bățul, în jos
+ [50.15] # 11 minimul bățului (A->B)
+ ramp(50.15, 53.55, 3) # 12-14 pe latura de sus
+ ramp(53.55, 51.65, 3) # 15-17 pe latura de jos
+ ramp(51.65, 55.05, 3) # 18-20 pe latura de sus (C)
+ [54.40, 53.70] # 21-22 inca in canal
+ [52.40] # 23 breakdown
+ [51.60, 50.40, 51.00, 49.60, 48.20, 48.80,
47.00, 45.60, 44.20, 43.00, 42.20]) # 24-34 spre TP1 si TP2
wk = ([(0.12, 0.12), (0.30, 0.10), (0.10, 0.15), (0.25, 0.10)]
+ [(0.15, 0.20)] * 7 + [(0.12, 0.15)]
+ [(0.15, 0.05), (0.15, 0.05), (0.20, 0.05)]
+ [(0.05, 0.10), (0.05, 0.10), (0.05, 0.15)]
+ [(0.15, 0.05), (0.15, 0.05), (0.20, 0.05)]
+ [(0.10, 0.12), (0.10, 0.12)] + [(0.15, 0.10)]
+ [(0.15, 0.18)] * 11)
cds = from_closes(cl, wicks=wk, start=59.60,
vols=vols(35, 46, 10, fade=(12, 22), b7=88, b9=92, b23=96, b31=82))
assert hi(cds, 0, 4) == POLE_A2, f"vârful bățului {hi(cds, 0, 4)}"
assert lo(cds, 11, 11) == POLE_B2, f"minimul bățului {lo(cds, 11, 11)}"
assert hi(cds, 14, 14) == up_b(14) and lo(cds, 17, 17) == lo_b(17)
assert hi(cds, 20, 20) == up_b(20)
assert abs((up_b(11) - lo_b(11)) - CANAL2) < 1e-9
assert abs((up_b(23) - lo_b(23)) - CANAL2) < 1e-9
E_B = cds[23].c # 52.40
SL_B = 54.00 # peste high-ul candelei de breakdown (53.85)
TP1_B = round(E_B - CANAL2, 2) # 49.40
TP2_B = round(E_B - BAT2, 2) # 42.40
R1_B = round((E_B - TP1_B) / (SL_B - E_B), 1)
R2_B = round((E_B - TP2_B) / (SL_B - E_B), 1)
assert cds[23].c < lo_b(23) and cds[22].c > lo_b(22), "breakdown exact pe bara 23"
assert E_B < hi(cds, 23, 23) < SL_B
assert lo(cds, 24, 34) <= TP2_B
print(f"bear flag: canal {CANAL2} bat {BAT2} intrare {E_B} SL {SL_B} "
f"TP1 {TP1_B}={R1_B}R TP2 {TP2_B}={R2_B}R")
deco(Chart(
candles=cds,
levels=[Level(SL_B, f"SL {SL_B:.2f}", "#c00"),
Level(E_B, f"intrare short {E_B:.2f}", "#188a3e"),
Level(TP1_B, f"TP1 {TP1_B:.2f} = canalul = {R1_B}R", "#1a5fb4",
label_at="left"),
Level(TP2_B, f"TP2 {TP2_B:.2f} = batul steagului = {R2_B}R", "#1a5fb4")],
marks=[Mark(2, "A", "#555", "below", dy=4), Mark(11, "B", "#555", "below", dy=14),
Mark(20, "C = aici era sell the rally", "#b06000", dy=-20),
Mark(23, "breakdown cu volum", "#c00", "below", dy=14)],
zones=[Zone(11, 22, "flamura (12 bare)", "#999")],
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
scale=13, gap=16, body_w=9, left=46, width=730, top=66, vol_h=70,
title="Bear flag = ABCD short",
caption=CAP),
"bear_flag",
segs=[seg(11, 53.00, 14, up_b(14), "#1a5fb4", "4 3", 1.2),
seg(14, up_b(14), 24, up_b(24), "#1a5fb4"),
seg(11, 50.00, 23, lo_b(23), "#1a5fb4"),
seg(11.0, 50.00, 11.0, 53.00, "#1a5fb4", "", 2.2),
seg(3.3, POLE_B2, 3.3, POLE_A2, "#c00", "", 2.2)],
texts=[txt(11.1, 51.50, f"canal {CANAL2:.2f}", "#1a5fb4", anchor="start", dx=4),
txt(3.2, 56.00, f"batul = {BAT2:.2f}", "#c00", anchor="end", dx=-4)],
distante=(CANAL2, BAT2))
# ============================================ 7. marubozu retestat — DA si NU
def marubozu_serie(retest_close, retest_wick_up, cont):
"""Downtrend scurt, marubozu de breakdown, apoi retest + continuare."""
cl = (ramp(74.0, 70.60, 8) + [70.00] # 0-8 coborare pana la breakdown
+ [66.00] # 9 marubozu: 70.00 -> 66.00
+ [retest_close] # 10 bara de retest
+ cont)
wk = ([(0.20, 0.20)] * 9
+ [(0.04, 0.06)] + [(retest_wick_up, 0.10)]
+ [(0.15, 0.20)] * len(cont))
return from_closes(cl, wicks=wk, start=74.40,
vols=vols(11 + len(cont), 46, 10, b9=98, b10=48))
for name, rc, rw, cont, jos, titlu, cap, dist in [
("maru_da", 67.85, 0.15,
[67.10, 66.30, 65.40, 64.20, 63.00, 63.40, 62.10, 60.80, 61.20],
True, "DA: marubozul retestează jumătatea — intri cu limit order la 68.00",
"Short limit la jumătatea corpului, stop la open. Risc 2.00, nu 4.00. "
"Exemplu construit.", (2.00, 4.00)),
("maru_nu", 67.20, 0.10,
[66.40, 65.10, 64.30, 65.00, 63.20, 62.00, 62.50, 60.90, 59.80],
False, "NU: retestul se oprește sub jumătate — limit order-ul nu se umple",
"Retestul urcă doar până la {t}: limit order-ul de la 68.00 rămâne "
"neumplut. Exemplu construit.", ()),
]:
cds = marubozu_serie(rc, rw, cont)
o9, c9 = cds[9].o, cds[9].c
assert (o9, c9) == (70.00, 66.00), (o9, c9)
jum = round((o9 + c9) / 2, 2) # 68.00
assert jum == 68.00
top_retest = hi(cds, 10, 10)
if jos:
assert top_retest == jum, f"{name}: retestul trebuie să atingă exact {jum}"
else:
assert c9 < top_retest < jum, f"{name}: retestul trebuie să stea sub {jum}"
lv = [Level(o9, f"SL {o9:.2f} = open-ul marubozului", "#c00"),
Level(jum, f"jumatatea corpului {jum:.2f}", "#b06000"),
Level(c9, f"close-ul marubozului {c9:.2f}", "#555")]
mk = [Mark(9, "marubozu de breakdown", "#c00", "below", dy=16)]
ar = [Arrow(10, ("retest exact la jumatatea corpului:\naici se umple short-ul"
if jos else
f"retestul se opreste la {top_retest:.2f}:\nlimit order-ul nu se umple"),
where="above", color="#b06000" if jos else "#c00", dx=150, dy=-66)]
deco(Chart(
candles=cds, levels=lv, marks=mk, arrows=ar,
zones=[Zone(9, 10, "marubozu + retest", "#8b5cf6")],
scale=20, gap=20, body_w=11, left=46, width=660, top=56, vol_h=60,
title=titlu + " — exemplu construit",
caption=cap.format(t=f"{top_retest:.2f}")),
name, distante=(top_retest,) + dist)
# ============================================== 8. trigger in capul wedge-ului
def trigger_serie(declansator_close):
"""Coborâre, doji în capul wedge-ului, apoi bara de declanșare."""
cl = (ramp(53.0, 50.40, 7) + [49.70] # 0-7 coborare in triunghi
+ [49.75] # 8 doji: corp mic
+ [declansator_close] # 9 bara de declansare
+ [51.10, 51.90, 51.40, 52.60, 53.40]) # 10-14
wk = ([(0.18, 0.18)] * 8 + [(0.55, 0.50)] + [(0.15, 0.15)] + [(0.18, 0.18)] * 5)
return from_closes(cl, wicks=wk, start=53.30,
vols=vols(15, 44, 8, b9=84))
for name, dc, da in [("trigger_da", 50.60, True), ("trigger_nu", 50.10, False)]:
cds = trigger_serie(dc)
doji = cds[8]
corp = abs(doji.c - doji.o)
assert corp / (doji.h - doji.l) < 0.10, f"{name}: corpul doji-ului {corp:.2f}"
dh = round(doji.h, 2) # high-ul doji-ului
if da:
assert cds[9].c > dh, f"{name}: {cds[9].c} trebuie peste {dh}"
else:
assert doji.c < cds[9].c < dh, f"{name}: {cds[9].c} trebuie sub {dh}"
deco(Chart(
candles=cds,
levels=[Level(dh, f"high-ul doji: {dh:.2f}", "#b06000", label_at="left"),
Level(round(cds[9].c, 2), f"close urmator: {cds[9].c:.2f}",
"#188a3e" if da else "#c00")],
marks=[Mark(8, "doji: corp foarte mic", "#b06000", "below", dy=14)],
arrows=[Arrow(9, ("a inchis PESTE high-ul doji-ului:\ntrigger valid"
if da else
"a urcat, dar a inchis SUB high-ul doji-ului:\nnu e trigger, e doar un buy hammer"),
where="above", color="#188a3e" if da else "#c00", dx=60, dy=-10)],
scale=90, gap=26, body_w=13, left=46, width=620, vol_h=52,
title=("Trigger DA: buy doji confirmat" if da
else "Trigger NU: doar buy hammer"),
caption="Exemplu construit (fără ticker). Regula de trigger e cea din "
"capitolul Buy Doji, nu din lecția asta."),
name)
# ============================================ 9. unde apare fiecare pattern
cl = (ramp(60.0, 51.00, 7) # 0-6 downtrend
+ [51.60, 50.80, 51.40, 50.60, 51.80] # 7-11 bottom (3 picioare)
+ ramp(51.80, 57.00, 5) # 12-16 urcare
+ [56.30, 55.60, 56.10, 55.40] # 17-20 bull flag / wedge desc
+ ramp(55.40, 62.00, 5) # 21-25 urcare
+ [62.60, 61.90, 63.20, 62.70, 63.60, 63.10] # 26-31 wedge ascendent (top)
+ ramp(63.10, 57.50, 5) # 32-36 downtrend nou
+ [58.10, 58.60, 57.90] # 37-39 bear flag
+ ramp(57.90, 54.00, 4)) # 40-43 cadere
wk = [(0.20, 0.20)] * 44
cds = from_closes(cl, wicks=wk, start=60.40, vols=vols(44, 44, 10))
assert lo(cds, 7, 11) < lo(cds, 17, 20), "bottom-ul trebuie sub consolidarea din trend"
assert hi(cds, 26, 31) == hi(cds, 0, 43), "wedge-ul ascendent e la maximul seriei"
deco(Chart(
candles=cds,
marks=[Mark(9, "cauti SCHIMBARE de trend", "#188a3e", "below", dy=16),
Mark(18, "cauti CONTINUARE: bull flag, wedge descendent", "#188a3e", dy=-34),
Mark(29, "cauti SCHIMBARE: wedge ascendent, triple top", "#c00", dy=-26),
Mark(41, "cauti CONTINUARE in bear: bear flag", "#c00", "below", dy=12)],
zones=[Zone(7, 11, "bottom", "#188a3e"), Zone(17, 20, "trend bull", "#188a3e"),
Zone(26, 31, "top", "#c00"), Zone(37, 39, "trend bear", "#c00")],
mas=[MA(20, "#b06000", dash="4 3", text="SMA-20")],
scale=18, gap=14, body_w=8, left=46, width=820, top=74,
title="Ce pattern cauti, in functie de unde e piata",
caption="Exemplu construit (fără ticker). La bottom cauți reversal, în "
"trend cauți continuare."),
"context_ciclu")
# ---------------------------------------------------------------- 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"<!--SVG:{n}-->" not in md]
assert not lipsa, f"marcatori lipsă în .md: {lipsa}"
for n, svg in SVG.items():
md = re.sub(rf"<!--SVG:{n}-->(\s*<svg\b[\s\S]*?</svg>)?",
lambda _m: f"<!--SVG:{n}-->\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")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff