wickworks: De Oliedomme OHLC-Analyzer Die Weigert Je Te Vertellen Wat Je Moet Kopen

Hou op me te vertellen wanneer ik moet kopen.
Dat is het hele verzoek. Teken geen pijlen op mijn grafiek. Vuur geen “KOOPSIGNAAL BEVESTIGD”-webhooks af. Genereer geen nep-winrate uit een backtest. Verkoop me geen Discord van 97 dollar per maand. Vertel me gewoon waar de RSI nu staat, waar het recente order block zit, waar de prijs staat ten opzichte van de VWAP van de sessie. De interpretatielaag is van mij. Dat is het stuk dat ik niet uitbesteed.
Elke TA-SaaS op het internet verkoopt precies het omgekeerde. Ze proppen vijftien indicatoren in een black box, plakken “AI-powered” op de marketingpagina en rekenen een maandabonnement voor een ondoorzichtig “signaal” dat het in een goede week 51% van de tijd bij het rechte eind heeft. De pitch is “wij hebben het moeilijke deel voor je gedaan”. Het moeilijke deel is precies het deel dat je nooit door iemand anders moet laten doen.
wickworks is hoe het omgekeerde daarvan eruitziet.

Candles erin, primitieven eruit

docker run --rm -p 8000:8000 psyb0t/wickworks:latest

Twee endpoints. GET /health geeft { "ok": true, "version": "..." }. POST / neemt je OHLC-candles en een map van de indicatoren die je wilt, en geeft precies die indicatoren terug. Verder niets. Geen state, geen database, geen queue, geen authenticatiemuur, geen rate limit, geen API-sleutel. De container is stateless en idempotent, dezelfde candles erin, dezelfde bytes eruit, elke keer. Draai tien replicas achter een load balancer en ze zijn het allemaal eens over de wiskunde.

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
    }
  }'

Eén veldnaam, met opzet: het volume van de candle heet volume. Sinds v0.7.0 zijn de MetaTrader-achtige spellingen weg en worden ze actief geweigerd, niet stilletjes geaccepteerd en omgezet: stuur tickVolume, realVolume, tick_volume of real_volume en het verzoek faalt. Twee namen voor één getal is hoe je met twee verschillende antwoorden eindigt, dus er is er één. Candles worden ook gevalideerd voordat er iets berekend wordt: eindige positieve OHLC, candlegeometrie die echt klopt, niet-negatief volume, en strikt oplopende unieke UTC-tijdstempels. Vraag een indicator aan die niet bestaat en je krijgt een 400 met unknown_indicator in plaats van een stilletjes leeg resultaat.
De keys in je indicators-map zijn de keys die je terugkrijgt. Vraag rsi, krijg rsi. Vraag rsi21 met "type": "rsi" en een lengte, krijg een tweede RSI onder die naam. Stapel vier stochs met verschillende parameters in één call, vier keys, vier objecten, vier aparte outputs. De API kan geen data teruggeven die je niet hebt gevraagd, en kan geen dubbele keys teruggeven omdat JSON-objecten die niet hebben. Je kunt hem niet verkeerd gebruiken.

Wat er in de doos zit

De catalogus beslaat het hele standaarduniversum van technische analyse en nog wat. Elke uitvoer is camelCase, NaN-veilig (opwarmposities zijn null, nooit een letterlijke NaN die parsers verderop sloopt) en geserialiseerd via een opschoonlaag, dus er ontsnapt geen enkele numpy.float64(...) door de deur.

  • 18 voortschrijdende gemiddelden: SMA, EMA, HMA, WMA, DEMA, TEMA, T3, KAMA, ALMA, linreg, JMA, ZLMA, RMA, FWMA, SWMA, sinwma, TRIMA, VWMA. Plus sessie-verankerde VWAP met dagelijkse, wekelijkse of maandelijkse reset, en een sessionOffset-parameter zodat NY- (-5h) of EET-sessies (-2h) verankeren waar ze horen.
  • Momentum-oscillatoren: RSI, MFI, Williams %R, CCI, ROC, MOM, Ultimate Oscillator, Stochastic, StochRSI, MACD, TSI, TRIX, Ehlers Fisher Transform.
  • Trendsterkte en -richting: ADX met +DI/-DI, Aroon, Vortex.
  • Volatiliteit en banden: ATR, NATR, Bollinger Bands, Keltner Channels, Donchian Channels, TTM Squeeze (met expliciete toestandsvlaggen on/off/no per candle, de candle waarop de squeeze loskomt is een discrete gebeurtenis die je kunt detecteren, geen gok).
  • Meelopende trendsignalen: Supertrend, Parabolic SAR, Chandelier Exit, Ichimoku-wolk. Kies er één, het zijn variaties op hetzelfde idee met andere afwegingen tussen lag en whipsaw.
  • Volume en geldstroom: OBV, A/D, CMF, A/D Oscillator, Klinger.
  • Smart Money Concepts: Order Blocks, Fair Value Gaps, BOS/CHoCH-structuurbreuken, swingniveaus, eigen S/R-niveaus, liquiditeitszones, retracements, sessies, hoogste en laagste punten van de vorige periode.

Het volledige schema, met elke parameter en elke retourvorm, staat in schema.json, JSON Schema Draft 2020-12, geschikt om er automatisch getypeerde clients mee te genereren in welke taal je ook wilt.

SMC fatsoenlijk gedaan

Smart Money Concepts is half echt bruikbare price action-analyse en half YouTube-influencercontent. Wickworks doet de bruikbare helft en slaat het dogma over.
Order Blocks leveren hun versheidssignalen mee in plaats van gefilterd te worden. Vroeger werkte het andersom: wickworks gooide gemitigeerde zones aan de serverkant weg en gaf je alleen de levende. Dat was de verkeerde keuze, “gemitigeerd” is niet één ding, en de server hoort jouw criterium niet te kiezen. Dus sinds v0.5.x komt elk order block terug met drie onafhankelijke versheidssignalen en bepaal jij wat als verbruikt telt:

  • mitigated_wick: de wick van een latere candle ging dwars door de zone. Ruim, dit is de standaard van de library en het is wat het oude serverfilter gebruikte.
  • mitigated_close: het lichaam van een latere candle brak er echt doorheen. Strenger, het criterium “ziet er met het blote oog gemitigeerd uit”.
  • touch_count: hoeveel afzonderlijke aanrakingen de zone heeft gehad.

De twee vlaggen uit de library gaan alleen af bij invaliderende breuken, de prijs is de zone in de verkeerde richting voorbijgegaan. touch_count gaat af bij elke doorsnijding van het bereik, dus hij vangt “de prijs is hier langs geweest” zonder een volledige breuk te eisen. Een zone die vier keer is aangeraakt en nooit gebroken is heel iets anders dan een die er in één keer doorheen is gereden, en nu kun je ze bij het tekenen uit elkaar houden.
De prijs is één extra smc.ob()-call per analyse om de vlaggen op het close-criterium af te leiden, en het OB-plafond ging van 20 naar 40 om te compenseren dat er niet meer gefilterd wordt. FVG’s blijven op 15 gemaximeerd. Alles is oplopend gesorteerd op afstand tot de huidige prijs, de zones die er nog toe doen, gerangschikt naar hoe snel je ze zou raken.
BOS en CHoCH zijn structurele feiten, geen signalen. Een Break of Structure (de prijs neemt de laatste swing in de richting van de trend mee) is de grafiek die zegt “de trend ging door”. Een Change of Character (de prijs breekt voor het eerst tegen de structuur van de vorige trend in) is de grafiek die zegt “de trend is zojuist voor het eerst gebroken”. Wickworks geeft de gebeurtenis uit met zijn niveau en richting. Het geeft niet uit “de trend is gedraaid, koop nu”. Dat is jouw lezing van de gebeurtenis. De grafiek legt de feiten vast, jij legt de interpretatie vast.
Het algoritme voor S/R-niveaus is eigen werk en een beschrijving waard. Neem de swingpivots uit een swingdetector over 7 candles. Hou alleen de pivots over die de prijs ≥2 keer heeft getest (een “test” = een hoogste of laagste punt binnen ½·ATR van het niveau, met candles tussen de aanrakingen). Dwing ≥3·ATR afstand af tussen de niveaus die je houdt, zodat je niet drie bijna identieke niveaus op elkaar gestapeld krijgt die als drie worden geteld. Geef tot drie dichtstbijzijnde boven de huidige prijs terug (weerstand) en drie dichtstbijzijnde eronder (steun). Dat is wat de meeste traders bedoelen als ze met de hand S/R-lijnen tekenen, echt geteste niveaus met zinvolle onderlinge afstand, en bijna geen enkele automatische indicator doet het zo. De meeste geven de ruwe swinglijst terug en noemen dat steun.

Geen signalen. Nooit.

Dit is het standpunt uit v0.3.0 en het is dragend voor het hele project:

  • Geen divergentiedetectie.
  • Geen MACD-kruisingsgebeurtenissen.
  • Geen “golden cross”- of “death cross”-labels.
  • Geen koop- of verkooplabels.
  • Geen “dit is een vers signaal”-vlaggen op gebeurtenisuitvoer.

Elke waarde in het antwoord is één van drie dingen: een ruwe indicatorreeks, een structureel feit (op deze candle is een order block gevormd; de prijs sloot voorbij dit swingniveau), of een voorgekookte numerieke samenvatting daarover (laatste close, laatste RSI, huidige positie ten opzichte van de belangrijkste MA’s). Nooit een oordeel. Wil je divergenties, bouw ze. Wil je kruisingsgebeurtenissen, bouw ze. Ze zijn triviaal te berekenen bovenop de primitieven die wickworks teruggeeft, ze leven in jouw code, en je kunt erop itereren zonder een Python-service opnieuw uit te rollen of iemand te betalen.
Dat standpunt is de hele reden dat dit project bestaat. Al het andere, de omvang van de catalogus, de vorm van het antwoord, de testsuite, de licentie, volgt daaruit.

“Geef me gewoon de huidige stand”

Er is een snelle route voor clients die geen volledige Series willen, alleen de momentopname van de laatste candle. Zes parameterloze outputs delen één analyseronde, vraag alle zes aan en ze kosten evenveel als één:

"indicators": {
  "price":    true,
  "levels":   true,
  "momentum": true,
  "volume":   true,
  "position": true,
  "slope":    true
}
  • price: laatste close.
  • levels: EMA21, SMA50/100/200, ATR, VWAP, Donchian boven/onder/midden.
  • momentum: RSI, MFI, MACD-lijn en -signaal plus histogram, ADX, stochastische K/D.
  • volume: verhouding tussen huidig en recent volume, OBV, en een isSpike-boolean die true is als het volume meer dan 2× het recente gemiddelde is.
  • position: voor elk van EMA21/SMA50/100/200/VWAP: "above" of "below". Biaskaart.
  • slope: dezelfde keys, "up" of "down" over de laatste 10 candles. Combineer het met position en je hebt het regime van de grafiek in twee korte objecten.

Het bewijs

Het vertrouwensmodel voor een TA-dienst is “kloppen de getallen met waar ze mee horen te kloppen”. Wickworks levert 370 tests in drie categorieën:

  1. Diffs tegen gesloten formules. Voor elke standaardindicator (RSI, MACD, ATR, Bollinger, Stochastic, Aroon, CCI, Williams %R, ROC, MOM, OBV, Donchian, VWMA, EMA, SMA) implementeert de testsuite de formule helemaal opnieuw in kale numpy en pandas en vergelijkt de waarde van de laatste candle met de uitvoer van wickworks op echte EURUSD H1-ticks. Tolerantie rtol=1e-5. Als pandas_ta afdrijft of onze bedrading verrot, schreeuwt de diff nog voordat de Docker-image wordt gebouwd.
  2. smc_fast-pariteit. De SMC-laag levert voor het hete pad een met numba versnelde port van smartmoneyconcepts. Acht tests bewijzen byte-identieke uitvoer ten opzichte van de upstream library bij dezelfde invoer. Het snelle pad kan nooit stilletjes andere getallen produceren.
  3. Pipelinecontract. Determinisme (dezelfde candles → dezelfde antwoordbytes). Stabiliteit bij toevoegen (causale indicatoren herschrijven hun eigen historie niet als er nieuwe candles binnenkomen). Aantal null in de opwarmzone. Isolatie van indicatoren (twee samen aanvragen geeft dezelfde getallen als twee apart aanvragen). De HTTP-foutpaden. Het contract van het volumeveld.

En de pakketset zit op slot achter het exclude-newer-mechanisme van uv: elke versie van een dependency die na een vaste datum is gepubliceerd wordt geweigerd bij het oplossen van de lock. Die datum wordt automatisch opgeschoven door de Make-targets die aan pakketten zitten, dus als je make pkg-add of make pkg-update niet draait, beweegt de datum niet, en een vers gepubliceerde kwaadaardige release die nog in zijn detectievenster zit kan niet in een passieve lock-refresh glippen. Saai, paranoïde, correct.

Hij zegt het als je het verkloot hebt

Vraag een sma aan met length: 200 terwijl je maar 100 candles hebt gestuurd en het hele verzoek wordt vooraf geweigerd, niet stilletjes teruggegeven als een array van 100 null. Het antwoord somt elke indicator op die je te weinig hebt gevoerd, in één klap, dus je repareert de hele call in één rondje:

{
  "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 }
    ]
  }
}

Het benodigde aantal candles wordt per indicator uit zijn parameters berekend, het is geen globale ondergrens. SMC-uitvoer deelt een MIN_BARS-basis (standaard 50) omdat de structuurpipeline zinvolle historie veronderstelt. Het totale foutoppervlak is precies vier codes: 400 (deze, met gestructureerde body), 413 (boven MAX_BARS, standaard 5000), 422 (Pydantic-schemafout op de candle-payload), 500 (open een issue, dit hoort niet te gebeuren).

Hij documenteert zichzelf

GET /metadata geeft een statische catalogus terug van elke indicator, elk signaal en elk niveau dat het rekenendpoint kan uitgeven, met leesbare labels, beschrijvingen, interpretatiehints, eenheden, categorieën. Haal hem één keer op bij het opstarten en cache hem, de cache vervalt bij een versiewissel. Hij dekt alle ~70 top-level indicatoren uit het register plus de geneste objecten.
Er is een lookup(path)-helper met terugval in drie lagen, exacte match, dan array-indices eraf, dan het kale blad, zodat dynamische puntpaden als retracements[42].Direction of prevTimeframe.rsi toch nog iets zinnigs opleveren in plaats van niets terug te geven.
Wat het meest uitmaakt zodra de afnemer een LLM is: hij kan vragen wat een veld betekent in plaats van een interpretatie van bosChoch[3].level te hallucineren.

MCP-server, skill en plugin

v0.6.0 voegde een MCP-server toe, dus een model met function calling kan wickworks een set candles aanreiken en primitieven terugkrijgen als toolaanroep. De repo levert ook een agent-skill en een OpenClaw-plugin onder .agents/, door CI gepubliceerd op ClawHub bij tag-pushes.
Het standpunt verandert niet omdat de aanroeper toevallig een LLM is: er komen nog steeds primitieven terug, nooit signalen. Het model krijgt feiten over de grafiek en moet zelf redeneren, wat precies is wat je wilt, want een model dat “STRONG BUY” napraat uit een indicatordienst is niets waard.

Hoe het op de rest aansluit

Wickworks is de centrale TA-dienst in de psyb0t-stack. mt5-httpapi heeft het als sidecar ingebouwd, vastgezet op de netwerk-namespace van de mt5-container, geen gepubliceerde poorten, geen aparte deploy. De mt5-API biedt POST /symbols/:symbol/rates/ta: hij haalt de candles uit MT5, stuurt ze onder de motorkap door naar wickworks en geeft de indicator-JSON terug aan de client. Eén call. De client hoeft nooit te weten dat wickworks bestaat.
Backtesters doen hetzelfde. Alertdiensten doen hetzelfde. Scrapers die live indicatoren moeten berekenen op stromende OHLC-data doen hetzelfde. Eén container, één set wiskunde, één set tests die die wiskunde vastzet. Elke afnemer is een dunne HTTP-client die candles kan sturen en het antwoord kan parsen, en het antwoord heeft dezelfde vorm ongeacht wie het vraagt.
De configuratie bestaat uit vier omgevingsvariabelen, verstandige standaardwaarden, niets verplicht: LOG_LEVEL=INFO, MAX_BARS=5000, MIN_BARS=50, WORKERS=2. Haal de image op, start de container, wijs je clients naar :8000.

Wees eigenaar van de wiskunde

De pitch valt terug te brengen tot één zin. Hou op met het huren van meningen over je eigen grafieken. Bereken de primitieven zelf, op je eigen hardware, met wiskunde die je kunt controleren en tests die je kunt lezen. Bouw de interpretatielaag in code die je zelf beheert. Itereer erop zo snel als je kunt deployen. Wacht nooit meer op een Discord-kanaal dat je vertelt wanneer het order block ertoe doet.
Onder WTFPL-licentie. Doe ermee wat je verdomme wilt.
github.com/psyb0t/docker-wickworks · hub.docker.com/r/psyb0t/wickworks

Zo Zet Je Het In Je Agent

De skill en de plugin installeren vanuit één gedeelde marketplace in plaats van repo voor repo. Alles onder .agents/ staat in één marketplace gecatalogiseerd, dus het zijn twee commando’s:

claude plugin marketplace add psyb0t/agents
claude plugin install wickworks@psyb0t

Codex gebruikt dezelfde marketplace met een ander werkwoord, codex plugin add wickworks@psyb0t, omdat codex plugin install niet bestaat. Hij vindt de skill ook uit zichzelf in een checkout van de repo, aangezien hij .agents/skills/ native scant zonder dat er iets geïnstalleerd is. Het staat nu ook in het officiële MCP Registry, dus een client die zijn servers daarvandaan oplost kan het vinden zonder dat je hem een URL geeft.