Nu îmi mai spune când să cumpăr.
Asta e toată cererea. Nu îmi desena săgeți pe grafic. Nu trage webhook-uri cu “SEMNAL DE CUMPĂRARE CONFIRMAT”. Nu îmi genera un winrate fals din backtest. Nu îmi vinde un Discord de 97 de dolari pe lună. Zi-mi doar unde e RSI-ul chiar acum, unde stă order blockul recent, unde e prețul față de VWAP-ul sesiunii. Stratul de interpretare e al meu. Aia e partea pe care nu o externalizez.
Orice SaaS de analiză tehnică de pe internet vinde exact opusul. Împachetează cincisprezece indicatori într-o cutie neagră, lipește “AI-powered” pe pagina de marketing și taxează un abonament lunar pentru un “semnal” opac care nimerește 51% din timp într-o săptămână bună. Pitchul e “am făcut noi partea grea pentru tine”. Partea grea e fix aia pe care nu ar trebui să o lași niciodată pe mâna altcuiva.
wickworks e cum arată opusul ăluia.
Bagi bare, ies primitive
docker run --rm -p 8000:8000 psyb0t/wickworks:latestDouă endpointuri. GET /health întoarce { "ok": true, "version": "..." }. POST / ia barele tale OHLC și o hartă de indicatori pe care îi vrei, și întoarce indicatorii ăia. Nimic altceva. Fără stare, fără bază de date, fără coadă, fără zid de autentificare, fără rate limit, fără cheie de API. Containerul e fără stare și idempotent, aceleași bare la intrare, aceiași octeți la ieșire, de fiecare dată. Rulează zece replici în spatele unui load balancer și toate cad de acord pe matematică.
curl -s -X POST https://ciprian.51k.eu00/
-H 'Content-Type: application/json'
-d '{
"bars": [ {"time":1700000000,"open":1.0832,"high":1.0851,"low":1.0828,"close":1.0844,"volume":1247}, ... ],
"indicators": {
"rsi": true,
"rsi21": { "type": "rsi", "length": 21 },
"macd": true,
"orderBlocks": true,
"fvg": true
}
}'Un singur nume de câmp, intenționat: volumul barei e volume. De la v0.7.0, scrierile în stil MetaTrader au dispărut și sunt respinse activ, nu acceptate tăcut și mapate: trimite tickVolume, realVolume, tick_volume sau real_volume și cererea pică. Două nume pentru un singur număr e cum ajungi cu două răspunsuri diferite, deci e unul singur. Barele sunt validate și ele înainte să se calculeze ceva: OHLC finit și pozitiv, geometrie de lumânare care chiar are sens, volum nenegativ și timestampuri UTC unice, strict crescătoare. Ceri un indicator care nu există și primești 400 cu unknown_indicator, în loc de un rezultat gol pe tăcute.
Cheile din harta ta de indicators sunt cheile pe care le primești înapoi. Ceri rsi, primești rsi. Ceri rsi21 cu "type": "rsi" și o lungime, primești un al doilea RSI sub numele ăla. Stivuiește patru stochuri cu parametri diferiți într-un singur apel, patru chei, patru obiecte, patru ieșiri separate. API-ul nu poate întoarce date pe care nu le-ai cerut, și nu poate întoarce chei duplicate pentru că obiectele JSON nu au așa ceva. Nu ai cum să îl folosești greșit.
Ce e în cutie
Catalogul acoperă tot universul standard de analiză tehnică și încă ceva pe deasupra. Fiecare ieșire e camelCase, sigură la NaN (pozițiile de încălzire sunt null, niciodată un NaN literal care sparge parserele din aval) și serializată printr-un strat de curățare, deci nu scapă pe ușă niciun numpy.float64(...).
- 18 medii mobile: SMA, EMA, HMA, WMA, DEMA, TEMA, T3, KAMA, ALMA, linreg, JMA, ZLMA, RMA, FWMA, SWMA, sinwma, TRIMA, VWMA. Plus VWAP ancorat pe sesiune, cu reset zilnic, săptămânal sau lunar, și un parametru
sessionOffsetca sesiunile NY (-5h) sau EET (-2h) să se ancoreze unde trebuie. - Oscilatoare de momentum: RSI, MFI, Williams %R, CCI, ROC, MOM, Ultimate Oscillator, Stochastic, StochRSI, MACD, TSI, TRIX, Ehlers Fisher Transform.
- Forța și direcția trendului: ADX cu +DI/-DI, Aroon, Vortex.
- Volatilitate și benzi: ATR, NATR, Bollinger Bands, Keltner Channels, Donchian Channels, TTM Squeeze (cu steaguri explicite de stare
on/off/noper bară, bara de eliberare a squeeze-ului e un eveniment discret pe care îl poți detecta, nu o ghiceală). - Semnale de trend trailing: Supertrend, Parabolic SAR, Chandelier Exit, norul Ichimoku. Alege unul, sunt variațiuni pe aceeași idee, cu compromisuri diferite între lag și whipsaw.
- Volum și flux de bani: OBV, A/D, CMF, A/D Oscillator, Klinger.
- Smart Money Concepts: Order Blocks, Fair Value Gaps, rupturi de structură BOS/CHoCH, niveluri de swing, niveluri de S/R făcute în casă, zone de lichiditate, retracement-uri, sesiuni, maximele și minimele perioadei precedente.
Schema completă, cu fiecare parametru și fiecare formă de răspuns, stă în schema.json, JSON Schema Draft 2020-12, bună de generat automat clienți tipizați în orice limbaj vrei.
SMC făcut cum trebuie
Smart Money Concepts e pe jumătate analiză de price action chiar folositoare și pe jumătate conținut de influenceri de YouTube. Wickworks face jumătatea folositoare și sare peste dogmă.
Order Blockurile își livrează semnalele de prospețime, în loc să fie filtrate. Înainte mergea invers: wickworks arunca zonele mitigate pe server și îți dădea doar zonele vii. Aia a fost decizia greșită, “mitigat” nu e un singur lucru, iar alegerea criteriului nu e treaba serverului. Deci de la v0.5.x, fiecare order block vine înapoi cu trei semnale independente de prospețime, și tu decizi ce înseamnă consumat:
mitigated_wick: wickul unei bare ulterioare a traversat zona. Larg, ăsta e defaultul bibliotecii și e ce folosea vechiul filtru de pe server.mitigated_close: corpul unei bare ulterioare chiar a rupt dincolo. Mai strict, criteriul “arată mitigat cu ochiul liber”.touch_count: câte evenimente distincte de atingere a avut zona.
Cele două steaguri din bibliotecă se aprind doar la rupturi invalidante, prețul a trecut de zonă în direcția greșită. touch_count se aprinde la orice intersecție de interval, deci prinde “prețul a vizitat nivelul ăsta” fără să ceară o rupere completă. O zonă atinsă de patru ori și nespartă niciodată e cu totul altceva decât una prin care s-a trecut ca prin brânză o singură dată, iar acum le poți deosebi la desen.
Costul e un apel smc.ob() în plus per analiză, ca să derive steagurile pe criteriul de close, iar plafonul de OB a urcat de la 20 la 40 ca să compenseze faptul că nu mai filtrează. FVG-urile rămân plafonate la 15. Totul e sortat crescător după distanța față de prețul curent, zonele care încă contează, ordonate după cât de repede ai da de ele.
BOS și CHoCH sunt fapte structurale, nu semnale. Un Break of Structure (prețul scoate ultimul swing în direcția trendului) e graficul care zice “trendul a continuat”. Un Change of Character (prețul rupe împotriva structurii trendului anterior pentru prima oară) e graficul care zice “trendul tocmai s-a rupt pentru prima oară”. Wickworks emite evenimentul cu nivelul și direcția lui. Nu emite “trendul s-a inversat, cumpără acum”. Aia e citirea ta a evenimentului. Graficul pune faptele, tu pui interpretarea.
Algoritmul de niveluri S/R e făcut în casă și merită descris. Iei pivoții de swing de la un detector de swing pe 7 bare. Păstrezi doar pivoții pe care prețul i-a testat de ≥2 ori (un “test” = un maxim sau un minim la mai puțin de ½·ATR de nivel, cu bare între atingeri). Impui o distanță de ≥3·ATR între nivelurile păstrate, ca să nu ajungi cu trei niveluri aproape identice stivuite unul peste altul și numărate ca trei. Întorci până la trei dintre cele mai apropiate deasupra prețului curent (rezistență) și trei dintre cele mai apropiate dedesubt (suport). Asta e ce vor să zică majoritatea traderilor când desenează linii de S/R manual, niveluri chiar testate, cu separare cu sens, și aproape niciun indicator automat nu o face așa. Majoritatea întorc lista brută de swinguri și îi zic suport.
Fără semnale. Niciodată.
Asta e poziția din v0.3.0 și e de rezistență pentru tot proiectul:
- Fără detecție de divergențe.
- Fără evenimente de încrucișare MACD.
- Fără etichete de “golden cross” sau “death cross”.
- Fără etichete de cumpărare sau vânzare.
- Fără steaguri de “ăsta e un semnal proaspăt” pe ieșirile de evenimente.
Fiecare valoare din răspuns e una din trei: o serie brută de indicator, un fapt structural (s-a format un order block la bara asta; prețul a închis dincolo de nivelul ăsta de swing), sau un sumar numeric pre-copt peste alea (ultimul close, ultimul RSI, poziția curentă față de mediile mobile cheie). Niciodată o judecată. Vrei divergențe, construiește-ți-le. Vrei evenimente de crossover, construiește-ți-le. Sunt banale de calculat peste primitivele pe care le întoarce wickworks, stau în codul tău și poți itera pe ele fără să redeploiezi un serviciu Python și fără să plătești pe nimeni.
Poziția aia e tot motivul pentru care există proiectul ăsta. Tot restul, mărimea catalogului, forma răspunsului, suita de teste, licențierea, decurge din ea.
“Dă-mi doar starea curentă”
Există o cale rapidă pentru clienții care nu vor serii întregi, ci doar instantaneul ultimei bare. Șase ieșiri fără parametri împart o singură trecere de analiză, ceri toate șase și costă cât una singură:
"indicators": {
"price": true,
"levels": true,
"momentum": true,
"volume": true,
"position": true,
"slope": true
}price: ultimul close.levels: EMA21, SMA50/100/200, ATR, VWAP, Donchian sus/jos/mijloc.momentum: RSI, MFI, linia și semnalul MACD plus histograma, ADX, K/D stochastic.volume: raportul dintre volumul curent și cel recent, OBV, și un booleanisSpikecare e true când volumul e de peste 2× media recentă.position: pentru fiecare dintre EMA21/SMA50/100/200/VWAP:"above"sau"below". Hartă de bias.slope: aceleași chei,"up"sau"down"pe ultimele 10 bare. Combină-l cupositionși ai regimul graficului în două obiecte scurte.
Dovezile
Modelul de încredere pentru un serviciu de analiză tehnică e “se potrivesc numerele cu ce ar trebui să se potrivească”. Wickworks livrează 370 de teste în trei categorii:
- Diferențe pe matematică în formă închisă. Pentru fiecare indicator standard (RSI, MACD, ATR, Bollinger, Stochastic, Aroon, CCI, Williams %R, ROC, MOM, OBV, Donchian, VWMA, EMA, SMA), suita de teste reimplementează formula de la zero, direct în numpy și pandas și compară valoarea ultimei bare cu ieșirea wickworks pe ticuri EURUSD H1 reale. Toleranță
rtol=1e-5. Dacă pandas_ta o ia razna sau cablajul nostru putrezește, diferența țipă înainte să se construiască imaginea Docker. - Paritate smc_fast. Stratul SMC livrează un port accelerat cu numba al
smartmoneyconceptspentru calea fierbinte. Opt teste dovedesc ieșire identică octet cu octet față de biblioteca din amonte, pe aceleași intrări. Calea rapidă nu poate produce niciodată numere diferite pe tăcute. - Contractul pipeline-ului. Determinism (aceleași bare → aceiași octeți de răspuns). Stabilitate la adăugare (indicatorii cauzali nu își rescriu propriul istoric când sosesc bare noi). Numărul de null-uri din zona de încălzire. Izolarea indicatorilor (doi ceruți împreună dau aceleași numere ca doi ceruți separat). Căile de eroare HTTP. Contractul câmpului de volum.
Iar setul de pachete e închis în spatele mecanismului exclude-newer din uv: orice versiune de dependență publicată după o dată fixă e refuzată la rezolvarea lockului. Data e urcată automat de target-urile Make care mută pachete, deci dacă nu rulezi make pkg-add sau make pkg-update, data nu se mișcă, și un release rău-intenționat proaspăt publicat, încă în fereastra lui de detecție, nu se poate strecura într-un refresh pasiv de lock. Plictisitor, paranoic, corect.
Îți zice când ai dat-o în bară
Ceri un sma cu length: 200 când ai trimis doar 100 de bare și toată cererea e respinsă din start, nu întoarsă pe tăcute ca un array de 100 de null-uri. Răspunsul enumeră fiecare indicator pe care l-ai subalimentat, pe toți odată, deci repari tot apelul într-un singur drum:
{
"detail": {
"error": "insufficient_bars",
"message": "insufficient bars: have 30, but: slowSma (type=sma) needs 200, longRsi (type=rsi) needs 51",
"available": 30,
"deficits": [
{ "outputKey": "slowSma", "type": "sma", "required": 200, "available": 30 },
{ "outputKey": "longRsi", "type": "rsi", "required": 51, "available": 30 }
]
}
}Numărul de bare necesar e calculat per indicator din parametrii lui, nu e un prag global. Ieșirile SMC împart o bază MIN_BARS (implicit 50), pentru că pipeline-ul structural presupune istoric cu sens. Suprafața totală de eroare e exact patru coduri: 400 (ăsta, cu corp structurat), 413 (peste MAX_BARS, implicit 5000), 422 (schema Pydantic pică pe payloadul de bare), 500 (deschide un issue, asta nu ar trebui să se întâmple).
Se documentează singur
GET /metadata întoarce un catalog static cu fiecare indicator, semnal și nivel pe care le poate emite endpointul de calcul, cu etichete pentru oameni, descrieri, indicii de interpretare, unități, categorii. Îl iei o dată la pornire și îl pui în cache, cache-ul se invalidează la schimbarea versiunii. Acoperă toți cei ~70 de indicatori de prim nivel din registru, plus obiectele imbricate.
Există un helper lookup(path) cu fallback pe trei niveluri, potrivire exactă, apoi scoaterea indicilor de array, apoi frunza goală, deci căi dinamice cu puncte de genul retracements[42].Direction sau prevTimeframe.rsi tot se rezolvă în ceva cu sens, în loc să întoarcă nimic.
Ceea ce contează cel mai mult când consumatorul e un LLM: poate să întrebe ce înseamnă un câmp, în loc să halucineze o interpretare a lui bosChoch[3].level.
Server MCP, skill și plugin
v0.6.0 a adăugat un server MCP, deci un model cu function calling poate să îi dea lui wickworks un set de bare și să primească primitive înapoi ca apel de unealtă. Repo-ul livrează și un skill de agent și un plugin OpenClaw sub .agents/, publicate pe ClawHub de CI la push-uri de tag.
Poziția nu se schimbă doar pentru că apelantul e un LLM: tot primitive întoarce, niciodată semnale. Modelul primește fapte despre grafic și trebuie să își facă singur raționamentul, ceea ce e fix ce vrei, pentru că un model care repetă ca papagalul “STRONG BUY” de la un serviciu de indicatori nu valorează nimic.
Cum se leagă de restul
Wickworks e serviciul central de analiză tehnică din stackul psyb0t. mt5-httpapi îl încorporează ca sidecar legat la namespace-ul de rețea al containerului mt5, fără porturi publicate, fără deploy separat. API-ul mt5 expune POST /symbols/:symbol/rates/ta: ia lumânările din MT5, le trimite mai departe la wickworks pe sub capotă, întoarce JSON-ul cu indicatori către client. Un singur apel. Clientul nu trebuie să știe vreodată că wickworks există.
Backtesterele fac la fel. Serviciile de alerte fac la fel. Scraperele care trebuie să calculeze indicatori live pe date OHLC în flux fac la fel. Un container, un set de matematică, un set de teste care țintuiesc matematica aia. Fiecare consumator e un client HTTP subțire care știe să trimită bare și să parseze răspunsul, iar răspunsul are aceeași formă indiferent cine întreabă.
Configurarea e patru variabile de mediu, defaulturi rezonabile, nimic obligatoriu: LOG_LEVEL=INFO, MAX_BARS=5000, MIN_BARS=50, WORKERS=2. Tragi imaginea, pornești containerul, îți îndrepți clienții spre :8000.
Stăpânește-ți matematica
Pitchul se reduce la o singură propoziție. Nu mai închiria opinii despre propriile tale grafice. Calculează-ți singur primitivele, pe hardware-ul tău, cu matematică pe care o poți audita și teste pe care le poți citi. Construiește stratul de interpretare în cod pe care îl controlezi. Iterează pe el cât de repede poți deploia. Nu mai aștepta niciodată un canal de Discord să îți zică când contează order blockul.
Licențiat WTFPL. Fă ce mama dracului vrei cu el.
github.com/psyb0t/docker-wickworks · hub.docker.com/r/psyb0t/wickworks
Cum Îl Instalezi în Agentul Tău
Skillul și pluginul se instalează dintr-un marketplace comun, nu dintr-un repo pe rând. Tot ce e sub .agents/ e catalogat într-un singur marketplace, deci sunt două comenzi:
claude plugin marketplace add psyb0t/agents
claude plugin install wickworks@psyb0tCodex folosește același marketplace cu alt verb, codex plugin add wickworks@psyb0t, pentru că nu există codex plugin install. Găsește singur și skillul într-un checkout al repo-ului, pentru că scanează .agents/skills/ nativ, fără să fie instalat absolut nimic. E listat acum și pe MCP Registry-ul oficial, deci un client care rezolvă servere de acolo îl poate găsi fără să i se dea un URL.