Pular para o conteúdo principal

Ordem a Limite — status finais e retry

Disponibilidade

As rotas de ordem a limite ainda não estão ativas. Este guia e as páginas de API estão publicados para você preparar a integração; a Liqi avisa quando as rotas entrarem em Sandbox e, depois, em Produção. Chamadas feitas antes disso respondem 404.

Uma ordem a limite fica registrada e aberta (OPEN) e só executa quando o mercado atinge o preço definido em limitPrice, dentro do prazo de validade (expiresAt / expiresInDays). Enquanto isso não acontece, a ordem é avaliada a cada ciclo pelo motor de execução.

Este guia explica como criar e cancelar uma ordem a limite pela API, os dois desfechos de encerramento sem execuçãoEXPIRED e REJECTED — e a política de retry (re-arm) que decide quando uma falha volta para a fila e quando ela encerra a ordem de forma definitiva.

Como criar uma ordem a limite

Abra uma ordem a limite com createLimitOrder (POST /orders/limit/orders). No corpo da requisição:

CampoObrigatório?O que é
idSimChave de idempotência do cliente (client order id). Ver detalhe abaixo.
profileIdSimCliente (profile) dono da ordem.
symbolSimPar de negociação — ex.: BTC/BRL, ETH/BRL, USDC/BRL.
sideSimbuy (compra) ou sell (venda).
limitPriceSimPreço-alvo. Na compra executa quando o mercado cai até ele; na venda, quando sobe até ele.
quoteAmount ou amountSimTamanho da ordem, e o campo depende do lado: compra usa quoteAmount (valor na moeda de cotação); venda usa amount (quantidade na cripto). Ver abaixo.
expiresInDays / expiresAtNãoPrazo de validade (GTD). Um ou o outro; omitir assume o prazo padrão da conta.
O campo de quantidade depende do lado da ordem

Não é uma escolha livre entre os dois. Compra (side: buy) exige quoteAmount e venda (side: sell) exige amount. Mandar os dois, nenhum, ou o campo do outro lado é recusado com 400 — uma compra que envia amount recebe a mensagem Field quoteAmount is required for buy orders.

id é obrigatório e garante idempotência — de forma assíncrona

O id é uma chave de idempotência fornecida por você (client order id), e é ele que vira o orderId da ordem. Um novo POST com o mesmo id para a mesma conta não cria uma segunda ordem — mas a recusa não vem na resposta: a requisição repetida também responde 201 com status: OPEN. A duplicata é identificada em seguida e encerrada em REJECTED (ver ciclo de vida), ficando de pé apenas uma ordem. O motivo fica registrado para auditoria, mas nenhum endpoint devolve esse texto: o que você lê é o status da ordem (ver como o motivo do encerramento aparece).

Na prática: o reenvio é seguro (não duplica), mas o 201 não confirma que aquela chamada foi a que sobreviveu. Se a resposta se perder e você reenviar com o mesmo id, consulte a ordem pelo orderId para saber o desfecho.

Como cancelar uma ordem a limite

Com a ordem em OPEN, o cancelamento registra o pedido e a ordem termina em CANCELLED (ver ciclo de vida).

As duas rotas avisam de formas diferentes

A diferença está no estado intermediário, e ela é intencional:

CANCELLINGCANCELLED
Uma ordem (cancelLimitOrder)sim, assim que o pedido é aceitosim, quando o pod conclui
Em lote (cancelLimitOrdersBatch)nãosim, um evento por ordem cancelada

O lote responde "pedido aceito" sem percorrer as ordens do seletor — é o que permite cancelar um símbolo inteiro, ou a conta inteira, com uma chamada só. Como ele não percorre, também não marca CANCELLING em cada ordem.

Não espere CANCELLING depois de um cancelamento em lote. O que chega é o CANCELLED de cada ordem, conforme o pod as processa.

Ordem em execução: o 409 não quer dizer que o pedido foi perdido

Cancelamento de ordem que não está em OPEN responde 409, e o 409 tem dois significados diferentes:

  • em execução (TRIGGERED, PROCESSING) — o pedido fica registrado e é honrado automaticamente se a ordem voltar para OPEN pelo re-arm. Não reenvie: acompanhe a ordem.
  • já finalizada — o pedido não é registrado, e não há mais o que cancelar.

Reserva de saldo na venda

Uma ordem a limite de venda (side: sell) reserva o saldo do ativo base no momento da criação, e não só quando dispara. A quantidade reservada é o amount da ordem (a quantidade da cripto — por exemplo, o BTC em BTC/BRL), nunca amount × limitPrice.

Na prática, ao criar a venda o saldo daquele ativo sai de free e entra em used no seu saldo da carteira: ele continua seu, mas fica bloqueado para aquela ordem enquanto ela está aberta, e por isso não pode ser usado por outra ordem. O efeito visível no saldo é o mesmo de uma venda a mercado; a diferença é o momento, que na ordem a limite é a criação e não a execução.

Compra não reserva

A reserva vale apenas para a venda. Uma ordem a limite de compra (side: buy) não bloqueia saldo na criação: o valor em moeda de cotação (por exemplo, BRL) permanece livre até a execução.

O que acontece com o saldo reservado depende do desfecho da ordem:

Desfecho da ordemEfeito no saldo reservado
Execução (EXECUTED / CLOSED)O saldo reservado é consumido — a cripto reservada sai da carteira para liquidar a venda.
Encerramento sem executar (EXPIRED, CANCELLED, REJECTED)O saldo é devolvido: volta de used para free e fica disponível de novo.

Saldo insuficiente na criação

Se, na criação de uma venda, não houver saldo livre suficiente para reservar o amount pedido, a ordem não fica aberta: ela termina em REJECTED (por saldo insuficiente). Como cada venda aberta mantém seu saldo reservado, abrir várias vendas cujo total ultrapasse o saldo disponível faz as que couberem reservarem normalmente e a excedente ser recusada com REJECTED.

Ciclo de vida resumido

OPEN ──(preço atingido)──▶ TRIGGERED ──(execução)──▶ EXECUTED ──▶ CLOSED
▲ │
└──────────── re-arm (falha recuperável) ──┘

OPEN ──(prazo vencido, expiresAt < now)──▶ EXPIRED
OPEN ──(cancelamento pedido)──▶ CANCELLING ──▶ CANCELLED
qualquer ponto ──(recusa definitiva)──▶ REJECTED
  • OPEN, TRIGGERED, PROCESSING, EXECUTED — estados ativos (a ordem ainda está em andamento).
  • CANCELLING — cancelamento em andamento: a rota registrou o pedido e o pod conclui (encerra a observação e grava CANCELLED). Nesse estado a ordem já não executa mais.
  • CLOSED, EXPIRED, CANCELLED, REJECTED — estados finalizados (a ordem não executa mais).
EXECUTED não é o fim

EXECUTED significa que a ordem foi preenchida na exchange, e não que o ciclo terminou: a liquidação segue em andamento e o campo statusGroup ainda vem como ACTIVE. O estado final de uma ordem executada é CLOSED. Quem para de consultar em EXECUTED para antes do desfecho.

Acompanhamento de status

As mudanças de status da ordem a limite acontecem de forma assíncrona (disparo, re-arme, conclusão do cancelamento, expiração), e o acompanhamento é por consulta REST:

  • GET /orders/fetchOrder — lê do registro e responde o estado atual da ordem. É a consulta a usar para confirmar um desfecho.
  • GET /orders/limit/orders — listagem, eventualmente consistente: serve para varrer e reconciliar, não para confirmar o que acabou de acontecer.

Para reconciliar, use orderId + status + updatedAt: a mesma ordem pode ser lida várias vezes, e updatedAt é o que diz qual leitura é a mais recente.

O canal watchOrders passou a cobrir a ordem a limite

O WebSocket publica todas as mudanças de status da ordem a limite: abertura, disparo, re-arm, CANCELLING, CANCELLED, EXPIRED e REJECTED. Cada evento carrega a ordem completa, no mesmo formato do fetchOrder — com type: "limit" e os campos de limite.

Esta seção dizia o contrário até agosto de 2026, quando o canal ainda não cobria essas transições. Se a sua integração faz polling do fetchOrder para saber de cancelamento ou expiração, ele deixou de ser necessário.

O canal continua sendo complemento, e não substituto da consulta: entrega é de melhor esforço, e a fonte do estado é o REST. Para reconciliar, use fetchOrder.

EXPIRED — encerramento por tempo

A ordem venceu (expiresAt < now) sem atingir o alvo. É um encerramento por tempo, não por erro: o mercado simplesmente não chegou ao limitPrice dentro da validade. Não há falha de execução envolvida.

REJECTED — recusa definitiva

REJECTED é o encerramento por recusa, e não por tempo. Ele vem de duas origens diferentes, e vale distinguir as duas:

  • A política de retry do fluxo de execução, que classifica a falha de uma tentativa em três categorias: recuperável (re-arma, volta para OPEN), irrecuperável (encerra em REJECTED) ou genérica com teto de tentativas (re-arma, mas encerra em REJECTED se persistir — ver Erro genérico da exchange). A lista de condições irrecuperáveis começa vazia e cresce por operação, conforme novos casos definitivos são mapeados; a terceira categoria, essa sim, encerra a ordem quando o erro se repete.
  • As recusas fora dessa política, na criação e em pontos do fluxo de execução que não passam pelo retry. São essas que produzem REJECTED hoje, e estão listadas abaixo.
O que hoje termina em REJECTED

A lista de condições irrecuperáveis está vazia, então nenhuma falha encerra a ordem por essa via — a rejeição da exchange, por exemplo, re-arma. Mas a política de retry produz REJECTED em um caso: o erro genérico que se repete três vezes (ver Erro genérico da exchange).

Mas REJECTED continua acontecendo, por caminhos que não passam por essa política. Hoje são estes:

QuandoCaso
Logo após a criação, sem chegar a executarid duplicado; venda sem saldo suficiente para reservar
Na entrada do fluxo de execuçãoRecusa não transitória (uma recusa transitória, como mercado inativo, mantém a ordem em OPEN sem registrar tentativa); ordem sem a quantidade correspondente ao lado
Durante o fluxo de execução, antes da tentativa na exchangeCliente (profile) com compra ou venda desabilitada
Após três tentativas de execuçãoErro genérico da exchange (INTERNAL SERVER ERROR) que se repetiu nas três — ver a seção abaixo
ResidualFalha que a própria política de retry não consegue tratar encerra a ordem em REJECTED

Ou seja: REJECTED pode chegar durante a execução. Trate-o como desfecho possível em qualquer ponto, e não só na criação.

Erro genérico da exchange: três tentativas

Quando a exchange responde com um erro genérico (INTERNAL SERVER ERROR), a Liqi não consegue afirmar duas coisas ao mesmo tempo: nem que o erro é definitivo, nem que a ordem não chegou a ser aberta do outro lado.

Esse desconhecimento é o que define o tratamento. A ordem a limite não pode ser encerrada por um erro isolado — uma instabilidade momentânea da exchange não deve custar a ordem do cliente. Mas re-armar indefinidamente depois de um desfecho desconhecido tem o risco oposto: executar a mesma ordem mais de uma vez.

O acordo entre os dois é um teto:

  • a ordem é tentada até três vezes (a original mais duas);
  • entre uma tentativa e outra ela volta para OPEN, com o expiresAt original preservado — o prazo não é reiniciado;
  • se o erro genérico se repetir nas três, a ordem termina em REJECTED.

Na prática, um erro realmente transitório se resolve dentro das três tentativas. Um erro permanente que apenas se apresenta como genérico encerra a ordem em vez de ficar girando até o vencimento, que pode ser de até 30 dias.

Entre uma tentativa e outra a ordem fica em OPEN como qualquer outra, e o expiresAt continua valendo. Se o prazo vencer nesse intervalo, o desfecho é EXPIRED; se as três tentativas se esgotarem antes, é REJECTED. Vale o que acontecer primeiro.

Como o motivo do encerramento aparece

O motivo de cada mudança de status é registrado para auditoria, e é por ele que o suporte da Liqi investiga um caso. Nenhum endpoint devolve esse texto hoje:

  • O GET /orders/limit/orders traz status, e não o motivo.
  • O GET /orders/fetchOrder traz errorMessage preenchido apenas em rejeição escrita pelo fluxo de execução. Nas recusas da própria ordem a limite — id duplicado, venda sem saldo, expiração, cancelamento — ele vem vazio.

Na prática: o status é a informação de desfecho disponível na API. Para saber o motivo de um caso específico, acione o suporte com o orderId.

Re-arm — falha recuperável volta para OPEN

Quando a execução falha de forma recuperável, a ordem é re-armada: volta para OPEN (e não para TRIGGERED), ficando novamente elegível para ser avaliada e disparada no próximo ciclo.

Cada etapa do ciclo é carimbada na trilha da ordem, o que permite auditar o que aconteceu em cada tentativa:

avaliou → disparou → tentou → falhou → re-armou

Uma mesma ordem pode passar por vários ciclos de re-arm antes de finalmente executar, expirar por tempo (EXPIRED) ou, no caso de uma falha irrecuperável, ser rejeitada (REJECTED).

Margem pós-execução — proteção contra preenchimento ruim

Além do gatilho de preço, há uma margem pós-execução: se a execução sair muito distante do limitPrice — além da tolerância configurada na sua conta — a ordem também volta para OPEN em vez de aceitar um preenchimento ruim. Essa tolerância é definida pela Liqi na habilitação da conta e não é devolvida por nenhum endpoint; consulte o suporte para saber o valor em vigor.

A margem é assimétrica, sempre a favor do cliente:

LadoCondição que devolve a ordem para OPEN
Compra (buy)Preenchimento acima do teto (limitPrice + tolerância)
Venda (sell)Preenchimento abaixo do piso (limitPrice − tolerância)

Ou seja, uma compra não é aceita acima do teto e uma venda não é aceita abaixo do piso. Nesses casos a ordem re-arma e tenta novamente, protegendo o cliente de um preço pior do que o contratado.

Resumo

StatusTerminal?Significado
OPENNãoRegistrada e aguardando o preço-alvo (estado para onde o re-arm devolve)
TRIGGEREDNãoPreço atingido, execução em andamento
CANCELLINGNãoCancelamento pedido e em andamento (o pod conclui para CANCELLED)
EXECUTEDNãoPreenchida na exchange, liquidação em andamento (statusGroup ainda ACTIVE)
CLOSEDSimEncerrada com execução concluída — estado final de uma ordem executada
EXPIREDSimPrazo vencido (expiresAt < now) sem atingir o alvo
CANCELLEDSimCancelamento concluído
REJECTEDSimRecusa na criação (venda sem saldo, id duplicado) ou recusa definitiva dentro do fluxo de execução — lista dos casos. Falha classificada pela política de retry não cai aqui: re-arma
dica

Falha classificada pela política de retry → re-arm → OPEN (nova tentativa), e hoje isso vale para a rejeição da exchange e para a falha desconhecida. Recusa na criação, ou recusa definitiva dentro do fluxo de execução → REJECTED. Fim do prazo → EXPIRED. Preenchimento fora da margem → re-arm → OPEN. EXECUTED não é terminal; o fim é CLOSED. Venda reserva o ativo base na criação (sai de free para used); saldo devolvido em EXPIRED/CANCELLED/REJECTED e consumido na execução. O motivo do encerramento não é devolvido por nenhum endpoint.