NexusTrade Financial MCP
Investigación cuantitativa, backtesting, suscripciones de marketplace de creadores, bifurcaciones de estrategias editables, copy trading continuo y flujos de trabajo de corretaje controlados a través de más de 120 herramientas MCP.
Documentación
NexusTrade TypeScript SDK
Escribe estrategias de trading en TypeScript tipado. Pruébalas con el motor que las ejecuta en vivo.
Inicio rápido · Autoría · Sondeo · Agentes · Lake SQL · Autenticación · Errores
npm install nexustrade
Cero dependencias en tiempo de ejecución. Las compilaciones ESM y CommonJS se distribuyen juntas, con tipos.
Servidor MCP
NexusTrade también expone la plataforma como un servidor de Model Context Protocol remoto y alojado. Los clientes MCP modernos se conectan directamente al endpoint Streamable HTTP de producción y descubren NexusTrade OAuth automáticamente:
claude mcp add --transport http nexustrade https://nexustrade.io/api/mcp
Cursor y otros clientes con capacidad remota usan:
{
"mcpServers": {
"nexustrade": {
"url": "https://nexustrade.io/api/mcp"
}
}
}
Para Claude Desktop y otros clientes solo stdio, usa el puente mcp-remote ya establecido: no se requiere clonar ni ejecutar un servidor NexusTrade local:
{
"mcpServers": {
"nexustrade": {
"command": "npx",
"args": [
"-y",
"mcp-remote@latest",
"https://nexustrade.io/api/mcp",
"--transport",
"http-only"
]
}
}
}
El servidor en vivo expone más de 120 herramientas en investigación de mercado, construcción de carteras, backtesting, optimización, validación walk-forward, cómputo gestionado, agentes Aurora, trading en papel y operaciones de corretaje controladas. Sus herramientas de marketplace de creadores cubren toda la ruta de adopción de estrategias:
search_creatorsdescubre creadores públicos y sus carteras de marketplace.subscribe_portfoliovalida un listado monetizado y devuelve una vista previa segura de pago; el usuario completa el pago en NexusTrade, nunca a través de la herramienta MCP.fork_shared_portfoliocrea una copia editable de una sola vez de una estrategia de marketplace en una cartera nueva o existente.copy_trade_sharedreplica continuamente una estrategia accesible en una cartera de papel o en vivo con una asignación explícita.
Consulta la guía para desarrolladores, la referencia de herramientas de utilidad y la referencia de herramientas Aurora.
La investigación y los resultados históricos no son consejos de inversión y no garantizan rendimientos futuros. Mantén explícitos los modos de papel y en vivo. Las herramientas que pueden afectar carteras, horarios u órdenes de corretaje siguen sujetas a los permisos y controles de aprobación de la cuenta autenticada de NexusTrade.
Inicio 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);
Las operaciones de backtesting pueden incluir warnings: string[] inmediatamente después del envío y nuevamente en el result final. Trátalas como advertencias materiales; no convierten una operación exitosa en un fallo.
Autoría de estrategias
Cada constructor se genera a partir de la misma especificación de indicadores que ejecuta el motor de NexusTrade, por lo que un libro es válido por construcción, no por convención.
TypeScript no puede sobrecargar operadores de comparación, por lo que los indicadores se componen mediante gt / gte / lt / lte / eq / neq y 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 }
);
Qué puedes construir — 170+ constructores generados
| Grupo | Ejemplos |
|---|---|
| Precio y volumen | Price OpeningPrice HighOfDay VWAP Volume GapPercentage |
| Técnicos | SMA EMA RSI BollingerBand AverageTrueRange CrossAbove |
| Estado de posición | PositionValue PositionPercentChange PositionMaxDrawdown |
| Estado de cartera | PortfolioValue BuyingPower MaxDrawdown InitialValue |
| Fundamentales | Fundamental Economic DaysUntilEarnings IsIndexMember IsIndustry |
| Opciones | OptionDaysToExpiration OptionCollateral OptionUnrealizedPnL openOption closeOption |
| Acciones | buy sell alert dynamicRebalance rebalanceOption |
| Selección | filter selectTop selectPercentile universe |
| Lógica | always atLeast atMost exactly fewerThan multi and or |
Cada constructor está completamente tipado: tu editor completa toda la superficie.
Los trabajos se ejecutan en el motor: tú haces sondeo
create* pone el trabajo en cola y regresa inmediatamente. No se resuelve cuando existen resultados. Hoy no hay webhooks.
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.
Cada tipo de trabajo informa el mismo formato de respuesta, por lo que un solo sondeo sirve para 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);
| Opción | Predeterminado | Significado |
|---|---|---|
timeoutSeconds | 900 | Deja de esperar (el trabajo continúa ejecutándose) |
pollIntervalSeconds | 2 | Primer intervalo; retroceso 1.5× |
maxPollIntervalSeconds | 15 | Límite de intervalo |
throwOnFailure | true | Lanzar en failed/cancelled en lugar de devolver el resultado |
Un tiempo de espera agotado lanza operation_timeout y no cancela el trabajo: vuelve a llamar al sondeo con el mismo id en lugar de reenviarlo.
Lotes. createBacktests envía muchos en una sola solicitud y devuelve una operación para cada uno; waitForBacktests(operations) espera a todos. Prefiérelo sobre un bucle: una solicitud, una clave de idempotencia, un único cupo de límite de tasa.
Optimización y walk-forward siguen la misma 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);
Despliegue de una cartera
Redactar y probar un libro no lo persiste. save lo escribe en tu cuenta; deploy comienza a ejecutarlo.
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 y deploy generan ids distintos, y la distinción importa.
save persiste un borrador y establece book.id a él. deploy crea la cartera de papel real y devuelve su propio portfolioId: el despliegue crea una cartera en lugar de convertir el borrador en una, por lo que los dos ids coexisten. Conserva deployment.portfolioId para cualquier cosa que lea el estado en vivo; book.id se refiere al borrador.
deployment.portfolioId; // the running portfolio
deployment.deploymentType; // paper, unless you deployed an existing live one
deployment.outcome; // created | reactivated
Los métodos de manejo aceptan un transport opcional; si se omite, resuelven uno del entorno. Las mismas operaciones existen en el cliente — client.deploy(id), client.undeploy(id) — cuando tienes un id en lugar de un manejador.
await client.listPortfolios({ includePaper: true, includePositions: true });
await client.getPortfolio(portfolioId);
listPortfolios filtra con includePaper, includeLive, includeInactive,
includeChatPortfolios, search, limit y page. includePositions
se desactiva por defecto cuando search está establecido.
Una cartera que creas aquí siempre es de papel, y la creación de una en vivo aún ocurre en la aplicación web. Los órdenes y el estado del corretaje son accesibles desde aquí; consulta Trading en vivo.
Pero deploy puede iniciar trading en vivo. Dado el id de una cartera ya desplegada, reactiva esa cartera como lo que ya es, así que client.deploy(id) en una cartera en pausa reanuda el trading en vivo contra el corretaje conectado, y includeLive: true anterior te dará ese id. Verifica deployment.deploymentType antes de tratar un despliegue como simulado.
Trading en vivo
El trading en vivo requiere un corretaje vinculado a tu cuenta. Vincular es una redirección OAuth, por lo que una clave API no puede completarla: un humano abre la 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 defecto solo cuando stdout es una TTY. En CI, cron o run_compute rechaza con brokerage_not_connected inmediatamente, con la URL en el mensaje, en lugar de detenerse por cinco minutos frente a nadie. Pasa { wait: true } o { wait: false } para forzar cualquiera de los dos.
Un listado solo en vivo que devuelve vacío rechaza con el mismo error en lugar de un arreglo vacío, ya que un arreglo vacío no dice nada sobre por qué:
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
Órdenes
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" }
);
Las órdenes de papel se aceptan inmediatamente. Las órdenes en vivo se ponen en escena para aprobación y nunca se envían a un corredor mediante esta llamada.
if (result.requiresApproval) {
console.log("nothing has traded yet — approve at", result.approvalUrl);
}
No hay argumento, alcance ni bandera que envíe una orden en vivo sin aprobación. El límite de corretaje rechaza una orden en vivo no aprobada sin importar lo que pida cualquier llamada, así que esta es una propiedad del sistema, no una promesa hecha por este método. Máximo 50 órdenes por solicitud.
Tus propios datos
Una fuente de datos personalizada es una serie temporal que posees: conteos de sentimiento, un factor propietario, cualquier cosa que la plataforma no tenga ya. Créala y luego refiérela desde una estrategia con 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 es "global" (una serie) o "asset" (una serie por ticker, así que cada punto necesita un ticker). No se puede cambiar después de la creación.
Declara pointKind siempre que se conozca la semántica temporal: observation para muestras puntuales, period_aggregate más aggregatePeriod (1d, 1w, 1mo o 1q) para valores de período cerrado, y disclosed para valores con un tiempo de publicación explícito en cada fila. El SDK aplica este contrato antes de las escrituras tanto en línea como de carga masiva. Una observación solo con fecha del mismo día se convierte en un instante UTC explícito del mismo día en lugar de desplazarse al siguiente día calendario.
El tamaño no es una restricción. points es ilimitado. Un lote que cabe en la solicitud va con ella; uno más grande se sube al almacenamiento y se valida antes de que la llamada se resuelva. En cualquier caso, el indicador devuelto refleja lo que realmente llegó, y una carga que falla la validación rechaza en lugar de reportar éxito.
Creciendo una serie. Agrega al mismo id en cada ejecución:
await client.appendCustomIndicatorPoints(
String(series.customIndicatorId),
[{ timestamp: "2024-04-03", value: 118, ticker: "NVDA" }],
{ idempotencyKey: "wsb-mentions-2024-04-03" }
);
Crear una serie nueva por ejecución divide el historial en fragmentos que ninguna estrategia puede leer. Reenviar un lote idéntico es seguro: el duplicado no se escribe dos veces.
| Llamada | Propósito |
|---|---|
createCustomIndicator(spec, { idempotencyKey }) | Crear, opcionalmente inicializado |
appendCustomIndicatorPoints(id, points, { idempotencyKey }) | Agregar puntos |
replaceCustomIndicatorPoints(id, points, { idempotencyKey }) | Reemplazar puntos, conservar id |
archiveCustomIndicator(id) / restoreCustomIndicator(id) | Ciclo de vida reversible |
listCustomIndicators() / getCustomIndicator(id) | Descubrir ids y cobertura |
Los puntos aceptan timestamp, value, ticker, assetType y availableAt — camelCase o snake_case, con objetos Date permitidos. Establece availableAt cuando un valor se hizo conocible más tarde de su fecha: una cifra de ganancias con sello de fin de trimestre pero publicada semanas después. Un campo no reconocido lanza una excepción en lugar de descartarse silenciosamente.
Para entregar un archivo que ya tienes en disco, createCustomIndicatorUpload / completeCustomIndicatorUpload / waitForCustomIndicatorUpload exponen los tres pasos directamente. CSV, JSON y JSONL hasta 100 MB.
Ejecuciones de agente
Todos los demás trabajos son de disparar y sondear. Los agentes no — tres estados (pending_plan_approval, pending_action_approval, awaiting_user_input) no pueden avanzar sin ti. Itera la ejecución y responde cuando se bloquee:
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 de solo lectura sobre el lake de datos de mercado de NexusTrade, contra el catálogo lake.* resuelto por el servidor. Los resultados son partes Durables de Parquet en lugar de un arreglo en memoria implícitamente 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);
Lenguaje natural
Describe el filtro en lugar de escribir el SQL. El servidor lo genera, lo valida contra el mismo catálogo lake.* que lee el motor, lo ejecuta y devuelve tanto las filas como la sentencia.
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 tiene como valor predeterminado true porque el SQL es la pista de auditoría: sin él, las filas son un número que no se puede volver a derivar. Se devuelve en caso de error pase lo que pase, ya que una consulta rechazada es lo más útil de leer.
Ramifica según result.outcome, no solo según el estado:
outcome | Significado |
|---|---|
ROWS | Coincidencias encontradas |
EMPTY | Todos los filtros se ejecutaron y ninguno los descartó todos: una respuesta |
CLARIFICATION | La pregunta era ambigua; result.clarification pregunta |
GENERATION_FAILED | Se agotó el presupuesto de reintentos: el único caso que vale la pena reintentar |
Este método gasta créditos de LLM. La API estructurada lake a continuación no lo hace.
Usa el manifiesto más downloadLakeQueryPart para transmitir resultados dentro de tu propio presupuesto de memoria. NexusTrade elige un motor de respaldo compatible para las tablas referenciadas; tu SQL no cambia cuando lo hace.
El SDK de Python además incluye
nt.lake.sql(...), una capa de conveniencia DuckDB/pandas sobre estos mismos endpoints.
Referencia completa de métodos
Cada método público en NexusTradeClient. Una prueba en este paquete falla si falta uno aquí, por lo que esta lista no puede desviarse del código.
Trading en vivo y órdenes
| Método | Propósito |
|---|---|
listBrokerages() | Cada bróker conectable y si está vinculado |
getBrokerage(brokerage) | Si un bróker está vinculado |
connectBrokerage(brokerage, { wait }) | Registrar la URL de conexión y esperar el vínculo |
createOrders(portfolioId, orders, { idempotencyKey }) | Preparar órdenes; las reales necesitan aprobación |
Carteras
| Método | Propósito |
|---|---|
createPortfolio(book, { idempotencyKey }) | Persistir una definición de cartera |
listPortfolios(options) | Listar carteras, con filtros y paginación |
getPortfolio(portfolioId) | Leer una cartera |
deploy(portfolioId, { frequency }) | Comenzar trading en papel con ella |
undeploy(portfolioId) | Detenerla |
Backtests
| Método | Propósito |
|---|---|
createBacktest(handle, { idempotencyKey }) | Enviar un backtest |
createBacktests(handles, { idempotencyKey }) | Enviar muchos en una sola solicitud |
getBacktest(backtestId) | Leer la operación |
waitForBacktest(backtestId, options) | Bloquear hasta el estado terminal |
waitForBacktests(operations, options) | Bloquear en un lote completo |
Optimización y walk-forward
| Método | Propósito |
|---|---|
createOptimization(handle, { idempotencyKey }) | Enviar una optimización |
getOptimization(optimizationId) | Leer la operación |
waitForOptimization(optimizationId, options) | Bloquear hasta el estado terminal |
createWalkForward(handle, { idempotencyKey }) | Enviar un estudio walk-forward |
getWalkForward(studyId) | Leer la operación |
waitForWalkForward(studyId, options) | Bloquear hasta el estado terminal |
Fuentes de datos personalizadas
| Método | Propósito |
|---|---|
createCustomIndicator(spec, { idempotencyKey }) | Crear una serie, opcionalmente inicializada |
listCustomIndicators(options) | Listar series propias |
getCustomIndicator(id) | Leer una, con su recuento de puntos y rango |
appendCustomIndicatorPoints(id, points, { idempotencyKey }) | Añadir puntos |
replaceCustomIndicatorPoints(id, points, { idempotencyKey, allowShrink }) | Reemplazar la serie completa conservando su id |
archiveCustomIndicator(id, { confirm }) | Archivar suavemente una serie |
restoreCustomIndicator(id) | Restaurar una serie archivada |
createCustomIndicatorUpload(id, options) | Abrir un espacio de carga (CSV/JSON/JSONL) |
completeCustomIndicatorUpload(id, jobId) | Comenzar a validar los bytes cargados |
getCustomIndicatorUpload(id, jobId) | Leer la operación de carga |
waitForCustomIndicatorUpload(id, jobId, options) | Bloquear hasta que se valide |
Ejecuciones de agentes
| Método | Propósito |
|---|---|
createAgent(prompt, { idempotencyKey }) | Iniciar una ejecución |
getAgent(agentId) | Leer su estado |
attachAgent(agentId, { cursor }) | Volver a conectarse a una ejecución ya en curso |
Lake SQL
| Método | Propósito |
|---|---|
createLakeQuery(request, { idempotencyKey }) | Enviar SQL de solo lectura |
getLakeQuery(queryId) | Leer la operación |
waitForLakeQuery(queryId, options) | Bloquear hasta el estado terminal |
cancelLakeQuery(queryId) | Cancelar una consulta propia |
createLakeAsk(question) | Preguntar al lake en lenguaje natural |
getLakeAsk(askId) | Leer la operación |
waitForLakeAsk(askId, options) | Bloquear hasta el estado terminal |
cancelLakeAsk(askId) | Cancelar una pregunta propia |
getLakeQueryManifest(queryId) | Esquema, sumas de verificación y metadatos de particiones |
downloadLakeQueryPart(queryId, part, options) | Descargar una partición Parquet |
getLakeCatalog() | Listar tablas consultables |
describeLakeTable(table) | Columnas y tipos para una tabla |
Lenguaje natural
| Método | Propósito |
|---|---|
createNlScreen(question, { returnQuery }) | Filtrar acciones a partir de una pregunta en lenguaje natural |
getNlScreen(screenId) | Leer la operación |
waitForNlScreen(screenId, options) | Bloquear hasta el estado terminal |
cancelNlScreen(screenId) | Cancelar un filtro propio |
Construcción del cliente
| Método | Propósito |
|---|---|
new NexusTradeClient({ apiKey, baseUrl }) | Credenciales explícitas |
NexusTradeClient.fromEnvironment() | Leerlas del entorno o de .env |
PortfolioHandle — devuelto por el constructor portfolio(...) y por getPortfolio / listPortfolios.
| Método | Propósito |
|---|---|
save({ idempotencyKey }) | Persistirlo como borrador, estableciendo .id |
backtest({ startDate, endDate, idempotencyKey }) | Hacer backtest, prefiriendo el id guardado |
deploy({ frequency }) | Crear la cartera de papel real (nuevo id) |
undeploy() | Desactivar su despliegue |
Autenticación
Crea una clave en nexustrade.io/developers (Perfil → Claves de API). Las claves comienzan con sk- y se muestran una sola 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 variables también se leen de un archivo .env en el directorio actual o en uno superior, por lo que un proyecto local funciona sin exports, sin dependencia de dotenv y sin la bandera --env-file:
# .env
NEXUSTRADE_API_KEY=sk-...
NEXUSTRADE_API_BASE_URL=https://nexustrade.io/api/v1
El entorno real siempre gana: un valor de .env se usa solo cuando la variable está ausente, por lo que un archivo obsoleto nunca puede anular lo que exportaste. No se escribe nada de vuelta en process.env. Exclúyete con NEXUSTRADE_DISABLE_DOTENV=1.
| Alcance | Otorga |
|---|---|
read | getBacktest, getOptimization, getWalkForward |
write | createPortfolio, createBacktest(s), createOptimization, createWalkForward |
lake | Catálogo del lake, ciclo de vida de consultas, manifiestos, partes de resultados |
Una clave a la que le falta el alcance recibe 403 insufficient_scope.
OAuth no se acepta aquí. El flujo OAuth de NexusTrade sirve al servidor MCP. Estos endpoints solo aceptan claves API
sk-; un JWT de tipo bearer se rechaza con401 invalid_token.
Endurecimiento del transporte. HTTPS es obligatorio (excepto loopback). El cliente rechaza redirecciones de origen cruzado, por lo que la credencial no puede reproducirse en otro host, y se niega a seguir una redirección en cualquier solicitud que no sea GET, por lo que una redirección nunca puede reenviar un trabajo de pago. La clave se guarda en un campo #private y nunca aparece en un cliente serializado.
Idempotencia
Cada mutación requiere una clave. Reutilizar la misma clave con la misma solicitud devuelve el recurso original en lugar de lanzar un segundo trabajo de pago, por lo que un reintento después de una falla de red es gratuito.
await client.createBacktest(handle, { idempotencyKey: "momentum-2024-v1" });
Errores
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;
}
| Estado | Código | Significado |
|---|---|---|
| 401 | invalid_token | Clave faltante, malformada o caducada (o un JWT de OAuth) |
| 403 | insufficient_scope | A la clave le falta read, write o lake |
| 400 | invalid_request, invalid_portfolio | Entrada malformada |
| 400 | invalid_idempotency_key | Debe coincidir con [A-Za-z0-9._:-]{1,160} |
| 409 | idempotency_conflict | Clave reutilizada con un payload diferente |
| 409 | idempotency_in_progress | Misma clave, la primera llamada sigue en ejecución. Vuelve a consultar, no reenvíes |
| 404 | not_found, operation_not_found | Desconocido o no es tuyo |
| 429 | rate_limit_exceeded | Retrocede y reintenta |
status es 0 cuando ningún estado HTTP describe la falla: transport_error (nunca se llegó a la API), unsafe_redirect o una verificación de envoltura invalid_response en una respuesta que por lo demás fue exitosa.
Tiempos de espera
new HttpTransport({ timeoutSeconds }) (predeterminado 30) es un plazo total de tiempo real para una solicitud. Ni este ni el tiempo de espera de sondeo limitan cuánto tarda un trabajo.
Alcance
Elaboración de carteras, backtesting, optimización, estudios walk-forward y
SQL de solo lectura sobre el lago de datos de mercado, versionado bajo /api/v1/nexustrade.
El screener y la creación de un despliegue en vivo quedan fuera de esta superficie.
Las órdenes son accesibles, pero una orden en vivo solo se prepara para aprobación humana —
nunca se envía. deploy y undeploy actúan sobre cualquier id existente,
incluido el en vivo.
Requisitos
Node 18+ (usa el global fetch). Contribución: el conjunto de pruebas ejecuta TypeScript
directamente mediante node --test, que necesita Node 22.6+ para la eliminación de tipos. El
dist/ publicado es JavaScript simple y no tiene tal requisito.
Uso de este SDK con un agente de codificación
Consulta AGENTS.md — las convenciones, invariantes y recetas que un agente necesita para escribir estrategias correctas de NexusTrade en el primer intento.
Licencia
MIT