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

NexusTrade TypeScript SDK

Autoriza estrategias de trading en TypeScript tipado. Haz backtesting con el motor que las ejecuta en vivo.

npm Node License Deps

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 establecido: no se requiere clonar ni 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, computación gestionada, agentes Aurora, paper trading y operaciones de corretaje controladas. Sus herramientas de marketplace de creadores cubren la ruta completa de adopción de estrategias:

  • search_creators descubre creadores públicos y sus carteras de marketplace.
  • subscribe_portfolio valida 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_portfolio crea una copia editable única de una estrategia de marketplace en una cartera nueva o existente.
  • copy_trade_shared replica continuamente una estrategia accesible en una cartera paper 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 de Aurora.

Los resultados de investigación e históricos no son asesoramiento de inversión y no garantizan rendimiento futuro. Mantén explícitos los modos paper 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 NexusTrade de la cuenta autenticada.

Inicio rápido

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

const client = new NexusTradeClient();

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 terminal. Trátalos como advertencias materiales; no convierten una operación exitosa en un fallo.

Colateral en riesgo

El result.statistics de una operación terminal responde cuánto capital tenía la ejecución en juego, no solo lo que devolvió. Dos campos lo transportan, tipados como BacktestCollateralStatistics:

CampoSignificado
peakReservedCollateralMayor colateral bloqueado en cualquier tick, en la moneda de la cuenta
medianReservedCollateralMediana entre los ticks que mantuvieron al menos una posición

Ambos son opcionales y pueden ser null, y esa ausencia es una respuesta real: una ejecución de backtesting anterior a que el motor reportara colateral no tiene valor, lo cual no es lo mismo que un libro que no bloqueó nada. Muestra "no registrado" en lugar de $0 — un cero aquí se lee como "esta estrategia no arriesga nada", lo opuesto de lo que significa un campo sin poblar. Tampoco sustituyas el valor de la cartera: un libro que arriesga unos pocos miles de dólares se reportaría como si arriesgara todo.

Nunca reconstruyas ninguno de los dos números a partir de cash - buyingPower. El poder de compra está limitado en ambos extremos y lleva un término de prima de spread de crédito abierto, por lo que la inversión se rompe precisamente en los libros fuertemente colateralizados que esto mide.

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 en lugar de 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: nt.Value(80),
      })
    ),
  ],
  { initialValue: 100_000 }
);

Las señales de divulgación del Congreso también son indicadores nativos. La Cámara y el Senado se combinan a menos que elijas explícitamente una cámara; las métricas de monto por defecto usan el límite inferior divulgado y las divulgaciones de opciones confirmadas se excluyen por defecto:

const pelosiPurchases = nt.PoliticalTrades(
  nt.CANDIDATE,
  "Nancy Pelosi",
  "BuyAmount",
  90,
  "LowerBound",
  "Equity",
);

Usa "" como presentador para incluir a todos los miembros. BuyCount cuenta eventos de compra canónicos, mientras que BuyAmount mide la magnitud divulgada. Selecciona "Option" solo cuando quieras actividad de divulgación de opciones asociada con el ticker subyacente.

La ejecución de órdenes pertenece a la estrategia. Omítela para el valor por defecto retrocompatible de Market, usa un precio unitario fijo para Buy/Sell, o establece el débito neto máximo / crédito neto mínimo de una estrategia de opciones:

nt.strategy("Buy SPY at my price", nt.always(), nt.buy(nt.stockAsset("SPY"), 10), {
  orderExecution: nt.limitOrder({
    price: nt.unitPriceLimit(500),
    workingTime: nt.goodForDay(),
  }),
});

nt.strategy("Sell the spread for $1.50 or better", nt.always(), optionAction, {
  orderExecution: nt.limitOrder({
    price: nt.minimumNetCredit(1.5),
    workingTime: nt.goodForMinutes(30),
  }),
});

Un límite neto de opciones también puede seguir una posición que la estrategia mantiene. Pasa un indicador en lugar de un número y el motor lo evalúa cuando la estrategia se activa. Si no tiene un valor positivo en ese momento, no se coloca ninguna orden:

nt.strategy("Sell the credit spread for the debit fill plus $0.50", whenDebitSpreadHeld, creditSpreadAction, {
  orderExecution: nt.limitOrder({
    price: nt.minimumNetCredit(
      nt.Plus(nt.OptionSpreadEntryPrice("SPX", "call", "long", "vertical"), nt.Value(0.5)),
    ),
    workingTime: nt.goodForDay(),
  }),
});

currentLimit() crea un Limit relativo a la cotización para estrategias de rebalanceo dinámico. Mantiene la protección de no-peor-que-la-cotización-actual, pero no es un objetivo de precio en reposo. Las estrategias de opciones en vivo deben elegir una política de Limit explícita.

Lo que puedes construir — 170+ constructores generados
GrupoEjemplos
Precio y volumenPrice OpeningPrice HighOfDay MinuteBarHigh MinuteBarLow HeikinAshi VWAP Volume GapPercentage
TécnicosSMA EMA RSI BollingerBand AverageTrueRange CrossAbove
Estado de posiciónPositionValue PositionPercentChange PositionMaxDrawdown
Estado de carteraPortfolioValue BuyingPower MaxDrawdown InitialValue
FundamentalesFundamental Economic DaysUntilEarnings IsIndexMember IsIndustry
OpcionesOptionDaysToExpiration OptionCollateral OptionSpreadEntryPrice OptionUnrealizedPnL openOption closeOption
Accionesbuy sell alert dynamicRebalance rebalanceOption
Selecciónfilter selectTop selectPercentile universe
Lógicaalways atLeast atMost exactly fewerThan multi and or sequence
Niveles ancladosIndicatorAtEntry LastOrderPrice IndicatorAtMinutesAfterOpen IndicatorWindowAgo

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 los resultados existen. 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 reporta el mismo sobre, por lo que un solo sondeador 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ónValor por defectoSignificado
timeoutSeconds900Deja de esperar (el trabajo sigue ejecutándose)
pollIntervalSeconds2Primer intervalo; retrocede 1.5×
maxPollIntervalSeconds15Tope del intervalo
throwOnFailuretrueLanza failed/cancelled en lugar de devolver

Un tiempo de espera agotado lanza operation_timeout y no cancela el trabajo: llama al esperador nuevamente con el mismo id en lugar de reenviar.

Lotes. createBacktests envía muchos en una sola solicitud y devuelve una operación por cada uno; waitForBacktests(operations) espera por todos. Prefiérelo sobre un bucle: una solicitud, una clave de idempotencia, una ranura de límite de tasa.

Optimización y walk-forward siguen la misma forma idéntica:

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);

Desplegando una cartera

Autorizar y hacer backtesting de 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 producen ids diferentes, y la distinción importa. save persiste un borrador y establece book.id a él. deploy acuña la cartera paper real y devuelve su propio portfolioId — desplegar 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 desde el entorno. Las mismas operaciones existen en el cliente — client.deploy(id), client.undeploy(id) — cuando tienes un id en lugar de un manejo.

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

Editando una cartera guardada

updatePortfolio aplica ediciones deterministas sin LLM en el camino. El array operations es una unión tipada, por lo que el compilador sabe qué campos necesita cada edición.

import type { PortfolioEditOperation } from "nexustrade";

const operations: PortfolioEditOperation[] = [
  { type: "rename", name: "AAPL Income" },
  {
    type: "replaceStrategy",
    targetStrategyId: strategyId, // or targetStrategyName
    strategyObject: nt.strategy(
      "Buy AAPL",
      nt.always(),
      nt.buy(nt.stockAsset("AAPL"), 25, "percent of portfolio"),
      {
        orderExecution: nt.limitOrder({
          price: nt.unitPriceLimit(150),
          workingTime: nt.goodForDay(),
        }),
      },
    ),
  },
];

await client.updatePortfolio(portfolioId, operations, {
  idempotencyKey: "aapl-limit-v1",
});

Las cinco ediciones son rename, addStrategies, removeStrategies, replaceStrategy y replaceStrategies. Las operaciones de desplegar, desdesplegar, eliminar, programación y políticas de trading no son accesibles en esta ruta.

replaceStrategies reemplaza el conjunto completo, por lo que una estrategia omitida del array se elimina. Lleva las estrategias sin cambios textualmente, incluido el orderExecution que cada una ya tiene, o un Limit de trabajo se revierte silenciosamente a Market. removeStrategies toma los ids de estrategia de una cartera obtenida; la eliminación por nombre se rechaza.

La elegibilidad de acciones es parte de lo que autorizas. Pasa policy: { stockEligibility } a portfolio(...), o llama setStockEligibility(...) en un manejo, para establecer límites de capitalización de mercado, un filtro de industria, missingMarketCapBehavior o shareClassBehavior. Los campos omitidos toman los valores por defecto. Un libro de pares GOOG/GOOGL necesita ALL_CLASSES, porque el valor por defecto mantiene una clase de acción por empresa:

const pair = portfolio("GOOG/GOOGL pair", strategies, {
  policy: { stockEligibility: { shareClassBehavior: "ALL_CLASSES" } },
});

El trading automatizado nunca se autoriza. Solo el propietario lo habilita, en Configuración de la cartera, y una política que nombre automatedApproval se rechaza antes de enviarse. Los manejos obtenidos incluyen una instantánea tipada y de solo lectura policy; guardar o desplegar una copia de uno lleva su elegibilidad de acciones y nada más.

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í es siempre paper, y acuñar una en vivo todavía ocurre en la aplicación web. Las órdenes y el estado de corretaje son accesibles desde aquí; consulta Trading en vivo.

Pero deploy puede iniciar trading en vivo. Dado el id de una cartera que ya está desplegada, reactiva esa cartera como lo que ya es — por lo que client.deploy(id) en una cartera en vivo pausada reanuda el trading en vivo contra el corretaje conectado, y includeLive: true arriba te dará tal id. Verifica deployment.deploymentType antes de tratar un despliegue como simulado.

Trading en vivo

El trading en vivo necesita un bróker vinculado a tu cuenta. La vinculación es una redirección OAuth, por lo que una clave API no puede completarla: un humano debe abrir 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 cinco minutos frente a nadie. Pasa { wait: true } o { wait: false } para forzar cualquiera de los dos.

Una lista solo de trading 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 el porqué:

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 en papel se aceptan inmediatamente. Las órdenes en vivo se preparan para aprobación y nunca se envían a un bróker mediante esta llamada.

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

No existe argumento, alcance o bandera que envíe una orden en vivo sin aprobación. El límite del bróker rechaza una orden en vivo no aprobada sin importar lo que pida cualquier llamador, por lo que esto es una propiedad del sistema más que 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érete a ella 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, por lo que cada punto necesita un ticker). No se puede cambiar después de la creación.

Declara pointKind siempre que la semántica temporal sea conocida: 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 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 limitación. points es ilimitado. Un lote que cabe en la solicitud se envía con ella; uno más grande se sube al almacenamiento y se valida antes de que la llamada se resuelva. De cualquier manera, 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. Añade 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.

LlamadaPropósito
createCustomIndicator(spec, { idempotencyKey })Crear, opcionalmente sembrada
appendCustomIndicatorPoints(id, points, { idempotencyKey })Añadir 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 volvió conocible más tarde de lo que está fechado: una cifra de ganancias sellada a 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 agentes

Cada otro trabajo es de disparar y consultar. Los agentes no lo son — 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",
  costCeilingUsd: 20,
});
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 lago de datos de mercado de NexusTrade, contra el catálogo lake.* resuelto por el servidor. Los resultados son partes Parquet duraderas en lugar de un arreglo en memoria materializado implícitamente.

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 la pantalla 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 declaración.

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 por defecto es true porque el SQL es el rastro de auditoría: sin él, las filas son un número que no puedes volver a derivar. Se devuelve en caso de fallo pase lo que pases, ya que una consulta rechazada es lo más útil de leer.

Rama en result.outcome, no solo en el estado:

outcomeSignificado
ROWSCoincidencias encontradas
EMPTYCada filtro se ejecutó y nada las superó todas — una respuesta
CLARIFICATIONLa pregunta era ambigua; result.clarification pregunta
GENERATION_FAILEDEl presupuesto de reintentos se agotó — 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étodoPropó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 de vivo necesitan aprobación

Carteras

MétodoPropósito
createPortfolio(book, { idempotencyKey })Persistir una definición de cartera
listPortfolios(options)Listar carteras, con filtros y paginación
getPortfolio(portfolioId)Leer una cartera
updatePortfolio(id, operations, { idempotencyKey })Renombrar o editar estrategias de manera determinista
forkPublicPortfolio(sharedId, { idempotencyKey })Bifurcar una cartera pública al espacio de trabajo
deploy(portfolioId, { frequency })Iniciar trading en papel
undeploy(portfolioId)Detenerlo

Backtests

MétodoPropósito
createBacktest(handle, { idempotencyKey })Enviar un backtest
createBacktests(handles, { idempotencyKey })Enviar muchos en una solicitud
getBacktest(backtestId)Leer la operación
waitForBacktest(backtestId, options)Bloquear hasta terminal
waitForBacktests(operations, options)Bloquear en un lote completo

Optimización y walk-forward

MétodoPropósito
createOptimization(handle, { idempotencyKey })Enviar una optimización
getOptimization(optimizationId)Leer la operación
waitForOptimization(optimizationId, options)Bloquear hasta terminal
createSystematicSweep(handle, { idempotencyKey })Enviar un barrido de genes explícito
getSystematicSweep(optimizationId)Leer la operación de barrido
waitForSystematicSweep(optimizationId, options)Bloquear hasta terminal
createWalkForward(handle, { idempotencyKey })Enviar un estudio walk-forward
getWalkForward(studyId)Leer la operación
waitForWalkForward(studyId, options)Bloquear hasta terminal

Fuentes de datos personalizadas

MétodoPropósito
createCustomIndicator(spec, { idempotencyKey })Crear una serie, opcionalmente sembrada
listCustomIndicators(options)Listar series propias
getCustomIndicator(id)Leer una, con su conteo 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)Iniciar validación de bytes subidos
getCustomIndicatorUpload(id, jobId)Leer la operación de carga
waitForCustomIndicatorUpload(id, jobId, options)Bloquear hasta validado

Ejecuciones de agentes

MétodoPropósito
createAgent(prompt, { idempotencyKey })Iniciar una ejecución
getAgent(agentId)Leer su estado
attachAgent(agentId, { cursor })Reanudar una ejecución ya en vuelo

Lake SQL

MétodoPropósito
createLakeQuery(request, { idempotencyKey })Enviar SQL de solo lectura
getLakeQuery(queryId)Leer la operación
waitForLakeQuery(queryId, options)Bloquear hasta terminal
cancelLakeQuery(queryId)Cancelar una consulta propia
createLakeAsk(question)Preguntar al lago en lenguaje plano
getLakeAsk(askId)Leer la operación
waitForLakeAsk(askId, options)Bloquear hasta terminal
cancelLakeAsk(askId)Cancelar una pregunta propia
getLakeQueryManifest(queryId)Esquema, sumas de verificación y metadatos de partes
downloadLakeQueryPart(queryId, part, options)Descargar una parte Parquet
getLakeCatalog()Listar tablas consultables
describeLakeTable(table)Columnas y tipos para una tabla

Lenguaje natural

MétodoPropósito
createNlScreen(question, { returnQuery })Filtrar acciones desde una pregunta en lenguaje plano
getNlScreen(screenId)Leer la operación
waitForNlScreen(screenId, options)Bloquear hasta terminal
cancelNlScreen(screenId)Cancelar un filtro propio

Construcción de cliente

MétodoPropósito
new NexusTradeClient({ apiKey, baseUrl })Credenciales explícitas
new NexusTradeClient()Espacio de trabajo anónimo perezoso con límites estrictos
NexusTradeClient.fromEnvironment()Léelos del entorno o de .env
exportWorkspaceSession()Exporta un espacio de trabajo anónimo para uso posterior
importWorkspaceSession(token)Reanuda un espacio de trabajo anónimo existente

PortfolioHandle — devuelto por el constructor portfolio(...) y por getPortfolio / listPortfolios.

MétodoPropósito
save({ idempotencyKey })Persistirlo como borrador, estableciendo .id
backtest({ startDate, endDate, idempotencyKey })Hacer backtesting, prefiriendo el id guardado
deploy({ frequency })Acuñar el portafolio de papel real (nuevo id)
undeploy()Desactivar su despliegue
setStockEligibility(eligibility)Establecer la elegibilidad de acciones que enviará

Autenticación

Una clave API es opcional. Sin clave, la primera operación API crea perezosamente un espacio de trabajo NexusTrade real no registrado y aplica límites más estrictos de solicitudes, backtesting y IA. Los espacios de trabajo anónimos pueden crear, editar y bifurcar portafolios, lanzar backtests y usar la superficie programática de agente/chat. Cada operación de optimización—incluidos los lanzamientos de barridos genéticos y sistemáticos, lecturas de resultados, reejecuciones, promoción y flujos de trabajo fuera de muestra—requiere una clave API registrada. Exporta el token opaco del espacio de trabajo si el trabajo debe sobrevivir a un nuevo proceso:

const guest = new NexusTradeClient();
await guest.listPortfolios();
const token = guest.exportWorkspaceSession();

const resumed = new NexusTradeClient({ workspaceSession: token });

Un token de espacio de trabajo explícito expirado genera NexusTradeWorkspaceSessionExpiredError; el SDK nunca crea un espacio de trabajo de reemplazo que haría que el trabajo guardado pareciera eliminado.

Los usuarios registrados pueden crear una clave en nexustrade.io/developers (Perfil → Claves API). Las claves comienzan con sk- y se muestran una vez. Cuando se proporcionan ambas credenciales, el Authorization registrado tiene prioridad y el encabezado del espacio de trabajo no se envía.

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 superior, por lo que un proyecto local funciona sin exportaciones, sin dependencia de dotenv y sin el indicador --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 a process.env. Opta por no participar con NEXUSTRADE_DISABLE_DOTENV=1.

AlcanceOtorga
readLecturas de portafolio, backtest, genético, barrido y walk-forward
writeCrear/editar/bifurcar portafolios, backtests, lanzamientos genéticos/barridos y walk-forward
lakeCatálogo del lago, ciclo de vida de consultas, manifiestos, partes de resultados

Una clave que carece del alcance recibe 403 insufficient_scope.

OAuth no se acepta aquí. El flujo OAuth de NexusTrade sirve al servidor MCP. Estos endpoints toman solo claves API sk-; un JWT de portador se rechaza con 401 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 rechaza seguir una redirección en cualquier solicitud que no sea GET, por lo que una redirección nunca puede reenviar un trabajo pagado. La clave se mantiene en un campo #private y nunca aparece en un cliente serializado.

Idempotencia

Cada mutación toma una clave. Reutilizar la misma clave con la misma solicitud devuelve el recurso original en lugar de lanzar un segundo trabajo pagado, 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;
}
EstadoCódigoSignificado
401invalid_tokenClave faltante, malformada o expirada (o un JWT OAuth)
403insufficient_scopeLa clave carece de read, write o lake
400invalid_request, invalid_portfolioEntrada malformada
400invalid_idempotency_keyDebe coincidir con [A-Za-z0-9._:-]{1,160}
409idempotency_conflictClave reutilizada con un payload diferente
409idempotency_in_progressMisma clave, la primera llamada aún se está ejecutando. Vuelve a consultar, no reenvíes
404not_found, operation_not_foundDesconocido o no tuyo
429rate_limit_exceededRetrocede y reintenta

status es 0 cuando ningún estado HTTP describe la falla: transport_error (nunca se alcanzó la API), unsafe_redirect o una verificación de envoltura invalid_response en una respuesta que de otro modo fue exitosa.

Tiempos de espera

new HttpTransport({ timeoutSeconds }) (predeterminado 30) es un plazo total de tiempo de pared para una solicitud. Ni este ni el tiempo de espera de sondeo limitan cuánto tiempo toma un trabajo.

Alcance

Redacción de portafolios, backtesting, optimización, estudios walk-forward y SQL de solo lectura sobre el lago de datos de mercado, versionado bajo /api/v1/nexustrade. La superficie completa requiere una clave API registrada; los espacios de trabajo anónimos están limitados a autoría/bifurcación de portafolios, backtests y llamadas programáticas de agente/chat. El screener y la creación de un despliegue en vivo permanecen fuera de esta superficie. Las órdenes son alcanzables, pero una orden en vivo solo se prepara para aprobación humana— nunca se envía. deploy y undeploy actúan sobre lo que un id existente ya es, incluido en vivo.

Requisitos

Node 18+ (usa el fetch global). Contribución: el conjunto de pruebas ejecuta TypeScript directamente a través de node --test, que necesita Node 22.6+ para la eliminación de tipos. El dist/ publicado es JavaScript plano 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 NexusTrade correctas en el primer intento.

Licencia

MIT