Live Execution API
Inscreva uma estratégia num ativo e timeframe ao vivo — receba um sinal no momento em que sua condition de entrada ou saída dispara num candle real e fechado.
#Visão geral
Live Execution está em beta. A API é estável e disponível em todo plano, incluindo o Free — espere que o ferramental ao redor (dashboards, webhooks) continue crescendo a partir daqui.
Uma subscription ao vivo observa um par de ativo num timeframe. Toda vez que um candle fecha pra esse par, sua estratégia é avaliada contra ele — a mesma Strategy DSL e o mesmo engine de avaliação que a Backtesting API usa, só que rodando continuamente em vez de sobre um intervalo histórico.
URL base: https://live.emidlabs.com/api/public/v1/live
x-api-key com uma API key válida escopada pro serviço live. Keys são geradas no Console — o toggle de Live Execution aparece lá, ao lado de Backtesting.#Providers e ativos suportados
Uma subscription ao vivo se conecta direto no próprio feed de dados de mercado da corretora escolhida — não há lista fixa de ativos pra consultar antes. Qualquer par que o provider realmente liste pode ser inscrito, no formato de assetPair próprio daquele provider (veja provider abaixo). Isso é mais amplo, de propósito, do que o que Backtesting suporta.
assetPair/provider abaixo pro que acontece quando um par acaba não existindo.#Como funciona
Não há loop de polling pra construir. Inscreva uma vez; um sinal só existe porque uma condition real de entrada ou saída ficou true num candle real e fechado:
| Etapa | O que acontece |
|---|---|
| 1. Inscrever | Faça POST com uma estratégia + assetPair. O timeframe vem do próprio configuration.timeframe da estratégia. |
| 2. Candle fecha | Todo candle fechado pra esse ativo+timeframe avalia sua estratégia — o mesmo StrategyRunner que a Backtesting API usa. |
| 3. Sinal dispara | Se entry ou exit avalia true, é registrado imediatamente — faça polling em GET .../signals ou confira o dashboard do Console. |
| 4. Pare a qualquer momento | Faça DELETE na subscription — nenhuma avaliação a mais acontece pra ela, sem cobrança de período parcial. |
#Signal Model: Stateless vs Stateful
Todo fechamento de candle reavalia decision.entry/decision.exit do zero — não há memória embutida de se o último candle já disparou o mesmo sinal. Se uma condition não é edge-triggered (uma comparação simples como rsi14 > 55 em vez de crossUp(...)/crossDown(...)), ela permanece true por vários candles consecutivos, e o comportamento padrão redispara esse mesmo sinal em cada um deles.
| model | Comportamento |
|---|---|
| "stateless" (padrão) | Toda subscription começa aqui. Um sinal é registrado/entregue exatamente quando decision.entry/decision.exit avalia true — toda vez, mesmo repetidamente pra uma condition que fica true por vários candles. |
| "stateful" | A subscription rastreia sua própria posição de Entry/Exit internamente (isPositioned) — nunca consultada da sua corretora — e só registra/entrega um sinal numa transição de verdade: Entry enquanto não posicionado, ou Exit enquanto posicionado. O resto é suprimido silenciosamente, nenhum sinal registrado, nenhum webhook enviado. |
isPositioned nunca é inferido de um saldo ou posição real da corretora — essa API não tem integração própria com corretora nenhuma. Se desviar da realidade (um trade feito manualmente fora desse sistema), corrija você mesmo via PATCH /subscriptions/{id}. Você pode definir model diretamente em POST /subscriptions/POST /subscriptions/batch pra criar uma subscription já "stateful" numa única chamada, ou mudar uma existente a qualquer momento do mesmo jeito.#POST /subscriptions — Criar
/subscriptionsInscreve uma estratégia pra avaliação ao vivo num par de ativo.
Mesmo formato de strategySnapshotJson da Backtesting API — veja Strategy System pra referência completa da DSL. configuration.timeframe dentro dele é o que determina o limite de candle em que sua estratégia avalia (5M, 15M, 1H, etc.) — não há um campo de timeframe separado na própria subscription.
Corpo da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| strategySnapshotJson | object | Sim | Objeto de estratégia — configuration, inputs, conditions, score, decision, riskManagement. configuration.timeframe determina a frequência de avaliação. |
| assetPair | string | Sim | Par de negociação, no formato exato que o provider escolhido usa — veja provider abaixo. Nenhuma tradução acontece em lugar nenhum: um ativo que não existe sob essa string exata nesse provider é aceito aqui e falha assincronamente, não é rejeitado na criação. |
| provider | string | Não | Qual provedor de dados de mercado escutar: "coinbase" ou "binance". Usa "coinbase" por padrão quando omitido. |
| webhookUrl | string | Não | URL https:// absoluta. Quando definida, todo sinal emitido também é enviado via POST pra ela — veja Webhooks abaixo. |
| model | string | Não | "stateless" (padrão quando omitido) ou "stateful" — veja Signal Model acima. |
| isPositioned | boolean | Não | Só tem significado quando model é "stateful". Usa false (ainda não posicionado) por padrão quando omitido. |
assetPair depende inteiramente de provider — eles não são intercambiáveis. Coinbase usa hífen ("BTC-USDC"); Binance não usa ("BTCUSDC"). Não há checagem de existência contra nenhuma das duas corretoras no momento da criação: um par inválido, ou o par certo no formato errado de provider, retorna 201 imediatamente e depois o status da subscription muda pra "Errored" em cerca de um minuto — confira depois de criar, ou fique de olho na falha via o log de webhook/sinal ficando vazio por mais tempo que o esperado.Resposta
| Campo | Tipo | Descrição |
|---|---|---|
| id | string (UUID) | Identificador único da subscription. |
| status | string | "Active" imediatamente após a criação. |
| model | string | "Stateless" a menos que model tenha sido definido como "stateful" na requisição. |
| isPositioned | boolean | Reflete o isPositioned da requisição quando model é "Stateful"; false caso contrário. |
| webhookSecret | string | null | Só presente quando webhookUrl foi definida. Retornado uma vez, só aqui — nenhum endpoint o repete depois. |
#POST /subscriptions/batch — Criar em Batch
/subscriptions/batchInscreve a MESMA estratégia pra avaliação ao vivo em vários pares de ativo de uma vez.
Uma subscription por entrada em assetPairs, todas compartilhando o mesmo strategySnapshotJson/provider/webhookUrl. Best-effort por item — um par de ativo ruim (desconhecido, formato errado de provider, limite de subscription concorrente atingido) nunca falha o resto do batch.
Corpo da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| strategySnapshotJson | object | Sim | Compartilhado por toda subscription criada nessa chamada. |
| assetPairs | string[] | Sim | Uma entrada por subscription a criar, cada uma no próprio formato do provider escolhido. Máximo de 50 por chamada. |
| provider | string | Não | Compartilhado por toda subscription desse batch. Usa "coinbase" por padrão quando omitido. |
| webhookUrl | string | Não | Compartilhado por toda subscription desse batch — cada uma ainda ganha suas próprias entregas assinadas e seu próprio webhookSecret. |
| model | string | Não | Compartilhado por toda subscription desse batch. "stateless" (padrão quando omitido) ou "stateful" — veja Signal Model acima. |
| isPositioned | boolean | Não | Compartilhado por toda subscription desse batch, só tem significado quando model é "stateful". Usa false por padrão quando omitido. |
Resposta
| Campo | Tipo | Descrição |
|---|---|---|
| items | array | Uma entrada por par de ativo requisitado, na ordem submetida. |
| items[].assetPair | string | Ecoa o par de ativo requisitado. |
| items[].id | string | null | Definido em caso de sucesso. |
| items[].status | string | null | Definido em caso de sucesso — "Active". |
| items[].webhookSecret | string | null | Definido em caso de sucesso, só quando webhookUrl foi fornecida. Retornado uma vez, só aqui. |
| items[].error | string | null | Definido em vez de id/status/webhookSecret quando esse item específico falhou. |
#Webhooks
Passe webhookUrl na criação e todo sinal que essa subscription emitir também é enviado via POST pra lá como JSON, no mesmo formato do objeto de sinal abaixo, assinado via um header X-Emidlabs-Signature — t={timestamp unix},v1={HMAC-SHA256 hex} de `${timestamp}.${rawBody}` usando webhookSecret como chave. Entrega tenta de novo até 3 vezes em erros de rede, timeouts, 429s, e respostas 5xx do seu endpoint — um webhook lento ou inacessível nunca bloqueia a avaliação de sinal em si.
#DELETE /subscriptions/{id} — Parar
/subscriptions/{id}Para uma subscription. Nenhum candle a mais é avaliado pra ela.
Retorna 404 se a subscription não existe ou não pertence à sua conta.
#POST /subscriptions/stop-all — Parar Todas
/subscriptions/stop-allPara toda subscription atualmente ativa na sua conta, numa chamada.
Sem corpo de requisição. Idempotente — subscriptions já paradas são simplesmente ignoradas, não é erro.
Resposta
| Campo | Tipo | Descrição |
|---|---|---|
| items | array | Uma entrada por subscription que estava ativa quando essa chamada começou. |
| items[].id | string | UUID da subscription. |
| items[].status | string | null | Definido em caso de sucesso — "Stopped". |
| items[].error | string | null | Definido em vez de status quando esse item específico falhou. |
#POST /subscriptions/stop — Parar Por Ids
/subscriptions/stopPara uma lista específica de subscriptions, numa chamada.
Corpo da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| ids | string[] | Sim | Ids de subscription a parar. Máximo de 200 por chamada. |
Resposta
| Campo | Tipo | Descrição |
|---|---|---|
| items | array | Uma entrada por id requisitado, na ordem submetida. |
| items[].id | string | Ecoa o id requisitado. |
| items[].status | string | null | Definido em caso de sucesso — "Stopped". Idempotente, igual ao stop único. |
| items[].error | string | null | Definido em vez de status quando esse id específico falhou (ex: não encontrado). |
#PATCH /subscriptions/{id} — Atualizar
/subscriptions/{id}Atualiza o signal model, estado de posição, ou webhook de uma subscription.
assetPair, timeframe, provider, e a estratégia em si nunca podem ser mudados depois da criação — pare a subscription e crie uma nova pra isso. Todo campo abaixo é opcional; omita um campo pra deixá-lo inalterado.
Corpo da requisição
| Campo | Tipo | Descrição |
|---|---|---|
| model | string | "stateless" ou "stateful" — veja Signal Model abaixo. |
| isPositioned | boolean | Só tem significado quando model é (ou está sendo definido pra) "stateful". Uma correção manual, nunca inferida automaticamente — veja Signal Model abaixo. |
| webhookUrl | string | Uma nova URL https:// absoluta, ou uma string vazia pra remover o webhook existente. Qualquer mudança não-vazia gera um webhookSecret novo. |
Resposta
| Campo | Tipo | Descrição |
|---|---|---|
| id | string | UUID da subscription. |
| model | string | O model da subscription depois dessa atualização. |
| isPositioned | boolean | A flag de posição da subscription depois dessa atualização. |
| webhookSecret | string | null | Só presente quando webhookUrl acabou de ser mudada pra um valor não-vazio nessa chamada. Retornado uma vez, só aqui. |
Retorna 404 se a subscription não existe ou não pertence à sua conta.
#GET /subscriptions/{id} — Status
/subscriptions/{id}Busca o status atual de uma única subscription.
Campos da resposta
| Campo | Tipo | Descrição |
|---|---|---|
| id | string | UUID da subscription. |
| assetPair | string | Par de ativo sendo observado, exatamente como submetido. |
| provider | string | Provedor de dados de mercado que essa subscription escuta — "coinbase" ou "binance". |
| timeframe | string | Resolvido a partir do próprio configuration da estratégia no momento da criação. |
| status | string | "Active", "Stopped", ou "Errored" (o provider nunca conseguiu produzir dados pra esse assetPair — veja o Callout do request de criação acima). |
| lastEvaluatedCandleOpenTime | number | null | Segundos unix — OpenTime do candle mais recente que essa subscription avaliou. null se nenhum ainda. |
| model | string | "Stateless" ou "Stateful" — veja Signal Model abaixo. |
| isPositioned | boolean | Só tem significado quando model é "Stateful" — veja Signal Model abaixo. |
| createdAtUtc | string | Timestamp de criação (UTC). |
| stoppedAtUtc | string | null | Quando a subscription foi parada, se foi. |
#GET /subscriptions — Listar
/subscriptionsLista subscriptions da sua conta, paginado.
Parâmetros de query
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| page | integer | 1 | Número da página, começando em 1. |
| pageSize | integer | 20 | Resultados por página. Deve estar entre 1 e 100. |
| status | string | (nenhum) | Filtro opcional de correspondência exata: "Active", "Stopped", ou "Errored". Omita pra retornar todos os status. |
Resposta
Mesmos campos por item da resposta de subscription única acima, sob items, mais metadados de paginação:
| Campo | Tipo | Descrição |
|---|---|---|
| items | array | Subscriptions dessa página — mesmo formato de GET /subscriptions/{id}. |
| totalCount | number | Total de subscriptions que batem com o filtro de status (ou todas, se omitido) em todas as páginas. |
| page | number | A página retornada. |
| pageSize | number | O tamanho de página usado. |
| totalPages | number | Número total de páginas nesse pageSize. |
#GET /subscriptions/{id}/signals — Formato de Sinal
/subscriptions/{id}/signalsO log de sinais emitidos de uma subscription, paginado — o análogo ao vivo do tradesDetail da Backtesting API.
Parâmetros de query
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| page | integer | 1 | Número da página, começando em 1. |
| pageSize | integer | 20 | Resultados por página. Deve estar entre 1 e 100. |
| type | string | (nenhum) | Filtro opcional de correspondência exata: "Entry" ou "Exit". Omita pra retornar os dois. |
Campos da resposta
| Campo | Tipo | Descrição |
|---|---|---|
| items | array | Sinais dessa página, mais recentes primeiro. |
| items[].candleOpenTime | number | Segundos unix — OpenTime do candle cujo fechamento disparou esse sinal. |
| items[].type | string | "Entry" ou "Exit". |
| items[].price | number | Preço de fechamento do candle que disparou. |
| items[].score | number | Soma dos pesos de toda condition que avaliou true nesse candle. |
| items[].conditions | object | Toda condition nomeada da estratégia, mapeada pra se era true nesse candle. |
| items[].scoreBreakdown | object | Peso por condition que contribuiu pro score — só conditions true contribuem. |
| items[].stopLossPrice | number | null | Só sinais de Entry. O preço de stop-loss que riskManagement colocaria pra essa entrada, computado na hora — null em sinais de Exit. |
| items[].takeProfitPrice | number | null | Só sinais de Entry. O preço de take-profit que riskManagement colocaria pra essa entrada — null em sinais de Exit. |
| totalCount | number | Total de sinais que batem com o filtro de type (ou todos, se omitido) em todas as páginas. |
| page | number | A página retornada. |
| pageSize | number | O tamanho de página usado. |
| totalPages | number | Número total de páginas nesse pageSize. |
v1 só emite sinais — não há camada de tracking de posição ou execução de ordem aqui. O que você faz com um sinal (abrir um trade, alertar alguém, registrar) é inteiramente sua própria integração.
#Confirmation Sources
Faça uma subscription exigir corroboração de outra antes do próprio sinal contar como real — ex: uma entrada de XRP em M30 só disparando quando uma subscription de BTC reporta tendência neutra, ou quando a própria subscription de tendência em H4 do mesmo ativo concorda. Dois campos em create_subscription fazem uma subscription (A) depender de outra (B) — sem endpoint novo, sem mudança na DSL de nenhuma das duas estratégias.
Campos da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| role | string | Não | "tradeable" (padrão) ou "support". Uma subscription "support" é uma fonte de confirmação pura — ela continua avaliando e emitindo sinais normalmente, mas existe pra ser referenciada no confirmationSources de outras subscriptions, não pra ser operada diretamente. |
| confirmationSources | array | Não | Quando definido, um sinal de Entry/Exit dessa subscription só vira "Confirmed" quando toda entrada aqui bater. Omita pro comportamento atual, incondicional. |
| confirmationSources[].sourceId | string (UUID) | Sim* | O id de outra subscription na sua própria conta. Um sourceId que não existe, não é seu, ou nunca emite um sinal correspondente simplesmente nunca confirma — não falha a criação. |
| confirmationSources[].signalType | string | Não | "entry" (padrão) ou "exit" — qual tipo de sinal da fonte conta como confirmação. |
| confirmationSources[].validityWindow.count | integer | Não | Padrão 1. Quantos candles da própria timeframe da FONTE um sinal correspondente permanece válido. Escala automaticamente com a estratégia que está confirmando — a janela de uma fonte em H4 é medida em candles H4, independente da timeframe de A. |
confirmationSources ou role via Atualizar depois da criação, mesmo padrão de assetPair/provider. Pare a subscription e crie uma nova pra mudar o pareamento.Como a confirmação é decidida
A confirmação olha pra trás, não pra frente: quando o candle de A fecha e um sinal candidato dispara, o EmidLabs checa se a fonte já tem um sinal recente o bastante pra ainda estar dentro da janela de validade — nunca espera um sinal da fonte que ainda não aconteceu. Se A declara mais de uma entrada em confirmationSources, todas precisam bater (E lógico).
Uma exceção estreita: se o próprio sinal de B pro mesmo momento real ainda está sendo processado quando a checagem de A roda (os dois fecham candle por volta do mesmo instante), o veredito "Unconfirmed" ainda pode virar "Confirmed" um instante depois, assim que B alcançar — limitado a um candle da própria timeframe de A, não uma espera geral. Veja os campos de resposta abaixo pra como isso aparece.
Campos de resposta do sinal
| Campo | Tipo | Descrição |
|---|---|---|
| role | string | "Tradeable" ou "Support", copiado da subscription no momento em que esse sinal foi emitido. |
| confirmationStatus | string | null | "Confirmed" ou "Unconfirmed". null quando a subscription não declara confirmationSources — um sinal comum não é afetado por esse recurso em nenhum ponto. |
| matchedSources | array | null | Uma entrada por requirement de confirmationSources que bateu — { sourceId, signalType, signalCandleOpenTime }. Vazio/ausente quando confirmationStatus é "Unconfirmed" ou null. |
confirmationStatus como filtro de query (mesmo nome de parâmetro) pra ver só um dos dois grupos. A entrega pro webhookUrl é onde o bloqueio de verdade acontece: um sinal "Unconfirmed" nunca dispara webhook, então qualquer coisa depois do webhook (uma bridge de execução, um alerta) só vê sinais confirmados.Quando uma corrida resolve tarde (a "exceção estreita" acima), o mesmo sinal é entregue no seu webhook uma segunda vez, agora "Confirmed" — trate entregas de webhook como identificadas pelo id do sinal, não como exatamente-uma-vez.
#Strategy DSL
Live Execution e Backtesting compartilham exatamente o mesmo DSL e engine de avaliação — uma estratégia que passa num backtest pode ser inscrita ao vivo sem mudanças. Veja a referência completa de Strategy System pros indicadores, conditions, e a lista de funções nativas.
warmupBars ainda importa ao vivo: os primeiros candles depois que uma subscription começa são excluídos da avaliação até acumular histórico suficiente pros seus indicadores estabilizarem — mesma regra do backtesting, só que medida a partir do momento da subscription em vez da data de início do backtest.#Códigos de erro
Toda resposta de erro tem o mesmo formato: { "error": "<código>", "message": "<texto>" }.
| Status | Código | Descrição |
|---|---|---|
| 400 | invalid_payload | O corpo da requisição está malformado, faltando campos obrigatórios, ou o strategySnapshotJson falha na validação. |
| 401 | api_key_missing | O header x-api-key não foi enviado. |
| 401 | api_key_invalid | A API key fornecida é inválida, revogada ou inativa. |
| 403 | service_not_enabled | Essa API key não está escopada pro serviço de live execution — habilite no Console ao criar ou editar a key. |
| 404 | not_found | A subscription não existe ou não pertence à sua conta. |
| 429 | rate_limit_exceeded | Muitas requisições pra essa conta. Reduza o ritmo e tente de novo. |