Skip to main content

Qué es

Si corrés tu propia estrategia, la podés conectar al motor de copia de Innova sin compartir tu código con nosotros. Vos corrés la estrategia en tu propio servidor y nos mandás solo la señal ya generada. Nosotros la validamos, comprobamos que siga fresca y la copiamos a las cuentas suscritas.
El proveedor nunca nos envía su código, solo señales.
Todos los ejemplos usan el host de producción https://cloud.innova-trading.com. Desarrollá siempre primero contra el endpoint de prueba (/signals/test).

Cómo pedir acceso

El acceso lo da Innova. Todavía no hay alta por cuenta propia.
  1. Pedile a tu contacto en Innova un token de proveedor y el x-community-slug de tu comunidad (por ejemplo mi-comunidad).
  2. Guardá los dos en privado. Nunca los pegues en chats, mails ni repositorios.
  3. Empezá por el endpoint de prueba (/signals/test) antes de mandar algo real.

Endpoints

Autenticación

Dos cabeceras en todas las llamadas:
  • Acá la llave no es un texto libre: el token es personal y te lo entrega Innova (ver “Cómo pedir acceso”). La cabecera x-community-slug es obligatoria en todas las llamadas.
  • El token es personal: quien lo tenga puede mandar señales en tu nombre, así que no lo compartas ni lo pegues en chats o mails.
  • Sin token, o con un token revocado: HTTP 401.
  • Tus señales quedan aisladas dentro de tu comunidad.

El JSON que nos mandás

No controlamos que sl y tps estén del lado correcto de entry. Hoy el servidor no rechaza un stop loss del lado equivocado (por ejemplo un sl por encima de entry en un buy). Validalo en tu propio código antes de enviar: un stop mal puesto se acepta tal como lo mandás.
Un campo desconocido hace fallar la llamada con HTTP 422. Es a propósito: si escribís stop_loss en lugar de sl, preferimos avisarte antes que aceptar una señal sin stop. Un generated_at sin zona horaria se interpreta como UTC. Mantené el reloj de tu servidor sincronizado por NTP.

Idempotencia

La combinación (tu proveedor, tu signal_id) es única de nuestro lado.
  • Si reintentás por un corte de red, mandá el mismo signal_id (y el mismo cuerpo).
  • La segunda llamada devuelve "status": "duplicate" con el resultado de la primera y no se abre ninguna operación adicional.
  • Nunca reutilices un signal_id para una señal distinta: la segunda quedaría descartada como repetida.
  • También respondemos duplicate con el código active_same_direction si ya tenés una señal abierta en el mismo símbolo y la misma dirección. No apilamos posiciones.
Un buen signal_id es determinista: estrategia + símbolo + timeframe + hora de la vela. Así, reiniciar tu proceso sobre la misma vela nunca duplica.

Frescura

Una señal que llega tarde no se puede copiar a un precio que ya no existe. Antes de publicar evaluamos: Hoy estos valores son globales (iguales para todos los proveedores). Si tu estrategia opera en marcos más largos y 120 s te queda corto, avisanos. La regla de deriva de 0,2 % es direccional: un movimiento a favor nunca rechaza la señal. La de 1 % absoluto no: un entry a más de 1 % del mercado vivo, para cualquiera de los dos lados, se rechaza con price_drift_exceeded, porque ese precio no existe en el mercado. Si no hay cotización viva para comparar, una señal con entry numérico también se rechaza (no_market_price, o price_drift_exceeded si el precio no se puede medir).

Códigos de rechazo

Ramificá siempre por code, nunca por el texto de detail. Nada se descarta en silencio. Toda decisión, aceptada o rechazada, queda registrada y la podés consultar en GET /api/v1/provider/signals/decisions.

La respuesta

(Respuesta abreviada: trae más campos.)
  • status: published (se copió), simulated (prueba), duplicate o rejected.
  • published es true solo si la señal llegó al motor de copia. Si es false, mirá code.
  • signal_source es el identificador con el que se suscriben las cuentas a tu estrategia. Pasáselo al dueño de tu comunidad.
  • received_at - generated_at es tu latencia de entrega. Registrala.
Códigos HTTP: 200 siempre que la llamada haya sido bien formada (incluso si la señal fue rechazada: el motivo va en code), 401 sin token válido, 409 si no se pudo guardar la señal por un conflicto raro de unicidad (reintentá con otro signal_id), 422 si el JSON está mal, 429 si superás 120 señales por minuto.

Prueba (sandbox)

POST /api/v1/provider/signals/test es donde deberías desarrollar. Hace lo mismo que el real (valida el JSON, corre el filtro de frescura, resuelve el símbolo contra las cuentas suscritas, calcula el lote y el plan de cierres parciales) y devuelve exactamente qué pasaría, incluidos los motivos de rechazo. Nunca publica ni abre nada. La diferencia visible: "status": "simulated", "dry_run": true, "published": false, y un bloque extra execution_preview. Si targets_matched es 0, tu señal es válida pero todavía no hay ninguna cuenta suscrita a tu signal_source (eso lo configura el dueño de la comunidad).

Ejemplos

Para enviar de verdad, la misma llamada cambiando /signals/test por /signals.
Reemplazá <TU_TOKEN> y <TU_COMUNIDAD> por los valores que te damos. Nunca los subas a un repositorio.

Checklist de integración

  1. Pedile a tu contacto en Innova el token y el x-community-slug.
  2. Mandá una señal a /signals/test. Esperá "status": "simulated".
  3. Provocá un rechazo a propósito (mandá un generated_at de hace una hora) y confirmá que tu código maneja code: "stale".
  4. Mandá un campo inventado y confirmá que tu código maneja el 422.
  5. Verificá que tu código reintente con el mismo signal_id cuando una llamada falla (el kit de Python, cuando esté disponible, lo hace solo). El sandbox siempre responde simulated: duplicate solo aparece en /signals real, así que lo vas a ver ahí por primera vez. Es lo esperado.
  6. Recién ahí pasá a /signals.
  7. Pasanos el signal_source que devuelve la respuesta, para suscribir las cuentas.

Convertir tu indicador de PineScript

Próximamente. Esta sección y la siguiente describen el kit de Python (innova-signal-kit), que todavía no se puede descargar. Si querés acceso anticipado, pedíselo a tu contacto en Innova. Mientras tanto usá el contrato HTTP descripto arriba, que funciona hoy.
Si tu estrategia vive en TradingView como PineScript, no la reescribís a mano. Todo esto viene en el kit de Python (un paquete pip, ver “Kit de Python listo para usar” más abajo). Trae un prompt listo (PROMPT-CONVERTIR-PINE.md, que innova-provider init copia a tu carpeta, o se imprime con innova-provider prompt) que pegás en un asistente de IA junto con tu código Pine, y te devuelve tu strategy.py en el formato que espera el kit.
Innova no recibe tu código Pine. Pero la IA a la que se lo pegues sí: un asistente en la nube (Claude, ChatGPT u otro) es un tercero y recibe tu Pine. Si tu estrategia es sensible, usá una IA local o revisá antes las condiciones de privacidad de ese servicio.
  1. Pegá el prompt y tu código Pine en tu asistente, y guardá el resultado como strategy.py.
  2. Exportá de TradingView tus velas y las señales de tu indicador a dos archivos CSV. La lista de operaciones del Strategy Tester trae la hora de la vela siguiente a la señal: en ese caso agregá --expected-shift 1 al comando del paso 3.
  3. Corré innova-provider parity --bars bars.csv --expected expected.csv --symbol EURUSD --timeframe 15 --strategy-name mi-estrategia.
  4. Criterio de aceptación: coincide el 99 % de las señales. --warmup por defecto es tu REQUIRED_BARS (en vivo, evaluate siempre arranca con esa historia completa): no hace falta pasarlo. Verificá la convergencia: corré la herramienta con dos valores de REQUIRED_BARS (por ejemplo 300 y 600) y quedate con el mayor si el resultado mejora; la herramienta también imprime un AVISO: cuando con más historia sube el %. Los indicadores con estado que depende del camino (supertrend, ATR/RMA/EMA, trailing stops) no convergen con “5 veces el período”: medido con un supertrend + RSI, REQUIRED_BARS = 70 dio 94 % y 150 dio 100 %; empezá en 300. La herramienta responde NO APROBADO: historia insuficiente (aun con 100 %) cuando el CSV tiene menos de 4 veces REQUIRED_BARS velas (el mensaje dice la cifra exacta) o cuando se comparan menos de 10 señales. Tu CSV tiene que cumplir las dos condiciones: al menos 4 veces REQUIRED_BARS velas Y al menos 10 señales de tu Pine en ese tramo; si tu Pine da menos, exportá más historia hasta alcanzarlas (con M15 y poca actividad pueden hacer falta meses); las señales del Pine anteriores a la primera vela evaluada se ignoran y solo se informan. Si igual no llega, la herramienta lista cada diferencia. Pegáselas a tu asistente y pedile que corrija la conversión.
  5. Recién después, pasá al sandbox (/signals/test) y, al final, a /signals.
Hay cosas que no se convierten solas: indicadores que leen otro timeframe (request.security), lógica dentro de la vela y estrategias que dependen de una posición abierta. El prompt las marca como “Dudas” para que decidas.

Kit de Python listo para usar

Próximamente, pedinos acceso. El kit no está publicado: todavía no hay pip install público. Pedíselo a tu contacto en Innova. No dependas de él para tu integración hoy; usá el contrato HTTP (ver “¿Preferís otro lenguaje?”).
Tenemos un kit en Python (cliente con reintentos y medición de latencia, un runner que espera el cierre de cada vela y una plantilla de estrategia donde solo editás strategy.py). Es un paquete pip llamado innova-signal-kit, con un único comando: innova-provider.

Instalación

Python 3.9 o más nuevo. Las instrucciones de instalación te las pasamos cuando te den acceso al kit. Todavía no hay paquete en un índice público. Tu strategy.py no es parte del paquete: vive en tu propia carpeta, en tu servidor, e Innova nunca lo recibe.

Flujo

run acepta --dry-run, --once, --env-file, --margin, --bars, --fetch-attempts y -v; parity acepta las opciones descritas arriba. El README dentro del kit explica todo.

Si la red falla

  • El cliente hace hasta 4 intentos por envío (el primero y 3 reintentos). Si igual no llega, el log dice SEÑAL PERDIDA con la hora de la vela y el signal_id.
  • Si la causa fue la red o un 5xx, el runner la reintenta una vez en el ciclo siguiente con el mismo signal_id (el servidor no duplica), solo mientras la vela siga dentro de los 120 s de frescura (en M1 sí, en velas más largas no; el log lo dice). Un 401 o un 422 no se reintentan.
  • Tras un corte largo el log dice Se omitieron N velas sin evaluar (desde X hasta Y): el runner solo evalúa la última vela cerrada, así que las señales de esas velas no se envían ni se reponen (llegarían viejas). Un reinicio entre el cierre de una vela y el próximo ciclo también pierde esa vela.
  • En --dry-run no se aplica el límite de 120 s de antigüedad de la vela: una prueba con el mercado cerrado pasa y esa misma vela se saltearía en real. La latencia generated_at->received_at que se informa incluye los reintentos del cliente.

Tus señales pasadas (historial)

Qué es. Tu estrategia corre en tu máquina sobre velas pasadas y da tus últimas N señales (50 por defecto, --last N). Usa la misma ventana que en vivo (REQUIRED_BARS velas cerradas, la última es la vela recién cerrada). Las velas salen de --bars-csv (mismo formato que parity) o, si no lo pasás, de /api/external/bars paginando hacia atrás con before_time (hasta 1000 por pedido, como máximo --max-bars, 10 000 por defecto; si no alcanza para N señales, el comando dice cuántas encontró y por qué se detuvo). El resultado es history.json en tu carpeta: cada señal trae solo los campos del contrato más kind: "backtest" y source: "kit-history"; generated_at es el cierre de la vela de la señal y el signal_id empieza con backtest-. El comando también imprime un resumen (cuántas señales, rango de fechas, buy/sell). Qué no es. No es un backtest con PnL: nada simula SL ni TP y no hay curva de capital; solo sabés qué señales habría dado tu estrategia. Está siempre marcado backtest, así que no se puede confundir con una señal en vivo, y nunca se mezcla con el registro en vivo. Tu código y tus parámetros no salen de tu máquina: solo viajan los campos del contrato. Límite conocido hoy. El servidor acepta before_time pero todavía no lo aplica, así que bajar más de una página de velas no funciona: el comando se detiene con un mensaje claro. Usá --bars-csv con tu propio historial. Enviarlo: el envío automático desde el kit todavía no está disponible. El endpoint POST /api/v1/provider/signals/history ya existe en el servidor, pero el flag --send del kit todavía no está disponible (--send falla con un error y no envía nada). Si lo enviás vos por HTTP, no mandes history.json tal cual: tiene las claves de primer nivel kind, source y summary, y el endpoint rechaza las claves desconocidas con un 422 extra_forbidden. El cuerpo de POST /api/v1/provider/signals/history es {"signals": [ ... ]}: solo el array que está bajo la clave signals de history.json, sin kind, source ni summary. Mandá como máximo 50 señales por pedido (partí en lotes si tenés más; una lista más larga recibe un 422 too_long). Cada elemento de ese array trae los campos del contrato más kind: "backtest" y source, que el endpoint acepta por señal; en el historial entry tiene que ser un número.

Límites del historial

Estos límites aplican al historial que enviás a POST /api/v1/provider/signals/history (el endpoint al que se refiere la nota “Enviarlo” de arriba). Cada comunidad puede tener los suyos. El equipo de Innova (admin de plataforma) puede ajustar ambos límites para tu comunidad sin release (rango permitido: retención de 7 a 365 días, tope de 50 a 5000 señales). Los valores vigentes para vos vuelven en cada respuesta del endpoint, en los campos retention_days y max_per_provider: guiate por esos, no por los de arriba. Cuando una señal no se guarda, su entrada en results trae status: "rejected" y uno de estos code. El resto del lote se procesa igual. Las señales que ya enviaste se responden como duplicate incluso estando en el tope, así que reintentar un lote es siempre seguro. Bajar un límite nunca borra lo que ya está guardado.

¿Preferís otro lenguaje?

Este es el camino que funciona hoy. No hay SDK de JS ni de otro lenguaje, y no lo prometemos: el contrato es HTTP simple y sirve desde cualquier lenguaje. Lo que tenés que implementar:
  • Endpoints (ver “Endpoints” arriba): leer velas de /api/external/bars, enviar señales a /api/v1/provider/signals/test (sandbox) y después a /api/v1/provider/signals.
  • Cabeceras: Authorization: Bearer <token> y x-community-slug.
  • Campos: exactamente los de “El JSON que nos mandás”; un campo desconocido da 422.
  • Stop y objetivos: controlá de tu lado que sl y tps estén del lado correcto de entry; el servidor no lo controla.
  • signal_id: determinista (estrategia + símbolo + timeframe + hora de la vela), y reintentar con el mismo. Ver “Idempotencia”.
  • Guard de 120 s: generá la señal al cierre de la vela y enviala solo si ese cierre fue hace 120 s o menos. generated_at es “ahora” y el servidor la rechaza como stale pasados 120 s (ver “Frescura”). No envíes la señal de una vela vieja (fin de semana, datos atrasados).
  • Velas: usá solo las cerradas; descartá la que está en formación.
Las secciones de arriba (endpoints, JSON, idempotencia, frescura, códigos de rechazo) son la referencia del contrato. El kit de Python es la implementación de referencia: podés portar su runner y su módulo parity línea por línea.

Próximos pasos

Autenticación

Cómo funcionan los tokens de API

Mejores Prácticas de Señales

Tips para mejores señales