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 TypeScript SDK
Crie estratégias de trading em TypeScript tipado. Execute backtests no mesmo motor que as executa ao vivo.
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_creatorsdescobre criadores públicos e seus portfólios de marketplace.subscribe_portfoliovalida 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_portfoliocria uma cópia editável única de uma estratégia de marketplace em um portfólio novo ou existente.copy_trade_sharedespelha 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
| Grupo | Exemplos |
|---|---|
| Preço e volume | Price OpeningPrice HighOfDay VWAP Volume GapPercentage |
| Técnicos | SMA EMA RSI BollingerBand AverageTrueRange CrossAbove |
| Estado da posição | PositionValue PositionPercentChange PositionMaxDrawdown |
| Estado do portfólio | PortfolioValue BuyingPower MaxDrawdown InitialValue |
| Fundamentos | Fundamental Economic DaysUntilEarnings IsIndexMember IsIndustry |
| Opções | OptionDaysToExpiration OptionCollateral OptionUnrealizedPnL openOption closeOption |
| Ações | buy sell alert dynamicRebalance rebalanceOption |
| Seleção | filter selectTop selectPercentile universe |
| Lógica | always 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ção | Padrão | Significado |
|---|---|---|
timeoutSeconds | 900 | Desistir de esperar (o job continua rodando) |
pollIntervalSeconds | 2 | Primeiro intervalo; faz backoff de 1,5× |
maxPollIntervalSeconds | 15 | Teto do intervalo |
throwOnFailure | true | Lanç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.
| Chamada | Finalidade |
|---|---|
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:
outcome | Significado |
|---|---|
ROWS | Correspondências encontradas |
EMPTY | Todos os filtros rodaram e nenhum passou — uma resposta |
CLARIFICATION | A pergunta era ambígua; result.clarification pergunta |
GENERATION_FAILED | O 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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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étodo | Finalidade |
|---|---|
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.
| Escopo | Concede |
|---|---|
read | getBacktest, getOptimization, getWalkForward |
write | createPortfolio, createBacktest(s), createOptimization, createWalkForward |
lake | Catá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 com401 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;
}
| Status | Código | Significado |
|---|---|---|
| 401 | invalid_token | Chave ausente, malformada ou expirada (ou um JWT OAuth) |
| 403 | insufficient_scope | Chave sem read, write ou lake |
| 400 | invalid_request, invalid_portfolio | Entrada malformada |
| 400 | invalid_idempotency_key | Deve corresponder a [A-Za-z0-9._:-]{1,160} |
| 409 | idempotency_conflict | Chave reutilizada com um payload diferente |
| 409 | idempotency_in_progress | Mesma chave, primeira chamada ainda em execução. Reconsulte, não reenvie |
| 404 | not_found, operation_not_found | Desconhecido ou não é seu |
| 429 | rate_limit_exceeded | Aguarde 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