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

@pipsend/sdk

v1.1.0

Published

SDK oficial de Pipsend compatible con NodeJS y TypeScript

Readme

@pipsend/sdk

npm version License: MIT

Official Pipsend SDK for Node.js and TypeScript. Typed client for the public /api/v1 REST surface and the realtime WebSocket stream.

v1.x targets the /api/v1 contract: business identifiers only (no internal database ids), unwrapped { data, meta } envelopes, and a frame-based realtime protocol. It is not backward compatible with 0.x.

Features

  • Typed REST modules: Accounts, Trading Groups, Positions, Orders, Trades, Market Data, Sessions
  • Realtime stream over WebSocket with auto-reconnect, resubscribe and heartbeat
  • Automatic auth: JWT login + transparent token refresh, with rate-limit backoff
  • Business identifiers: accounts by number, trading groups by path, symbols by code
  • Dual package (ESM + CommonJS) with complete type definitions
  • Node.js ≥ 16

Installation

npm install @pipsend/sdk
# or: yarn add @pipsend/sdk · pnpm add @pipsend/sdk

Authentication

The SDK authenticates with the account number and password, and refreshes the token transparently. You don't call a login method — just create the client and make calls.

import { createClient } from "@pipsend/sdk";

const client = createClient({
  server: "http://localhost:8080",
  number: 10001, // account commercial number
  password: "your-api-password", // the account's API password
});

// The SDK authenticates on the first call, caches the token in memory,
// and refreshes it automatically before expiry.

The public /api/v1/auth/login validates the account's API password (not the master/investor one).

Auth control

client.auth.isAuthenticated(); // boolean
client.auth.getToken(); // current access token (or undefined)
client.auth.getAccount(); // account block from login (incl. internal id for WS)
await client.auth.refresh(); // force a refresh / login

await client.auth.logout(); // close the current session (server-side) + clear token
await client.auth.logoutAll(); // close every session for the account
await client.auth.logoutOthers(); // close all sessions except the current one

Quick start

import { createClient } from "@pipsend/sdk";

const client = createClient({
  server: "http://localhost:8080",
  number: 10001,
  password: "your-api-password",
});

// Accounts list (items + pagination meta)
const { items } = await client.accounts.list({ per_page: 20 });

// Open a market trade
const order = await client.trades.open({
  login: 10001,
  symbol: "EURUSD",
  action: "buy",
  type: "market",
  volume: 0.1,
  stop_loss: 1.085,
  take_profit: 1.095,
});

// Historical candles
const { data: candles } = await client.marketData.getCandles({
  symbol: "BTCUSD",
  timeframe: "1h",
  limit: 100,
});

Response shapes

Responses are unwrapped from the server envelope — you get the payload directly. Two pagination shapes exist:

// Accounts & Trading Groups → ItemsPage<T>
const page = await client.accounts.list({ page: 1, per_page: 20 });
page.items; // T[]
page.meta; // { page, per_page, total, last_page }

// Positions, Orders & Sessions → PaginatedList<T>
const res = await client.positions.list({ login: "10001" });
res.data; // T[]
res.meta.pagination; // { page, per_page, total }

Modules

Accounts

await client.accounts.list({ search: "john", per_page: 20 }); // ItemsPage<Account>
await client.accounts.get(10001); // Account
await client.accounts.getStatus(10001); // { login, balance, equity, credit, margin, currency }
await client.accounts.getRights(10001); // bitmask + unpacked booleans
await client.accounts.getPermissions(10001); // { account_id, roles, permissions }

await client.accounts.create({
  trading_group: "demo/standard", // path (exact match)
  country: "CO", // ISO 3166 alpha-2
  first_name: "John",
  last_name: "Doe",
  email: "[email protected]",
  master_password: "secret123",
  investor_password: "viewer123", // optional
  api_password: "apikey123", // optional
  leverage: "1:100", // "1:100" or 100
});

await client.accounts.update(10001, { name: "Renamed", phone: "3160000000" });
await client.accounts.setPassword(10001, { kind: "api", password: "newsecret1" });
await client.accounts.updateRights(10001, {
  rights: ["RIGHT_ENABLED", "RIGHT_LOGIN_ENABLED", "RIGHT_TRADE_ENABLED"],
});
await client.accounts.adjust(10001, { type: "balance", amount: 150, comment: "deposit" });
await client.accounts.archive(10001, { reason: "inactive" });
await client.accounts.unarchive(10001);
await client.accounts.delete(10001); // soft-delete

Trading groups

await client.tradingGroups.list({ per_page: 100 }); // ItemsPage<TradingGroup>
await client.tradingGroups.getByName("standard"); // TradingGroup
await client.tradingGroups.getByPath("demo/standard"); // TradingGroup
await client.tradingGroups.tree(); // TradingGroupTreeNode[] (forest)

Positions

await client.positions.list({ login: "10001", state: "open" }); // PaginatedList<Position>
await client.positions.history({ login: "10001", from_date: "2026-01-01", to_date: "2026-06-30" });
await client.positions.get(888); // Position

await client.positions.setSLTP(888, { stop_loss: 1.085, take_profit: 1.13 }); // null clears
await client.positions.setTrailingStop(888, { enabled: true, distance: 20, distance_type: "pips" });
await client.positions.close(888, { qty: 0.5 }); // omit qty for full close
await client.positions.closeAll({ login: "10001", pnl: "negative" });

from_date / to_date are YYYY-MM-DD.

Orders

Enums are numeric with *_name labels: side 0 BUY · 1 SELL; type 0 MARKET · 1 LIMIT · 2 STOP · 3 STOP_LIMIT; status 2 ACCEPTED · 3 PARTIAL · 4 FILLED · 5 CANCELLED · 6 REJECTED.

await client.orders.list({ login: "10001", status: ["ACCEPTED", "PARTIAL"] }); // PaginatedList<Order>
await client.orders.history({ login: "10001", from_date: "2026-01-01" });
await client.orders.get(4242); // Order
await client.orders.modify(4242, { limit_price: "1.1060", stop_loss: "1.0950" });
await client.orders.cancel(4242, { reason: "manual" });

Trades

await client.trades.open({
  login: 10001,
  symbol: "EURUSD",
  action: "buy", // buy | sell
  type: "market", // market | limit | stop | stop_limit
  volume: 0.1,
  stop_loss: 1.075,
  take_profit: 1.095,
});
await client.trades.close({ login: 10001, position_id: 888, volume: 0.05 });
await client.trades.modify({ login: 10001, position_id: 888, stop_loss: 1.08 });

Market data

Proxied to the MarketData service (historical only, min ~1h age).

await client.marketData.getCandles({ symbol: "BTCUSD", timeframe: "1h", limit: 100 }); // MarketDataPage<Candle>
await client.marketData.getSymbols({ group: "CRYPTO_MAJOR", has_data: true }); // MarketDataPage<Symbol>
await client.marketData.getGroups({ root_only: true }); // Group[] (never paginated)

Timeframes: 1m, 5m, 15m, 1h, 4h, 1d.

Sessions

await client.sessions.list(); // PaginatedList<Session>
await client.sessions.history();
await client.sessions.terminate(99);

Realtime stream (WebSocket)

Topics are keyed by the account number (login) and the symbol code. Build them with the topics helper. Account-scoped topics support * wildcards (the admin "firehose") when the token has the tracking permission.

import { createClient, topics } from "@pipsend/sdk";

const client = createClient({
  server: "http://localhost:8080",
  number: 10001,
  password: "your-api-password",
  websocket: { enabled: true, autoConnect: false },
});

await client.stream.connect();

// Subscribe to specific topics or wildcards
client.stream.subscribe([
  topics.position(10001),
  topics.accountMetrics(10001),
  topics.tick("BTCUSD"),
]);
// Firehose: client.stream.subscribe([topics.position("*"), topics.order("*")]);

// Listen to a topic pattern ("position:*" matches every account's positions)
client.stream.onTopic("position:*", ({ topic, data }) => {
  if (data.event_type === "position.opened") {
    console.log("opened", data.login, data.symbol_code, data.qty);
  }
});

// Or every push frame
client.stream.onPush(({ topic, data }) => console.log(topic, data));

client.stream.onConnected(() => console.log("connected"));
client.stream.onDisconnected(() => console.log("disconnected"));
client.stream.onError((e) => console.error(e));

client.stream.unsubscribe([topics.tick("BTCUSD")]);
client.stream.disconnect();

Topic builders: topics.position, positionsPnl, accountMetrics, order, orderRejected, trade, alert (by login); tick, ohlc, ohlcFinal (by symbol). Pass "*" as the login for a firehose topic.

Configuration

const client = createClient({
  server: "http://localhost:8080", // required
  number: 10001, // account number
  password: "your-api-password",
  device: { application: "my-integration" }, // optional session metadata
  timezone: "UTC", // optional
  autoRefresh: true, // optional (default true)
  refreshMarginMinutes: 5, // refresh this many minutes before expiry
  websocket: {
    enabled: true,
    autoConnect: false,
    autoReconnect: true,
    maxReconnectAttempts: 5,
    reconnectBaseDelayMs: 1000,
    reconnectMaxDelayMs: 30000,
    heartbeatIntervalMs: 30000,
  },
});

Error handling

Every error is a PipsendError with code and statusCode. Auth failures are AuthenticationError and carry retryAfter (seconds) on a 429.

import { PipsendError, AuthenticationError } from "@pipsend/sdk";

try {
  await client.orders.cancel(4242);
} catch (error) {
  if (error instanceof PipsendError) {
    console.error(error.code, error.statusCode, error.message);
    // e.g. "order_terminal" 409 — order already FILLED/CANCELLED
  }
}

try {
  await client.auth.refresh();
} catch (error) {
  if (error instanceof AuthenticationError && error.code === "rate_limited") {
    console.warn(`rate limited; retry in ${error.retryAfter ?? 60}s`);
  }
}

After a failed login the SDK applies a short cooldown so repeated calls (e.g. a reconnect loop) back off instead of hammering the rate-limited login endpoint.

Common codes: validation_error, invalid_account_number, invalid_leverage, invalid_right, account_not_found / position_not_found, order_terminal, invalid_trading_group, invalid_country, rate_limited, not_implemented.

TypeScript

import type {
  Account,
  Position,
  Order,
  TradingGroup,
  Candle,
  Symbol,
  Session,
  ItemsPage,
  PaginatedList,
  PositionEvent,
  OrderEvent,
  TickEvent,
} from "@pipsend/sdk";

Development

npm install
npm run build      # tsup → dist (cjs, esm, dts)
npm test           # vitest
npm run typecheck  # tsc --noEmit

License

MIT © Pipsend