Rescrie patternurile de candele: diagrame generate din preturi + explicatii complete
Semnalat de user: diagramele nu erau proportionale cu textul si patternurile erau doar prezentate, fara sa spuna ce fac si cand se tranzactioneaza. Verificat numeric, erau reale: - hammer: entry->SL 75 px/unitate, entry->TP(2R) 41 px/unitate - engulfing: 110 vs 17.5 px/unitate - morning star: 82 vs 10.6 px/unitate - marubozu bull: eticheta de open pusa deasupra celei de close (axa inversata) - etichete taiate de viewBox si suprapuse Fix: - svg_candles.py - generator care calculeaza coordonatele din preturi, cu o singura scara pe desen. Are selftest care verifica exact cazul de mai sus. - gen/patternuri_candele.py - regenereaza fisierul intreg; diagramele nu se mai editeaza de mana - fiecare pattern are acum: ce arata, ce s-a intamplat mecanic, DE CE il urmaresti, CE FACI cand il vezi, trigger DA/NU, si trade complet unde e cazul - adaugate explicatii practice unde lipseau complet: inverted hammer, hanging man, shooting star, spinning top, harami, marubozu (comparatia de risc la intrare directa vs retest midpoint), abandoned baby, piercing line, three white soldiers - STYLE.md: regula de generare + verificarea automata de scara Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
This commit is contained in:
20
STYLE.md
20
STYLE.md
@@ -90,6 +90,26 @@ de la început ("nu mă interesează doar mecanic, vreau și de ce și cum").
|
||||
ori — o explic o singură dată, aici, înainte de a o aplica"), nu sări
|
||||
direct la "de ce X" ca și cum ar fi un follow-up la ceva spus anterior.
|
||||
|
||||
## Diagrame de candele: generează-le, nu le desena
|
||||
`svg_candles.py` construiește diagramele de candele din prețuri (OHLC +
|
||||
niveluri), cu o singură scară px/unitate pe tot desenul. **Folosește-l pentru
|
||||
orice diagramă cu candele sau cu entry/SL/TP** — nu scrie SVG de mână.
|
||||
|
||||
Motivul, găsit ca bug real în `01_MODUL_1/05_patternuri_candele.md`
|
||||
(semnalat de user, 2026-09-12): o țintă de 2R desenată la 41 px/unitate în
|
||||
sus și un stop la 75 px/unitate în jos — adică 2R arăta ca 1R. Trei
|
||||
diagrame de trade aveau aceeași problemă, una de 8 ori. Plus o etichetă de
|
||||
`open` pusă deasupra celei de `close` la un marubozu bull, deci axa de preț
|
||||
inversată. Ochiul nu prinde asta; calculul da.
|
||||
|
||||
Generatoarele stau în `gen/` (ex. `gen/patternuri_candele.py`) și rescriu
|
||||
fișierul `.md` întreg. Ca să corectezi un preț, schimbi cifra în generator
|
||||
și rulezi din nou — nu edita SVG-ul rezultat.
|
||||
|
||||
Verificare automată de pus în orice pagină cu trade-uri: pentru fiecare
|
||||
diagramă cu trei niveluri (SL / intrare / TP), raportul dintre distanțele în
|
||||
preț trebuie să fie identic cu raportul dintre distanțele în pixeli.
|
||||
|
||||
## Diagrame (obligatorii, nu opționale — userul e vizual learner)
|
||||
- **Orice paragraf "Exemplu real" cu cifre concrete are nevoie de propria
|
||||
diagramă chiar acolo, lipită de text** — nu doar undeva mai jos în fișier,
|
||||
|
||||
681
gen/patternuri_candele.py
Normal file
681
gen/patternuri_candele.py
Normal file
@@ -0,0 +1,681 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Regenerează summaries/01_MODUL_1/05_patternuri_candele.md.
|
||||
|
||||
Diagramele NU se editează de mână. Versiunea desenată manual avea ținta de
|
||||
2R plasată la distanța de 1R față de intrare și eticheta de open a unui
|
||||
marubozu deasupra celei de close. Aici coordonatele vin din prețuri prin
|
||||
svg_candles.py, deci scara e corectă prin construcție.
|
||||
|
||||
Rulează: .venv/bin/python gen/patternuri_candele.py
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from svg_candles import Chart, Candle, Level
|
||||
|
||||
P = []
|
||||
def w(t): P.append(t)
|
||||
def svg(ch): P.append(ch.render())
|
||||
|
||||
w("""# Modul 1 / #5 — Pattern-uri de bază pe candele (candlestick)
|
||||
|
||||
## TLDR
|
||||
O candelă codifică patru prețuri dintr-un interval — open, high, low, close —
|
||||
într-o formă. Formele care se repetă se numesc pattern-uri, și fiecare e o
|
||||
poveste despre **cine a controlat prețul la începutul intervalului și cine la
|
||||
final**.
|
||||
|
||||
Un pattern nu e un semnal de cumpărare. E o întrebare la care graficul
|
||||
răspunde în candela următoare. Mai jos, pentru fiecare: ce arată, ce s-a
|
||||
întâmplat mecanic, **de ce îl urmărești**, **ce faci concret când îl vezi**,
|
||||
și unde e cazul — trade-ul complet, cu intrare, stop-loss și țintă.
|
||||
|
||||
Toate diagramele de mai jos sunt desenate la scară: o unitate de preț are
|
||||
aceeași înălțime oriunde pe desen, inclusiv între intrare, stop-loss și
|
||||
țintă.
|
||||
|
||||
## Anatomia unei candele
|
||||
|
||||
Corpul arată distanța dintre open și close. Fitilele arată cât de departe a
|
||||
ajuns prețul în fiecare direcție **înainte să fie împins înapoi**.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(50.00, 50.65, 49.80, 50.60, "bull", "bull — O 50.00, H 50.65, L 49.80, C 50.60 (close peste open)"),
|
||||
Candle(50.60, 50.65, 49.80, 50.00, "bear", "bear — O 50.60, H 50.65, L 49.80, C 50.00 (close sub open)")],
|
||||
scale=150, gap=140, left=90,
|
||||
caption="Aceleași patru prețuri, ordine inversă a lui open și close. Scară: 0.10 = 15 px."))
|
||||
|
||||
w("""
|
||||
Verde (bull) înseamnă close peste open — cumpărătorii au terminat intervalul
|
||||
în avantaj. Roșu (bear) înseamnă invers.
|
||||
|
||||
**De ce contează distincția corp / fitil:** corpul e ce a rămas, fitilul e ce
|
||||
a fost respins. Două candele cu același corp dar fitile diferite spun povești
|
||||
complet diferite despre cine a încercat ce.
|
||||
|
||||
## Mecanismul comun: ce înseamnă un fitil lung
|
||||
|
||||
Toate pattern-urile de reversal de mai jos funcționează pe același
|
||||
mecanism, așa că îl explic o singură dată, aici.
|
||||
|
||||
Un fitil lung în jos înseamnă că prețul a **ajuns** acolo — cineva a vândut
|
||||
efectiv la acele prețuri — și apoi a fost împins înapoi sus. Ca să se
|
||||
întâmple asta, cumpărătorii au trebuit să absoarbă toată vânzarea de la
|
||||
nivelul acela **și** să mai rămână cu destulă cerere ca să urce prețul la loc.
|
||||
|
||||
<svg viewBox="0 0 620 240" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;font:12px sans-serif">
|
||||
<text x="24" y="20" fill="#666">preț ↑</text>
|
||||
<line x1="120" y1="60" x2="120" y2="200" stroke="#1a1a1a" stroke-width="1.5"/>
|
||||
<rect x="107" y="60" width="26" height="34" fill="#188a3e"/>
|
||||
<line x1="150" y1="94" x2="330" y2="94" stroke="#888" stroke-dasharray="3 3"/>
|
||||
<text x="340" y="90" fill="#333">aici s-a închis intervalul</text>
|
||||
<line x1="150" y1="200" x2="330" y2="200" stroke="#888" stroke-dasharray="3 3"/>
|
||||
<text x="340" y="196" fill="#c00">până aici au împins vânzătorii</text>
|
||||
<line x1="86" y1="94" x2="86" y2="200" stroke="#188a3e" stroke-width="2"/>
|
||||
<line x1="81" y1="94" x2="91" y2="94" stroke="#188a3e" stroke-width="2"/>
|
||||
<line x1="81" y1="200" x2="91" y2="200" stroke="#188a3e" stroke-width="2"/>
|
||||
<text x="24" y="152" fill="#188a3e" style="font-weight:bold">absorbit</text>
|
||||
<text x="340" y="150" fill="#333">Toată vânzarea din zona asta a fost cumpărată</text>
|
||||
<text x="340" y="168" fill="#333">de cineva, la acele prețuri.</text>
|
||||
<text x="24" y="228" fill="#555">Cu cât fitilul e mai lung față de corp, cu atât respingerea a fost mai decisă.</text>
|
||||
</svg>
|
||||
|
||||
**De ce te interesează, concret:** fitilul îți dă un **nivel**, nu doar o
|
||||
senzație. Extrema fitilului e prețul la care cererea a apărut efectiv. Acel
|
||||
nivel devine reperul tău de invalidare: dacă prețul trece sub el, ipoteza ta
|
||||
a fost greșită, și știi asta imediat, fără să aștepți.
|
||||
|
||||
---
|
||||
|
||||
# Pattern-uri de o candelă
|
||||
|
||||
## Hammer — reversal bull, după downtrend
|
||||
|
||||
**Ce arată:** corp mic sus, fitil lung în jos (cel puțin dublul corpului),
|
||||
fitil de sus aproape inexistent.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(63.05, 63.30, 62.10, 63.30, "hammer", "O 63.05, H = C 63.30, L 62.10 — fitil de 0.95, corp de 0.25")],
|
||||
scale=100, left=110, trend="down",
|
||||
caption="Fitilul e de aproape 4 ori corpul. Scară: 0.10 = 10 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** vânzătorii au dus prețul până la 62.10 și au eșuat.
|
||||
Cumpărătorii au absorbit tot și au închis intervalul la maxim.
|
||||
|
||||
**De ce îl urmărești:** îți spune că la **62.10** există cerere reală, nu
|
||||
presupusă. Asta transformă o zonă vagă de „pare suport" într-un nivel
|
||||
testabil. Decizia pe care ți-o ajută să iei: dacă merită să încerci un long
|
||||
aici, și unde pui stopul ca să afli repede că te-ai înșelat.
|
||||
|
||||
**Ce faci când îl vezi:** nimic încă. Notezi 62.10 ca nivel de invalidare și
|
||||
63.30 ca prag de confirmare. **Aștepți candela următoare.**
|
||||
|
||||
**Trigger valid / invalid:**""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(63.05, 63.30, 62.10, 63.30, "hammer", ""),
|
||||
Candle(63.20, 64.20, 63.10, 64.10, "DA", "DA — candela următoare închide la 64.10, peste high-ul hammer-ului (63.30)")],
|
||||
scale=64, left=110, gap=110,
|
||||
levels=[Level(63.30, "prag de confirmare 63.30", "#888", "2 2")],
|
||||
caption=""))
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(63.05, 63.30, 62.10, 63.30, "hammer", ""),
|
||||
Candle(63.20, 63.28, 61.90, 62.00, "NU", "NU — închide la 62.00, sub low-ul hammer-ului: downtrend-ul continuă")],
|
||||
scale=64, left=110, gap=110,
|
||||
levels=[Level(62.10, "nivel de invalidare 62.10", "#888", "2 2")],
|
||||
caption=""))
|
||||
|
||||
w("""
|
||||
**Trade complet, long:**""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(63.05, 63.30, 62.10, 63.30, "hammer", "hammer — low-ul 62.10 e nivelul care ține ipoteza"),
|
||||
Candle(63.40, 64.20, 63.30, 64.10, "confirmare", "confirmare — închide 64.10, peste 63.30")],
|
||||
levels=[Level(65.95, "TP 65.95 — 2R", "#188a3e"),
|
||||
Level(63.35, "intrare 63.35", "#1a5fb4"),
|
||||
Level(62.05, "SL 62.05 — sub low-ul hammer-ului", "#c00")],
|
||||
scale=52, gap=110, left=110, trend="down",
|
||||
caption="Scară: 1.00 = 52 px. Distanța intrare→TP e exact dublul distanței intrare→SL."))
|
||||
|
||||
w("""
|
||||
| | Preț | De ce acolo |
|
||||
|---|---|---|
|
||||
| Intrare | 63.35 | peste high-ul hammer-ului — cumpărătorii au depășit pragul pe care nu-l puteau trece |
|
||||
| Stop-loss | 62.05 | sub 62.10; dacă se tranzacționează acolo, cererea care a făcut fitilul nu mai există |
|
||||
| Risc | 1.30 | 63.35 − 62.05 |
|
||||
| Țintă | 65.95 | 2R = 63.35 + 2 × 1.30 |
|
||||
|
||||
**Mărimea poziției:** dacă riști 2% dintr-un cont de 10.000 $, adică 200 $,
|
||||
și riscul per acțiune e 1.30 $, cumperi **153 de acțiuni** (200 ÷ 1.30).
|
||||
Nu „cât simți".
|
||||
|
||||
## Inverted Hammer — tot bull, tot după downtrend, dar mai slab
|
||||
|
||||
**Ce arată:** aceeași logică, oglindită — corp mic **jos**, fitil lung în
|
||||
**sus**.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.15, 63.30, 62.10, 62.40, "inverted hammer", "O 62.15, H 63.30, L 62.10, C 62.40")],
|
||||
scale=100, left=130, trend="down",
|
||||
caption="Fitilul de sus e de aproape 4 ori corpul. Scară: 0.10 = 10 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** cumpărătorii au dus prețul până la 63.30 — o încercare
|
||||
reală, după o cădere — dar au fost respinși și au închis aproape de unde au
|
||||
deschis.
|
||||
|
||||
**De ce îl urmărești, deși încercarea a eșuat:** într-un downtrend, cel mai
|
||||
important lucru nu e cine câștigă, e **dacă apare cerere deloc**. Un inverted
|
||||
hammer e prima dovadă că există cumpărători dispuși să plătească mult mai
|
||||
sus. Au pierdut runda asta, dar existența lor e informația.
|
||||
|
||||
**Ce faci când îl vezi:** îl tratezi ca pe un avertisment, nu ca pe un
|
||||
set-up. Concret: **nu mai deschizi short-uri noi** în direcția trendului, și
|
||||
aștepți. Confirmarea cerută e mai strictă decât la hammer — o candelă care
|
||||
închide peste **63.30**, nu doar peste corp.
|
||||
|
||||
**De ce e mai slab decât hammer:** la hammer, intervalul s-a încheiat cu
|
||||
cumpărătorii în control (close la maxim). Aici s-a încheiat cu vânzătorii în
|
||||
control (close aproape de open). Aceeași informație, dar cu rezultatul
|
||||
final încă în favoarea taberei vechi.
|
||||
|
||||
## Hanging Man — aceeași formă ca hammer, sens opus
|
||||
|
||||
**Ce arată:** geometric **identic** cu hammer. Singura diferență e ce a fost
|
||||
înainte pe grafic: un **uptrend**.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(63.05, 63.30, 62.10, 63.30, "hanging man", "O 63.05, H = C 63.30, L 62.10 — exact aceleași cifre ca la hammer")],
|
||||
scale=100, left=130, trend="up",
|
||||
caption="Formă identică cu hammer. Contextul de trend e singura diferență."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** în mijlocul unui trend de creștere, cineva a vândut
|
||||
suficient cât să ducă prețul cu 1.20 mai jos în timpul intervalului.
|
||||
Cumpărătorii au recuperat până la închidere — dar **vânzarea aia n-ar fi
|
||||
trebuit să existe**.
|
||||
|
||||
**De ce îl urmărești:** îți semnalează prima ofertă serioasă într-un trend
|
||||
care până atunci nu întâlnise rezistență. Nu îți spune că trendul s-a
|
||||
terminat; îți spune că a apărut cineva de partea cealaltă.
|
||||
|
||||
**Ce faci când îl vezi, concret** — și aici e diferența practică față de
|
||||
hammer: dacă ești deja long, **muți stop-loss-ul sub 62.10**. Nu ieși, nu
|
||||
inversezi. Îți protejezi profitul la nivelul pe care vânzătorii tocmai l-au
|
||||
testat.
|
||||
|
||||
## Shooting Star — reversal bear, după uptrend
|
||||
|
||||
**Ce arată:** corp mic jos, fitil lung în sus, la vârful unei mișcări de
|
||||
creștere. Geometric identic cu inverted hammer.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(64.10, 65.10, 63.90, 63.95, "shooting star", "O 64.10, H 65.10, L 63.90, C 63.95")],
|
||||
scale=90, left=130, trend="up",
|
||||
caption="Scară: 0.10 = 9 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** cumpărătorii au împins până la 65.10 și au fost
|
||||
respinși complet. Tot avansul intervalului a fost anulat până la închidere.
|
||||
|
||||
**De ce îl urmărești:** îți dă **nivelul exact al ofertei** — 65.10. Acolo
|
||||
există vânzători care au fost dispuși să vândă în cantitate. Nivelul ăla
|
||||
devine rezistență până e depășit la închidere.
|
||||
|
||||
**Ce faci când îl vezi:**
|
||||
- dacă ești long — muți stop-ul sau marchezi profit parțial;
|
||||
- dacă vrei short — aștepți o candelă care închide **sub low-ul** shooting
|
||||
star-ului (63.90), cu stop peste 65.10;
|
||||
- dacă nu ești în piață — notezi 65.10 ca rezistență și nu cumperi sub el.
|
||||
|
||||
**Trade complet, short:**""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(64.10, 65.10, 63.90, 63.95, "shooting star", "shooting star — high-ul 65.10 e oferta"),
|
||||
Candle(64.00, 64.05, 63.60, 63.70, "confirmare", "confirmare — închide 63.70, sub low-ul 63.90")],
|
||||
levels=[Level(65.15, "SL 65.15 — peste high", "#c00"),
|
||||
Level(63.85, "intrare 63.85 short", "#1a5fb4"),
|
||||
Level(61.25, "TP 61.25 — 2R", "#188a3e")],
|
||||
scale=44, gap=110, left=110, trend="up",
|
||||
caption="Scară: 1.00 = 44 px. Risc 1.30 în sus, țintă 2.60 în jos — exact dublul."))
|
||||
|
||||
w("""
|
||||
## Doji — nimeni n-a câștigat
|
||||
|
||||
**Ce arată:** open și close practic egale, cu fitile în ambele direcții.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.75, 63.30, 62.10, 62.73, "doji", "O 62.75, C 62.73 — corp de 0.02, interval total de 1.20")],
|
||||
scale=100, left=130,
|
||||
caption="Prețul s-a mișcat 1.20 și a închis exact de unde a plecat. Scară: 0.10 = 10 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** amândouă taberele au împins, niciuna n-a păstrat
|
||||
teren. Intervalul s-a încheiat la egalitate perfectă.
|
||||
|
||||
**De ce îl urmărești — și de ce e diferit de celelalte:** un doji **nu îți
|
||||
dă direcție**. Îți spune un singur lucru: partea care domina până acum
|
||||
și-a pierdut avantajul. E informație despre trendul vechi, nu despre cel nou.
|
||||
|
||||
**Ce faci când îl vezi:**
|
||||
1. Dacă ești într-o poziție pe direcția trendului — **nu mai adaugi**.
|
||||
2. Dacă voiai să intri pe direcția trendului — amâni.
|
||||
3. Dacă vrei reversal — aștepți confirmarea: o candelă care închide dincolo
|
||||
de **extrema doji-ului**, în direcția opusă trendului anterior.
|
||||
|
||||
Fără pasul 3, un doji e zgomot. Apar des.
|
||||
|
||||
**Trade complet, long (după downtrend):**""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.75, 63.30, 62.10, 62.73, "doji", "doji — extremele 63.30 și 62.10 sunt pragurile"),
|
||||
Candle(62.80, 64.10, 62.70, 64.00, "confirmare", "confirmare — închide 64.00, peste 63.30")],
|
||||
levels=[Level(65.95, "TP 65.95 — 2R", "#188a3e"),
|
||||
Level(63.35, "intrare 63.35", "#1a5fb4"),
|
||||
Level(62.05, "SL 62.05 — sub low-ul doji", "#c00")],
|
||||
scale=52, gap=110, left=110, trend="down",
|
||||
caption="Scară: 1.00 = 52 px. Risc 1.30, țintă 2.60 — exact dublul."))
|
||||
|
||||
w("""
|
||||
**Short (oglindă, după uptrend):** același doji la vârf, urmat de o candelă
|
||||
care închide sub 62.10. Intrare **62.05** short, SL **63.35** (peste high-ul
|
||||
doji-ului), risc 1.30, TP **59.45**.
|
||||
|
||||
**Invalidarea, în ambele cazuri:** candela următoare închide în interiorul
|
||||
doji-ului, nu dincolo de o extremă. Atunci nu s-a rezolvat nimic — aștepți
|
||||
în continuare.
|
||||
|
||||
## Spinning Top — doji cu corp vizibil
|
||||
|
||||
**Ce arată:** fitile lungi în ambele direcții, corp mic **dar nu neglijabil**.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.60, 63.25, 62.15, 62.80, "spinning top", "O 62.60, C 62.80 — corp de 0.20 într-un interval de 1.10")],
|
||||
scale=100, left=130,
|
||||
caption="Corpul e sub 20% din interval. Scară: 0.10 = 10 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** același echilibru ca la doji, cu un avantaj minim
|
||||
pentru o parte. Prețul a explorat 1.10 și a păstrat 0.20.
|
||||
|
||||
**De ce îl urmărești — și de ce nu îl tranzacționezi:** un spinning top
|
||||
izolat nu înseamnă aproape nimic; apare constant. Valoarea lui e **în
|
||||
serie**. Dacă vezi două-trei spinning tops consecutive într-un trend,
|
||||
trendul a intrat în echilibru: mișcarea per interval a scăzut, dar
|
||||
volatilitatea nu.
|
||||
|
||||
**Ce faci când îl vezi, concret:**
|
||||
- **nu intri** pe el, niciodată singur;
|
||||
- dacă ai o poziție deschisă pe trend și apar 2-3 la rând, **strângi
|
||||
stop-ul** — impulsul care ți-a dus poziția s-a terminat;
|
||||
- îl folosești ca filtru pentru alte set-up-uri: un breakout care are în
|
||||
spate trei spinning tops are mai puține șanse decât unul care vine dintr-o
|
||||
mișcare direcțională.
|
||||
|
||||
**Diferența practică față de doji:** doji-ul e un eveniment (echilibru
|
||||
perfect, rar), spinning top-ul e o stare (echilibru aproximativ, frecvent).
|
||||
De aia doji-ul merită o confirmare și un trade, iar spinning top-ul merită
|
||||
doar o ajustare de risc.
|
||||
|
||||
## Marubozu — control total, și de ce e periculos să intri pe el
|
||||
|
||||
**Ce arată:** corp maxim, **fără fitile**. La cel bull: open = low și
|
||||
close = high. La cel bear: invers.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.00, 64.00, 62.00, 64.00, "marubozu bull", "bull — O = L = 62.00 (jos), C = H = 64.00 (sus)"),
|
||||
Candle(64.00, 64.00, 62.00, 62.00, "marubozu bear", "bear — O = H = 64.00 (sus), C = L = 62.00 (jos)")],
|
||||
scale=70, gap=150, left=140,
|
||||
caption="La cel bull, open-ul e la minim; la cel bear, open-ul e la maxim. Scară: 1.00 = 70 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** o singură parte a controlat prețul din primul până în
|
||||
ultimul tick. N-a existat niciun moment în care cealaltă tabără să împingă
|
||||
prețul înapoi, nici măcar temporar.
|
||||
|
||||
**De ce îl urmărești:** e cea mai clară afirmație de direcție pe care o poate
|
||||
face o singură candelă. Îți spune fără ambiguitate cine deține controlul
|
||||
**acum**.
|
||||
|
||||
**De ce e, totuși, cea mai proastă candelă pe care să intri:** tocmai pentru
|
||||
că nu are fitile, întreaga ei înălțime e mișcare deja consumată. Dacă intri
|
||||
la închiderea unui marubozu bull de 2.00, intri la maximul intervalului, iar
|
||||
stop-ul logic (sub low-ul candelei) e la 2.00 distanță. Raportul risc/câștig
|
||||
pornește prost.
|
||||
|
||||
**Ce faci când îl vezi, concret:** aștepți retestul la **midpoint** —
|
||||
jumătatea corpului, 63.00 în exemplul de sus.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.00, 64.00, 62.00, 64.00, "marubozu", "marubozu bull — corp de 2.00, fără fitile"),
|
||||
Candle(63.90, 63.95, 63.05, 63.15, "pullback", "pullback — coboară până la 63.05, aproape de midpoint"),
|
||||
Candle(63.20, 64.30, 63.10, 64.25, "reluare", "reluare — cumpărătorii revin din midpoint")],
|
||||
levels=[Level(63.00, "midpoint 63.00", "#b06000", "5 3")],
|
||||
scale=70, gap=105, left=95,
|
||||
caption="Intri la 63.10 cu stop sub 62.95, nu la 64.00 cu stop la 62.00. Același trade, sfert de risc."))
|
||||
|
||||
w("""
|
||||
**Comparația care justifică așteptarea:**
|
||||
|
||||
| Cum intri | Intrare | Stop logic | Risc |
|
||||
|---|---|---|---|
|
||||
| La închiderea marubozu-ului | 64.00 | sub 62.00 | **2.00** |
|
||||
| La retestul midpoint-ului | 63.10 | sub 62.95 | **0.15** |
|
||||
|
||||
Aceeași idee, aceeași direcție. Riscul diferă de peste 13 ori.
|
||||
|
||||
**Ce te costă răbdarea:** uneori prețul nu mai revine la midpoint și pleacă
|
||||
fără tine. Asta e prețul — și e motivul pentru care un marubozu foarte mare,
|
||||
într-un trend puternic, se tranzacționează cu jumătate de poziție la retest,
|
||||
nu cu tot.""")
|
||||
|
||||
w("""
|
||||
---
|
||||
|
||||
# Pattern-uri de două candele
|
||||
|
||||
## Engulfing — schimbare directă de control
|
||||
|
||||
**Ce arată:** a doua candelă acoperă complet corpul primei, în culoarea
|
||||
opusă. În varianta strictă folosită în curs, îi depășește și fitilele.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.80, 62.85, 62.25, 62.30, "candela 1", "candela 1 (mică, bear) — O 62.80, H 62.85, L 62.25, C 62.30"),
|
||||
Candle(62.20, 63.05, 62.15, 63.00, "candela 2", "candela 2 (mare, bull) — O 62.20, H 63.05, L 62.15, C 63.00")],
|
||||
scale=200, gap=120, left=130, trend="down",
|
||||
caption="Corpul candelei 2 (62.20→63.00) cuprinde complet corpul candelei 1 (62.80→62.30). Scară: 0.10 = 20 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat — și de ce e cel mai puternic reversal de două candele:**
|
||||
candela 2 a deschis **sub** close-ul candelei 1 și a închis **peste** open-ul
|
||||
ei. Concret: **toți cei care au vândut în intervalul 1 sunt acum pe
|
||||
pierdere**, fără excepție. Nu o parte dintre ei — toți.
|
||||
|
||||
Asta contează pentru că oamenii aceia devin cumpărători forțați: ca să iasă
|
||||
dintr-un short, trebuie să cumperi. Presiunea de cumpărare din zilele
|
||||
următoare nu vine doar din convingere, vine din nevoia lor de a ieși.
|
||||
|
||||
**De ce îl urmărești:** e singurul pattern de două candele care îți dă
|
||||
schimbarea de control **și** lista de perdanți în același timp.
|
||||
|
||||
**Ce faci când îl vezi — diferența față de hammer și doji:** aici **poți
|
||||
intra la închiderea candelei 2**, fără o a treia candelă de confirmare, cu
|
||||
două condiții:
|
||||
1. e la un nivel care conta deja (suport, pivot, fostă rezistență);
|
||||
2. corpul candelei 2 e vizibil mai mare decât media ultimelor candele.
|
||||
|
||||
Fără condiția 1, aștepți confirmare ca la restul.
|
||||
|
||||
**Trade complet, long:**""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.80, 62.85, 62.25, 62.30, "c1", "candela 1 — cei care au vândut aici sunt acum pe pierdere"),
|
||||
Candle(62.20, 63.05, 62.15, 63.00, "c2 — engulfing", "candela 2 — intri la închiderea ei")],
|
||||
levels=[Level(65.10, "TP 65.10 — 2R", "#188a3e"),
|
||||
Level(63.10, "intrare 63.10", "#1a5fb4"),
|
||||
Level(62.10, "SL 62.10 — sub low-ul c2", "#c00")],
|
||||
scale=62, gap=110, left=110, trend="down",
|
||||
caption="Scară: 1.00 = 62 px. Risc 1.00, țintă 2.00 — exact dublul."))
|
||||
|
||||
w("""
|
||||
| | Preț | De ce acolo |
|
||||
|---|---|---|
|
||||
| Intrare | 63.10 | la închiderea candelei 2, fără să aștepți alta |
|
||||
| Stop-loss | 62.10 | sub low-ul candelei 2 — dacă se rupe, cumpărătorii care au înghițit candela 1 au cedat |
|
||||
| Risc | 1.00 | |
|
||||
| Țintă | 65.10 | 2R |
|
||||
|
||||
**Bearish Engulfing** e simetric: după uptrend, candelă mare roșie care
|
||||
înghite corpul verde precedent. Toți cei care au cumpărat în intervalul 1
|
||||
sunt pe pierdere. Short la închiderea candelei 2, stop peste high-ul ei.
|
||||
|
||||
## Harami — contracție bruscă, nu inversare
|
||||
|
||||
**Ce arată:** exact opusul engulfing-ului. Candela 2 e mică și încape
|
||||
complet **în interiorul corpului** candelei 1. Numele vine din japoneză,
|
||||
„gravidă".""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(64.00, 64.10, 62.10, 62.20, "candela 1", "candela 1 (mare, bear) — O 64.00, H 64.10, L 62.10, C 62.20"),
|
||||
Candle(62.60, 62.95, 62.55, 62.90, "candela 2", "candela 2 (mică, bull) — O 62.60, C 62.90, interval total 0.40")],
|
||||
scale=95, gap=120, left=130, trend="down",
|
||||
caption="Candela 2 se tranzacționează într-un interval de 0.40, în interiorul corpului de 1.80 al candelei 1."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** vânzarea masivă din candela 1 s-a **oprit brusc**. În
|
||||
intervalul următor nimeni n-a mai reușit — sau n-a mai vrut — să miște
|
||||
prețul.
|
||||
|
||||
**Diferența esențială față de engulfing**, și motivul pentru care harami e un
|
||||
semnal mai slab: la engulfing, cumpărătorii au **bătut** vânzătorii. Aici,
|
||||
vânzătorii pur și simplu **nu s-au mai prezentat**. Absența unei forțe nu e
|
||||
același lucru cu prezența forței opuse.
|
||||
|
||||
**De ce îl urmărești:** e un detector de **epuizare**, nu de inversare. Îți
|
||||
spune că mișcarea care era în curs și-a pierdut combustibilul. Ce urmează
|
||||
poate fi inversare, dar la fel de bine consolidare laterală sau reluarea
|
||||
trendului după o pauză.
|
||||
|
||||
**Ce faci când îl vezi, concret:**
|
||||
- **Nu intri pe el.** Un harami singur nu e set-up de intrare.
|
||||
- Dacă ești în poziție pe direcția trendului — **strângi stop-ul** la
|
||||
extrema candelei 1. Ai luat mișcarea; ce urmează e incert.
|
||||
- Dacă aștepți un reversal — ai nevoie de o a treia candelă care închide
|
||||
dincolo de extrema candelei 1 (peste 64.10 pentru bull). Abia atunci
|
||||
devine set-up, și atunci e de fapt un alt pattern.
|
||||
|
||||
**Bear Harami** e simetric: candelă mare verde după uptrend, urmată de una
|
||||
mică roșie în interiorul corpului ei. Aceeași citire — cumpărătorii au
|
||||
rămas fără muniție, nu au fost bătuți.
|
||||
|
||||
## Piercing Line și Dark Cloud Cover — engulfing incomplet
|
||||
|
||||
**Ce arată:** două candele, ca la engulfing, dar candela 2 **nu** acoperă tot
|
||||
corpul candelei 1 — doar trece de jumătatea lui (midpoint).""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(63.80, 63.90, 62.40, 62.50, "candela 1", "candela 1 (bear) — O 63.80, C 62.50, midpoint 63.15"),
|
||||
Candle(62.30, 63.45, 62.25, 63.40, "candela 2", "candela 2 (bull) — deschide sub 62.50 și închide la 63.40, peste midpoint")],
|
||||
levels=[Level(63.15, "midpoint 63.15", "#b06000", "5 3")],
|
||||
scale=120, gap=120, left=130, trend="down",
|
||||
caption="Candela 2 recuperează peste jumătate din corpul candelei 1, dar nu tot. Scară: 0.10 = 12 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** candela 2 a deschis cu gap în jos — adică sentimentul
|
||||
era încă negativ la deschidere — și apoi a recuperat mai mult de jumătate din
|
||||
căderea precedentă.
|
||||
|
||||
**De ce contează pragul de 50%, și nu altul:** midpoint-ul e prețul mediu
|
||||
plătit de cei care au vândut în candela 1. Când prețul trece peste el,
|
||||
**mai mult de jumătate** dintre ei sunt pe pierdere. Sub prag, majoritatea
|
||||
încă sunt pe profit și n-au niciun motiv să acționeze.
|
||||
|
||||
**De ce îl urmărești:** e versiunea slabă a engulfing-ului — aceeași direcție,
|
||||
aceeași logică, dar cu jumătate din perdanți în loc de toți.
|
||||
|
||||
**Ce faci când îl vezi:** spre deosebire de engulfing, **ceri confirmare**.
|
||||
Aștepți o candelă care închide peste high-ul candelei 2 (63.45). Stop-ul
|
||||
merge sub low-ul candelei 2.
|
||||
|
||||
**Dark Cloud Cover** e oglinda: după uptrend, candela 2 deschide cu gap în
|
||||
sus și închide sub midpoint-ul corpului verde precedent.
|
||||
|
||||
**Avertisment practic:** ambele cer un **gap la deschidere**. Pe instrumente
|
||||
care se tranzacționează continuu (forex, cripto) gap-urile aproape nu există,
|
||||
deci pattern-urile astea apar aproape exclusiv pe acțiuni și indici, la
|
||||
deschiderea sesiunii.""")
|
||||
|
||||
w("""
|
||||
---
|
||||
|
||||
# Pattern-uri de trei candele
|
||||
|
||||
## Morning Star — reversal bull, cel mai fiabil dintre clasice
|
||||
|
||||
**Ce arată:** candelă mare bear, candelă mică izolată jos, candelă mare bull
|
||||
care închide peste midpoint-ul primei.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(64.00, 64.10, 62.10, 62.20, "c1 — bear", "candela 1 — O 64.00, C 62.20, midpoint 63.10"),
|
||||
Candle(62.05, 62.20, 61.90, 62.00, "c2 — indecizie", "candela 2 — corp mic, izolată jos: nici vânzătorii nu mai coboară"),
|
||||
Candle(62.10, 63.50, 62.05, 63.40, "c3 — bull", "candela 3 — închide 63.40, peste midpoint-ul 63.10")],
|
||||
levels=[Level(63.10, "midpoint 63.10 — pragul de validare", "#b06000", "5 3")],
|
||||
scale=95, gap=105, left=95, trend="down",
|
||||
caption="Scară: 0.10 = 9.5 px. Candela 3 trebuie să închidă peste 63.10, altfel nu e Morning Star."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat, pe etape:**
|
||||
|
||||
1. **Candela 1** — vânzătorii în control total, 1.80 în jos.
|
||||
2. **Candela 2** — vânzătorii ajung jos și **nu mai pot coborî**. Nici
|
||||
cumpărătorii nu preiau. Echilibru, la minimul mișcării.
|
||||
3. **Candela 3** — cumpărătorii preiau și recuperează peste jumătate din
|
||||
candela 1.
|
||||
|
||||
**De ce e mai fiabil decât un hammer sau un doji:** pattern-urile de o
|
||||
candelă îți arată **un singur moment**. Morning Star îți arată o **secvență**:
|
||||
epuizare, pauză, preluare. Fiecare etapă confirmă pe precedenta, iar
|
||||
confirmarea e deja inclusă în pattern — nu mai aștepți o a patra candelă.
|
||||
|
||||
**De ce îl urmărești:** e unul dintre puținele pattern-uri care justifică
|
||||
intrarea **fără confirmare suplimentară**, pentru că a treia candelă *este*
|
||||
confirmarea.
|
||||
|
||||
**Ce faci când îl vezi:** verifici pragul. Dacă închiderea candelei 3 e sub
|
||||
midpoint-ul candelei 1, **nu e Morning Star** — e doar un rebound, și îl
|
||||
tratezi ca atare.
|
||||
|
||||
**Trade complet, long:**""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(64.00, 64.10, 62.10, 62.20, "c1", ""),
|
||||
Candle(62.05, 62.20, 61.90, 62.00, "c2", "low-ul candelei 2 (61.90) e nivelul de invalidare"),
|
||||
Candle(62.10, 63.50, 62.05, 63.40, "c3", "intri la închiderea candelei 3, la 63.45")],
|
||||
levels=[Level(66.65, "TP 66.65 — 2R", "#188a3e"),
|
||||
Level(63.45, "intrare 63.45", "#1a5fb4"),
|
||||
Level(61.85, "SL 61.85 — sub low-ul c2", "#c00")],
|
||||
scale=42, gap=105, left=95, trend="down",
|
||||
caption="Scară: 1.00 = 42 px. Risc 1.60, țintă 3.20 — exact dublul."))
|
||||
|
||||
w("""
|
||||
**Evening Star** e oglinda exactă, după uptrend: candelă mare bull, candelă
|
||||
mică izolată sus, candelă mare bear care închide sub midpoint-ul primei.
|
||||
Short la închiderea candelei 3, stop peste high-ul candelei 2.
|
||||
|
||||
## Abandoned Baby — Morning Star cu gap-uri de ambele părți
|
||||
|
||||
**Ce arată:** aceeași structură, dar candela din mijloc e **complet izolată**
|
||||
prin gap-uri — high-ul ei e sub low-ul candelei 1 **și** sub low-ul candelei 3.""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(64.00, 64.10, 62.60, 62.70, "c1 — bear", "candela 1 — low-ul ei e 62.60"),
|
||||
Candle(62.15, 62.25, 62.00, 62.10, "c2 — abandonată", "candela 2 — high-ul ei (62.25) e SUB low-ul candelei 1: gap"),
|
||||
Candle(62.50, 63.80, 62.45, 63.70, "c3 — bull", "candela 3 — low-ul ei (62.45) e PESTE high-ul candelei 2: al doilea gap")],
|
||||
scale=110, gap=105, left=95, trend="down",
|
||||
caption="Două gap-uri, unul de fiecare parte a candelei din mijloc. Scară: 0.10 = 11 px."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat în plus față de Morning Star:** gap-urile înseamnă că
|
||||
**nu s-a tranzacționat nimic** la prețurile dintre candele. Schimbarea de
|
||||
control a fost atât de bruscă încât n-a existat niciun preț intermediar la
|
||||
care cumpărătorii și vânzătorii să se întâlnească.
|
||||
|
||||
**De ce e considerat mai puternic:** un gap e o zonă fără proprietari. Nimeni
|
||||
nu a cumpărat acolo, deci nimeni nu are pierderi de recuperat acolo, deci nu
|
||||
există ofertă care să oprească mișcarea în drumul înapoi.
|
||||
|
||||
**Ce faci când îl vezi:** identic cu Morning Star. Diferența e doar în
|
||||
așteptări — merită dimensionat ceva mai mare, dacă e la un nivel.
|
||||
|
||||
**De ce îl vei vedea rar:** cere două gap-uri consecutive, deci apare aproape
|
||||
exclusiv pe acțiuni și indici, la deschiderea sesiunii, și de obicei în jurul
|
||||
unui catalizator (rezultate financiare, știre). Pe forex sau cripto, practic
|
||||
nu apare.
|
||||
|
||||
## Three White Soldiers / Three Black Crows
|
||||
|
||||
**Ce arată:** trei candele consecutive cu corpuri mari în aceeași direcție,
|
||||
fiecare deschizând în interiorul corpului precedentei și închizând peste
|
||||
high-ul ei (sau sub low, la variantă bear).""")
|
||||
|
||||
svg(Chart(
|
||||
candles=[Candle(62.10, 62.90, 62.05, 62.85, "1", ""),
|
||||
Candle(62.55, 63.60, 62.50, 63.55, "2", "fiecare deschide în corpul precedentei și închide peste high-ul ei"),
|
||||
Candle(63.25, 64.30, 63.20, 64.25, "3", "")],
|
||||
scale=95, gap=105, left=110,
|
||||
caption="Three White Soldiers. Varianta bear (Three Black Crows) e oglinda exactă."))
|
||||
|
||||
w("""
|
||||
**Ce s-a întâmplat:** control susținut pe trei intervale consecutive, fără
|
||||
respingeri serioase. E cel mai clar semnal de **continuare** din setul clasic.
|
||||
|
||||
**De ce cursul nu insistă pe ele — motivul real:** nu doar că apar rar.
|
||||
Problema e **când** apar: patternul se completează abia la închiderea celei
|
||||
de-a treia candele, adică după ce mișcarea a parcurs deja trei intervale
|
||||
întregi. Intri la capătul lui, cu stop-ul logic sub low-ul primei candele —
|
||||
adică riști toată mișcarea ca să prinzi ce urmează după ea.
|
||||
|
||||
**Ce faci când îl vezi, concret:** nu îl folosești ca semnal de intrare. Îl
|
||||
folosești ca **confirmare de context**: dacă ai deja o poziție long și apar
|
||||
trei soldați, ai dovada că trendul e susținut și poți ține mai mult. Dacă
|
||||
nu ai poziție, aștepți un pullback, nu intri în al patrulea interval.
|
||||
|
||||
---
|
||||
|
||||
## Regula care le leagă pe toate
|
||||
|
||||
Toate pattern-urile de mai sus au aceeași structură logică:
|
||||
|
||||
| Element | Ce e | Exemplu la hammer |
|
||||
|---|---|---|
|
||||
| **Nivelul de invalidare** | prețul care, dacă e depășit, arată că ipoteza e greșită | low-ul, 62.10 |
|
||||
| **Pragul de confirmare** | prețul care, dacă e depășit, arată că ipoteza e corectă | high-ul, 63.30 |
|
||||
| **Zona de așteptare** | între ele, nu știi nimic | 62.10 - 63.30 |
|
||||
|
||||
Dacă un pattern nu îți dă cele două niveluri, nu e tranzacționabil — e doar
|
||||
context.
|
||||
|
||||
## Gotchas practice
|
||||
|
||||
- **Fără nivel, nu există pattern.** Un hammer în mijlocul unei consolidări
|
||||
nu înseamnă nimic. Același hammer la un pivot quarterly e un set-up.
|
||||
Contextul nu e un bonus, e o condiție.
|
||||
- **Formele identice se citesc invers.** Hammer și hanging man sunt aceeași
|
||||
candelă. Inverted hammer și shooting star, la fel. Ce le separă e exclusiv
|
||||
trendul dinainte — verifică-l întotdeauna înainte să numești patternul.
|
||||
- **Un pattern nu îți spune cât de departe merge prețul.** Îți dă direcția și
|
||||
nivelul de invalidare. Ținta vine din altceva: un nivel de rezistență, un
|
||||
multiplu de risc, sau o proiecție geometrică.
|
||||
- **Marubozu mare = risc mare la intrare directă.** Așteaptă retestul
|
||||
midpoint-ului; diferența de risc e de peste zece ori, pe același trade.
|
||||
- **Gap-urile nu apar pe orice instrument.** Abandoned Baby, Piercing Line și
|
||||
Dark Cloud Cover cer gap-uri, deci apar pe acțiuni și indici, la
|
||||
deschidere. Nu le aștepta pe forex sau cripto.
|
||||
|
||||
## Ce e completare a mea, dincolo de transcriere
|
||||
|
||||
Cursul descrie formele și denumirile, plus partea de scanare în TradeStation.
|
||||
Următoarele sunt completările mele: mecanismul comun al fitilului, toate
|
||||
exemplele numerice OHLC, secțiunile **de ce îl urmărești** și **ce faci când
|
||||
îl vezi** pentru fiecare pattern, trade-urile complete cu intrare/SL/țintă,
|
||||
comparația de risc la marubozu, explicația pragului de 50% la Piercing Line,
|
||||
motivul real pentru care Three Soldiers se tranzacționează prost, tabelul cu
|
||||
nivelul de invalidare / pragul de confirmare, și gotchas-urile.
|
||||
|
||||
Toate diagramele sunt generate din prețuri cu `svg_candles.py`, nu desenate
|
||||
de mână — o unitate de preț are aceeași înălțime oriunde pe același desen,
|
||||
deci o țintă de 2R e desenată la exact dublul distanței față de stop-loss.
|
||||
|
||||
## Referințe
|
||||
- [IG — 16 Candlestick Patterns Every Trader Should Know](https://www.ig.com/en/trading-strategies/16-candlestick-patterns-every-trader-should-know-180615)
|
||||
- [Investopedia — Hammer Candlestick](https://www.investopedia.com/terms/h/hammer.asp)
|
||||
- [Investopedia — Engulfing Pattern](https://www.investopedia.com/terms/b/bullishengulfingpattern.asp)
|
||||
- [CandleScanner — Piercing Pattern](https://www.candlescanner.com/candlestick-patterns/piercing/)""")
|
||||
|
||||
out = Path(__file__).resolve().parent.parent / "summaries/01_MODUL_1/05_patternuri_candele.md"
|
||||
out.write_text("\n".join(P) + "\n", encoding="utf-8")
|
||||
print(f"scris {out} ({len(P)} blocuri)")
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
132
svg_candles.py
Normal file
132
svg_candles.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""
|
||||
Generator de diagrame de candele pentru summaries/.
|
||||
|
||||
Motivul existenței: diagramele desenate de mână ies la scară greșită. În
|
||||
`01_MODUL_1/05_patternuri_candele.md` ținta de 2R era desenată la distanța
|
||||
de 1R față de intrare (75 px/unitate în jos, 41 px/unitate în sus), iar la
|
||||
Marubozu eticheta de open era pusă deasupra celei de close — axa de preț
|
||||
inversată. Aici coordonatele se calculează din prețuri, deci scara e
|
||||
corectă prin construcție.
|
||||
|
||||
Convenție: un singur `scale` (px per unitate de preț) pentru tot desenul.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class Candle:
|
||||
o: float
|
||||
h: float
|
||||
l: float
|
||||
c: float
|
||||
tag: str = "" # eticheta de sub candelă
|
||||
note: str = "" # a doua linie de etichetă
|
||||
color: str | None = None # implicit: verde dacă c >= o
|
||||
|
||||
|
||||
@dataclass
|
||||
class Level:
|
||||
price: float
|
||||
text: str
|
||||
color: str = "#1a5fb4"
|
||||
dash: str = "4 3"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Chart:
|
||||
candles: list[Candle]
|
||||
levels: list[Level] = field(default_factory=list)
|
||||
scale: float = 60.0 # px per unitate de preț
|
||||
width: int = 620
|
||||
body_w: int = 26
|
||||
gap: int = 92 # distanță între candele
|
||||
left: int = 64 # x-ul primei candele
|
||||
top: int = 34 # spațiu deasupra celui mai mare preț
|
||||
label_gap: int = 34 # spațiu sub cea mai joasă candelă pentru etichete
|
||||
caption: str = ""
|
||||
trend: str = "" # "down" / "up" / "" — segment de trend înainte
|
||||
|
||||
def _y(self, price: float) -> float:
|
||||
return self.top + (self._pmax - price) * self.scale
|
||||
|
||||
def render(self) -> str:
|
||||
prices = [p for c in self.candles for p in (c.o, c.h, c.l, c.c)]
|
||||
prices += [lv.price for lv in self.levels]
|
||||
self._pmax, pmin = max(prices), min(prices)
|
||||
# segmentul de trend iese din zona candelelor: rezervă loc pentru el,
|
||||
# altfel se suprapune peste etichete sau peste marginea desenului
|
||||
if self.trend == "down":
|
||||
self.top += 56
|
||||
body_bottom = self.top + (self._pmax - pmin) * self.scale
|
||||
extra = 62 if self.trend == "up" else 0
|
||||
n_notes = sum(1 for c in self.candles if c.note)
|
||||
height = body_bottom + extra + self.label_gap + 17 * n_notes + (24 if self.caption else 8)
|
||||
|
||||
out = [f'<svg viewBox="0 0 {self.width} {height:.0f}" xmlns="http://www.w3.org/2000/svg" '
|
||||
f'style="width:100%;height:auto;font:12px sans-serif">']
|
||||
|
||||
# nivelurile: linie pe toată lățimea, eticheta deasupra liniei la dreapta
|
||||
for lv in self.levels:
|
||||
y = self._y(lv.price)
|
||||
out.append(f'<line x1="24" y1="{y:.1f}" x2="{self.width - 14}" y2="{y:.1f}" '
|
||||
f'stroke="{lv.color}" stroke-width="1.5" stroke-dasharray="{lv.dash}"/>')
|
||||
out.append(f'<text x="{self.width - 28}" y="{y - 6:.1f}" fill="{lv.color}" '
|
||||
f'text-anchor="end">{lv.text}</text>')
|
||||
|
||||
x0 = self.left
|
||||
if self.trend:
|
||||
y_start = self._y(self.candles[0].h if self.trend == "down" else self.candles[0].l)
|
||||
dy = -46 if self.trend == "down" else 46
|
||||
out.append(f'<polyline points="24,{y_start + dy:.1f} {x0 - 8:.0f},{y_start:.1f}" '
|
||||
f'fill="none" stroke="#999" stroke-width="2"/>')
|
||||
# la downtrend linia coboară spre dreapta (etichetă deasupra),
|
||||
# la uptrend urcă (etichetă dedesubt) — altfel textul stă pe linie
|
||||
ty = y_start + dy - 12 if self.trend == "down" else y_start + dy + 20
|
||||
out.append(f'<text x="24" y="{ty:.1f}" fill="#999">'
|
||||
f'{"downtrend anterior" if self.trend == "down" else "uptrend anterior"}</text>')
|
||||
|
||||
for i, cd in enumerate(self.candles):
|
||||
cx = x0 + i * self.gap
|
||||
top_body, bot_body = max(cd.o, cd.c), min(cd.o, cd.c)
|
||||
color = cd.color or ("#188a3e" if cd.c >= cd.o else "#c00")
|
||||
out.append(f'<line x1="{cx}" y1="{self._y(cd.h):.1f}" x2="{cx}" y2="{self._y(cd.l):.1f}" '
|
||||
f'stroke="#1a1a1a" stroke-width="1.5"/>')
|
||||
h = max(self._y(bot_body) - self._y(top_body), 2.0)
|
||||
out.append(f'<rect x="{cx - self.body_w // 2}" y="{self._y(top_body):.1f}" '
|
||||
f'width="{self.body_w}" height="{h:.1f}" fill="{color}"/>')
|
||||
if cd.tag:
|
||||
out.append(f'<text x="{cx}" y="{body_bottom + extra + 18:.1f}" fill="{color}" '
|
||||
f'text-anchor="middle" style="font-weight:bold">{cd.tag}</text>')
|
||||
|
||||
# detaliile OHLC ca bloc stânga-jos, câte o linie per candelă: centrate sub
|
||||
# candelă se suprapun între ele de îndată ce textul depășește `gap`.
|
||||
ny = body_bottom + extra + self.label_gap + 4
|
||||
for cd in self.candles:
|
||||
if cd.note:
|
||||
color = cd.color or ("#188a3e" if cd.c >= cd.o else "#c00")
|
||||
out.append(f'<text x="24" y="{ny:.1f}" fill="{color}">{cd.note}</text>')
|
||||
ny += 17
|
||||
|
||||
if self.caption:
|
||||
out.append(f'<text x="24" y="{ny + 8:.0f}" fill="#555">{self.caption}</text>')
|
||||
out.append("</svg>")
|
||||
return "\n ".join(out).replace("\n </svg>", "\n</svg>")
|
||||
|
||||
|
||||
def selftest():
|
||||
"""O unitate de preț trebuie să dea aceeași distanță oriunde pe desen."""
|
||||
ch = Chart(candles=[Candle(o=63.05, h=63.30, l=62.10, c=63.30)],
|
||||
levels=[Level(62.05, "SL"), Level(63.35, "entry"), Level(65.95, "TP 2R")],
|
||||
scale=60)
|
||||
svg = ch.render()
|
||||
ys = {lv.price: ch._y(lv.price) for lv in ch.levels}
|
||||
risc = ys[62.05] - ys[63.35] # px, în jos
|
||||
castig = ys[63.35] - ys[65.95] # px, în sus
|
||||
assert abs(castig / risc - 2.0) < 0.01, f"2R desenat la {castig/risc:.2f}R"
|
||||
assert "<svg" in svg and svg.count("\n\n") == 0, "linie goală în svg rupe markdown-ul"
|
||||
print("selftest ok: 2R desenat la exact 2R, fără linii goale")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
selftest()
|
||||
Reference in New Issue
Block a user