NexusTrade Financial MCP

Pesquisa quantitativa, backtesting, assinaturas de marketplace de criadores, forks de estratégia editáveis, copy trading contínuo e fluxos de trabalho de corretagem controlados por meio de mais de 120 ferramentas MCP.

Documentação

NexusTrade

NexusTrade TypeScript SDK

Crie estratégias de trading em TypeScript tipado. Execute backtests no mesmo motor que as executa ao vivo.

npm Node License Deps

Quickstart · Authoring · Polling · Agents · Lake SQL · Auth · Errors


npm install nexustrade

Zero dependências de runtime. Os builds ESM e CommonJS vêm juntos, com tipos.

Servidor MCP

O NexusTrade também expõe a plataforma como um servidor Model Context Protocol remoto e hospedado. Clientes MCP modernos conectam-se diretamente ao endpoint Streamable HTTP de produção e descobrem o OAuth do NexusTrade automaticamente:

claude mcp add --transport http nexustrade https://nexustrade.io/api/mcp

O Cursor e outros clientes com capacidade remota usam:

{
  "mcpServers": {
    "nexustrade": {
      "url": "https://nexustrade.io/api/mcp"
    }
  }
}

Para o Claude Desktop e outros clientes exclusivamente stdio, use a ponte mcp-remote estabelecida — não é necessário clonar ou rodar um servidor NexusTrade local:

{
  "mcpServers": {
    "nexustrade": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote@latest",
        "https://nexustrade.io/api/mcp",
        "--transport",
        "http-only"
      ]
    }
  }
}

O servidor ao vivo expõe mais de 120 ferramentas em pesquisa de mercado, construção de portfólio, backtesting, otimização, validação walk-forward, computação gerenciada, agentes Aurora, paper trading e operações de corretagem controladas. Suas ferramentas de marketplace para criadores cobrem o caminho completo de adoção de estratégias:

  • search_creators descobre criadores públicos e seus portfólios de marketplace.
  • subscribe_portfolio valida uma listagem monetizada e retorna uma prévia segura de checkout; o usuário conclui o pagamento no NexusTrade, nunca por meio da ferramenta MCP.
  • fork_shared_portfolio cria uma cópia editável única de uma estratégia de marketplace em um portfólio novo ou existente.
  • copy_trade_shared espelha continuamente uma estratégia acessível em um portfólio paper ou ao vivo com uma alocação explícita.

Consulte o guia do desenvolvedor, a referência de ferramentas utilitárias e a referência de ferramentas Aurora.

Os resultados de pesquisa e históricos não são aconselhamento de investimento e não garantem desempenho futuro. Mantenha os modos paper e ao vivo explícitos. Ferramentas que podem afetar portfólios, agendamentos ou ordens de corretagem permanecem sujeitas às permissões e controles de aprovação do NexusTrade da conta autenticada.

Início rápido

import {
  NexusTradeClient,
  always,
  backtest,
  buy,
  portfolio,
  stockAsset,
  strategy,
} from "nexustrade";

const client = new NexusTradeClient({
  apiKey: "sk-...",
  baseUrl: "https://nexustrade.io/api/v1",
});

const book = portfolio("Example", [
  strategy("Buy SPY", always(), buy(stockAsset("SPY"), 100)),
]);

const operation = await client.createBacktest(
  backtest(book, { startDate: "2024-01-01", endDate: "2024-12-31" }),
  { idempotencyKey: "example-v1" }
);
const result = await client.waitForBacktest(operation.id as string);
console.log(result.result);

As operações de backtest podem incluir warnings: string[] imediatamente após o envio e novamente na fase final result. Trate-os como avisos materiais; eles não transformam uma operação bem-sucedida em falha.

Autoria de estratégias

Todo builder é gerado a partir da mesma especificação de indicadores que o motor do NexusTrade executa, então um livro é válido por construção, não por convenção.

O TypeScript não pode sobrecarregar operadores de comparação, então os indicadores se compõem por meio de gt / gte / lt / lte / eq / neq e and / or:

import * as nt from "nexustrade";

const book = nt.portfolio(
  "Momentum",
  [
    nt.strategy(
      "Rotate into strength",
      nt.always(),
      nt.dynamicRebalance({
        universe: nt.universe("SP500"),
        pipeline: [
          nt.filter(nt.gt(nt.Price(nt.CANDIDATE), nt.SMA(nt.CANDIDATE, 200))),
          nt.selectTop(nt.RSI(nt.CANDIDATE, 14), 10),
        ],
        weightIndicator: nt.RSI(nt.CANDIDATE, 14),
        limit: 10,
        deploymentPercent: 80,
      })
    ),
  ],
  { initialValue: 100_000 }
);
O que você pode construir — 170+ builders gerados
GrupoExemplos
Preço e volumePrice OpeningPrice HighOfDay VWAP Volume GapPercentage
TécnicosSMA EMA RSI BollingerBand AverageTrueRange CrossAbove
Estado da posiçãoPositionValue PositionPercentChange PositionMaxDrawdown
Estado do portfólioPortfolioValue BuyingPower MaxDrawdown InitialValue
FundamentosFundamental Economic DaysUntilEarnings IsIndexMember IsIndustry
OpçõesOptionDaysToExpiration OptionCollateral OptionUnrealizedPnL openOption closeOption
Açõesbuy sell alert dynamicRebalance rebalanceOption
Seleçãofilter selectTop selectPercentile universe
Lógicaalways atLeast atMost exactly fewerThan multi and or

Todo builder é totalmente tipado — seu editor completa toda a superfície.

Jobs rodam no motor — você faz polling

create* enfileira o trabalho e retorna imediatamente. Não resolve quando os resultados existirem. Não há webhooks hoje.

sequenceDiagram
    participant You
    participant SDK
    participant Engine

    You->>SDK: createBacktest(book)
    SDK->>Engine: POST (enqueue)
    Engine-->>SDK: id, status=queued
    SDK-->>You: operation (returns immediately)

    loop waitForBacktest — backoff 2s→15s
        SDK->>Engine: GET /operations/{id}
        Engine-->>SDK: status update
    end

    SDK-->>You: result (when completed)

    Note over You,Engine: Poll timeout throws operation_timeout.<br/>The job keeps running — call wait again with the same id.

Todo tipo de job reporta o mesmo envelope, então um único poller atende a todos:

{
  id: "op_...",
  kind: "backtest",          // backtest | optimization | walk_forward
  status: "queued",          // queued | running | completed | failed | cancelled
  result: {...},             // present only once terminal
  error: { code, message, retryable },
}
const finished = await client.waitForBacktest(operation.id as string);
OpçãoPadrãoSignificado
timeoutSeconds900Desistir de esperar (o job continua rodando)
pollIntervalSeconds2Primeiro intervalo; faz backoff de 1,5×
maxPollIntervalSeconds15Teto do intervalo
throwOnFailuretrueLançar erro em failed/cancelled em vez de retornar

Um timeout lança operation_timeout e não cancela o job — chame o waiter novamente com o mesmo id em vez de reenviar.

Lotes. createBacktests envia muitos em uma única solicitação e retorna uma operação para cada um; waitForBacktests(operations) espera por todos. Prefira isso a um loop: uma solicitação, uma chave de idempotência, um slot de rate-limit.

Otimização e walk-forward seguem a mesma forma:

const study = await client.createWalkForward(
  nt.walkForward(book, {
    globalStartDate: "2022-01-01",
    globalEndDate: "2024-12-31",
    foldCount: 4,
  }),
  { idempotencyKey: "wf-v1" }
);
await client.waitForWalkForward(study.id as string);

Implantando um portfólio

Escrever e testar um livro não o persiste. save o grava na sua conta; deploy começa a executá-lo.

const book = nt.portfolio("Momentum", [
  /* … */
]);

await book.save({ idempotencyKey: "momentum-v1" }); // persists; sets book.id
const deployment = await book.deploy(); // starts paper trading
await book.undeploy(); // stops it

save e deploy produzem ids diferentes, e a distinção importa. save persiste um rascunho e define book.id para ele. deploy gera o portfólio paper real e retorna seu próprio portfolioId — a implantação cria um portfólio em vez de converter o rascunho em um, então os dois ids coexistem. Guarde deployment.portfolioId para qualquer coisa que leia o estado ao vivo; book.id endereça o rascunho.

deployment.portfolioId; // the running portfolio
deployment.deploymentType; // paper, unless you deployed an existing live one
deployment.outcome; // created | reactivated

Os métodos do handle aceitam um transport opcional; se omitido, eles resolvem um a partir do ambiente. As mesmas operações existem no cliente — client.deploy(id), client.undeploy(id) — quando você tem um id em vez de um handle.

await client.listPortfolios({ includePaper: true, includePositions: true });
await client.getPortfolio(portfolioId);

listPortfolios filtra com includePaper, includeLive, includeInactive, includeChatPortfolios, search, limit e page. includePositions fica desativado por padrão quando search está definido.

Um portfólio que você cria aqui é sempre paper, e a criação de um portfólio ao vivo ainda acontece no aplicativo web. Ordens e status de corretagem são acessíveis daqui; veja Live trading.

Mas deploy pode iniciar trading ao vivo. Dado o id de um portfólio já implantado, ele reativa esse portfólio como ele já é — então client.deploy(id) em um portfólio ao vivo pausado retoma o trading ao vivo com a corretora conectada, e includeLive: true acima fornecerá um id assim. Verifique deployment.deploymentType antes de tratar uma implantação como simulada.

Trading ao vivo

Trading ao vivo precisa de uma corretora vinculada à sua conta. O vínculo é um redirecionamento OAuth, então uma chave de API não pode completá-lo — um humano abre a URL.

await client.listBrokerages();
// [{ brokerage: "Alpaca", connected: false,
//    connectUrl: "https://nexustrade.io/live-trading" }, ...]

await client.connectBrokerage("Alpaca"); // logs the URL, waits until connected

connectBrokerage espera por padrão apenas quando o stdout é um TTY. Em CI, cron ou run_compute, ele rejeita com brokerage_not_connected imediatamente, com a URL na mensagem, em vez de travar por cinco minutos diante de ninguém. Passe { wait: true } ou { wait: false } para forçar qualquer um dos comportamentos.

Uma listagem somente ao vivo que retorna vazia rejeita com o mesmo erro, em vez de um array vazio, já que um array vazio não diz nada sobre o motivo:

await client.listPortfolios({ includeLive: true, includePaper: false });
// NexusTradeApiError: brokerage_not_connected: No live portfolios, and no
// brokerage is connected. Connect one at https://nexustrade.io/live-trading

Ordens

const result = await client.createOrders(
  portfolioId,
  [
    {
      asset: { name: "SPY", type: "STOCK", symbol: "SPY" },
      side: "BUY",
      quantity: 10,
      orderType: "MARKET",
    },
  ],
  { idempotencyKey: "rebalance-2024-04-01" }
);

// Dollar notional (stock/crypto only — options require contract quantity):
await client.createOrders(
  portfolioId,
  [
    {
      asset: { name: "AAPL", type: "STOCK", symbol: "AAPL" },
      side: "BUY",
      amount: 500,
      orderType: "MARKET",
    },
  ],
  { idempotencyKey: "buy-aapl-500" }
);

Ordens paper são aceitas imediatamente. Ordens ao vivo são colocadas em aprovação e nunca são enviadas a um corretor por esta chamada.

if (result.requiresApproval) {
  console.log("nothing has traded yet — approve at", result.approvalUrl);
}

Não há argumento, escopo ou flag que envie uma ordem ao vivo sem aprovação. O limite da corretora recusa uma ordem ao vivo não aprovada independentemente do que qualquer chamador pedir, então isso é uma propriedade do sistema, não uma promessa feita por este método. No máximo 50 ordens por solicitação.

Seus próprios dados

Uma fonte de dados personalizada é uma série temporal que você possui — contagens de sentimento, um fator proprietário, qualquer coisa que a plataforma ainda não carregue. Crie uma e referencie-a em uma estratégia com CustomIndicator.

const series = await client.createCustomIndicator(
  {
    name: "WSB NVDA Mentions",
    scope: "asset",
    description: "Daily r/wallstreetbets mentions",
    pointKind: "observation",
    points: [
      { timestamp: "2024-04-01", value: 152, ticker: "NVDA" },
      { timestamp: "2024-04-02", value: 90, ticker: "NVDA" },
    ],
  },
  { idempotencyKey: "wsb-mentions-v1" }
);

const busy = nt.gt(
  nt.CustomIndicator(nt.stockAsset("NVDA"), String(series.customIndicatorId)),
  100
);
const book = nt.portfolio("Attention", [
  nt.strategy("Buy the buzz", busy, nt.buy(nt.stockAsset("NVDA"), 25)),
]);

scope é "global" (uma série) ou "asset" (uma série por ticker, então todo ponto precisa de um ticker). Não pode ser alterada após a criação.

Declare pointKind sempre que a semântica temporal for conhecida: observation para amostras no momento do evento, period_aggregate mais aggregatePeriod (1d, 1w, 1mo ou 1q) para valores de período fechado, e disclosed para valores com horário de publicação explícito em cada linha. O SDK aplica este contrato antes de gravações inline e uploads grandes. Uma observação somente com data no mesmo dia vira um instante UTC explícito no mesmo dia, em vez de mudar para o próximo dia calendário.

Tamanho não é uma limitação. points é ilimitado. Um lote que cabe na solicitação vai junto; um maior é enviado ao armazenamento e validado antes de a chamada resolver. Em ambos os casos, o indicador retornado reflete o que realmente chegou, e um upload que falha na validação rejeita em vez de reportar sucesso.

Crescendo uma série. Anexe ao mesmo id a cada execução:

await client.appendCustomIndicatorPoints(
  String(series.customIndicatorId),
  [{ timestamp: "2024-04-03", value: 118, ticker: "NVDA" }],
  { idempotencyKey: "wsb-mentions-2024-04-03" }
);

Criar uma nova série a cada execução divide o histórico em fragmentos que nenhuma estratégia consegue ler. Reenviar um lote idêntico é seguro — o duplicado não é escrito duas vezes.

ChamadaFinalidade
createCustomIndicator(spec, { idempotencyKey })Criar, opcionalmente com dados iniciais
appendCustomIndicatorPoints(id, points, { idempotencyKey })Adicionar pontos
replaceCustomIndicatorPoints(id, points, { idempotencyKey })Substituir pontos, manter o id
archiveCustomIndicator(id) / restoreCustomIndicator(id)Ciclo de vida reversível
listCustomIndicators() / getCustomIndicator(id)Descobrir ids e cobertura

Os pontos aceitam timestamp, value, ticker, assetType e availableAt — em camelCase ou snake_case, com objetos Date permitidos. Defina availableAt quando um valor se tornou conhecível depois de sua data: um lucro por ação registrado para o fim do trimestre, mas publicado semanas depois. Um campo não reconhecido lança erro em vez de ser silenciosamente descartado.

Para entregar um arquivo que você já tem em disco, createCustomIndicatorUpload / completeCustomIndicatorUpload / waitForCustomIndicatorUpload expõem as três etapas diretamente. CSV, JSON e JSONL até 100 MB.

Execuções de agente

Todos os outros jobs são fire-and-poll. Agentes não são — três estados (pending_plan_approval, pending_action_approval, awaiting_user_input) não podem avançar sem você. Itere a execução e responda quando bloquear:

sequenceDiagram
    participant You
    participant Run as AgentRun
    participant Engine

    You->>Run: createAgent(prompt)
    Run->>Engine: POST /agents
    Engine-->>Run: run id

    loop for await (const event of run)
        Run->>Engine: GET events (cursor)
        Engine-->>Run: new events

        alt event.needsApproval
            Run-->>You: plan or action awaiting approval
            You->>Run: approve() or reject()
            Run->>Engine: POST approval
        else event.needsInput
            Run-->>You: awaiting user input
            You->>Run: say("...")
            Run->>Engine: POST message
        else
            Run-->>You: event.text
        end
    end

    Run-->>You: terminal

    Note over You,Engine: Without approve/say, the run stalls and bills.<br/>Reattach later with attachAgent(run.id).
const run = await client.createAgent("Find momentum names in the S&P 500", {
  idempotencyKey: "momentum-scan-v1",
});
for await (const event of run) {
  console.log(event.text);
  if (event.needsApproval) await run.approve();
  if (event.needsInput) await run.say("Focus on tech");
}

Lake SQL

SQL somente leitura sobre o lake de dados de mercado do NexusTrade, contra o catálogo lake.* resolvido pelo servidor. Os resultados são partes Parquet duráveis, em vez de um array em memória implicitamente materializado.

flowchart LR
    A[createLakeQuery] --> B[waitForLakeQuery]
    B --> C[getLakeQueryManifest]
    C --> D[downloadLakeQueryPart]
    D --> E[Stream Parquet within your memory budget]
const query = await client.createLakeQuery(
  {
    query:
      "SELECT ticker, date, closingPrice FROM lake.daily_ohlc WHERE ticker = ?",
    params: ["AAPL"],
    limits: { maxRows: 10_000 },
  },
  { idempotencyKey: "aapl-daily-v1" }
);
const finished = await client.waitForLakeQuery(query.id as string);
const manifest = await client.getLakeQueryManifest(finished.id as string);

Linguagem natural

Descreva a tela em vez de escrever o SQL. O servidor o gera, valida-o contra o mesmo catálogo lake.* que o mecanismo lê, executa-o e devolve tanto as linhas quanto a instrução.

const screen = await client.createNlScreen(
  "technology stocks with a market cap over 100 billion and a PE under 30"
);
const done = await client.waitForNlScreen(screen.id as string);

const result = done.result as Record<string, unknown>;
console.log(result.rows);
console.log(result.sql); // always check the SQL — it is model-generated

returnQuery usa como padrão true porque o SQL é a trilha de auditoria: sem ele as linhas são um número que você não consegue rederivar. Ele é retornado em caso de falha, não importa o que você passe, já que uma consulta rejeitada é a coisa mais útil de se ler.

Use result.outcome como base, não apenas o status:

outcomeSignificado
ROWSCorrespondências encontradas
EMPTYTodos os filtros rodaram e nenhum passou — uma resposta
CLARIFICATIONA pergunta era ambígua; result.clarification pergunta
GENERATION_FAILEDO orçamento de novas tentativas foi esgotado — o único caso que vale tentar novamente

Este método gasta créditos de LLM. A API estruturada lake abaixo não gasta.

Use o manifesto mais o downloadLakeQueryPart para transmitir resultados dentro do seu próprio orçamento de memória. O NexusTrade escolhe um mecanismo de armazenamento compatível para as tabelas referenciadas; seu SQL não muda quando isso acontece.

O SDK Python também inclui nt.lake.sql(...), uma camada de conveniência DuckDB/pandas sobre esses mesmos endpoints.

Referência completa de métodos

Todo método público de NexusTradeClient. Um teste neste pacote falha se um estiver faltando aqui, então esta lista não pode divergir do código.

Negociação e ordens em tempo real

MétodoFinalidade
listBrokerages()Toda corretora conectável e se está vinculada
getBrokerage(brokerage)Se uma corretora está vinculada
connectBrokerage(brokerage, { wait })Registra a URL de conexão e aguarda o vínculo
createOrders(portfolioId, orders, { idempotencyKey })Prepara ordens; as reais precisam de aprovação

Carteiras

MétodoFinalidade
createPortfolio(book, { idempotencyKey })Persiste uma definição de carteira
listPortfolios(options)Lista carteiras, com filtros e paginação
getPortfolio(portfolioId)Lê uma carteira
deploy(portfolioId, { frequency })Inicia negociação em papel com ela
undeploy(portfolioId)Interrompe

Backtests

MétodoFinalidade
createBacktest(handle, { idempotencyKey })Envia um backtest
createBacktests(handles, { idempotencyKey })Envia vários em uma única solicitação
getBacktest(backtestId)Lê a operação
waitForBacktest(backtestId, options)Bloqueia até o estado terminal
waitForBacktests(operations, options)Bloqueia em um lote inteiro

Otimização e walk-forward

MétodoFinalidade
createOptimization(handle, { idempotencyKey })Envia uma otimização
getOptimization(optimizationId)Lê a operação
waitForOptimization(optimizationId, options)Bloqueia até o estado terminal
createWalkForward(handle, { idempotencyKey })Envia um estudo walk-forward
getWalkForward(studyId)Lê a operação
waitForWalkForward(studyId, options)Bloqueia até o estado terminal

Fontes de dados personalizadas

MétodoFinalidade
createCustomIndicator(spec, { idempotencyKey })Cria uma série, opcionalmente com semente
listCustomIndicators(options)Lista séries próprias
getCustomIndicator(id)Lê uma, com sua contagem de pontos e intervalo
appendCustomIndicatorPoints(id, points, { idempotencyKey })Adiciona pontos
replaceCustomIndicatorPoints(id, points, { idempotencyKey, allowShrink })Substitui a série completa preservando o id
archiveCustomIndicator(id, { confirm })Arquivamento leve de uma série
restoreCustomIndicator(id)Restaura uma série arquivada
createCustomIndicatorUpload(id, options)Abre um slot de upload (CSV/JSON/JSONL)
completeCustomIndicatorUpload(id, jobId)Inicia a validação dos bytes enviados
getCustomIndicatorUpload(id, jobId)Lê a operação de upload
waitForCustomIndicatorUpload(id, jobId, options)Bloqueia até a validação

Execuções de agente

MétodoFinalidade
createAgent(prompt, { idempotencyKey })Inicia uma execução
getAgent(agentId)Lê o status
attachAgent(agentId, { cursor })Reconecta a uma execução já em andamento

Lake SQL

MétodoFinalidade
createLakeQuery(request, { idempotencyKey })Envia SQL somente leitura
getLakeQuery(queryId)Lê a operação
waitForLakeQuery(queryId, options)Bloqueia até o estado terminal
cancelLakeQuery(queryId)Cancela uma consulta própria
createLakeAsk(question)Consulta o lake em linguagem natural
getLakeAsk(askId)Lê a operação
waitForLakeAsk(askId, options)Bloqueia até o estado terminal
cancelLakeAsk(askId)Cancela uma pergunta própria
getLakeQueryManifest(queryId)Schema, somas de verificação e metadados de partições
downloadLakeQueryPart(queryId, part, options)Baixa uma partição Parquet
getLakeCatalog()Lista tabelas consultáveis
describeLakeTable(table)Colunas e tipos de uma tabela

Linguagem natural

MétodoFinalidade
createNlScreen(question, { returnQuery })Filtra ações a partir de uma pergunta em linguagem natural
getNlScreen(screenId)Lê a operação
waitForNlScreen(screenId, options)Bloqueia até o estado terminal
cancelNlScreen(screenId)Cancela um filtro próprio

Construção do cliente

MétodoFinalidade
new NexusTradeClient({ apiKey, baseUrl })Credenciais explícitas
NexusTradeClient.fromEnvironment()Lê-as do ambiente ou do .env

PortfolioHandle — retornado pelo construtor portfolio(...) e por getPortfolio / listPortfolios.

MétodoFinalidade
save({ idempotencyKey })Persiste como rascunho, definindo .id
backtest({ startDate, endDate, idempotencyKey })Executa backtest, preferindo o id salvo
deploy({ frequency })Gera a carteira de papel real (novo id)
undeploy()Desativa a implantação

Autenticação

Crie uma chave em nexustrade.io/developers (Perfil → Chaves de API). As chaves começam com sk- e são exibidas uma única vez.

const client = new NexusTradeClient({
  apiKey: "sk-...",
  baseUrl: "https://nexustrade.io/api/v1",
});
// or set NEXUSTRADE_API_KEY / NEXUSTRADE_API_BASE_URL and:
const fromEnv = new NexusTradeClient();

Ambas as variáveis também são lidas de um arquivo .env no diretório atual ou acima dele, então um projeto local funciona sem exports, sem dependência de dotenv e sem flag --env-file:

# .env
NEXUSTRADE_API_KEY=sk-...
NEXUSTRADE_API_BASE_URL=https://nexustrade.io/api/v1

O ambiente real sempre tem prioridade — um valor de .env é usado somente quando a variável está ausente, então um arquivo desatualizado nunca pode sobrescrever o que você exportou. Nada é gravado de volta em process.env. Desative com NEXUSTRADE_DISABLE_DOTENV=1.

EscopoConcede
readgetBacktest, getOptimization, getWalkForward
writecreatePortfolio, createBacktest(s), createOptimization, createWalkForward
lakeCatálogo do lake, ciclo de vida de consultas, manifestos, partes de resultado

Uma chave sem o escopo recebe 403 insufficient_scope.

OAuth não é aceito aqui. O fluxo OAuth do NexusTrade atende ao servidor MCP. Esses endpoints aceitam apenas chaves de API sk-; um JWT bearer é rejeitado com 401 invalid_token.

Reforço de transporte. HTTPS é obrigatório (exceto loopback). O cliente recusa redirecionamentos entre origens, então a credencial não pode ser reproduzida em outro host, e recusa-se a seguir um redirecionamento em qualquer solicitação não GET, então um redirecionamento nunca pode reenviar um trabalho pago. A chave é mantida em um campo #private e nunca aparece em um cliente serializado em string.

Idempotência

Toda mutação recebe uma chave. Reutilizar a mesma chave com a mesma solicitação retorna o recurso original em vez de iniciar um segundo trabalho pago — então uma nova tentativa após uma falha de rede é gratuita.

await client.createBacktest(handle, { idempotencyKey: "momentum-2024-v1" });

Erros

import { NexusTradeApiError } from "nexustrade";

try {
  await client.createBacktest(handle, { idempotencyKey: "run-1" });
} catch (error) {
  if (
    error instanceof NexusTradeApiError &&
    error.code === "rate_limit_exceeded"
  ) {
    // back off
  }
  throw error;
}
StatusCódigoSignificado
401invalid_tokenChave ausente, malformada ou expirada (ou um JWT OAuth)
403insufficient_scopeChave sem read, write ou lake
400invalid_request, invalid_portfolioEntrada malformada
400invalid_idempotency_keyDeve corresponder a [A-Za-z0-9._:-]{1,160}
409idempotency_conflictChave reutilizada com um payload diferente
409idempotency_in_progressMesma chave, primeira chamada ainda em execução. Reconsulte, não reenvie
404not_found, operation_not_foundDesconhecido ou não é seu
429rate_limit_exceededAguarde e tente novamente

status é 0 quando nenhum status HTTP descreve a falha: transport_error (nunca alcançou a API), unsafe_redirect ou uma verificação de envelope invalid_response em uma resposta que, fora isso, foi bem-sucedida.

Timeouts

new HttpTransport({ timeoutSeconds }) (padrão 30) é um prazo total de relógio de parede para uma única solicitação. Nem ele nem o timeout de polling limitam quanto tempo um trabalho leva.

Escopo

Elaboração de portfólio, backtesting, otimização, estudos walk-forward e SQL somente leitura sobre o data lake de dados de mercado, versionados sob /api/v1/nexustrade. O screener e a criação de uma implantação ao vivo permanecem fora dessa superfície. Pedidos são acessíveis, mas um pedido ao vivo é apenas preparado para aprovação humana — nunca submetido. deploy e undeploy atuam sobre o que um id existente já é, incluindo ao vivo.

Requisitos

Node 18+ (usa o fetch global). Contribuindo: a suíte de testes executa TypeScript diretamente via node --test, que precisa de Node 22.6+ para remoção de tipos. O dist/ publicado é JavaScript puro e não tem tal requisito.

Usando este SDK com um agente de codificação

Veja AGENTS.md — as convenções, invariantes e receitas que um agente precisa para escrever estratégias NexusTrade corretas na primeira tentativa.

Licença

MIT