Hör auf, mir zu sagen, wann ich kaufen soll.
Das ist die ganze Bitte. Mal keine Pfeile in meinen Chart. Feuer keine “KAUFSIGNAL BESTÄTIGT”-Webhooks. Generier mir keine gefälschte Backtest-Winrate. Verkauf mir keinen Discord für 97 Dollar im Monat. Sag mir einfach, wo der RSI gerade steht, wo der jüngste Order Block sitzt, wo der Kurs relativ zum Session-VWAP liegt. Die Interpretationsschicht gehört mir. Das ist der Teil, den ich nicht auslagere.
Jedes TA-SaaS im Netz verkauft das Gegenteil. Sie packen fünfzehn Indikatoren in eine Blackbox, klatschen “AI-powered” auf die Marketingseite und kassieren ein Monatsabo für ein undurchsichtiges “Signal”, das in einer guten Woche zu 51% richtig liegt. Der Pitch lautet “wir haben den schweren Teil für dich erledigt”. Der schwere Teil ist genau der, den du niemals jemand anderen erledigen lassen solltest.
wickworks ist, wie das Gegenteil davon aussieht.
Kerzen rein, Primitive raus
docker run --rm -p 8000:8000 psyb0t/wickworks:latestZwei Endpoints. GET /health liefert { "ok": true, "version": "..." }. POST / nimmt deine OHLC-Kerzen und eine Map der Indikatoren, die du willst, und liefert genau diese Indikatoren. Sonst nichts. Kein Zustand, keine Datenbank, keine Queue, keine Auth-Mauer, kein Rate Limit, kein API-Key. Der Container ist zustandslos und idempotent, dieselben Kerzen rein, dieselben Bytes raus, jedes Mal. Fahr zehn Replicas hinter einem Load Balancer und sie sind sich alle über die Mathematik einig.
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
}
}'Ein einziger Feldname, mit Absicht: das Volumen der Kerze heißt volume. Seit v0.7.0 sind die MetaTrader-Schreibweisen weg und werden aktiv abgelehnt, nicht stillschweigend angenommen und umgebogen: schick tickVolume, realVolume, tick_volume oder real_volume und die Anfrage scheitert. Zwei Namen für eine Zahl ist der Weg zu zwei verschiedenen Antworten, also gibt es einen. Kerzen werden ebenfalls validiert, bevor irgendetwas gerechnet wird: endliche positive OHLC, Kerzengeometrie, die tatsächlich Sinn ergibt, nicht negatives Volumen, und streng aufsteigende, eindeutige UTC-Zeitstempel. Frag einen Indikator an, den es nicht gibt, und du bekommst einen 400 mit unknown_indicator statt eines stillschweigend leeren Ergebnisses.
Die Keys in deiner indicators-Map sind die Keys, die du zurückbekommst. Frag rsi, bekomm rsi. Frag rsi21 mit "type": "rsi" und einer Länge, bekomm einen zweiten RSI unter diesem Namen. Stapel vier Stochs mit verschiedenen Parametern in einen einzigen Aufruf, vier Keys, vier Objekte, vier getrennte Ausgaben. Die API kann keine Daten liefern, die du nicht angefragt hast, und sie kann keine doppelten Keys liefern, weil JSON-Objekte keine haben. Du kannst sie nicht falsch benutzen.
Was in der Kiste ist
Der Katalog deckt das gesamte Standarduniversum der technischen Analyse ab und noch etwas mehr. Jede Ausgabe ist camelCase, NaN-sicher (Aufwärmpositionen sind null, nie ein literales NaN, das nachgelagerte Parser zerlegt) und durch eine Aufräumschicht serialisiert, es entkommt also kein numpy.float64(...) durch die Tür.
- 18 gleitende Durchschnitte: SMA, EMA, HMA, WMA, DEMA, TEMA, T3, KAMA, ALMA, linreg, JMA, ZLMA, RMA, FWMA, SWMA, sinwma, TRIMA, VWMA. Dazu session-verankertes VWAP mit täglichem, wöchentlichem oder monatlichem Reset und ein
sessionOffset-Parameter, damit NY- (-5h) oder EET-Sessions (-2h) dort ankern, wo sie sollen. - Momentum-Oszillatoren: RSI, MFI, Williams %R, CCI, ROC, MOM, Ultimate Oscillator, Stochastic, StochRSI, MACD, TSI, TRIX, Ehlers Fisher Transform.
- Trendstärke und -richtung: ADX mit +DI/-DI, Aroon, Vortex.
- Volatilität und Bänder: ATR, NATR, Bollinger Bands, Keltner Channels, Donchian Channels, TTM Squeeze (mit expliziten Zustandsflags
on/off/nopro Kerze, die Kerze der Squeeze-Auflösung ist ein diskretes Ereignis, das du erkennen kannst, keine Vermutung). - Nachziehende Trendsignale: Supertrend, Parabolic SAR, Chandelier Exit, Ichimoku-Wolke. Such dir eins aus, das sind Variationen derselben Idee mit unterschiedlichen Kompromissen zwischen Lag und Whipsaw.
- Volumen und Geldfluss: OBV, A/D, CMF, A/D Oscillator, Klinger.
- Smart Money Concepts: Order Blocks, Fair Value Gaps, BOS/CHoCH-Strukturbrüche, Swing-Level, hauseigene S/R-Level, Liquiditätszonen, Retracements, Sessions, Hochs und Tiefs der Vorperiode.
Das vollständige Schema mit jedem Parameter und jeder Rückgabeform liegt in schema.json, JSON Schema Draft 2020-12, tauglich zum automatischen Generieren typisierter Clients in welcher Sprache du willst.
SMC richtig gemacht
Smart Money Concepts ist halb ernsthaft nützliche Price-Action-Analyse und halb YouTube-Influencer-Content. Wickworks macht die nützliche Hälfte und lässt das Dogma weg.
Order Blocks liefern ihre Frische-Signale mit, statt gefiltert zu werden. Früher lief das andersherum: wickworks warf mitigierte Zonen serverseitig weg und gab dir nur die lebenden. Das war die falsche Entscheidung, “mitigiert” ist nicht eine einzige Sache, und der Server hat dir dein Kriterium nicht auszusuchen. Also kommt seit v0.5.x jeder Order Block mit drei unabhängigen Frische-Signalen zurück, und du entscheidest, was als verbraucht gilt:
mitigated_wick: der Docht einer späteren Kerze hat die Zone durchquert. Locker, das ist der Default der Bibliothek und war das Kriterium des alten serverseitigen Filters.mitigated_close: der Körper einer späteren Kerze ist tatsächlich darüber hinausgebrochen. Strenger, das Kriterium “sieht mit bloßem Auge mitigiert aus”.touch_count: wie viele eigenständige Berührungsereignisse die Zone hatte.
Die beiden Bibliotheksflags feuern nur bei invalidierenden Brüchen, der Kurs ist in die falsche Richtung über die Zone hinausgegangen. touch_count feuert bei jeder Bereichsüberschneidung überhaupt, fängt also “der Kurs war an diesem Level” ein, ohne einen vollen Bruch zu verlangen. Eine Zone, die viermal berührt und nie gebrochen wurde, ist etwas völlig anderes als eine, die einmal durchschlagen wurde, und jetzt kannst du sie beim Zeichnen auseinanderhalten.
Der Preis ist ein zusätzlicher smc.ob()-Aufruf pro Analyse, um die Flags nach dem Close-Kriterium abzuleiten, und das OB-Limit ging von 20 auf 40 hoch, um das Wegfallen der Filterung auszugleichen. FVGs bleiben bei 15 gedeckelt. Alles ist aufsteigend nach Abstand zum aktuellen Kurs sortiert, die Zonen, die noch zählen, gereiht danach, wie bald du auf sie treffen würdest.
BOS und CHoCH sind strukturelle Fakten, keine Signale. Ein Break of Structure (der Kurs nimmt den letzten Swing in Trendrichtung heraus) ist der Chart, der sagt “der Trend ging weiter”. Ein Change of Character (der Kurs bricht zum ersten Mal gegen die Struktur des vorherigen Trends) ist der Chart, der sagt “der Trend ist gerade zum ersten Mal gebrochen”. Wickworks gibt das Ereignis mit Level und Richtung aus. Es gibt nicht aus “der Trend hat gedreht, kauf jetzt”. Das ist deine Lesart des Ereignisses. Der Chart legt die Fakten fest, du legst die Interpretation fest.
Der S/R-Level-Algorithmus ist hauseigen und eine Beschreibung wert. Nimm die Swing-Pivots aus einem 7-Kerzen-Swing-Detektor. Behalte nur Pivots, die der Kurs ≥2 mal getestet hat (ein “Test” = ein Hoch oder Tief innerhalb von ½·ATR um das Level, mit Kerzen zwischen den Berührungen). Erzwinge ≥3·ATR Abstand zwischen den behaltenen Levels, damit du nicht drei fast identische Levels übereinandergestapelt und als drei gezählt bekommst. Gib bis zu drei der nächstgelegenen über dem aktuellen Kurs zurück (Widerstand) und drei der nächstgelegenen darunter (Unterstützung). Genau das meinen die meisten Trader, wenn sie S/R-Linien von Hand zeichnen, tatsächlich getestete Levels mit sinnvollem Abstand, und fast kein automatischer Indikator macht es so. Die meisten geben die rohe Swing-Liste zurück und nennen das Unterstützung.
Keine Signale. Niemals.
Das ist die Haltung aus v0.3.0, und sie ist tragend für das ganze Projekt:
- Keine Divergenzerkennung.
- Keine MACD-Kreuzungsereignisse.
- Keine “Golden Cross”- oder “Death Cross”-Tags.
- Keine Kauf- oder Verkaufslabels.
- Keine “das ist ein frisches Signal”-Flags auf Ereignisausgaben.
Jeder Wert in der Antwort ist eines von dreien: eine rohe Indikatorserie, ein struktureller Fakt (bei dieser Kerze hat sich ein Order Block gebildet; der Kurs hat über diesem Swing-Level geschlossen), oder eine vorgekochte numerische Zusammenfassung darüber (letzter Close, letzter RSI, aktuelle Position zu den wichtigen MAs). Nie ein Urteil. Du willst Divergenzen, bau sie. Du willst Kreuzungsereignisse, bau sie. Sie sind trivial über den Primitiven zu rechnen, die wickworks zurückgibt, sie leben in deinem Code, und du kannst daran iterieren, ohne einen Python-Service neu auszurollen oder irgendwem Geld zu geben.
Diese Haltung ist der ganze Grund, warum es dieses Projekt gibt. Alles andere, die Größe des Katalogs, die Form der Antwort, die Testsuite, die Lizenzierung, fällt daraus ab.
“Gib mir einfach den aktuellen Stand”
Es gibt einen schnellen Pfad für Clients, die keine vollen Series wollen, nur die Momentaufnahme der letzten Kerze. Sechs parameterlose Ausgaben teilen sich einen einzigen Analysedurchlauf, frag alle sechs an und sie kosten so viel wie eine:
"indicators": {
"price": true,
"levels": true,
"momentum": true,
"volume": true,
"position": true,
"slope": true
}price: letzter Close.levels: EMA21, SMA50/100/200, ATR, VWAP, Donchian oben/unten/Mitte.momentum: RSI, MFI, MACD-Linie und -Signal plus Histogramm, ADX, stochastisches K/D.volume: Verhältnis von aktuellem zu jüngstem Volumen, OBV, und einisSpike-Boolean, das true ist, wenn das Volumen mehr als 2× über dem jüngsten Durchschnitt liegt.position: für jedes von EMA21/SMA50/100/200/VWAP:"above"oder"below". Bias-Karte.slope: dieselben Keys,"up"oder"down"über die letzten 10 Kerzen. Kombinier das mitpositionund du hast das Regime des Charts in zwei kurzen Objekten.
Belege
Das Vertrauensmodell für einen TA-Service ist “stimmen die Zahlen mit dem überein, womit sie übereinstimmen sollen”. Wickworks liefert 370 Tests in drei Kategorien:
- Diffs gegen geschlossene Formeln. Für jeden Standardindikator (RSI, MACD, ATR, Bollinger, Stochastic, Aroon, CCI, Williams %R, ROC, MOM, OBV, Donchian, VWMA, EMA, SMA) implementiert die Testsuite die Formel von Grund auf in nacktem numpy und pandas nach und vergleicht den Wert der letzten Kerze mit der Ausgabe von wickworks auf echten EURUSD-H1-Ticks. Toleranz
rtol=1e-5. Wenn pandas_ta abdriftet oder unsere Verkabelung verrottet, schreit der Diff, noch bevor das Docker-Image überhaupt gebaut wird. - smc_fast-Parität. Die SMC-Schicht liefert für den heißen Pfad eine mit numba beschleunigte Portierung von
smartmoneyconcepts. Acht Tests beweisen byteidentische Ausgabe zur Upstream-Bibliothek bei gleichen Eingaben. Der schnelle Pfad kann niemals stillschweigend andere Zahlen produzieren. - Pipeline-Vertrag. Determinismus (gleiche Kerzen → gleiche Antwort-Bytes). Anhängestabilität (kausale Indikatoren schreiben ihre eigene Historie nicht um, wenn neue Kerzen eintreffen). Null-Anzahl in der Aufwärmzone. Indikator-Isolation (zwei zusammen angefragt ergeben dieselben Zahlen wie zwei getrennt angefragt). Die HTTP-Fehlerpfade. Der Vertrag des Volumenfelds.
Und der Paketsatz ist hinter dem exclude-newer-Mechanismus von uv verriegelt: jede Abhängigkeitsversion, die nach einem festen Datum veröffentlicht wurde, wird beim Auflösen des Locks abgelehnt. Das Datum wird automatisch von den Make-Targets vorgerückt, die Pakete anfassen, wenn du also make pkg-add oder make pkg-update nicht laufen lässt, bewegt sich das Datum nicht, und ein frisch veröffentlichtes bösartiges Release, noch in seinem Erkennungsfenster, kann sich nicht in eine passive Lock-Aktualisierung schmuggeln. Langweilig, paranoid, richtig.
Es sagt dir, wenn du Mist gebaut hast
Frag einen sma mit length: 200 an, obwohl du nur 100 Kerzen geschickt hast, und die gesamte Anfrage wird vorab abgelehnt, nicht stillschweigend als Array aus 100 null zurückgegeben. Die Antwort listet jeden Indikator auf, den du unterversorgt hast, auf einen Schlag, du reparierst also den ganzen Aufruf in einem Umlauf:
{
"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 }
]
}
}Die benötigte Kerzenzahl wird pro Indikator aus dessen Parametern berechnet, nicht als globale Untergrenze. SMC-Ausgaben teilen sich eine MIN_BARS-Basis (Standard 50), weil die Strukturpipeline sinnvolle Historie voraussetzt. Die gesamte Fehleroberfläche sind exakt vier Codes: 400 (dieser, mit strukturiertem Body), 413 (über MAX_BARS, Standard 5000), 422 (Pydantic-Schemafehler beim Kerzen-Payload), 500 (mach ein Issue auf, das sollte nicht passieren).
Es dokumentiert sich selbst
GET /metadata liefert einen statischen Katalog jedes Indikators, Signals und Levels, das der Rechen-Endpoint ausgeben kann, mit menschenlesbaren Labels, Beschreibungen, Interpretationshinweisen, Einheiten, Kategorien. Hol ihn einmal beim Start und cache ihn, der Cache wird beim Versionswechsel ungültig. Er deckt alle ~70 Top-Level-Indikatoren der Registry ab plus die verschachtelten Objekte.
Es gibt einen lookup(path)-Helfer mit dreistufigem Rückfall, exakte Übereinstimmung, dann Array-Indizes abstreifen, dann das nackte Blatt, damit sich dynamische Punktpfade wie retracements[42].Direction oder prevTimeframe.rsi trotzdem in etwas Sinnvolles auflösen, statt gar nichts zurückzugeben.
Was am meisten zählt, wenn der Konsument ein LLM ist: es kann fragen, was ein Feld bedeutet, statt sich eine Interpretation von bosChoch[3].level zusammenzuhalluzinieren.
MCP-Server, Skill und Plugin
v0.6.0 hat einen MCP-Server dazugebracht, ein Modell mit Function Calling kann wickworks also einen Satz Kerzen reichen und Primitive als Tool-Aufruf zurückbekommen. Das Repo liefert außerdem einen Agent-Skill und ein OpenClaw-Plugin unter .agents/, von der CI bei Tag-Pushes auf ClawHub veröffentlicht.
Die Haltung ändert sich nicht, nur weil der Aufrufer ein LLM ist: es gibt weiterhin Primitive zurück, niemals Signale. Das Modell bekommt Fakten über den Chart und muss selbst denken, was genau das ist, was du willst, denn ein Modell, das “STRONG BUY” aus einem Indikatordienst nachplappert, ist nichts wert.
Wie es sich einfügt
Wickworks ist der zentrale TA-Dienst im psyb0t-Stack. mt5-httpapi bettet es als Sidecar ein, festgenagelt auf den Netzwerk-Namespace des mt5-Containers, keine veröffentlichten Ports, kein separates Deployment. Die mt5-API stellt POST /symbols/:symbol/rates/ta bereit: sie holt die Kerzen aus MT5, reicht sie unter der Haube an wickworks weiter, gibt das Indikator-JSON an den Client zurück. Ein Aufruf. Der Client muss nie erfahren, dass wickworks existiert.
Backtester machen dasselbe. Alert-Dienste machen dasselbe. Scraper, die Live-Indikatoren auf strömenden OHLC-Daten rechnen müssen, machen dasselbe. Ein Container, ein Satz Mathematik, ein Satz Tests, der diese Mathematik festnagelt. Jeder Konsument ist ein dünner HTTP-Client, der Kerzen schicken und die Antwort parsen kann, und die Antwort hat dieselbe Form, egal wer fragt.
Die Konfiguration sind vier Umgebungsvariablen, vernünftige Defaults, nichts ist Pflicht: LOG_LEVEL=INFO, MAX_BARS=5000, MIN_BARS=50, WORKERS=2. Image ziehen, Container starten, deine Clients auf :8000 zeigen lassen.
Nimm die Mathematik in Besitz
Der Pitch lässt sich auf einen Satz eindampfen. Hör auf, Meinungen über deine eigenen Charts zu mieten. Rechne die Primitiven selbst, auf deiner eigenen Hardware, mit Mathematik, die du prüfen kannst, und Tests, die du lesen kannst. Bau die Interpretationsschicht in Code, den du kontrollierst. Iterier daran so schnell, wie du deployen kannst. Warte nie wieder auf einen Discord-Kanal, der dir sagt, wann der Order Block zählt.
Unter WTFPL lizenziert. Mach damit, was immer du verdammt nochmal willst.
github.com/psyb0t/docker-wickworks · hub.docker.com/r/psyb0t/wickworks
Rein Damit In Deinen Agenten
Skill und Plugin installieren sich aus einem gemeinsamen Marketplace statt Repo für Repo. Alles unter .agents/ ist in einem einzigen Marketplace katalogisiert, also sind es zwei Befehle:
claude plugin marketplace add psyb0t/agents
claude plugin install wickworks@psyb0tCodex nutzt denselben Marketplace mit einem anderen Verb, codex plugin add wickworks@psyb0t, weil es kein codex plugin install gibt. Er findet den Skill außerdem von allein in einem Checkout des Repos, da er .agents/skills/ nativ scannt, ganz ohne Installation. Es ist jetzt auch im offiziellen MCP Registry gelistet, ein Client, der seine Server von dort auflöst, kann es also finden, ohne dass man ihm eine URL gibt.