Deja de decirme cuándo comprar.
Esa es toda la petición. No me dibujes flechas en el gráfico. No dispares webhooks de “SEÑAL DE COMPRA CONFIRMADA”. No me generes un winrate falso sacado de un backtest. No me vendas un Discord de 97 dólares al mes. Dime solo dónde está el RSI ahora mismo, dónde se sitúa el order block reciente, dónde está el precio respecto al VWAP de la sesión. La capa de interpretación es mía. Esa es la parte que no subcontrato.
Todos los SaaS de análisis técnico de internet venden justo lo contrario. Envuelven quince indicadores en una caja negra, pegan “AI-powered” en la página de marketing y cobran una suscripción mensual por una “señal” opaca que acierta el 51% de las veces en una buena semana. El pitch es “hemos hecho la parte difícil por ti”. La parte difícil es exactamente la que nunca deberías dejar en manos de otro.
wickworks es el aspecto que tiene lo contrario de eso.
Entran velas, salen primitivas
docker run --rm -p 8000:8000 psyb0t/wickworks:latestDos endpoints. GET /health devuelve { "ok": true, "version": "..." }. POST / coge tus velas OHLC y un mapa de los indicadores que quieres, y devuelve esos indicadores. Nada más. Sin estado, sin base de datos, sin cola, sin muro de autenticación, sin rate limit, sin clave de API. El container es sin estado e idempotente, las mismas velas dentro, los mismos bytes fuera, siempre. Levanta diez réplicas detrás de un balanceador y todas coinciden en las matemáticas.
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 solo nombre de campo, a propósito: el volumen de la vela es volume. Desde la v0.7.0 las grafías al estilo MetaTrader han desaparecido y se rechazan activamente, no se aceptan en silencio y se remapean: manda tickVolume, realVolume, tick_volume o real_volume y la petición falla. Dos nombres para un solo número es como acabas con dos respuestas distintas, así que hay uno. Las velas también se validan antes de calcular nada: OHLC finito y positivo, geometría de vela que tenga sentido de verdad, volumen no negativo y marcas de tiempo UTC únicas y estrictamente crecientes. Pide un indicador que no existe y te llevas un 400 con unknown_indicator en vez de un resultado vacío en silencio.
Las claves de tu mapa indicators son las claves que recuperas. Pide rsi, obtienes rsi. Pide rsi21 con "type": "rsi" y una longitud, obtienes un segundo RSI bajo ese nombre. Apila cuatro stochs con parámetros distintos en una sola llamada, cuatro claves, cuatro objetos, cuatro salidas separadas. La API no puede devolver datos que no has pedido, y no puede devolver claves duplicadas porque los objetos JSON no las tienen. No hay manera de usarla mal.
Qué hay en la caja
El catálogo abarca todo el universo estándar del análisis técnico y algo más. Cada salida va en camelCase, es segura frente a NaN (las posiciones de calentamiento son null, nunca un NaN literal que rompa los parseadores de aguas abajo) y se serializa a través de una capa de limpieza, así que ningún numpy.float64(...) sale por la puerta.
- 18 medias móviles: SMA, EMA, HMA, WMA, DEMA, TEMA, T3, KAMA, ALMA, linreg, JMA, ZLMA, RMA, FWMA, SWMA, sinwma, TRIMA, VWMA. Más el VWAP anclado a la sesión, con reseteo diario, semanal o mensual, y un parámetro
sessionOffsetpara que las sesiones de NY (-5h) o EET (-2h) se anclen donde toca. - Osciladores de momentum: RSI, MFI, Williams %R, CCI, ROC, MOM, Ultimate Oscillator, Stochastic, StochRSI, MACD, TSI, TRIX, Ehlers Fisher Transform.
- Fuerza y dirección de tendencia: ADX con +DI/-DI, Aroon, Vortex.
- Volatilidad y bandas: ATR, NATR, Bollinger Bands, Keltner Channels, Donchian Channels, TTM Squeeze (con banderas de estado explícitas
on/off/nopor vela, la vela de liberación del squeeze es un evento discreto que puedes detectar, no una adivinanza). - Señales de tendencia con arrastre: Supertrend, Parabolic SAR, Chandelier Exit, nube Ichimoku. Elige una, son variaciones de la misma idea con distintos compromisos entre lag y whipsaw.
- Volumen y flujo de dinero: OBV, A/D, CMF, A/D Oscillator, Klinger.
- Smart Money Concepts: Order Blocks, Fair Value Gaps, roturas de estructura BOS/CHoCH, niveles de swing, niveles de S/R propios, zonas de liquidez, retrocesos, sesiones, máximos y mínimos del periodo anterior.
El esquema completo, con cada parámetro y cada forma de retorno, vive en schema.json, JSON Schema Draft 2020-12, apto para autogenerar clientes tipados en el lenguaje que quieras.
SMC hecho como toca
Smart Money Concepts es mitad análisis de price action genuinamente útil y mitad contenido de influencer de YouTube. Wickworks hace la mitad útil y se salta el dogma.
Los Order Blocks entregan sus señales de frescura en vez de que los filtren. Antes funcionaba al revés: wickworks tiraba las zonas mitigadas en el servidor y te daba solo las vivas. Fue la decisión equivocada, “mitigado” no es una sola cosa, y el servidor no pinta nada eligiéndote el criterio. Así que desde la v0.5.x cada order block vuelve con tres señales de frescura independientes y decides tú qué cuenta como consumido:
mitigated_wick: la mecha de una vela posterior atravesó la zona. Laxo, es el valor por defecto de la librería y es lo que usaba el viejo filtro del servidor.mitigated_close: el cuerpo de una vela posterior rompió de verdad más allá. Más estricto, el criterio de “a ojo parece mitigado”.touch_count: cuántos eventos de contacto distintos ha tenido la zona.
Las dos banderas de la librería solo saltan con roturas invalidantes, el precio se pasó de la zona en la dirección equivocada. touch_count salta con cualquier intersección de rango, así que captura “el precio visitó este nivel” sin exigir una rotura completa. Una zona tocada cuatro veces y nunca rota no tiene nada que ver con una que reventaron de una pasada, y ahora puedes distinguirlas a la hora de dibujar.
El coste es una llamada extra a smc.ob() por análisis para derivar las banderas del criterio de cierre, y el tope de OB subió de 20 a 40 para compensar que ya no se filtra. Los FVG siguen topados en 15. Todo va ordenado ascendentemente por distancia al precio actual, las zonas que todavía importan, ordenadas por lo pronto que las tocarías.
BOS y CHoCH son hechos estructurales, no señales. Un Break of Structure (el precio se lleva por delante el último swing en la dirección de la tendencia) es el gráfico diciendo “la tendencia siguió”. Un Change of Character (el precio rompe en contra de la estructura de la tendencia previa por primera vez) es el gráfico diciendo “la tendencia acaba de romperse por primera vez”. Wickworks emite el evento con su nivel y su dirección. No emite “la tendencia se ha girado, compra ya”. Esa es tu lectura del evento. El gráfico pone los hechos, tú pones la interpretación.
El algoritmo de niveles de S/R es propio y merece que lo cuente. Coges los pivotes de swing de un detector de swing de 7 velas. Te quedas solo con los pivotes que el precio ha testeado ≥2 veces (un “test” = un máximo o un mínimo a menos de ½·ATR del nivel, con velas entre los contactos). Impones una separación de ≥3·ATR entre los niveles que conservas, para no acabar con tres niveles casi idénticos apilados y contados como tres. Devuelves hasta tres de los más cercanos por encima del precio actual (resistencia) y tres de los más cercanos por debajo (soporte). Eso es lo que la mayoría de traders quieren decir cuando dibujan líneas de S/R a mano, niveles testeados de verdad y con separación con sentido, y casi ningún indicador automático lo hace así. La mayoría devuelve la lista bruta de swings y lo llama soporte.
Sin señales. Nunca.
Esta es la postura de la v0.3.0 y es estructural para todo el proyecto:
- Sin detección de divergencias.
- Sin eventos de cruce de MACD.
- Sin etiquetas de “golden cross” o “death cross”.
- Sin etiquetas de compra o venta.
- Sin banderas de “esta es una señal fresca” en las salidas de eventos.
Cada valor de la respuesta es una de tres cosas: una serie bruta de indicador, un hecho estructural (se formó un order block en esta vela; el precio cerró más allá de este nivel de swing), o un resumen numérico precocinado sobre eso (último cierre, último RSI, posición actual respecto a las medias clave). Nunca un juicio. Quieres divergencias, constrúyelas. Quieres eventos de cruce, constrúyelos. Son triviales de calcular encima de las primitivas que devuelve wickworks, viven en tu código, y puedes iterar sobre ellos sin redesplegar un servicio de Python ni pagarle a nadie.
Esa postura es la razón entera de que exista este proyecto. Todo lo demás, el tamaño del catálogo, la forma de la respuesta, la suite de tests, la licencia, sale de ahí.
“Dame solo el estado actual”
Hay una vía rápida para los clientes que no quieren Series enteras, solo la foto de la última vela. Seis salidas sin parámetros comparten una única pasada de análisis, pide las seis y cuestan lo mismo que pedir una:
"indicators": {
"price": true,
"levels": true,
"momentum": true,
"volume": true,
"position": true,
"slope": true
}price: último cierre.levels: EMA21, SMA50/100/200, ATR, VWAP, Donchian superior/inferior/medio.momentum: RSI, MFI, línea y señal de MACD más el histograma, ADX, K/D estocástico.volume: ratio entre el volumen actual y el reciente, OBV, y un booleanoisSpikeque es true cuando el volumen supera 2× la media reciente.position: para cada una de EMA21/SMA50/100/200/VWAP:"above"o"below". Mapa de sesgo.slope: las mismas claves,"up"o"down"sobre las últimas 10 velas. Combínalo conpositiony tienes el régimen del gráfico en dos objetos cortos.
Las pruebas
El modelo de confianza de un servicio de análisis técnico es “¿cuadran los números con lo que deberían cuadrar?”. Wickworks trae 370 tests en tres categorías:
- Diffs de matemáticas en forma cerrada. Para cada indicador estándar (RSI, MACD, ATR, Bollinger, Stochastic, Aroon, CCI, Williams %R, ROC, MOM, OBV, Donchian, VWMA, EMA, SMA), la suite de tests reimplementa la fórmula desde cero en numpy y pandas pelados y compara el valor de la última vela con la salida de wickworks sobre ticks reales de EURUSD H1. Tolerancia
rtol=1e-5. Si pandas_ta se desvía o nuestro cableado se pudre, el diff chilla antes incluso de que se construya la imagen de Docker. - Paridad de smc_fast. La capa SMC trae un port acelerado con numba de
smartmoneyconceptspara la ruta caliente. Ocho tests demuestran salida idéntica byte a byte a la de la librería original con las mismas entradas. La ruta rápida nunca puede producir números distintos en silencio. - Contrato del pipeline. Determinismo (las mismas velas → los mismos bytes de respuesta). Estabilidad al añadir (los indicadores causales no reescriben su propio historial cuando llegan velas nuevas). Recuento de null en la zona de calentamiento. Aislamiento de indicadores (pedir dos juntos da los mismos números que pedirlos por separado). Las rutas de error HTTP. El contrato del campo de volumen.
Y el conjunto de paquetes está cerrado tras el mecanismo exclude-newer de uv: cualquier versión de dependencia publicada después de una fecha fija se rechaza al resolver el lock. La fecha la adelantan automáticamente los targets de Make que tocan paquetes, así que si no lanzas make pkg-add o make pkg-update, la fecha no se mueve, y una release maliciosa recién publicada, todavía en su ventana de detección, no puede colarse en un refresco pasivo del lock. Aburrido, paranoico, correcto.
Te dice cuándo la has cagado
Pide un sma con length: 200 cuando solo has mandado 100 velas y la petición entera se rechaza de entrada, no se devuelve en silencio como un array de 100 null. La respuesta lista cada indicador al que le has dado de menos de una tacada, así que arreglas la llamada entera en un solo viaje:
{
"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 }
]
}
}El número de velas requerido se calcula por indicador a partir de sus parámetros, no es un suelo global. Las salidas SMC comparten una base de MIN_BARS (50 por defecto) porque el pipeline estructural asume un historial con sentido. La superficie total de error son exactamente cuatro códigos: 400 (este, con cuerpo estructurado), 413 (por encima de MAX_BARS, 5000 por defecto), 422 (fallo del esquema Pydantic en el payload de velas), 500 (abre un issue, esto no debería pasar).
Se documenta solo
GET /metadata devuelve un catálogo estático de cada indicador, señal y nivel que el endpoint de cálculo puede emitir, con etiquetas legibles, descripciones, pistas de interpretación, unidades, categorías. Bájatelo una vez al arrancar y cachéalo, la caché se invalida al cambiar de versión. Cubre los ~70 indicadores de primer nivel del registro, más los objetos anidados.
Hay un helper lookup(path) con repliegue en tres niveles, coincidencia exacta, luego quitar los índices de array, luego la hoja pelada, así que las rutas con puntos dinámicas del tipo retracements[42].Direction o prevTimeframe.rsi siguen resolviéndose en algo con sentido en vez de no devolver nada.
Lo que más importa cuando el consumidor es un LLM: puede preguntar qué significa un campo en lugar de alucinar una interpretación de bosChoch[3].level.
Servidor MCP, skill y plugin
La v0.6.0 añadió un servidor MCP, así que un modelo con function calling puede pasarle a wickworks un conjunto de velas y recibir primitivas de vuelta como llamada a herramienta. El repo trae además un skill de agente y un plugin de OpenClaw bajo .agents/, publicados en ClawHub por CI en los pushes de tag.
La postura no cambia solo porque quien llama sea un LLM: sigue devolviendo primitivas, nunca señales. El modelo recibe hechos sobre el gráfico y tiene que razonar por su cuenta, que es exactamente lo que quieres, porque un modelo repitiendo como un loro “STRONG BUY” desde un servicio de indicadores no vale nada.
Cómo se enchufa a lo demás
Wickworks es el servicio central de análisis técnico del stack de psyb0t. mt5-httpapi lo lleva embebido como sidecar atado al namespace de red del container de mt5, sin puertos publicados, sin despliegue aparte. La API de mt5 expone POST /symbols/:symbol/rates/ta: coge las velas de MT5, se las pasa a wickworks por debajo, devuelve el JSON de indicadores al cliente. Una sola llamada. El cliente nunca tiene que enterarse de que wickworks existe.
Los backtesters hacen lo mismo. Los servicios de alertas hacen lo mismo. Los scrapers que necesitan calcular indicadores en vivo sobre datos OHLC en streaming hacen lo mismo. Un container, un conjunto de matemáticas, un conjunto de tests que clavan esas matemáticas. Cada consumidor es un cliente HTTP flaco que sabe mandar velas y parsear la respuesta, y la respuesta tiene la misma forma sin importar quién pregunta.
La configuración son cuatro variables de entorno, valores por defecto sensatos, nada obligatorio: LOG_LEVEL=INFO, MAX_BARS=5000, MIN_BARS=50, WORKERS=2. Bájate la imagen, levanta el container, apunta tus clientes a :8000.
Hazte dueño de las matemáticas
El pitch se reduce a una frase. Deja de alquilar opiniones sobre tus propios gráficos. Calcula las primitivas tú mismo, en tu propio hardware, con matemáticas que puedes auditar y tests que puedes leer. Construye la capa de interpretación en código que controlas. Itera sobre ella tan rápido como puedas desplegar. No vuelvas a esperar nunca a que un canal de Discord te diga cuándo importa el order block.
Licencia WTFPL. Haz lo que te salga de los cojones con ello.
github.com/psyb0t/docker-wickworks · hub.docker.com/r/psyb0t/wickworks
Cómo Instalarlo En Tu Agente
El skill y el plugin se instalan desde un marketplace compartido en vez de repo por repo. Todo lo que hay bajo .agents/ está catalogado en un solo marketplace, así que son dos comandos:
claude plugin marketplace add psyb0t/agents
claude plugin install wickworks@psyb0tCodex usa el mismo marketplace con un verbo distinto, codex plugin add wickworks@psyb0t, porque no existe codex plugin install. También encuentra el skill por su cuenta en un checkout del repo, ya que escanea .agents/skills/ de forma nativa sin nada instalado en absoluto. Además ahora está listado en el MCP Registry oficial, así que un cliente que resuelva servidores desde ahí puede encontrarlo sin que le den una URL.