bitmart-api
Version:
Complete & robust Node.js SDK for BitMart's REST APIs and WebSockets, with TypeScript declarations.
1,744 lines (1,682 loc) • 167 kB
Plain Text
This file is a merged representation of a subset of the codebase, containing files not matching ignore patterns, combined into a single document by Repomix.
The content has been processed where content has been compressed (code blocks are separated by ⋮---- delimiter).
================================================================
File Summary
================================================================
Purpose:
--------
This file contains a packed representation of a subset of the repository's contents that is considered the most important context.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
File Format:
------------
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
5. Multiple file entries, each consisting of:
a. A separator line (================)
b. The file path (File: path/to/file)
c. Another separator line
d. The full contents of the file
e. A blank line
Usage Guidelines:
-----------------
- This file should be treated as read-only. Any changes should be made to the
original repository files, not this packed version.
- When processing this file, use the file path to distinguish
between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
the same level of security as you would the original repository.
Notes:
------
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching these patterns are excluded: .github/, examples/apidoc/, docs/images/, docs/endpointFunctionList.md, test/, src/util/, dist/, lib/
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Content has been compressed - code blocks are separated by ⋮---- delimiter
- Files are sorted by Git change count (files with more changes are at the bottom)
================================================================
Directory Structure
================================================================
examples/
Auth/
fasterHmacSign.ts
Rest/
Futures/
futures-get-balances.ts
futures-get-klines.ts
futures-get-tickers.ts
futures-submit-order.ts
Spot/
spot-get-balances.ts
spot-get-klines.ts
spot-get-symbols.ts
spot-submit-order.ts
Websocket/
ws-custom-logger.ts
ws-futures-private.ts
ws-futures-public.ts
ws-spot-private.ts
ws-spot-public.ts
README.md
tsconfig.examples.json
src/
lib/
websocket/
websocket-util.ts
WsStore.ts
WsStore.types.ts
BaseRestClient.ts
BaseWSClient.ts
logger.ts
misc-util.ts
requestUtils.ts
webCryptoAPI.ts
types/
request/
futures.types.ts
spot.types.ts
response/
futures.types.ts
shared.types.ts
spot.types.ts
websockets/
client.ts
events.ts
requests.ts
FuturesClientV2.ts
index.ts
RestClient.ts
WebsocketClient.ts
webpack/
webpack.config.cjs
.eslintrc.cjs
.gitignore
.nvmrc
.prettierrc
jest.config.ts
LICENSE.md
package.json
postBuild.sh
README.md
tea.yaml
tsconfig.cjs.json
tsconfig.esm.json
tsconfig.json
tsconfig.linting.json
================================================================
Files
================================================================
================
File: examples/README.md
================
# Bitmart API Examples Node.js
Some examples written in Node.js/typescript showing how to use some of Bitmart's common API functionality, such as fetching prices, submitting orders, etc.
## Usage
Most of these examples can just be executed (e.g. using `ts-node` or `tsx`).
Any "private" examples that perform actions on an account (such as checking balance or submitting orders) will require an api key, secret and memo (provided by bitmart when you create an API key).
These can either be hardcoded or you can pass them as env vars to test the functionality.
For example on macOS or unix, using `ts-node` to execute a typescript file directly:
```bash
API_KEY="apiKeyHere" API_SECRET="secretHere" API_MEMO="memoHere" ts-node examples/futures-get-balances.ts
```
================
File: examples/tsconfig.examples.json
================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist/cjs",
"target": "esnext",
"rootDir": "../"
},
"include": ["../src/**/*.*", "../examples/**/*.*"]
}
================
File: src/lib/misc-util.ts
================
export function neverGuard(x: never, msg: string): Error
================
File: src/types/response/shared.types.ts
================
export interface APIResponse<TData = {}, TCode = number> {
message: string;
code: TCode;
trace: string;
data: TData;
}
⋮----
export type OrderSide = 'buy' | 'sell';
⋮----
/**
* Spot & Futures uses this
*/
export interface AccountCurrencyBalanceV1 {
currency: string;
name: string;
available: string;
available_usd_valuation: string;
frozen: string;
}
================
File: src/types/websockets/events.ts
================
export interface WsDataEvent<TData = any, TWSKey = string> {
data: TData;
table: string;
wsKey: TWSKey;
}
================
File: src/types/websockets/requests.ts
================
export type WsOperation =
| 'subscribe'
| 'unsubscribe'
| 'login'
| 'access'
| 'request';
⋮----
export interface WsSpotOperation<TWSTopic extends string = string> {
op: WsOperation;
args: TWSTopic[];
}
⋮----
export interface WsFuturesOperation<TWSTopic extends string> {
action: WsOperation;
args: TWSTopic[];
}
⋮----
export type WsRequestOperation<TWSTopic extends string> =
| WsSpotOperation<TWSTopic>
| WsFuturesOperation<TWSTopic>;
================
File: src/index.ts
================
================
File: .prettierrc
================
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all"
}
================
File: postBuild.sh
================
#!/bin/bash
#
# Add package.json files to cjs/mjs subtrees
#
cat >dist/cjs/package.json <<!EOF
{
"type": "commonjs"
}
!EOF
cat >dist/mjs/package.json <<!EOF
{
"type": "module"
}
!EOF
find src -name '*.d.ts' -exec cp {} dist/mjs \;
find src -name '*.d.ts' -exec cp {} dist/cjs \;
================
File: tea.yaml
================
---
version: 1.0.0
codeOwners:
- '0xeb1a7BF44a801e33a339705A266Afc0Cba3D6D54'
quorum: 1
================
File: tsconfig.cjs.json
================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist/cjs",
"target": "esnext"
},
"include": ["src/**/*.*"]
}
================
File: tsconfig.esm.json
================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "esnext",
"outDir": "dist/mjs",
"target": "esnext"
},
"include": ["src/**/*.*"]
}
================
File: examples/Auth/fasterHmacSign.ts
================
import { createHmac } from 'crypto';
⋮----
import { RestClient } from '../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { RestClient } from 'bitmart-api';
⋮----
/**
* Overkill in almost every case, but if you need any optimisation available,
* you can inject a faster sign mechanism such as node's native createHmac:
*/
⋮----
async function getSpotBalances()
================
File: examples/Rest/Futures/futures-get-balances.ts
================
import { FuturesClientV2 } from '../../../src/index.js';
// // import from npm, after installing via npm `npm install bitmart-api`
// import { FuturesClientV2 } from 'bitmart-api';
⋮----
async function getFuturesAssets()
================
File: examples/Rest/Futures/futures-get-klines.ts
================
import { FuturesClientV2 } from '../../../src/index.js';
// // import from npm, after installing via npm `npm install bitmart-api`
// import { FuturesClientV2 } from 'bitmart-api';
⋮----
async function getFuturesKlines()
================
File: examples/Rest/Futures/futures-get-tickers.ts
================
import { FuturesClientV2 } from '../../../src/index.js';
// // import from npm, after installing via npm `npm install bitmart-api`
// import { FuturesClientV2 } from 'bitmart-api';
⋮----
async function getFuturesTickers()
================
File: examples/Rest/Futures/futures-submit-order.ts
================
import { FuturesClientV2 } from '../../../src/index.js';
// // import from npm, after installing via npm `npm install bitmart-api`
// import { FuturesClientV2 } from 'bitmart-api';
⋮----
async function SumbitFuturesOrder()
⋮----
side: 1, // Order side - 1=buy_open_long -2=buy_close_short -3=sell_close_long -4=sell_open_short
================
File: examples/Rest/Spot/spot-get-balances.ts
================
import { RestClient } from '../../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { RestClient } from 'bitmart-api';
⋮----
async function getSpotBalances()
================
File: examples/Rest/Spot/spot-get-klines.ts
================
import { RestClient } from '../../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { RestClient } from 'bitmart-api';
⋮----
async function getKlines()
================
File: examples/Rest/Spot/spot-get-symbols.ts
================
import { RestClient } from '../../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { RestClient } from 'bitmart-api';
⋮----
async function getTickers()
================
File: examples/Rest/Spot/spot-submit-order.ts
================
import { RestClient } from '../../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { RestClient } from 'bitmart-api';
⋮----
async function start()
⋮----
// const usdValue = 6;
// const price = 52000;
// const qty = usdValue / price;
⋮----
// const limitBuyOrder = {
// symbol: 'BTC_USDT',
// side: 'buy',
// type: 'limit',
// size: String(qty),
// price: String(price),
// };
⋮----
// const res = await client.submitSpotOrder({
// symbol: 'BTC_USDT',
// side: 'buy',
// type: 'market',
// size: String(qty),
// });
================
File: examples/Websocket/ws-custom-logger.ts
================
import { DefaultLogger, LogParams, WebsocketClient } from '../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { DefaultLogger, LogParams, WebsocketClient } from 'bitmart-api';
⋮----
/** Optional, implement a custom logger */
⋮----
async function start()
⋮----
// Data received
⋮----
// Something happened, attempting to reconenct
⋮----
// Reconnect successful
⋮----
// Connection closed. If unexpected, expect reconnect -> reconnected.
⋮----
// Reply to a request, e.g. "subscribe"/"unsubscribe"/"authenticate"
⋮----
//
================
File: examples/Websocket/ws-futures-public.ts
================
import { WebsocketClient } from '../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { WebsocketClient } from 'bitmart-api';
⋮----
async function start()
⋮----
// Data received
⋮----
// Something happened, attempting to reconenct
⋮----
// Reconnect successful
⋮----
// Connection closed. If unexpected, expect reconnect -> reconnected.
⋮----
// Reply to a request, e.g. "subscribe"/"unsubscribe"/"authenticate"
⋮----
// Ticker Channel
// client.subscribe('futures/ticker', 'futures');
⋮----
// Depth Channel
// client.subscribe('futures/depth20:BTCUSDT', 'futures');
⋮----
// Trade Channel
// client.subscribe('futures/trade:BTCUSDT', 'futures');
⋮----
// KlineBin Channel
// client.subscribe('futures/klineBin1m:BTCUSDT', 'futures');
⋮----
// Or have multiple topics in one array:
================
File: examples/Websocket/ws-spot-public.ts
================
import { WebsocketClient } from '../../src/index.js';
⋮----
// import from npm, after installing via npm `npm install bitmart-api`
// import { WebsocketClient } from 'bitmart-api';
⋮----
async function start()
⋮----
// Some topics allow requests, here's an example for sending a request
// const wsKey = 'spotPublicV1';
// if (data?.wsKey === wsKey) {
// const depthIncreaseDataRequest: WsSpotOperation = {
// op: 'request',
// args: ['spot/depth/increase100:BTC_USDT'],
// };
⋮----
// client.tryWsSend(
// 'spotPublicV1',
// JSON.stringify(depthIncreaseDataRequest),
// );
// }
⋮----
// Data received
⋮----
// Something happened, attempting to reconenct
⋮----
// Reconnect successful
⋮----
// Connection closed. If unexpected, expect reconnect -> reconnected.
⋮----
// Reply to a request, e.g. "subscribe"/"unsubscribe"/"authenticate"
⋮----
/**
* Use the client subscribe(topic, market) pattern to subscribe to any websocket topic.
*
* You can subscribe to topics one at a time:
*/
⋮----
// Ticker Channel
// client.subscribe('spot/ticker:BTC_USDT', 'spot');
⋮----
// KLine/Candles Channel
// client.subscribe('spot/kline1m:BTC_USDT', 'spot');
⋮----
// Depth-All Channel
// client.subscribe('spot/depth5:BTC_USDT', 'spot');
⋮----
// Depth-Increase Channel
// client.subscribe('spot/depth/increase100:BTC_USDT', 'spot');
⋮----
// Trade Channel
// client.subscribe('spot/trade:BTC_USDT', 'spot');
⋮----
/**
* Or have multiple topics in one array, in a single request:
*/
================
File: src/lib/logger.ts
================
export type LogParams = null | any;
⋮----
export type DefaultLogger = typeof DefaultLogger;
⋮----
// eslint-disable-next-line @typescript-eslint/no-unused-vars
⋮----
// console.log(_params);
================
File: src/types/request/spot.types.ts
================
export interface SpotKlineV3Request {
symbol: string;
before?: number;
after?: number;
step?: number;
limit?: number;
}
⋮----
export interface SpotKlinesV1Request {
symbol: string;
from: number;
to: number;
step?: number;
}
⋮----
export interface SpotOrderBookDepthV1Request {
symbol: string;
precision?: string;
size?: number;
}
⋮----
export interface SubmitWithdrawalV1Request {
currency: string;
amount: string;
destination: 'To Digital Address';
address: string;
address_memo?: string;
}
⋮----
export interface DepositWithdrawHistoryV2Request {
currency?: string;
operation_type: 'deposit' | 'withdraw';
start_time?: number;
end_time?: number;
N: number;
}
⋮----
export interface SubmitMarginTransferV1Request {
symbol: string;
currency: string;
amount: string;
side: 'in' | 'out';
}
⋮----
export interface SubmitSpotOrderV2Request {
symbol: string;
side: 'buy' | 'sell';
type: 'limit' | 'market' | 'limit_maker' | 'ioc';
stpmode?: 'none' | 'cancel_maker' | 'cancel_taker' | 'cancel_both';
client_order_id?: string;
size?: string;
price?: string;
notional?: string;
}
⋮----
export type CancelOrdersV3Request = {
symbol: string;
order_id?: string;
client_order_id?: string;
} & ({ order_id: string } | { client_order_id: string });
⋮----
export interface SubmitSpotBatchOrdersV4Request {
symbol: string;
orderParams: {
clientOrderId?: string;
size?: string;
price?: string;
side: 'buy' | 'sell';
type: 'limit' | 'market' | 'limit_maker' | 'ioc';
stpmode?: 'none' | 'cancel_maker' | 'cancel_taker' | 'cancel_both';
notional?: string;
}[];
recvWindow?: number;
}
⋮----
export interface CancelSpotBatchOrdersV4Request {
symbol: string;
orderIds?: string[];
clientOrderIds?: string[];
recvWindow?: number;
}
⋮----
export interface SpotOrderByIdV4Request {
orderId: string;
queryState?: 'open' | 'history';
recvwindow?: number;
}
⋮----
export interface SpotOrderByClientOrderIdV4Request {
clientOrderId: string;
queryState?: 'open' | 'history';
recvwindow?: number;
}
⋮----
export interface SpotOpenOrdersV4Request {
orderMode?: 'spot' | 'iso_margin'; // Order mode: 'spot' for spot trade, 'iso_margin' for isolated margin trade
startTime?: number; // Start time in milliseconds, e.g., 1681701557927
endTime?: number; // End time in milliseconds, e.g., 1681701557927
limit?: number; // Number of queries, allowed range [1,200], default is 200
recvWindow?: number; // Trade time limit, allowed range (0,60000], default: 5000 milliseconds
}
⋮----
orderMode?: 'spot' | 'iso_margin'; // Order mode: 'spot' for spot trade, 'iso_margin' for isolated margin trade
startTime?: number; // Start time in milliseconds, e.g., 1681701557927
endTime?: number; // End time in milliseconds, e.g., 1681701557927
limit?: number; // Number of queries, allowed range [1,200], default is 200
recvWindow?: number; // Trade time limit, allowed range (0,60000], default: 5000 milliseconds
⋮----
export interface SpotOrderTradeHistoryV4Request {
orderMode?: 'spot' | 'iso_margin'; // Order mode: 'spot' for spot trade, 'iso_margin' for isolated margin trade
startTime?: number; // Start time in milliseconds, e.g., 1681701557927
endTime?: number; // End time in milliseconds, e.g., 1681701557927
limit?: number; // Number of queries, allowed range [1,200], default is 200
recvWindow?: number; // Trade time limit, allowed range (0,60000], default: 5000 milliseconds
symbol?: string; // Trading pair, e.g., BTC_USDT
}
⋮----
orderMode?: 'spot' | 'iso_margin'; // Order mode: 'spot' for spot trade, 'iso_margin' for isolated margin trade
startTime?: number; // Start time in milliseconds, e.g., 1681701557927
endTime?: number; // End time in milliseconds, e.g., 1681701557927
limit?: number; // Number of queries, allowed range [1,200], default is 200
recvWindow?: number; // Trade time limit, allowed range (0,60000], default: 5000 milliseconds
symbol?: string; // Trading pair, e.g., BTC_USDT
⋮----
export interface MarginBorrowRepayV1Request {
symbol: string;
currency: string;
amount: string;
}
⋮----
export interface MarginBorrowRecordsV1Request {
symbol: string;
start_time?: number;
end_time?: number;
N?: number;
borrow_id?: string;
}
⋮----
export interface MarginRepayRecordsV1Request {
symbol: string;
start_time?: number;
end_time?: number;
N?: number;
repay_id?: string;
currency?: string;
}
⋮----
export interface SubmitSubTransferSubToMainV1Request {
requestNo: string;
amount: string;
currency: string;
}
⋮----
export interface SubmitSubTransferV1Request {
requestNo: string;
amount: string;
currency: string;
subAccount: string;
}
⋮----
export interface SubmitMainTransferSubToSubV1Request {
requestNo: string;
amount: string;
currency: string;
fromAccount: string;
toAccount: string;
}
⋮----
export interface SubTransfersV1Request {
moveType: 'spot to spot';
N: number;
accountName?: string;
}
⋮----
export interface AccountSubTransfersV1Request {
moveType: 'spot to spot';
N: number;
}
⋮----
export interface SubSpotWalletBalancesV1Request {
subAccount: string;
currency?: string;
}
⋮----
export interface SpotBrokerRebateRequest {
start_time?: number;
end_time?: number;
}
⋮----
/** Spot algo order (v4): TP/SL or trigger. See `POST spot/v4/algo/submit_order`. */
export interface SubmitSpotAlgoOrderV4Request {
symbol: string;
side: 'buy' | 'sell';
type: 'tp/sl' | 'trigger';
client_order_id?: string;
trigger_price?: string;
trigger_type?: 'limit' | 'market';
price?: string;
notional?: string;
size?: string;
}
⋮----
export interface CancelSpotAlgoOrderV4Request {
symbol: string;
order_id: string;
type: 'tp/sl' | 'trigger';
}
⋮----
export interface CancelAllSpotAlgoOrdersV4Request {
symbol?: string;
type: 'tp/sl' | 'trigger';
}
⋮----
export interface SpotAlgoOrderByIdV4Request {
orderId: string;
queryState?: 'open' | 'history';
recvWindow?: number;
}
⋮----
export interface SpotAlgoOrderByClientOrderIdV4Request {
clientOrderId: string;
queryState?: 'open' | 'history';
recvWindow?: number;
}
⋮----
export interface SpotAlgoOpenOrdersV4Request {
symbol?: string;
orderMode?: 'trigger' | 'tp/sl';
startTime?: number;
endTime?: number;
limit?: number;
recvWindow?: number;
}
⋮----
export interface SpotAlgoHistoryOrdersV4Request {
symbol?: string;
orderMode?: 'trigger' | 'tp/sl';
startTime?: number;
endTime?: number;
limit?: number;
recvWindow?: number;
}
================
File: webpack/webpack.config.cjs
================
function generateConfig(name)
⋮----
// Add '.ts' and '.tsx' as resolvable extensions.
⋮----
// Node.js core modules not available in browsers
// The REST client's https.Agent (for keepAlive) is Node.js-only and won't work in browsers
⋮----
// Code is already transpiled from TypeScript, no additional loaders needed
================
File: .nvmrc
================
v22.17.1
================
File: jest.config.ts
================
/**
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/
⋮----
import type { Config } from 'jest';
⋮----
// All imported modules in your tests should be mocked automatically
// automock: false,
⋮----
// Stop running tests after `n` failures
// bail: 0,
bail: false, // enable to stop test when an error occur,
⋮----
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/private/var/folders/kf/2k3sz4px6c9cbyzj1h_b192h0000gn/T/jest_dx",
⋮----
// Automatically clear mock calls, instances, contexts and results before every test
⋮----
// Indicates whether the coverage information should be collected while executing the test
⋮----
// An array of glob patterns indicating a set of files for which coverage information should be collected
⋮----
// The directory where Jest should output its coverage files
⋮----
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
⋮----
// Indicates which provider should be used to instrument code for coverage
⋮----
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
⋮----
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
⋮----
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
⋮----
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
⋮----
// The default configuration for fake timers
// fakeTimers: {
// "enableGlobally": false
// },
⋮----
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
⋮----
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
⋮----
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
⋮----
// A set of global variables that need to be available in all test environments
// globals: {},
⋮----
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
⋮----
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
⋮----
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "mjs",
// "cjs",
// "jsx",
// "ts",
// "tsx",
// "json",
// "node"
// ],
⋮----
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
⋮----
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
⋮----
// Activates notifications for test results
// notify: false,
⋮----
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
⋮----
// A preset that is used as a base for Jest's configuration
// preset: undefined,
⋮----
// Run tests from one or more projects
// projects: undefined,
⋮----
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
⋮----
// Automatically reset mock state before every test
// resetMocks: false,
⋮----
// Reset the module registry before running each individual test
// resetModules: false,
⋮----
// A path to a custom resolver
// resolver: undefined,
⋮----
// Automatically restore mock state and implementation before every test
// restoreMocks: false,
⋮----
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
⋮----
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
⋮----
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
⋮----
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
⋮----
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
⋮----
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
⋮----
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
⋮----
// The test environment that will be used for testing
// testEnvironment: "jest-environment-node",
⋮----
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
⋮----
// Adds a location field to test results
// testLocationInResults: false,
⋮----
// The glob patterns Jest uses to detect test files
⋮----
// "**/__tests__/**/*.[jt]s?(x)",
⋮----
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
⋮----
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
⋮----
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
⋮----
// This option allows use of a custom test runner
// testRunner: "jest-circus/runner",
⋮----
// A map from regular expressions to paths to transformers
// transform: undefined,
⋮----
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/",
// "\\.pnp\\.[^\\/]+$"
// ],
⋮----
// Prevents import esm module error from v1 axios release, issue #5026
⋮----
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
⋮----
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
verbose: true, // report individual test
⋮----
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
⋮----
// Whether to use watchman for file crawling
// watchman: true,
================
File: LICENSE.md
================
Copyright 2025 Tiago Siebler
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================
File: tsconfig.json
================
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"noEmitOnError": true,
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": false,
"inlineSourceMap": false,
"lib": ["esnext"],
"listEmittedFiles": false,
"listFiles": false,
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noUnusedParameters": true,
"pretty": true,
"removeComments": false,
"resolveJsonModule": true,
"rootDir": "src",
"skipLibCheck": false,
"sourceMap": true,
"strict": true,
"strictNullChecks": true,
"types": ["node", "jest"],
"module": "commonjs",
"outDir": "dist/cjs",
"target": "esnext"
},
"compileOnSave": true,
"exclude": ["node_modules", "dist", "test"],
"include": ["src/**/*.*", ".eslintrc.cjs"]
}
================
File: tsconfig.linting.json
================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "dist/cjs",
"target": "esnext",
"rootDir": "../"
},
"include": ["src/**/*.*", "test/**/*.*", "examples/**/*.*", ".eslintrc.cjs", "jest.config.ts"]
}
================
File: examples/Websocket/ws-futures-private.ts
================
import { LogParams, WebsocketClient } from '../../src/index.js';
⋮----
// import from npm, after installing via npm `npm install bitmart-api`
// import { LogParams, WebsocketClient } from 'bitmart-api';
⋮----
// eslint-disable-next-line @typescript-eslint/no-unused-vars
⋮----
async function start()
⋮----
customLogger, // optional: inject a custom logger with all levels enabled (trace is disabled by default)
⋮----
// Data received
⋮----
// Something happened, attempting to reconenct
⋮----
// Reconnect successful
⋮----
// Connection closed. If unexpected, expect reconnect -> reconnected.
⋮----
// Reply to a request, e.g. "subscribe"/"unsubscribe"/"authenticate"
⋮----
// Assets Channel
⋮----
// Position Channel
// client.subscribe('futures/position', 'futures');
⋮----
// Order Channel
// client.subscribe('futures/order', 'futures');
================
File: examples/Websocket/ws-spot-private.ts
================
import { LogParams, WebsocketClient } from '../../src/index.js';
// import from npm, after installing via npm `npm install bitmart-api`
// import { LogParams, WebsocketClient } from 'bitmart-api';
⋮----
// eslint-disable-next-line @typescript-eslint/no-unused-vars
⋮----
async function start()
⋮----
customLogger, // optional: inject a custom logger with all levels enabled (trace is disabled by default)
⋮----
// Data received
⋮----
// Something happened, attempting to reconenct
⋮----
// Reconnect successful
⋮----
// Connection closed. If unexpected, expect reconnect -> reconnected.
⋮----
// Reply to a request, e.g. "subscribe"/"unsubscribe"/"authenticate"
⋮----
// order progress
⋮----
// balance updates
// client.subscribe('spot/user/balance:BALANCE_UPDATE', 'spot');
================
File: src/lib/websocket/websocket-util.ts
================
import WebSocket from 'isomorphic-ws';
⋮----
import { WsTopic } from '../../types/websockets/client.js';
import { MessageEventLike } from '../requestUtils.js';
⋮----
/**
* Normalised internal format for a request (subscribe/unsubscribe/etc) on a topic, with optional parameters.
*
* - Topic: the topic this event is for
* - Payload: the parameters to include, optional. E.g. auth requires key + sign. Some topics allow configurable parameters.
* - Category: required for bybit, since different categories have different public endpoints
*/
export interface WsTopicRequest<
TWSTopic extends string = string,
TWSPayload = unknown,
> {
topic: TWSTopic;
payload?: TWSPayload;
}
⋮----
/**
* Conveniently allow users to request a topic either as string topics or objects (containing string topic + params)
*/
export type WsTopicRequestOrStringTopic<
TWSTopic extends string,
TWSPayload = unknown,
> = WsTopicRequest<TWSTopic, TWSPayload> | string;
⋮----
/** Should be one WS key per unique URL */
⋮----
/** This is used to differentiate between each of the available websocket streams */
export type WsKey = (typeof WS_KEY_MAP)[keyof typeof WS_KEY_MAP];
⋮----
/**
* Some exchanges have two livenet environments, some have test environments, some dont. This allows easy flexibility for different exchanges.
* Examples:
* - One livenet and one testnet: NetworkMap<'livenet' | 'testnet'>
* - One livenet, sometimes two, one testnet: NetworkMap<'livenet' | 'testnet', 'livenet2'>
* - Only one livenet, no other networks: NetworkMap<'livenet'>
*/
type NetworkMap<
TRequiredKeys extends string,
TOptionalKeys extends string | undefined = undefined,
> = Record<TRequiredKeys, string> &
(TOptionalKeys extends string
? Record<TOptionalKeys, string | undefined>
: Record<TRequiredKeys, string>);
⋮----
export function neverGuard(x: never, msg: string): Error
⋮----
/**
* ws.terminate() is undefined in browsers.
* This only works in node.js, not in browsers.
* Does nothing if `ws` is undefined. Does nothing in browsers.
*/
export function safeTerminateWs(
ws?: WebSocket | any,
fallbackToClose?: boolean,
): boolean
⋮----
/**
* Users can conveniently pass topics as strings or objects (object has topic name + optional params).
*
* This method normalises topics into objects (object has topic name + optional params).
*/
export function getNormalisedTopicRequests(
wsTopicRequests: WsTopicRequestOrStringTopic<string>[],
): WsTopicRequest<WsTopic>[]
⋮----
// passed as string, convert to object
⋮----
// already a normalised object, thanks to user
⋮----
/**
* WebSocket.ping() is not available in browsers. This is a simple check used to
* disable heartbeats in browers, for exchanges that use native WebSocket ping/pong frames.
*/
export function isWSPingFrameAvailable(): boolean
⋮----
/**
* WebSocket.pong() is not available in browsers. This is a simple check used to
* disable heartbeats in browers, for exchanges that use native WebSocket ping/pong frames.
*/
export function isWSPongFrameAvailable(): boolean
⋮----
export async function decompressMessageEvent(
event: MessageEventLike<Buffer<ArrayBufferLike>>,
): Promise<MessageEventLike<any>>
⋮----
start(controller)
================
File: src/lib/websocket/WsStore.types.ts
================
import WebSocket from 'isomorphic-ws';
⋮----
export enum WsConnectionStateEnum {
INITIAL = 0,
CONNECTING = 1,
CONNECTED = 2,
CLOSING = 3,
RECONNECTING = 4,
ERROR = 5,
}
⋮----
export interface DeferredPromise<TSuccess = any, TError = any> {
resolve?: (value: TSuccess) => TSuccess;
reject?: (value: TError) => TError;
promise?: Promise<TSuccess>;
}
⋮----
export interface WSConnectedResult {
wsKey: string;
ws: WebSocket;
}
⋮----
export interface WsStoredState<TWSTopicSubscribeEvent extends string | object> {
/** The currently active websocket connection */
ws?: WebSocket;
/** The current lifecycle state of the connection (enum) */
connectionState?: WsConnectionStateEnum;
connectionStateChangedAt?: Date;
/** A timer that will send an upstream heartbeat (ping) when it expires */
activePingTimer?: ReturnType<typeof setTimeout> | undefined;
/** A timer tracking that an upstream heartbeat was sent, expecting a reply before it expires */
activePongTimer?: ReturnType<typeof setTimeout> | undefined;
/** If a reconnection is in progress, this will have the timer for the delayed reconnect */
activeReconnectTimer?: ReturnType<typeof setTimeout> | undefined;
/**
* When a connection attempt is in progress (even for reconnect), a promise is stored here.
*
* This promise will resolve once connected (and will then get removed);
*/
deferredPromiseStore: Record<string, DeferredPromise>;
/**
* All the topics we are expected to be subscribed to on this connection (and we automatically resubscribe to if the connection drops)
*
* A "Set" and a deep-object-match are used to ensure we only subscribe to a topic once (tracking a list of unique topics we're expected to be connected to)
*/
subscribedTopics: Set<TWSTopicSubscribeEvent>;
/** Whether this connection has completed authentication (only applies to private connections) */
isAuthenticated?: boolean;
/**
* Whether this connection has completed authentication before for the Websocket API, so it k
* nows to automatically reauth if reconnected
*/
didAuthWSAPI?: boolean;
/** To reauthenticate on the WS API, which channel do we send to? */
WSAPIAuthChannel?: string;
}
⋮----
/** The currently active websocket connection */
⋮----
/** The current lifecycle state of the connection (enum) */
⋮----
/** A timer that will send an upstream heartbeat (ping) when it expires */
⋮----
/** A timer tracking that an upstream heartbeat was sent, expecting a reply before it expires */
⋮----
/** If a reconnection is in progress, this will have the timer for the delayed reconnect */
⋮----
/**
* When a connection attempt is in progress (even for reconnect), a promise is stored here.
*
* This promise will resolve once connected (and will then get removed);
*/
⋮----
/**
* All the topics we are expected to be subscribed to on this connection (and we automatically resubscribe to if the connection drops)
*
* A "Set" and a deep-object-match are used to ensure we only subscribe to a topic once (tracking a list of unique topics we're expected to be connected to)
*/
⋮----
/** Whether this connection has completed authentication (only applies to private connections) */
⋮----
/**
* Whether this connection has completed authentication before for the Websocket API, so it k
* nows to automatically reauth if reconnected
*/
⋮----
/** To reauthenticate on the WS API, which channel do we send to? */
================
File: src/lib/webCryptoAPI.ts
================
import { neverGuard } from './misc-util.js';
⋮----
function bufferToB64(buffer: ArrayBuffer): string
⋮----
export type SignEncodeMethod = 'hex' | 'base64';
export type SignAlgorithm = 'SHA-256' | 'SHA-512';
⋮----
interface UTF8Encoder {
encode(input?: string): Uint8Array;
}
⋮----
encode(input?: string): Uint8Array;
⋮----
export type SignKeyType = 'HMAC' | 'RSASSA-PKCS1-v1_5' | 'Ed25519';
⋮----
export function getSignKeyType(secret: string): SignKeyType
⋮----
// Sometimes, not always, RSA keys include "RSA" in the header. That's a definite RSA key.
⋮----
// RSA keys are significantly longer than Ed25519 keys. 150 accounts for length of header & footer
⋮----
/**
* Import a key for signing based on its type
*/
async function importKey(
pem: string,
type: SignKeyType,
algorithm: SignAlgorithm,
encoder: UTF8Encoder,
): ReturnType<typeof globalThis.crypto.subtle.importKey>
⋮----
// const prefixRSA = /-----BEGIN RSA PRIVATE KEY-----/;
// const prefixEd25519 = /-----BEGIN PRIVATE KEY-----/;
⋮----
// const suffixRSA = /-----END RSA PRIVATE KEY-----/;
// const suffixEd25519 = /-----END PRIVATE KEY-----/;
⋮----
// const base64Key = pem
// .replace(prefixEd25519, '')
// .replace(prefixRSA, '')
// .replace(suffixEd25519, '')
// .replace(suffixRSA, '')
// .replace(/\s+/g, ''); // Remove spaces and newlines
⋮----
/**
* Sign a message, with a secret, using the Web Crypto API
*/
export async function signMessage(
message: string,
secret: string,
method?: SignEncodeMethod,
algorithm: SignAlgorithm = 'SHA-256',
): Promise<string>
⋮----
// Automatically determine encoding method based on key type if not specified
⋮----
export function checkWebCryptoAPISupported()
================
File: src/types/request/futures.types.ts
================
export interface FuturesKlinesRequest {
symbol: string;
start_time: number;
end_time: number;
step?: number;
}
⋮----
/** Get Order Detail - symbol and order_id required. */
export interface GetFuturesOrderRequest {
symbol: string;
order_id: string;
account?: 'futures' | 'copy_trading';
}
⋮----
/** Cancel Order - symbol required. order_id or client_order_id for specific cancel; omit both to cancel all under symbol. */
export interface CancelFuturesOrderRequest {
symbol: string;
order_id?: string;
client_order_id?: string;
}
⋮----
/** Cancel Plan Order - symbol required, order_id or client_order_id for specific cancel. */
export interface CancelFuturesPlanOrderRequest {
symbol: string;
order_id?: string;
client_order_id?: string;
}
⋮----
/** Cancel Trail Order - symbol required, order_id for specific cancel. No client_order_id. */
export interface CancelFuturesTrailOrderRequest {
symbol: string;
order_id?: string;
}
⋮----
export interface FuturesAccountHistoricOrderRequest {
symbol: string;
start_time?: number;
end_time?: number;
account?: string;
}
⋮----
export interface FuturesAccountOpenOrdersRequest {
symbol?: string;
type?: 'limit' | 'market' | 'trailing';
order_state?: 'all' | 'partially_filled';
limit?: number;
}
⋮----
export interface FuturesAccountPlanOrdersRequest {
symbol?: string;
type?: 'limit' | 'market';
limit?: number;
plan_type?: 'plan' | 'profit_loss';
}
⋮----
export interface FuturesAccountTradesRequest {
symbol?: string;
start_time?: number;
end_time?: number;
account?: string;
order_id?: number;
client_order_id?: string;
}
⋮----
export interface FuturesAccountHistoricTransactionRequest {
symbol?: string;
flow_type?: 0 | 1 | 2 | 3 | 4 | 5;
start_time?: number;
end_time?: number;
page_size?: number;
account?: string;
}
⋮----
export interface FuturesAccountTransfersRequest {
currency?: string;
time_start?: number;
time_end?: number;
page: number;
limit: number;
recvWindow?: number;
}
⋮----
export interface SubmitFuturesOrderRequest {
symbol: string;
client_order_id?: string;
side: 1 | 2 | 3 | 4;
mode?: 1 | 2 | 3 | 4;
type?: 'limit' | 'market';
leverage?: string;
open_type?: 'cross' | 'isolated';
size: number;
price?: string;
preset_take_profit_price_type?: 1 | 2;
preset_stop_loss_price_type?: 1 | 2;
preset_take_profit_price?: string;
preset_stop_loss_price?: string;
stp_mode?: number;
}
⋮----
export interface UpdateFuturesLimitOrderRequest {
symbol: string;
order_id?: number;
client_order_id?: string;
price?: string;
size?: string;
}
⋮----
export interface SubmitFuturesPlanOrderRequest {
symbol: string;
type?: 'limit' | 'market' | 'take_profit' | 'stop_loss';
side: 1 | 2 | 3 | 4;
leverage: string;
open_type: 'cross' | 'isolated';
mode?: 1 | 2 | 3 | 4;
size: number;
trigger_price: string;
executive_price?: string;
price_way: 1 | 2;
price_type: 1 | 2;
plan_category?: 1 | 2;
preset_take_profit_price_type?: 1 | 2;
preset_stop_loss_price_type?: 1 | 2;
preset_take_profit_price?: string;
preset_stop_loss_price?: string;
}
⋮----
export interface SubmitFuturesTransferRequest {
currency: string; // Only USDT is supported
amount: string; // Transfer amount, allowed range[0.01,10000000000]
type: 'spot_to_contract' | 'contract_to_spot';
recvWindow?: number; // Trade time limit, allowed range (0,60000], default: 5000 milliseconds
}
⋮----
currency: string; // Only USDT is supported
amount: string; // Transfer amount, allowed range[0.01,10000000000]
⋮----
recvWindow?: number; // Trade time limit, allowed range (0,60000], default: 5000 milliseconds
⋮----
export interface SetFuturesLeverageRequest {
symbol: string; // Symbol of the contract(like BTCUSDT)
leverage?: string; // Order leverage
open_type: 'cross' | 'isolated'; // Open type, required at close position
}
⋮----
symbol: string; // Symbol of the contract(like BTCUSDT)
leverage?: string; // Order leverage
open_type: 'cross' | 'isolated'; // Open type, required at close position
⋮----
export interface TransferFuturesAssetsRequest {
requestNo: string; // UUID, unique identifier, max length 64
amount: string; // Transfer amount
currency: 'USDT'; // Currently only USDT is supported
subAccount: string; // Sub-Account username
}
⋮----
requestNo: string; // UUID, unique identifier, max length 64
amount: string; // Transfer amount
currency: 'USDT'; // Currently only USDT is supported
subAccount: string; // Sub-Account username
⋮----
export interface SubmitFuturesSubToMainSubFromSubRequest {
requestNo: string; // UUID, unique identifier, max length 64
amount: string; // Transfer amount
currency: 'USDT'; // Currently only USDT is supported
}
⋮----
requestNo: string; // UUID, unique identifier, max length 64
amount: string; // Transfer amount
currency: 'USDT'; // Currently only USDT is supported
⋮----
export interface FuturesSubWalletRequest {
subAccount: string; // Sub-Account username
currency?: string; // Currency is optional
}
⋮----
subAccount: string; // Sub-Account username
currency?: string; // Currency is optional
⋮----
export interface FuturesSubTransfersRequest {
subAccount: string;
limit: number; // Range [1,100]
}
⋮----
limit: number; // Range [1,100]
⋮----
export interface FuturesAffiliateRebatesRequest {
user_id?: number;
page: number;
size: number;
currency: string;
rebate_start_time?: number;
rebate_end_time?: number;
register_start_time?: number;
register_end_time?: number;
}
⋮----
export interface FuturesAffiliateTradesRequest {
user_id?: number;
page: number;
type: 1 | 2;
size: number;
start_time: number;
end_time: number;
}
⋮----
export interface SubmitFuturesTPSLOrderRequest {
symbol: string;
type: 'take_profit' | 'stop_loss';
side: 2 | 3;
size?: number;
trigger_price: string;
executive_price: string;
price_type: 1 | 2;
plan_category?: 1 | 2;
client_order_id?: string;
category?: 'limit' | 'market';
}
⋮----
export interface UpdateFuturesPlanOrderRequest {
symbol: string;
order_id?: string;
trigger_price: string;
executive_price?: string;
price_type: 1 | 2;
type: 'limit' | 'market';
}
⋮----
export interface UpdateFuturesPresetPlanOrderRequest {
order_id: string;
symbol: string;
preset_take_profit_price_type?: 1 | 2;
preset_stop_loss_price_type?: 1 | 2;
preset_take_profit_price?: string;
preset_stop_loss_price?: string;
}
⋮----
export interface UpdateFuturesTPSLOrderRequest {
symbol: string;
order_id?: string;
client_order_id?: string;
trigger_price: string;
executive_price?: string;
price_type: 1 | 2;
plan_category?: 1 | 2;
category?: 'limit' | 'market';
}
⋮----
export interface SubmitFuturesTrailOrderRequest {
symbol: string;
side: 1 | 2 | 3 | 4;
leverage: string;
open_type: 'cross' | 'isolated';
size: number;
activation_price: string;
callback_rate: string;
activation_price_type: 1 | 2;
}
⋮----
export interface FuturesAffiliateRebateUserRequest {
cid?: number;
start_time: number;
end_time: number;
page: number;
size: number;
}
⋮----
/** Affiliate invited users deposit / withdrawal records (max 60 days, size max 50). */
export interface FuturesAffiliateDepositWithdrawalListRequest {
page: number;
size: number;
type?: 1 | 2;
cid: number;
start_time: number;
end_time: number;
}
⋮----
export interface FuturesAutoRepaymentRequest {
start_time?: number;
end_time?: number;
page?: number;
size?: number;
from_coin_code?: string;
type?: string;
}
⋮----
export interface FuturesCrossCollateralInterestLogRequest {
start_time?: number;
end_time?: number;
page?: number;
size?: number;
coin_code?: string;
}
⋮----
export interface FuturesAffiliateRebateApiRequest {
cid: number;
start_time: number;
end_time: number;
}
⋮----
export interface SubmitFuturesSimulatedClaimRequest {
currency?: string;
amount?: string;
}
================
File: src/types/response/spot.types.ts
================
import { OrderSide } from './shared.types.js';
⋮----
export interface ServiceStatus {
title: string;
service_type: string;
status: number;
start_time: number;
end_time: number;
}
⋮----
export interface SpotCurrencyV1 {
id: string;
name: string;
withdraw_enabled: boolean;
deposit_enabled: boolean;
}
⋮----
export interface SpotTradingPairDetailsV1 {
symbol: string;
symbol_id: string;
base_currency: string;
quote_currency: string;
quote_increment: string;
base_min_size: string;
base_max_size: string;
price_min_precision: number;
price_max_precision: number;
expiration: string;
min_buy_amount: string;
min_sell_amount: string;
}
⋮----
/**
* [symbol, last, v_24h, qv_24h, open_24h, high_24h, low_24h, fluctuation, bid_px, bid_sz, ask_px, ask_sz, ts]
*/
export type ArrayFormSpotTickerV3 = [
string, // symbol
string, // last
string, // v_24h
string, // qv_24h
string, // open_24h
string, // high_24h
string, // low_24h
string, // fluctuation
string, // bid_px
string, // bid_sz
string, // ask_px
string, // ask_sz
string, // ts
];
⋮----
string, // symbol
string, // last
string, // v_24h
string, // qv_24h
string, // open_24h
string, // high_24h
string, // low_24h
string, // fluctuation
string, // bid_px
string, // bid_sz
string, // ask_px
string, // ask_sz
string, // ts
⋮----
export interface SpotTickerV3 {
symbol: string;
last: string;
v_24h: string;
qv_24h: string;
open_24h: string;
high_24h: string;
low_24h: string;
fluctuation: string;
bid_px: string;
bid_sz: string;
ask_px: string;
ask_sz: string;
ts: string;
}
⋮----
/**
* [t,o,h