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
Autoriza estrategias de trading en TypeScript tipado. Haz backtesting 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 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_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 única de una estrategia de marketplace en una cartera nueva o existente.copy_trade_sharedreplica 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:
| Campo | Significado |
|---|---|
peakReservedCollateral | Mayor colateral bloqueado en cualquier tick, en la moneda de la cuenta |
medianReservedCollateral | Mediana 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
| Grupo | Ejemplos |
|---|---|
| Precio y volumen | Price OpeningPrice HighOfDay MinuteBarHigh MinuteBarLow HeikinAshi 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 OptionSpreadEntryPrice 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 sequence |
| Niveles anclados | IndicatorAtEntry 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ón | Valor por defecto | Significado |
|---|---|---|
timeoutSeconds | 900 | Deja de esperar (el trabajo sigue ejecutándose) |
pollIntervalSeconds | 2 | Primer intervalo; retrocede 1.5× |
maxPollIntervalSeconds | 15 | Tope del intervalo |
throwOnFailure | true | Lanza 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.
| Llamada | Propó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:
outcome | Significado |
|---|---|
ROWS | Coincidencias encontradas |
EMPTY | Cada filtro se ejecutó y nada las superó todas — una respuesta |
CLARIFICATION | La pregunta era ambigua; result.clarification pregunta |
GENERATION_FAILED | El 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é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 de vivo 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 |
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étodo | Propó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étodo | Propó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étodo | Propó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étodo | Propó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étodo | Propó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étodo | Propó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étodo | Propó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étodo | Propó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.
| Alcance | Otorga |
|---|---|
read | Lecturas de portafolio, backtest, genético, barrido y walk-forward |
write | Crear/editar/bifurcar portafolios, backtests, lanzamientos genéticos/barridos y walk-forward |
lake | Catá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 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
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;
}
| Estado | Código | Significado |
|---|---|---|
| 401 | invalid_token | Clave faltante, malformada o expirada (o un JWT OAuth) |
| 403 | insufficient_scope | La clave carece de 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 aún se está ejecutando. Vuelve a consultar, no reenvíes |
| 404 | not_found, operation_not_found | Desconocido o no tuyo |
| 429 | rate_limit_exceeded | Retrocede 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