npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@max89701/ctrader-layer

v1.5.1

Published

A Node.js communication layer for the cTrader Open API.

Readme

cTrader Layer

Node.js слой для работы с cTrader Open API. Форк Reiryoku ctrader-layer.

Установка

npm install @max89701/ctrader-layer

Требуется Node.js 14.17+.

Использование

Подробная документация по cTrader Open API: Open API Documentation.

Подключение к серверу

const { CTraderConnection } = require("@max89701/ctrader-layer");

const connection = new CTraderConnection({
    host: "demo.ctraderapi.com",
    port: 5035,
});

await connection.open();
console.log(connection.isOpen, connection.state); // true, "open"

Heartbeat отправляется автоматически каждые 25 секунд (сервер рвёт соединение при тишине > 30 с). Отключить: heartbeatIntervalMs: 0.

Отправка команд

Метод sendCommand отправляет команду и возвращает Promise, который разрешается при получении ответа от сервера. По умолчанию ответ ждут 30 секунд, иначе промис отклоняется с errorCode: "COMMAND_TIMEOUT".

const response = await connection.sendCommand("ProtoOAVersionReq", {});
console.log(response.version);

Обработка ошибок

При ошибке Open API, таймауте или закрытии соединения промис отклоняется экземпляром CTraderCommandError (это Error с полями errorCode, description, retryAfter).

const { CTraderCommandError } = require("@max89701/ctrader-layer");

try {
    await connection.sendCommand("ProtoOANewOrderReq", { /* ... */ });
} catch (error) {
    if (error instanceof CTraderCommandError) {
        console.error("Ошибка:", error.errorCode, error.description, error.retryAfter);
    }
}

// Без выброса CTraderCommandError:
const result = await connection.trySendCommand("ProtoOANewOrderReq", {});
if (result === undefined) {
    console.log("Команда не выполнена");
}

Один повтор после BLOCKED_PAYLOAD_TYPE: rateLimitRetry: true.

Аутентификация приложения

await connection.sendCommand("ProtoOAApplicationAuthReq", {
    clientId: "your-client-id",
    clientSecret: "your-client-secret",
});

Аутентификация аккаунта

await connection.sendCommand("ProtoOAAccountAuthReq", {
    ctidTraderAccountId: 12345678,
    accessToken: "your-access-token",
});

Heartbeat

Автоматический интервал задаётся heartbeatIntervalMs (по умолчанию 25000). Ручной вызов не создаёт висящую команду:

connection.sendHeartbeat();

Переподключение и переподписки

При разрыве TLS (в том числе ECONNRESET / событие close) можно включить автоматическое переподключение с повторной аутентификацией и подписками:

const { CTraderConnection } = require("@max89701/ctrader-layer");

const connection = new CTraderConnection({
    host: "demo.ctraderapi.com",
    port: 5035,
    autoReconnect: true,
    maxReconnectAttempts: 0, // 0 = без лимита; по умолчанию 5
    reconnectDelayMs: 1000,
    maxReconnectDelayMs: 30000,
});

connection.addReconnectHandler(async (conn) => {
    await conn.sendCommand("ProtoOAApplicationAuthReq", {
        clientId: "your-client-id",
        clientSecret: "your-client-secret",
    });
    await conn.sendCommand("ProtoOAAccountAuthReq", {
        ctidTraderAccountId: 12345678,
        accessToken: "your-access-token",
    });
    await conn.sendCommand("ProtoOASubscribeSpotsReq", {
        ctidTraderAccountId: 12345678,
        symbolId: [1, 2, 3],
    });
});

connection.on("reconnected", () => {
    console.log("Переподключение выполнено");
});

connection.on("reconnectFailed", (err) => {
    console.error("Не удалось переподключиться:", err);
});

После исчерпания попыток эмитятся и reconnectFailed, и close.

Закрытие соединения

connection.close();

Подписка на события

События можно подписывать по имени сообщения или по числовому payloadType. on / off / once / removeListener нормализуют имя в payloadType. События жизненного цикла (open, close, error, …) не ищутся в proto.

import { CTraderConnection } from "@max89701/ctrader-layer";

connection.on("ProtoOASpotEvent", (payload) => {
    console.log("Спот:", payload.bid, payload.ask);
});

connection.on("ProtoOADepthEvent", (payload) => {
    console.log("Стакан:", payload.symbolId);
});

connection.on("unknownMessage", ({ payloadType }) => {
    console.warn("Неизвестный payloadType", payloadType);
});

const onSpot = (payload) => { /* ... */ };
connection.on("ProtoOASpotEvent", onSpot);
connection.off("ProtoOASpotEvent", onSpot);

Для расширения маппинга используйте module augmentation:

declare module "@max89701/ctrader-layer" {
    interface CTraderEventMap {
        MyCustomEvent: { customField: string };
    }
}

Получение профиля и аккаунтов по access token (HTTP API)

Токен передаётся в заголовке Authorization: Bearer …, не в query string.

const profile = await CTraderConnection.getAccessTokenProfile("access-token");
const accounts = await CTraderConnection.getAccessTokenAccounts("access-token");

Параметры соединения

| Параметр | По умолчанию | Описание | |----------|--------------|----------| | host, port | — | Хост и порт Open API | | autoReconnect | false | Переподключение при обрыве | | maxReconnectAttempts | 5 | 0 — без лимита | | reconnectDelayMs | 1000 | Начальная задержка, экспоненциальный backoff | | maxReconnectDelayMs | 30000 | Потолок задержки | | reconnectJitter | true | Jitter 50–100% от backoff | | commandTimeoutMs | 30000 | 0 — без таймаута | | heartbeatIntervalMs | 25000 | 0 — выключить авто-heartbeat | | tlsTimeoutMs | — | Таймаут TLS-сокета | | rateLimitRetry | false | Повтор при BLOCKED_PAYLOAD_TYPE |

События соединения

| Событие | Описание | |---------|----------| | open | Соединение установлено | | close | Соединение закрыто (в т.ч. после исчерпания reconnect) | | error | Ошибка сокета (эмитится, только если есть слушатель) | | reconnecting | Начата попытка переподключения { attempt, maxAttempts, delayMs } | | reconnected | Переподключение успешно, handlers выполнены | | reconnectFailed | Исчерпаны попытки переподключения | | stateChange | Смена idle \| connecting \| open \| reconnecting \| closed | | unknownMessage | Сервер прислал payloadType, которого нет в локальных proto |

Proto-файлы

Снимок Spotware лежит в openapi-proto-messages-main. Обновление (Windows/Linux):

npm run pull-proto

Разработка

npm test
npm run build
npm run lint

Contribution

Создайте PR или откройте issue для сообщений об ошибках и предложений.

Беклог: BACKLOG.md.