Opera MetaTrader 5 desde Node.js con el SDK de TypeScript de fxapis
Instala el SDK de TypeScript de fxapis, conecta una cuenta de MT5, coloca una orden de mercado idempotente y maneja cada falla desde Node.js, sin terminal.
En esta página
El SDK oficial de TypeScript de fxapis envuelve la API REST alojada de MetaTrader 5 en un cliente pequeño: un método por endpoint, generado a partir de la especificación OpenAPI de la API, sin dependencias en tiempo de ejecución. Corre en Node.js 18 o más reciente, se distribuye como ESM con declaraciones de tipos completas y es de código abierto en github.com/FXapis/fxapis-typescript.
No hay ningún terminal de MetaTrader de tu lado. Los terminales corren en la nube de fxapis y tu código hace solicitudes HTTPS, así que el mismo programa corre en una laptop, un servidor Linux o una función serverless. Este artículo cubre cómo instalarlo, las llamadas que te llevan de un login de MT5 a una operación cerrada, y el manejo de errores que importa cuando una orden es dinero real.
Instálalo
npm install fxapis
Este artículo está escrito para fxapis 0.3.1 en npm, la versión actual al 26 de octubre de 2026.
Crea una API key en la consola y guárdala en una variable de entorno en tu servidor. La clave nunca debe llegar a un navegador ni a una app móvil: cualquiera que la tenga puede operar las cuentas que ve. Mientras construyes, conecta una cuenta demo de un broker: toda clave de fxapis, incluidas las de prueba, llega a un broker real.
De un login de MT5 a una operación cerrada
import { Fxapis, newIdempotencyKey } from "fxapis";
const fx = new Fxapis(process.env.FXAPIS_API_KEY!);
// 1. Connect the account once. The trading password, not the investor password.
const account = await fx.postAccounts({
login: "26177561",
server: "YourBroker-Demo", // exactly as MetaTrader shows it, e.g. Exness-MT5Trial8, ICMarketsSC-Demo or Pepperstone-Demo
password: process.env.MT5_PASSWORD,
mode: "warm_on_demand",
});
// 2. Bring it online, and wait for `ready` (about ten seconds for a typical broker).
await fx.postAccountsByIdWarm(account.id);
for (;;) {
const { state, detail } = await fx.getAccountsByIdStatus(account.id);
if (state === "ready") break;
if (["invalid_credentials", "needs_2fa", "needs_certificate", "trading_disabled"].includes(state)) {
throw new Error(`the account needs attention: ${state} — ${detail}`);
}
await new Promise((r) => setTimeout(r, 2000));
}
// 3. A market order, with a key you keep until the order has an answer.
const order = await fx.postAccountsByIdOrdersMarket(
account.id,
{ symbol: "EURUSD", side: "buy", volume: "0.01" },
{ idempotencyKey: newIdempotencyKey() },
);
console.log(order.state, order.filledPrice);
// 4. Close the position it opened — that position carries the order's own ticket.
await fx.postAccountsByIdReconcile(account.id); // refresh positions now rather than in ~15 s
const positions = await fx.getAccountsByIdPositions(account.id);
const opened = positions.find((p: { brokerPositionId: string }) => p.brokerPositionId === order.brokerOrderId);
if (opened) {
await fx.postAccountsByIdPositionsByPositionIdClose(account.id, opened.brokerPositionId, {}, {
idempotencyKey: newIdempotencyKey(),
});
}
Tres detalles de ese código son deliberados:
- Los volúmenes y los precios son strings:
"0.01", no0.01. Un número JSON ya pasó por el punto flotante, y la API lo rechaza en lugar de operar un tamaño que no pediste. - Conectar no inicia sesión.
postAccountsguarda la cuenta y cifra su contraseña;postAccountsByIdWarmresponde de inmediato mientras ocurre el inicio de sesión, y tú consultas el estado. Una contraseña equivocada aparece ahí, no en la primera operación. - Cada orden y cada cierre llevan una clave de idempotencia. Si la solicitud se queda sin respuesta, la reenvías con la misma clave y recibes la primera respuesta en lugar de una segunda posición. Órdenes idempotentes explica por qué esto importa más de lo que parece.
Nombres de métodos, parámetros de consulta y páginas
Cada método se llama como su ruta: getAccounts, postAccountsByIdWarm, getAccountsByIdPositions, postAccountsPrepare para poner en línea muchas cuentas a la vez. Primero van los parámetros de ruta, luego el cuerpo y luego las opciones. Los parámetros de consulta van en options.query, y una lista paginada vuelve como un array con su página adjunta:
const unknown = await fx.getOrders({ query: { state: "unknown", limit: 50 } });
const next = unknown.page?.nextCursor; // pass it back as `cursor` for the next page
Las respuestas son el data de la API, sin tipos a propósito: tu código nombra los campos que lee, y la referencia de la API documenta cada uno (en inglés).
Cuando una orden no simplemente se ejecuta
Cada falla es un FxapisError con un code estable, un message legible, el requestId para darle a soporte, y retryable:
import { FxapisError } from "fxapis";
try {
await fx.postAccountsByIdOrdersMarket(accountId, body, { idempotencyKey: key });
} catch (err) {
if (!(err instanceof FxapisError)) throw err;
if (err.code === "ORDER_UNRESOLVED") {
// It may be live at the broker. Never resend: poll getOrdersById until it settles.
} else if (err.code === "ORDER_REJECTED") {
// The broker refused it and nothing opened. Show err.message. A new attempt needs a NEW key.
} else if (err.retryable) {
// SEND_FAILED, ACCOUNT_NOT_READY, RATE_LIMITED, GATEWAY_ERROR …: nothing was done.
// Wait (err.retryAfter seconds when set) and resend with the SAME key.
}
}
La regla que te protege es la primera rama: ORDER_UNRESOLVED significa que nadie sabe todavía si la orden llegó al broker, y retryable es false para ese código, sea cual sea lo demás. fxapis confirma una orden de apertura con el historial de deals del propio broker y la saca de unknown. Una página de error de un proxy entre tú y fxapis (un 502 o 504 que no es una respuesta de fxapis) llega como GATEWAY_ERROR, reintentable con la misma clave, nunca como un error al leer el JSON.
El SDK nunca reintenta por su cuenta. Esa decisión queda en tu código, que sabe para qué era la orden.
Adónde ir después
- fxapis-examples tiene este inicio rápido, click-to-trade para proveedores de señales y un copiador de operaciones, listos para correr contra una cuenta demo; mira el artículo de los ejemplos.
- El starter de Next.js es una app pequeña que funciona sobre este SDK: conecta una cuenta, mira sus posiciones y coloca una operación confirmada.
- SDKs y ejemplos en la documentación, y la guía de Node.js si prefieres ver cada solicitud con
fetchsimple (en inglés). - Trading cubre cada resultado que puede tener una orden.