@pipsend/sdk
v1.1.0
Published
SDK oficial de Pipsend compatible con NodeJS y TypeScript
Maintainers
Readme
@pipsend/sdk
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/v1contract: 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 bypath, symbols bycode - Dual package (ESM + CommonJS) with complete type definitions
- Node.js ≥ 16
Installation
npm install @pipsend/sdk
# or: yarn add @pipsend/sdk · pnpm add @pipsend/sdkAuthentication
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/loginvalidates 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 oneQuick 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-deleteTrading 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 --noEmitLicense
MIT © Pipsend
