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.
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.- Pedile a tu contacto en Innova un token de proveedor y el
x-community-slugde tu comunidad (por ejemplomi-comunidad). - Guardá los dos en privado. Nunca los pegues en chats, mails ni repositorios.
- 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-sluges 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
Idempotencia
La combinación (tu proveedor, tusignal_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_idpara una señal distinta: la segunda quedaría descartada como repetida. - También respondemos
duplicatecon el códigoactive_same_directionsi ya tenés una señal abierta en el mismo símbolo y la misma dirección. No apilamos posiciones.
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 porcode, 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
status:published(se copió),simulated(prueba),duplicateorejected.publishedestruesolo si la señal llegó al motor de copia. Si esfalse, mirácode.signal_sourcees el identificador con el que se suscriben las cuentas a tu estrategia. Pasáselo al dueño de tu comunidad.received_at - generated_ates tu latencia de entrega. Registrala.
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
/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
- Pedile a tu contacto en Innova el token y el
x-community-slug. - Mandá una señal a
/signals/test. Esperá"status": "simulated". - Provocá un rechazo a propósito (mandá un
generated_atde hace una hora) y confirmá que tu código manejacode: "stale". - Mandá un campo inventado y confirmá que tu código maneja el
422. - Verificá que tu código reintente con el mismo
signal_idcuando una llamada falla (el kit de Python, cuando esté disponible, lo hace solo). El sandbox siempre respondesimulated:duplicatesolo aparece en/signalsreal, así que lo vas a ver ahí por primera vez. Es lo esperado. - Recién ahí pasá a
/signals. - Pasanos el
signal_sourceque 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.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.
- Pegá el prompt y tu código Pine en tu asistente, y guardá el resultado como
strategy.py. - 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 1al comando del paso 3. - Corré
innova-provider parity --bars bars.csv --expected expected.csv --symbol EURUSD --timeframe 15 --strategy-name mi-estrategia. - Criterio de aceptación: coincide el 99 % de las señales.
--warmuppor defecto es tuREQUIRED_BARS(en vivo,evaluatesiempre arranca con esa historia completa): no hace falta pasarlo. Verificá la convergencia: corré la herramienta con dos valores deREQUIRED_BARS(por ejemplo 300 y 600) y quedate con el mayor si el resultado mejora; la herramienta también imprime unAVISO: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 = 70dio 94 % y150dio 100 %; empezá en 300. La herramienta respondeNO APROBADO: historia insuficiente(aun con 100 %) cuando el CSV tiene menos de 4 vecesREQUIRED_BARSvelas (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 vecesREQUIRED_BARSvelas 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. - Recién después, pasá al sandbox (
/signals/test) y, al final, a/signals.
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
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ásstrategy.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. Tustrategy.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 PERDIDAcon la hora de la vela y elsignal_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-runno 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 latenciagenerated_at->received_atque se informa incluye los reintentos del cliente.
Tus señales pasadas (historial)
--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 aPOST /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>yx-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
slytpsestén del lado correcto deentry; 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_ates “ahora” y el servidor la rechaza comostalepasados 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.
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