Merge pull request #166 from tiagosiebler/expansion
v3.0.0: REST & WebSocket clients for USC options & perps, v3 spot, account asset, copy trading & unified margin APIs
This commit is contained in:
@@ -1,31 +0,0 @@
|
|||||||
version: 2.1
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
docker:
|
|
||||||
- image: cimg/node:15.1
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- restore_cache:
|
|
||||||
# See the configuration reference documentation for more details on using restore_cache and save_cache steps
|
|
||||||
# https://circleci.com/docs/2.0/configuration-reference/?section=reference#save_cache
|
|
||||||
keys:
|
|
||||||
- node-deps-v1-{{ .Branch }}-{{checksum "package-lock.json"}}
|
|
||||||
- run:
|
|
||||||
name: install packages
|
|
||||||
command: npm ci --ignore-scripts
|
|
||||||
- save_cache:
|
|
||||||
key: node-deps-v1-{{ .Branch }}-{{checksum "package-lock.json"}}
|
|
||||||
paths:
|
|
||||||
- ~/.npm
|
|
||||||
- run:
|
|
||||||
name: Run Build
|
|
||||||
command: npm run build
|
|
||||||
- run:
|
|
||||||
name: Run Tests
|
|
||||||
command: npm run test
|
|
||||||
|
|
||||||
workflows:
|
|
||||||
integrationtests:
|
|
||||||
jobs:
|
|
||||||
- test
|
|
||||||
36
.github/workflows/integrationtest.yml
vendored
Normal file
36
.github/workflows/integrationtest.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: "Build & Test"
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
# on:
|
||||||
|
# # pull_request:
|
||||||
|
# # branches:
|
||||||
|
# # - "master"
|
||||||
|
# push:
|
||||||
|
# branches:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: "Build & Test"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout source code"
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install
|
||||||
|
run: npm ci --ignore-scripts
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: npm run test
|
||||||
|
env:
|
||||||
|
API_KEY_COM: ${{ secrets.API_KEY_COM }}
|
||||||
|
API_SECRET_COM: ${{ secrets.API_SECRET_COM }}
|
||||||
17
.github/workflows/npmpublish.yml
vendored
17
.github/workflows/npmpublish.yml
vendored
@@ -9,16 +9,6 @@ on:
|
|||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 12
|
|
||||||
#- run: npm ci
|
|
||||||
#- run: npm test
|
|
||||||
|
|
||||||
publish-npm:
|
publish-npm:
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -37,7 +27,7 @@ jobs:
|
|||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
if: steps.version-updated.outputs.has-updated
|
if: steps.version-updated.outputs.has-updated
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 16
|
||||||
registry-url: https://registry.npmjs.org/
|
registry-url: https://registry.npmjs.org/
|
||||||
|
|
||||||
- run: npm ci --ignore-scripts
|
- run: npm ci --ignore-scripts
|
||||||
@@ -51,6 +41,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
||||||
|
|
||||||
|
- name: Create Github Release
|
||||||
|
if: steps.version-updated.outputs.has-updated
|
||||||
|
id: create_release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
|
||||||
#publish-gpr:
|
#publish-gpr:
|
||||||
#needs: build
|
#needs: build
|
||||||
#runs-on: ubuntu-latest
|
#runs-on: ubuntu-latest
|
||||||
|
|||||||
309
README.md
309
README.md
@@ -1,4 +1,4 @@
|
|||||||
# bybit-api
|
# Node.js & Typescript Bybit API SDK
|
||||||
[](https://circleci.com/gh/tiagosiebler/bybit-api)
|
[](https://circleci.com/gh/tiagosiebler/bybit-api)
|
||||||
[][1] [][1] [][1]
|
[][1] [][1] [][1]
|
||||||
[][1]
|
[][1]
|
||||||
@@ -9,9 +9,10 @@
|
|||||||
[1]: https://www.npmjs.com/package/bybit-api
|
[1]: https://www.npmjs.com/package/bybit-api
|
||||||
|
|
||||||
Node.js connector for the Bybit APIs and WebSockets:
|
Node.js connector for the Bybit APIs and WebSockets:
|
||||||
|
- Complete integration with all bybit APIs.
|
||||||
- TypeScript support (with type declarations for most API requests & responses).
|
- TypeScript support (with type declarations for most API requests & responses).
|
||||||
- Integration tests with real API calls validating any changes before they reach npm.
|
- Over 300 integration tests making real API calls & WebSocket connections, validating any changes before they reach npm.
|
||||||
- Robust WebSocket integration with connection heartbeats & automatic reconnection.
|
- Robust WebSocket integration with configurable connection heartbeats & automatic reconnect then resubscribe workflows.
|
||||||
- Browser support (via webpack bundle - see "Browser Usage" below).
|
- Browser support (via webpack bundle - see "Browser Usage" below).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -34,228 +35,145 @@ Check out my related projects:
|
|||||||
- [awesome-crypto-examples](https://github.com/tiagosiebler/awesome-crypto-examples)
|
- [awesome-crypto-examples](https://github.com/tiagosiebler/awesome-crypto-examples)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
Most methods accept JS objects. These can be populated using parameters specified by Bybit's API documentation.
|
Most methods accept JS objects. These can be populated using parameters specified by Bybit's API documentation, or check the type definition in each class within this repository (see table below for convenient links to each class).
|
||||||
- [Bybit API Inverse Documentation](https://bybit-exchange.github.io/docs/inverse/#t-introduction).
|
- [Bybit API Docs (choose API category from the tabs at the top)](https://bybit-exchange.github.io/docs/futuresV2/inverse/#t-introduction).
|
||||||
- [Bybit API Inverse Futures Documentation](https://bybit-exchange.github.io/docs/inverse_futures/#t-introduction).
|
|
||||||
- [Bybit API Linear Documentation](https://bybit-exchange.github.io/docs/linear/#t-introduction)
|
|
||||||
|
|
||||||
## REST Clients
|
|
||||||
Each REST API category has a dedicated REST client. Here are the REST clients and their API group:
|
|
||||||
| Class | Description |
|
|
||||||
|:-----------------------------------------------------: |:------------------------------: |
|
|
||||||
| [InverseClient](src/inverse-client.ts) | Inverse Perpetual Futures (v2) |
|
|
||||||
| [LinearClient](src/linear-client.ts) | USDT Perpetual Futures (v2) |
|
|
||||||
| [InverseFuturesClient](src/inverse-futures-client.ts) | Inverse Futures (v2) |
|
|
||||||
| [SpotClient](src/spot-client.ts) | Spot Markets |
|
|
||||||
| USDC Options & Perpetual Contracts | Under Development |
|
|
||||||
| Derivatives V3 unified margin | Under Development |
|
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
The connector is written in TypeScript. A pure JavaScript version can be built using `npm run build`, which is also the version published to [npm](https://www.npmjs.com/package/bybit-api). This connector is fully compatible with both TypeScript and pure JavaScript projects.
|
This connector is fully compatible with both TypeScript and pure JavaScript projects, while the connector is written in TypeScript. A pure JavaScript version can be built using `npm run build`, which is also the version published to [npm](https://www.npmjs.com/package/bybit-api).
|
||||||
|
|
||||||
|
The version on npm is the output from the `build` command and can be used in projects without TypeScript (although TypeScript is definitely recommended).
|
||||||
|
|
||||||
- [src](./src) - the whole connector written in TypeScript
|
- [src](./src) - the whole connector written in TypeScript
|
||||||
- [lib](./lib) - the JavaScript version of the project (built from TypeScript). This should not be edited directly, as it will be overwritten with each release.
|
- [lib](./lib) - the JavaScript version of the project (built from TypeScript). This should not be edited directly, as it will be overwritten with each release.
|
||||||
- [dist](./dist) - the packed bundle of the project for use in browser environments.
|
- [dist](./dist) - the webpack bundle of the project for use in browser environments (see guidance on webpack below).
|
||||||
- [examples](./examples) - some implementation examples & demonstrations. Contributions are welcome!
|
- [examples](./examples) - some implementation examples & demonstrations. Contributions are welcome!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
## REST API Clients
|
||||||
|
Each REST API group has a dedicated REST client. To avoid confusion, here are the available REST clients and the corresponding API groups:
|
||||||
|
| Class | Description |
|
||||||
|
|:------------------------------------------------------------------: |:----------------------------------------------------------------------------------------------------------------------------: |
|
||||||
|
| [InverseClient](src/inverse-client.ts) | [Inverse Perpetual Futures (v2) APIs](https://bybit-exchange.github.io/docs/futuresV2/inverse/) |
|
||||||
|
| [LinearClient](src/linear-client.ts) | [USDT Perpetual Futures (v2) APIs](https://bybit-exchange.github.io/docs/futuresV2/linear/#t-introduction) |
|
||||||
|
| [InverseFuturesClient](src/inverse-futures-client.ts) | [Inverse Futures (v2) APIs](https://bybit-exchange.github.io/docs/futuresV2/inverse_futures/#t-introduction) |
|
||||||
|
| [USDCPerpetualClient](src/usdc-perpetual-client.ts) | [USDC Perpetual APIs](https://bybit-exchange.github.io/docs/usdc/option/?console#t-querydeliverylog) |
|
||||||
|
| [USDCOptionClient](src/usdc-option-client.ts) | [USDC Option APIs](https://bybit-exchange.github.io/docs/usdc/option/#t-introduction) |
|
||||||
|
| [UnifiedMarginClient](src/unified-margin-client.ts) | [Derivatives (v3) unified margin APIs](https://bybit-exchange.github.io/docs/derivativesV3/unified_margin/#t-introduction) |
|
||||||
|
| [SpotClientV3](src/spot-client-v3.ts) | [Spot Market (v3) APIs](https://bybit-exchange.github.io/docs/spot/v3/#t-introduction) |
|
||||||
|
| [~SpotClient~](src/spot-client.ts) (deprecated, v3 client recommended)| [Spot Market (v1) APIs](https://bybit-exchange.github.io/docs/spot/v1/#t-introduction) |
|
||||||
|
| [AccountAssetClient](src/account-asset-client.ts) | [Account Asset APIs](https://bybit-exchange.github.io/docs/account_asset/#t-introduction) |
|
||||||
|
| [CopyTradingClient](src/copy-trading-client.ts) | [Copy Trading APIs](https://bybit-exchange.github.io/docs/copy_trading/#t-introduction) |
|
||||||
|
| [WebsocketClient](src/websocket-client.ts) | All WebSocket Events (Public & Private for all API categories) |
|
||||||
|
|
||||||
# Usage
|
Examples for using each client can be found in:
|
||||||
Create API credentials at Bybit
|
- the [examples](./examples) folder.
|
||||||
|
- the [awesome-crypto-examples](https://github.com/tiagosiebler/awesome-crypto-examples) repository.
|
||||||
|
|
||||||
|
If you're missing an example, you're welcome to request one. Priority will be given to [github sponsors](https://github.com/sponsors/tiagosiebler).
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
Create API credentials on Bybit's website:
|
||||||
- [Livenet](https://bybit.com/app/user/api-management?affiliate_id=9410&language=en-US&group_id=0&group_type=1)
|
- [Livenet](https://bybit.com/app/user/api-management?affiliate_id=9410&language=en-US&group_id=0&group_type=1)
|
||||||
- [Testnet](https://testnet.bybit.com/app/user/api-management)
|
- [Testnet](https://testnet.bybit.com/app/user/api-management)
|
||||||
|
|
||||||
## REST API Clients
|
All REST clients have can be used in a similar way. However, method names, parameters and responses may vary depending on the API category you're using!
|
||||||
|
|
||||||
There are three REST API modules as there are some differences in each contract type.
|
Not sure which function to call or which parameters to use? Click the class name in the table above to look at all the function names (they are in the same order as the official API docs), and check the API docs for a list of endpoints/paramters/responses.
|
||||||
1. `InverseClient` for inverse perpetual
|
|
||||||
2. `InverseFuturesClient` for inverse futures
|
|
||||||
3. `LinearClient` for linear perpetual
|
|
||||||
|
|
||||||
### REST Inverse
|
```typescript
|
||||||
To use the inverse REST APIs, import the `InverseClient`:
|
const {
|
||||||
|
InverseClient,
|
||||||
```javascript
|
LinearClient,
|
||||||
const { InverseClient } = require('bybit-api');
|
InverseFuturesClient,
|
||||||
|
SpotClient,
|
||||||
|
SpotClientV3,
|
||||||
|
UnifiedMarginClient,
|
||||||
|
USDCOptionClient,
|
||||||
|
USDCPerpetualClient,
|
||||||
|
AccountAssetClient,
|
||||||
|
CopyTradingClient,
|
||||||
|
} = require('bybit-api');
|
||||||
|
|
||||||
const restClientOptions = {
|
const restClientOptions = {
|
||||||
// override the max size of the request window (in ms)
|
/** Your API key. Optional, if you plan on making private api calls */
|
||||||
|
key?: string;
|
||||||
|
|
||||||
|
/** Your API secret. Optional, if you plan on making private api calls */
|
||||||
|
secret?: string;
|
||||||
|
|
||||||
|
/** Set to `true` to connect to testnet. Uses the live environment by default. */
|
||||||
|
testnet?: boolean;
|
||||||
|
|
||||||
|
/** Override the max size of the request window (in ms) */
|
||||||
recv_window?: number;
|
recv_window?: number;
|
||||||
|
|
||||||
// how often to sync time drift with bybit servers
|
/** Disabled by default. This can help on machines with consistent latency problems. */
|
||||||
sync_interval_ms?: number;
|
|
||||||
|
|
||||||
// Default: false. Disable above sync mechanism if true.
|
|
||||||
enable_time_sync?: boolean;
|
enable_time_sync?: boolean;
|
||||||
|
|
||||||
// Default: false. If true, we'll throw errors if any params are undefined
|
/** How often to sync time drift with bybit servers */
|
||||||
|
sync_interval_ms?: number | string;
|
||||||
|
|
||||||
|
/** Default: false. If true, we'll throw errors if any params are undefined */
|
||||||
strict_param_validation?: boolean;
|
strict_param_validation?: boolean;
|
||||||
|
|
||||||
// Optionally override API protocol + domain
|
/**
|
||||||
// e.g 'https://api.bytick.com'
|
* Optionally override API protocol + domain
|
||||||
|
* e.g baseUrl: 'https://api.bytick.com'
|
||||||
|
**/
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
|
|
||||||
// Default: true. whether to try and post-process request exceptions.
|
/** Default: true. whether to try and post-process request exceptions. */
|
||||||
parse_exceptions?: boolean;
|
parse_exceptions?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const API_KEY = 'xxx';
|
const API_KEY = 'xxx';
|
||||||
const PRIVATE_KEY = 'yyy';
|
const API_SECRET = 'yyy';
|
||||||
const useLivenet = false;
|
const useTestnet = false;
|
||||||
|
|
||||||
const client = new InverseClient(
|
const client = new InverseClient({
|
||||||
API_KEY,
|
key: API_KEY,
|
||||||
PRIVATE_KEY,
|
secret: API_SECRET,
|
||||||
|
testnet: useTestnet
|
||||||
// optional, uses testnet by default. Set to 'true' to use livenet.
|
},
|
||||||
useLivenet,
|
|
||||||
|
|
||||||
// restClientOptions,
|
|
||||||
// requestLibraryOptions
|
// requestLibraryOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// For public-only API calls, simply don't provide a key & secret or set them to undefined
|
||||||
|
// const client = new InverseClient({});
|
||||||
|
|
||||||
client.getApiKeyInfo()
|
client.getApiKeyInfo()
|
||||||
.then(result => {
|
.then(result => {
|
||||||
console.log("apiKey result: ", result);
|
console.log("getApiKeyInfo result: ", result);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error("apiKey error: ", err);
|
console.error("getApiKeyInfo error: ", err);
|
||||||
});
|
});
|
||||||
|
|
||||||
client.getOrderBook({ symbol: 'BTCUSD' })
|
client.getOrderBook({ symbol: 'BTCUSD' })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
console.log("getOrderBook inverse result: ", result);
|
console.log("getOrderBook result: ", result);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.error("getOrderBook inverse error: ", err);
|
console.error("getOrderBook error: ", err);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
See [inverse-client.ts](./src/inverse-client.ts) for further information.
|
|
||||||
|
|
||||||
### REST Inverse Futures
|
|
||||||
To use the inverse futures REST APIs, import the `InverseFuturesClient`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const { InverseFuturesClient } = require('bybit-api');
|
|
||||||
|
|
||||||
const API_KEY = 'xxx';
|
|
||||||
const PRIVATE_KEY = 'yyy';
|
|
||||||
const useLivenet = false;
|
|
||||||
|
|
||||||
const client = new InverseFuturesClient(
|
|
||||||
API_KEY,
|
|
||||||
PRIVATE_KEY,
|
|
||||||
|
|
||||||
// optional, uses testnet by default. Set to 'true' to use livenet.
|
|
||||||
useLivenet,
|
|
||||||
|
|
||||||
// restClientOptions,
|
|
||||||
// requestLibraryOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
client.getApiKeyInfo()
|
|
||||||
.then(result => {
|
|
||||||
console.log("apiKey result: ", result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("apiKey error: ", err);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.getOrderBook({ symbol: 'BTCUSDH21' })
|
|
||||||
.then(result => {
|
|
||||||
console.log("getOrderBook inverse futures result: ", result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("getOrderBook inverse futures error: ", err);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
See [inverse-futures-client.ts](./src/inverse-futures-client.ts) for further information.
|
|
||||||
|
|
||||||
### REST Linear
|
|
||||||
To use the Linear (USDT) REST APIs, import the `LinearClient`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const { LinearClient } = require('bybit-api');
|
|
||||||
|
|
||||||
const API_KEY = 'xxx';
|
|
||||||
const PRIVATE_KEY = 'yyy';
|
|
||||||
const useLivenet = false;
|
|
||||||
|
|
||||||
const client = new LinearClient(
|
|
||||||
API_KEY,
|
|
||||||
PRIVATE_KEY,
|
|
||||||
|
|
||||||
// optional, uses testnet by default. Set to 'true' to use livenet.
|
|
||||||
useLivenet,
|
|
||||||
|
|
||||||
// restClientOptions,
|
|
||||||
// requestLibraryOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
client.getApiKeyInfo()
|
|
||||||
.then(result => {
|
|
||||||
console.log(result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.getOrderBook({ symbol: 'BTCUSDT' })
|
|
||||||
.then(result => {
|
|
||||||
console.log("getOrderBook linear result: ", result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("getOrderBook linear error: ", err);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
See [linear-client.ts](./src/linear-client.ts) for further information.
|
|
||||||
|
|
||||||
### REST Spot
|
|
||||||
To use the Spot REST APIs, import the `SpotClient`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const { SpotClient } = require('bybit-api');
|
|
||||||
|
|
||||||
const API_KEY = 'xxx';
|
|
||||||
const PRIVATE_KEY = 'yyy';
|
|
||||||
const useLivenet = false;
|
|
||||||
|
|
||||||
const client = new SpotClient(
|
|
||||||
API_KEY,
|
|
||||||
PRIVATE_KEY,
|
|
||||||
|
|
||||||
// optional, uses testnet by default. Set to 'true' to use livenet.
|
|
||||||
useLivenet,
|
|
||||||
|
|
||||||
// restClientOptions,
|
|
||||||
// requestLibraryOptions
|
|
||||||
);
|
|
||||||
|
|
||||||
client.getSymbols()
|
|
||||||
.then(result => {
|
|
||||||
console.log(result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.getBalances()
|
|
||||||
.then(result => {
|
|
||||||
console.log("getBalances result: ", result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("getBalances error: ", err);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
See [spot-client.ts](./src/spot-client.ts) for further information.
|
|
||||||
|
|
||||||
## WebSockets
|
## WebSockets
|
||||||
Inverse, linear & spot WebSockets can be used via a shared `WebsocketClient`. However, make sure to make one instance of WebsocketClient per market type (spot vs inverse vs linear vs linearfutures):
|
All API groups can be used via a shared `WebsocketClient`. However, to listen to multiple API groups at once, you will need to make one WebsocketClient instance per API group.
|
||||||
|
|
||||||
|
The WebsocketClient can be configured to a specific API group using the market parameter. These are the currently available API groups:
|
||||||
|
| API Category | Market | Description |
|
||||||
|
|:----------------------------: |:-------------------: |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Unified Margin - Options | `market: 'unifiedOption'`| The [derivatives v3](https://bybit-exchange.github.io/docs/derivativesV3/unified_margin/#t-websocket) category for unified margin. Note: public topics only support options topics. If you need USDC/USDT perps, use `unifiedPerp` instead. |
|
||||||
|
| Unified Margin - Perps | `market: 'unifiedPerp'` | The [derivatives v3](https://bybit-exchange.github.io/docs/derivativesV3/unified_margin/#t-websocket) category for unified margin. Note: public topics only support USDT/USDC perpetual topics - use `unifiedOption` if you need public options topics. |
|
||||||
|
| Futures v2 - Inverse Perps | `market: 'inverse'` | The [inverse v2 perps](https://bybit-exchange.github.io/docs/futuresV2/inverse/#t-websocket) category. |
|
||||||
|
| Futures v2 - USDT Perps | `market: 'linear'` | The [USDT/linear v2 perps](https://bybit-exchange.github.io/docs/futuresV2/linear/#t-websocket) category. |
|
||||||
|
| Futures v2 - Inverse Futures | `market: 'inverse'` | The [inverse futures v2](https://bybit-exchange.github.io/docs/futuresV2/inverse_futures/#t-websocket) category uses the same market as inverse perps. |
|
||||||
|
| Spot v3 | `market: 'spotv3'` | The [spot v3](https://bybit-exchange.github.io/docs/spot/v3/#t-websocket) category. |
|
||||||
|
| Spot v1 | `market: 'spot'` | The older [spot v1](https://bybit-exchange.github.io/docs/spot/v1/#t-websocket) category. Use the `spotv3` market if possible, as the v1 category does not have automatic re-subscribe if reconnected. |
|
||||||
|
| Copy Trading | `market: 'linear'` | The [copy trading](https://bybit-exchange.github.io/docs/copy_trading/#t-websocket) category. Use the linear market to listen to all copy trading topics. |
|
||||||
|
| USDC Perps | `market: 'usdcPerp` | The [USDC perps](https://bybit-exchange.github.io/docs/usdc/perpetual/#t-websocket) category. |
|
||||||
|
| USDC Options | `market: 'usdcOption'`| The [USDC options](https://bybit-exchange.github.io/docs/usdc/option/#t-websocket) category. |
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const { WebsocketClient } = require('bybit-api');
|
const { WebsocketClient } = require('bybit-api');
|
||||||
@@ -271,15 +189,19 @@ const wsConfig = {
|
|||||||
The following parameters are optional:
|
The following parameters are optional:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// defaults to false == testnet. Set to true for livenet.
|
// defaults to true == livenet
|
||||||
// livenet: true
|
// testnet: false
|
||||||
|
|
||||||
// NOTE: to listen to multiple markets (spot vs inverse vs linear vs linearfutures) at once, make one WebsocketClient instance per market
|
// NOTE: to listen to multiple markets (spot vs inverse vs linear vs linearfutures) at once, make one WebsocketClient instance per market
|
||||||
|
|
||||||
// defaults to inverse:
|
market: 'linear',
|
||||||
// market: 'inverse'
|
// market: 'inverse',
|
||||||
// market: 'linear'
|
// market: 'spot',
|
||||||
// market: 'spot'
|
// market: 'spotv3',
|
||||||
|
// market: 'usdcOption',
|
||||||
|
// market: 'usdcPerp',
|
||||||
|
// market: 'unifiedPerp',
|
||||||
|
// market: 'unifiedOption',
|
||||||
|
|
||||||
// how long to wait (in ms) before deciding the connection should be terminated & reconnected
|
// how long to wait (in ms) before deciding the connection should be terminated & reconnected
|
||||||
// pongTimeout: 1000,
|
// pongTimeout: 1000,
|
||||||
@@ -328,18 +250,14 @@ ws.on('close', () => {
|
|||||||
console.log('connection closed');
|
console.log('connection closed');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Optional: Listen to raw error events.
|
// Optional: Listen to raw error events. Recommended.
|
||||||
// Note: responses to invalid topics are currently only sent in the "response" event.
|
|
||||||
ws.on('error', err => {
|
ws.on('error', err => {
|
||||||
console.error('ERR', err);
|
console.error('error', err);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
See [websocket-client.ts](./src/websocket-client.ts) for further information.
|
See [websocket-client.ts](./src/websocket-client.ts) for further information.
|
||||||
|
|
||||||
Note: for linear websockets, pass `linear: true` in the constructor options when instancing the `WebsocketClient`. To connect to both linear and inverse websockets, make two instances of the WebsocketClient.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Customise Logging
|
## Customise Logging
|
||||||
@@ -349,11 +267,14 @@ Pass a custom logger which supports the log methods `silly`, `debug`, `notice`,
|
|||||||
const { WebsocketClient, DefaultLogger } = require('bybit-api');
|
const { WebsocketClient, DefaultLogger } = require('bybit-api');
|
||||||
|
|
||||||
// Disable all logging on the silly level
|
// Disable all logging on the silly level
|
||||||
DefaultLogger.silly = () => {};
|
const customLogger = {
|
||||||
|
...DefaultLogger,
|
||||||
|
silly: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
const ws = new WebsocketClient(
|
const ws = new WebsocketClient(
|
||||||
{ key: 'xxx', secret: 'yyy' },
|
{ key: 'xxx', secret: 'yyy' },
|
||||||
DefaultLogger
|
customLogger
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -381,7 +302,7 @@ Or buy me a coffee using any of these:
|
|||||||
- ETH (ERC20): `0xd773d8e6a50758e1ada699bb6c4f98bb4abf82da`
|
- ETH (ERC20): `0xd773d8e6a50758e1ada699bb6c4f98bb4abf82da`
|
||||||
|
|
||||||
#### pixtron
|
#### pixtron
|
||||||
The original library was started by @pixtron. If this library helps you to trade better on bybit, feel free to donate a coffee to @pixtron:
|
An early generation of this library was started by @pixtron. If this library helps you to trade better on bybit, feel free to donate a coffee to @pixtron:
|
||||||
- BTC `1Fh1158pXXudfM6ZrPJJMR7Y5SgZUz4EdF`
|
- BTC `1Fh1158pXXudfM6ZrPJJMR7Y5SgZUz4EdF`
|
||||||
- ETH `0x21aEdeC53ab7593b77C9558942f0c9E78131e8d7`
|
- ETH `0x21aEdeC53ab7593b77C9558942f0c9E78131e8d7`
|
||||||
- LTC `LNdHSVtG6UWsriMYLJR3qLdfVNKwJ6GSLF`
|
- LTC `LNdHSVtG6UWsriMYLJR3qLdfVNKwJ6GSLF`
|
||||||
|
|||||||
@@ -1,223 +0,0 @@
|
|||||||
# Websocket API
|
|
||||||
## Class: WebsocketClient
|
|
||||||
|
|
||||||
The `WebsocketClient` inherits from `EventEmitter`. After establishing a
|
|
||||||
connection, the client sends heartbeats in regular intervalls, and reconnects
|
|
||||||
to the server once connection has been lost.
|
|
||||||
|
|
||||||
### new WebsocketClient([options][, logger])
|
|
||||||
- `options` {Object} Configuration options
|
|
||||||
- `key` {String} Bybit API Key. Only needed if private topics are subscribed
|
|
||||||
- `secret` {String} Bybit private Key. Only needed if private topics are
|
|
||||||
subscribed
|
|
||||||
- `livenet` {Bool} Weather to connect to livenet (`true`). Default `false`.
|
|
||||||
- `pingInterval` {Integer} Interval in ms for heartbeat ping. Default: `10000`,
|
|
||||||
- `pongTimeout` {Integer} Timeout in ms waiting for heartbeat pong response
|
|
||||||
from server. Default: `1000`,
|
|
||||||
- `reconnectTimeout` {Integer} Timeout in ms the client waits before trying
|
|
||||||
to reconnect after a lost connection. Default: 500
|
|
||||||
- `logger` {Object} Optional custom logger
|
|
||||||
|
|
||||||
Custom logger must contain the following methods:
|
|
||||||
```js
|
|
||||||
const logger = {
|
|
||||||
silly: function(message, data) {},
|
|
||||||
debug: function(message, data) {},
|
|
||||||
notice: function(message, data) {},
|
|
||||||
info: function(message, data) {},
|
|
||||||
warning: function(message, data) {},
|
|
||||||
error: function(message, data) {},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ws.subscribe(topics)
|
|
||||||
- `topics` {String|Array} Single topic as string or multiple topics as array of strings.
|
|
||||||
Subscribe to one or multiple topics. See [available topics](#available-topics)
|
|
||||||
|
|
||||||
### ws.unsubscribe(topics)
|
|
||||||
- `topics` {String|Array} Single topic as string or multiple topics as array of strings.
|
|
||||||
Unsubscribe from one or multiple topics.
|
|
||||||
|
|
||||||
### ws.close()
|
|
||||||
Close the connection to the server.
|
|
||||||
|
|
||||||
### Event: 'open'
|
|
||||||
Emmited when the connection has been opened for the first time.
|
|
||||||
|
|
||||||
### Event: 'reconnected'
|
|
||||||
Emmited when the client has been opened after a reconnect.
|
|
||||||
|
|
||||||
### Event: 'update'
|
|
||||||
- `message` {Object}
|
|
||||||
- `topic` {String} the topic for which the update occured
|
|
||||||
- `data` {Array|Object} updated data (see docs for each [topic](#available-topics)).
|
|
||||||
- `type` {String} Some topics might have different update types (see docs for each [topic](#available-topics)).
|
|
||||||
|
|
||||||
Emmited whenever an update to a subscribed topic occurs.
|
|
||||||
|
|
||||||
### Event: 'response'
|
|
||||||
- `response` {Object}
|
|
||||||
- `success` {Bool}
|
|
||||||
- `ret_msg` {String} empty if operation was successfull, otherwise error message.
|
|
||||||
- `conn_id` {String} connection id
|
|
||||||
- `request` {Object} Original request, to which the response belongs
|
|
||||||
- `op` {String} operation
|
|
||||||
- `args` {Array} Request Arguments
|
|
||||||
|
|
||||||
Emited when the server responds to an operation sent by the client (usually after subscribing to a topic).
|
|
||||||
|
|
||||||
### Event: 'close'
|
|
||||||
Emitted when the connection has been finally closed, after a call to `ws.close()`
|
|
||||||
|
|
||||||
### Event: 'reconnect'
|
|
||||||
Emitted when the connection has been closed, but the client will try to reconnect.
|
|
||||||
|
|
||||||
### Event: 'error'
|
|
||||||
- `error` {Error}
|
|
||||||
|
|
||||||
Emitted when an error occurs.
|
|
||||||
|
|
||||||
## Available Topics
|
|
||||||
Generaly all [public](https://bybit-exchange.github.io/docs/inverse/#t-publictopics) and [private](https://bybit-exchange.github.io/docs/inverse/#t-privatetopics)
|
|
||||||
topics are available.
|
|
||||||
|
|
||||||
### Private topics
|
|
||||||
#### Positions of your account
|
|
||||||
All positions of your account.
|
|
||||||
Topic: `position`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketposition)
|
|
||||||
|
|
||||||
#### Execution message
|
|
||||||
Execution message, whenever an order has been (partially) filled.
|
|
||||||
Topic: `execution`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketexecution)
|
|
||||||
|
|
||||||
#### Update for your orders
|
|
||||||
Updates for your active orders
|
|
||||||
Topic: `order`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketorder)
|
|
||||||
|
|
||||||
#### Update for your conditional orders
|
|
||||||
Updates for your active conditional orders
|
|
||||||
Topic: `stop_order`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketstoporder)
|
|
||||||
|
|
||||||
### Public topics
|
|
||||||
#### Candlestick chart
|
|
||||||
Candlestick OHLC "candles" for selected symbol and interval.
|
|
||||||
Example topic: `klineV2.BTCUSD.1m`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketklinev2)
|
|
||||||
|
|
||||||
#### Real-time trading information
|
|
||||||
All trades as they occur.
|
|
||||||
Topic: `trade`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websockettrade)
|
|
||||||
|
|
||||||
#### Daily insurance fund update
|
|
||||||
Topic: `insurance`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketinsurance)
|
|
||||||
|
|
||||||
#### OrderBook of 25 depth per side
|
|
||||||
OrderBook for selected symbol
|
|
||||||
Example topic: `orderBookL2_25.BTCUSD`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketorderbook25)
|
|
||||||
|
|
||||||
#### OrderBook of 200 depth per side
|
|
||||||
OrderBook for selected symbol
|
|
||||||
Example topic: `orderBook_200.100ms.BTCUS`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketorderbook200)
|
|
||||||
|
|
||||||
#### Latest information for symbol
|
|
||||||
Latest information for selected symbol
|
|
||||||
Example topic: `instrument_info.100ms.BTCUSD`
|
|
||||||
|
|
||||||
[See bybit documentation](https://bybit-exchange.github.io/docs/inverse/#t-websocketinstrumentinfo)
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
### Klines
|
|
||||||
```javascript
|
|
||||||
const {WebsocketClient} = require('bybit-api');
|
|
||||||
|
|
||||||
const API_KEY = 'xxx';
|
|
||||||
const PRIVATE_KEY = 'yyy';
|
|
||||||
|
|
||||||
const ws = new WebsocketClient({key: API_KEY, secret: PRIVATE_KEY});
|
|
||||||
|
|
||||||
ws.subscribe(['position', 'execution', 'trade']);
|
|
||||||
ws.subscribe('kline.BTCUSD.1m');
|
|
||||||
|
|
||||||
ws.on('open', function() {
|
|
||||||
console.log('connection open');
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('update', function(message) {
|
|
||||||
console.log('update', message);
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('response', function(response) {
|
|
||||||
console.log('response', response);
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('close', function() {
|
|
||||||
console.log('connection closed');
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('error', function(err) {
|
|
||||||
console.error('ERR', err);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### OrderBook Events
|
|
||||||
```javascript
|
|
||||||
const { WebsocketClient, DefaultLogger } = require('bybit-api');
|
|
||||||
const { OrderBooksStore, OrderBookLevel } = require('orderbooks');
|
|
||||||
|
|
||||||
const OrderBooks = new OrderBooksStore({ traceLog: true, checkTimestamps: false });
|
|
||||||
|
|
||||||
// connect to a websocket and relay orderbook events to handlers
|
|
||||||
DefaultLogger.silly = () => {};
|
|
||||||
const ws = new WebsocketClient({ livenet: true });
|
|
||||||
ws.on('update', message => {
|
|
||||||
if (message.topic.toLowerCase().startsWith('orderbook')) {
|
|
||||||
return handleOrderbookUpdate(message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ws.subscribe('orderBookL2_25.BTCUSD');
|
|
||||||
|
|
||||||
// parse orderbook messages, detect snapshot vs delta, and format properties using OrderBookLevel
|
|
||||||
const handleOrderbookUpdate = message => {
|
|
||||||
const { topic, type, data, timestamp_e6 } = message;
|
|
||||||
const [ topicKey, symbol ] = topic.split('.');
|
|
||||||
|
|
||||||
if (type == 'snapshot') {
|
|
||||||
return OrderBooks.handleSnapshot(symbol, data.map(mapBybitBookSlice), timestamp_e6 / 1000, message).print();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == 'delta') {
|
|
||||||
const deleteLevels = data.delete.map(mapBybitBookSlice);
|
|
||||||
const updateLevels = data.update.map(mapBybitBookSlice);
|
|
||||||
const insertLevels = data.insert.map(mapBybitBookSlice);
|
|
||||||
return OrderBooks.handleDelta(
|
|
||||||
symbol,
|
|
||||||
deleteLevels,
|
|
||||||
updateLevels,
|
|
||||||
insertLevels,
|
|
||||||
timestamp_e6 / 1000
|
|
||||||
).print();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Low level map of exchange properties to expected local properties
|
|
||||||
const mapBybitBookSlice = level => {
|
|
||||||
return OrderBookLevel(level.symbol, +level.price, level.side, level.size);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { SpotClient } from '../src/index';
|
import { SpotClientV3 } from '../src/index';
|
||||||
|
|
||||||
// or
|
// or
|
||||||
// import { SpotClient } from 'bybit-api';
|
// import { SpotClientV3 } from 'bybit-api';
|
||||||
|
|
||||||
const client = new SpotClient();
|
const client = new SpotClientV3();
|
||||||
|
|
||||||
const symbol = 'BTCUSDT';
|
const symbol = 'BTCUSDT';
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { DefaultLogger } from '../src';
|
import { WebsocketClient, WS_KEY_MAP, DefaultLogger } from '../src';
|
||||||
import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
|
|
||||||
|
|
||||||
// or
|
// or
|
||||||
// import { DefaultLogger, WebsocketClient } from 'bybit-api';
|
// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bybit-api';
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const logger = {
|
const logger = {
|
||||||
...DefaultLogger,
|
...DefaultLogger,
|
||||||
// silly: () => {},
|
silly: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const key = process.env.API_KEY;
|
const key = process.env.API_KEY;
|
||||||
@@ -25,7 +24,7 @@ import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
|
|||||||
key: key,
|
key: key,
|
||||||
secret: secret,
|
secret: secret,
|
||||||
market: market,
|
market: market,
|
||||||
livenet: true,
|
// testnet: true,
|
||||||
restOptions: {
|
restOptions: {
|
||||||
// enable_time_sync: true,
|
// enable_time_sync: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,37 +1,46 @@
|
|||||||
import { DefaultLogger } from '../src';
|
import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src';
|
||||||
import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
|
|
||||||
|
|
||||||
// or
|
// or
|
||||||
// import { DefaultLogger, WebsocketClient } from 'bybit-api';
|
// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bybit-api';
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const logger = {
|
const logger = {
|
||||||
...DefaultLogger,
|
...DefaultLogger,
|
||||||
// silly: () => {},
|
silly: (...params) => console.log('silly', ...params),
|
||||||
};
|
};
|
||||||
|
|
||||||
const wsClient = new WebsocketClient({
|
const wsClient = new WebsocketClient(
|
||||||
|
{
|
||||||
// key: key,
|
// key: key,
|
||||||
// secret: secret,
|
// secret: secret,
|
||||||
// market: 'inverse',
|
|
||||||
// market: 'linear',
|
// market: 'linear',
|
||||||
market: 'spot',
|
// market: 'inverse',
|
||||||
}, logger);
|
// market: 'spot',
|
||||||
|
// market: 'spotv3',
|
||||||
|
// market: 'usdcOption',
|
||||||
|
// market: 'usdcPerp',
|
||||||
|
market: 'unifiedPerp',
|
||||||
|
// market: 'unifiedOption',
|
||||||
|
},
|
||||||
|
logger
|
||||||
|
);
|
||||||
|
|
||||||
wsClient.on('update', (data) => {
|
wsClient.on('update', (data) => {
|
||||||
console.log('raw message received ', JSON.stringify(data, null, 2));
|
console.log('raw message received ', JSON.stringify(data));
|
||||||
|
// console.log('raw message received ', JSON.stringify(data, null, 2));
|
||||||
});
|
});
|
||||||
|
|
||||||
wsClient.on('open', (data) => {
|
wsClient.on('open', (data) => {
|
||||||
console.log('connection opened open:', data.wsKey);
|
console.log('connection opened open:', data.wsKey);
|
||||||
|
|
||||||
if (data.wsKey === wsKeySpotPublic) {
|
// if (data.wsKey === WS_KEY_MAP.spotPublic) {
|
||||||
// Spot public.
|
// // Spot public, but not recommended - use spotv3 client instead
|
||||||
// wsClient.subscribePublicSpotTrades('BTCUSDT');
|
// // The old spot websockets dont automatically resubscribe if they disconnect
|
||||||
// wsClient.subscribePublicSpotTradingPair('BTCUSDT');
|
// // wsClient.subscribePublicSpotTrades('BTCUSDT');
|
||||||
// wsClient.subscribePublicSpotV1Kline('BTCUSDT', '1m');
|
// // wsClient.subscribePublicSpotTradingPair('BTCUSDT');
|
||||||
// wsClient.subscribePublicSpotOrderbook('BTCUSDT', 'full');
|
// // wsClient.subscribePublicSpotV1Kline('BTCUSDT', '1m');
|
||||||
}
|
// // wsClient.subscribePublicSpotOrderbook('BTCUSDT', 'full');
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
wsClient.on('response', (data) => {
|
wsClient.on('response', (data) => {
|
||||||
console.log('log response: ', JSON.stringify(data, null, 2));
|
console.log('log response: ', JSON.stringify(data, null, 2));
|
||||||
@@ -49,7 +58,27 @@ import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';
|
|||||||
// Linear
|
// Linear
|
||||||
// wsClient.subscribe('trade.BTCUSDT');
|
// wsClient.subscribe('trade.BTCUSDT');
|
||||||
|
|
||||||
|
// Spot V3
|
||||||
|
// wsClient.subscribe('trade.BTCUSDT');
|
||||||
|
|
||||||
|
// usdc options
|
||||||
|
// wsClient.subscribe([
|
||||||
|
// `recenttrades.BTC`,
|
||||||
|
// `recenttrades.ETH`,
|
||||||
|
// `recenttrades.SOL`,
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// usdc perps
|
||||||
|
// wsClient.subscribe('trade.BTCPERP');
|
||||||
|
|
||||||
|
// unified perps
|
||||||
|
wsClient.subscribe('publicTrade.BTCUSDT');
|
||||||
|
|
||||||
|
// setTimeout(() => {
|
||||||
|
// console.log('unsubscribing');
|
||||||
|
// wsClient.unsubscribe('trade.BTCUSDT');
|
||||||
|
// }, 5 * 1000);
|
||||||
|
|
||||||
// For spot, request public connection first then send required topics on 'open'
|
// For spot, request public connection first then send required topics on 'open'
|
||||||
// wsClient.connectPublic();
|
// wsClient.connectPublic();
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ module.exports = {
|
|||||||
__PROD__: false
|
__PROD__: false
|
||||||
},
|
},
|
||||||
testEnvironment: 'node',
|
testEnvironment: 'node',
|
||||||
preset: "ts-jest",
|
preset: 'ts-jest',
|
||||||
verbose: true, // report individual test
|
verbose: true, // report individual test
|
||||||
bail: false, // enable to stop test when an error occur,
|
bail: false, // enable to stop test when an error occur,
|
||||||
detectOpenHandles: false,
|
detectOpenHandles: true,
|
||||||
moduleDirectories: ['node_modules', 'src', 'test'],
|
moduleDirectories: ['node_modules', 'src', 'test'],
|
||||||
testMatch: ['**/test/**/*.test.ts?(x)'],
|
testMatch: ['**/test/**/*.test.ts?(x)'],
|
||||||
testPathIgnorePatterns: ['node_modules/', 'dist/', '.json'],
|
testPathIgnorePatterns: ['node_modules/', 'dist/', '.json'],
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "2.3.2",
|
"version": "3.0.0",
|
||||||
"description": "Node.js connector for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.",
|
"description": "Complete & robust node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
148
src/account-asset-client.ts
Normal file
148
src/account-asset-client.ts
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import {
|
||||||
|
AccountAssetInformationRequest,
|
||||||
|
APIResponseWithTime,
|
||||||
|
DepositRecordsRequest,
|
||||||
|
EnableUniversalTransferRequest,
|
||||||
|
InternalTransferRequest,
|
||||||
|
SubAccountTransferRequest,
|
||||||
|
SupportedDepositListRequest,
|
||||||
|
TransferQueryRequest,
|
||||||
|
UniversalTransferRequest,
|
||||||
|
WithdrawalRecordsRequest,
|
||||||
|
WithdrawalRequest,
|
||||||
|
} from './types';
|
||||||
|
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API client for Account Asset APIs
|
||||||
|
*/
|
||||||
|
export class AccountAssetClient extends BaseRestClient {
|
||||||
|
getClientType() {
|
||||||
|
return REST_CLIENT_TYPE_ENUM.accountAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Transfer Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
createInternalTransfer(
|
||||||
|
params: InternalTransferRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('/asset/v1/private/transfer', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
createSubAccountTransfer(
|
||||||
|
params: SubAccountTransferRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('/asset/v1/private/sub-member/transfer', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInternalTransfers(
|
||||||
|
params?: TransferQueryRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/transfer/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubAccountTransfers(
|
||||||
|
params?: TransferQueryRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/asset/v1/private/sub-member/transfer/list',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubAccounts(): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/sub-member/member-ids');
|
||||||
|
}
|
||||||
|
|
||||||
|
enableUniversalTransfer(
|
||||||
|
params?: EnableUniversalTransferRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('/asset/v1/private/transferable-subs/save', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
createUniversalTransfer(
|
||||||
|
params: UniversalTransferRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('/asset/v1/private/universal/transfer', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUniversalTransfers(
|
||||||
|
params?: TransferQueryRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/universal/transfer/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Wallet & Deposit Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getSupportedDepositList(
|
||||||
|
params?: SupportedDepositListRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.get('/asset/v1/public/deposit/allowed-deposit-list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepositRecords(
|
||||||
|
params?: DepositRecordsRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/deposit/record/query', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWithdrawRecords(
|
||||||
|
params?: WithdrawalRecordsRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/withdraw/record/query', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCoinInformation(coin?: string): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/coin-info/query', { coin });
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssetInformation(
|
||||||
|
params?: AccountAssetInformationRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/asset-info/query', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
submitWithdrawal(
|
||||||
|
params: WithdrawalRequest
|
||||||
|
): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('/asset/v1/private/withdraw', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelWithdrawal(withdrawalId: number): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.postPrivate('/asset/v1/private/withdraw/cancel', {
|
||||||
|
id: withdrawalId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepositAddress(coin: string): Promise<APIResponseWithTime<any>> {
|
||||||
|
return this.getPrivate('/asset/v1/private/deposit/address', { coin });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime> {
|
||||||
|
return this.get('/v2/public/time');
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiAnnouncements(): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('/v2/public/announcement');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,20 @@ export const positionTpSlModeEnum = {
|
|||||||
export const API_ERROR_CODE = {
|
export const API_ERROR_CODE = {
|
||||||
BALANCE_INSUFFICIENT_SPOT: -1131,
|
BALANCE_INSUFFICIENT_SPOT: -1131,
|
||||||
ORDER_NOT_FOUND_OR_TOO_LATE_SPOT: -2013,
|
ORDER_NOT_FOUND_OR_TOO_LATE_SPOT: -2013,
|
||||||
|
SUCCESS: 0,
|
||||||
/** This could mean bad request, incorrect value types or even incorrect/missing values */
|
/** This could mean bad request, incorrect value types or even incorrect/missing values */
|
||||||
PARAMS_MISSING_OR_WRONG: 10001,
|
PARAMS_MISSING_OR_WRONG: 10001,
|
||||||
|
INVALID_API_KEY_OR_PERMISSIONS: 10003,
|
||||||
|
INCORRECT_API_KEY_PERMISSIONS: 10005,
|
||||||
|
/** Account not unified margin, update required */
|
||||||
|
ACCOUNT_NOT_UNIFIED: 10020,
|
||||||
|
BALANCE_INSUFFICIENT_SPOT_V3: 12131,
|
||||||
|
ORDER_NOT_FOUND_SPOT_V3: 12213,
|
||||||
|
ORDER_NOT_FOUND_LEVERAGED_TOKEN: 12407,
|
||||||
|
EXCEEDED_UPPER_LIMIT_LEVERAGED_TOKEN: 12409,
|
||||||
|
QUERY_ACCOUNT_INFO_ERROR: 12602,
|
||||||
|
CROSS_MARGIN_USER_NOT_FOUND: 12607,
|
||||||
|
CROSS_MARGIN_REPAYMENT_NOT_REQUIRED: 12616,
|
||||||
ORDER_NOT_FOUND_OR_TOO_LATE: 20001,
|
ORDER_NOT_FOUND_OR_TOO_LATE: 20001,
|
||||||
POSITION_STATUS_NOT_NORMAL: 30013,
|
POSITION_STATUS_NOT_NORMAL: 30013,
|
||||||
CANNOT_SET_TRADING_STOP_FOR_ZERO_POS: 30024,
|
CANNOT_SET_TRADING_STOP_FOR_ZERO_POS: 30024,
|
||||||
@@ -39,6 +51,12 @@ export const API_ERROR_CODE = {
|
|||||||
INSUFFICIENT_BALANCE_FOR_ORDER_COST_LINEAR: 130080,
|
INSUFFICIENT_BALANCE_FOR_ORDER_COST_LINEAR: 130080,
|
||||||
SAME_SLTP_MODE_LINEAR: 130150,
|
SAME_SLTP_MODE_LINEAR: 130150,
|
||||||
RISK_ID_NOT_MODIFIED: 134026,
|
RISK_ID_NOT_MODIFIED: 134026,
|
||||||
|
ORDER_NOT_EXIST: 3100136,
|
||||||
|
NO_ACTIVE_ORDER: 3100205,
|
||||||
|
/** E.g. USDC Options trading when the account hasn't been opened for USDC Options yet */
|
||||||
|
ACCOUNT_NOT_EXIST: 3200200,
|
||||||
|
INCORRECT_MMP_PARAMETERS: 3500712,
|
||||||
|
INSTITION_MMP_PROFILE_NOT_FOUND: 3500713,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
157
src/copy-trading-client.ts
Normal file
157
src/copy-trading-client.ts
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import {
|
||||||
|
APIResponseWithTime,
|
||||||
|
CopyTradingCancelOrderRequest,
|
||||||
|
CopyTradingCloseOrderRequest,
|
||||||
|
CopyTradingOrderListRequest,
|
||||||
|
CopyTradingOrderRequest,
|
||||||
|
CopyTradingTradingStopRequest,
|
||||||
|
CopyTradingTransferRequest,
|
||||||
|
APIResponseV3,
|
||||||
|
} from './types';
|
||||||
|
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API client for USDC Perpetual APIs
|
||||||
|
*/
|
||||||
|
export class CopyTradingClient extends BaseRestClient {
|
||||||
|
getClientType() {
|
||||||
|
// Follows the same authentication mechanism as USDC APIs
|
||||||
|
return REST_CLIENT_TYPE_ENUM.v3;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Market Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getSymbols(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/contract/v3/public/copytrading/symbol/list');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Account Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** -> Order API */
|
||||||
|
|
||||||
|
/** Create order */
|
||||||
|
submitOrder(params: CopyTradingOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/contract/v3/private/copytrading/order/create',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set Trading Stop */
|
||||||
|
setTradingStop(
|
||||||
|
params: CopyTradingTradingStopRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/contract/v3/private/copytrading/order/trading-stop',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Order List */
|
||||||
|
getActiveOrders(
|
||||||
|
params?: CopyTradingOrderListRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/contract/v3/private/copytrading/order/list',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel order */
|
||||||
|
cancelOrder(
|
||||||
|
params: CopyTradingCancelOrderRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/contract/v3/private/copytrading/order/cancel',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Close Order. This endpoint's rate_limit will decrease by 10 per request; ie, one request to this endpoint consumes 10 from the limit allowed per minute. */
|
||||||
|
closeOrder(
|
||||||
|
params: CopyTradingCloseOrderRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/contract/v3/private/copytrading/order/close', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Positions API */
|
||||||
|
|
||||||
|
/** Position List */
|
||||||
|
getPositions(symbol?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/contract/v3/private/copytrading/position/list', {
|
||||||
|
symbol,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Close Position */
|
||||||
|
closePosition(
|
||||||
|
symbol: string,
|
||||||
|
positionIdx: string
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/contract/v3/private/copytrading/position/close', {
|
||||||
|
symbol,
|
||||||
|
positionIdx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Only integers can be set to set the leverage */
|
||||||
|
setLeverage(
|
||||||
|
symbol: string,
|
||||||
|
buyLeverage: string,
|
||||||
|
sellLeverage: string
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/contract/v3/private/copytrading/position/set-leverage',
|
||||||
|
{ symbol, buyLeverage, sellLeverage }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Wallet Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Get Wallet Balance */
|
||||||
|
getBalances(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/contract/v3/private/copytrading/wallet/balance');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transfer */
|
||||||
|
transfer(params: CopyTradingTransferRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/contract/v3/private/copytrading/wallet/transfer',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime> {
|
||||||
|
return this.get('/v2/public/time');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnnouncements(): Promise<APIResponseWithTime<any[]>> {
|
||||||
|
return this.get('/v2/public/announcement');
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/index.ts
10
src/index.ts
@@ -1,9 +1,15 @@
|
|||||||
|
export * from './account-asset-client';
|
||||||
|
export * from './copy-trading-client';
|
||||||
export * from './inverse-client';
|
export * from './inverse-client';
|
||||||
export * from './inverse-futures-client';
|
export * from './inverse-futures-client';
|
||||||
export * from './linear-client';
|
export * from './linear-client';
|
||||||
export * from './spot-client';
|
export * from './spot-client';
|
||||||
|
export * from './spot-client-v3';
|
||||||
|
export * from './usdc-option-client';
|
||||||
|
export * from './usdc-perpetual-client';
|
||||||
|
export * from './unified-margin-client';
|
||||||
export * from './websocket-client';
|
export * from './websocket-client';
|
||||||
export * from './logger';
|
export * from './util/logger';
|
||||||
|
export * from './util';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export * from './util/WsStore';
|
|
||||||
export * from './constants/enum';
|
export * from './constants/enum';
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||||
import {
|
|
||||||
getRestBaseUrl,
|
|
||||||
RestClientOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM,
|
|
||||||
} from './util/requestUtils';
|
|
||||||
import {
|
import {
|
||||||
APIResponseWithTime,
|
APIResponseWithTime,
|
||||||
AssetExchangeRecordsReq,
|
AssetExchangeRecordsReq,
|
||||||
CoinParam,
|
CoinParam,
|
||||||
|
InverseActiveConditionalOrderRequest,
|
||||||
|
InverseActiveOrdersRequest,
|
||||||
|
InverseCancelConditionalOrderRequest,
|
||||||
|
InverseCancelOrderRequest,
|
||||||
|
InverseChangePositionMarginRequest,
|
||||||
|
InverseConditionalOrderRequest,
|
||||||
|
InverseGetClosedPnlRequest,
|
||||||
|
InverseGetOrderRequest,
|
||||||
|
InverseGetTradeRecordsRequest,
|
||||||
|
InverseOrderRequest,
|
||||||
|
InverseReplaceConditionalOrderRequest,
|
||||||
|
InverseReplaceOrderRequest,
|
||||||
|
InverseSetLeverageRequest,
|
||||||
|
InverseSetMarginTypeRequest,
|
||||||
|
InverseSetSlTpPositionModeRequest,
|
||||||
|
InverseSetTradingStopRequest,
|
||||||
SymbolInfo,
|
SymbolInfo,
|
||||||
SymbolIntervalFromLimitParam,
|
SymbolIntervalFromLimitParam,
|
||||||
SymbolLimitParam,
|
SymbolLimitParam,
|
||||||
@@ -15,35 +26,15 @@ import {
|
|||||||
SymbolPeriodLimitParam,
|
SymbolPeriodLimitParam,
|
||||||
WalletFundRecordsReq,
|
WalletFundRecordsReq,
|
||||||
WithdrawRecordsReq,
|
WithdrawRecordsReq,
|
||||||
} from './types/shared';
|
} from './types';
|
||||||
import BaseRestClient from './util/BaseRestClient';
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
export class InverseClient extends BaseRestClient {
|
|
||||||
/**
|
/**
|
||||||
* @public Creates an instance of the inverse REST API client.
|
* REST API client for Inverse Perpetual Futures APIs (v2)
|
||||||
*
|
|
||||||
* @param {string} key - your API key
|
|
||||||
* @param {string} secret - your API secret
|
|
||||||
* @param {boolean} [useLivenet=false]
|
|
||||||
* @param {RestClientOptions} [restClientOptions={}] options to configure REST API connectivity
|
|
||||||
* @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
export class InverseClient extends BaseRestClient {
|
||||||
key?: string | undefined,
|
getClientType() {
|
||||||
secret?: string | undefined,
|
return REST_CLIENT_TYPE_ENUM.inverse;
|
||||||
useLivenet: boolean = false,
|
|
||||||
restClientOptions: RestClientOptions = {},
|
|
||||||
requestOptions: AxiosRequestConfig = {}
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
key,
|
|
||||||
secret,
|
|
||||||
getRestBaseUrl(useLivenet, restClientOptions),
|
|
||||||
restClientOptions,
|
|
||||||
requestOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM.inverse
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchServerTime(): Promise<number> {
|
async fetchServerTime(): Promise<number> {
|
||||||
@@ -190,39 +181,21 @@ export class InverseClient extends BaseRestClient {
|
|||||||
* Active orders
|
* Active orders
|
||||||
*/
|
*/
|
||||||
|
|
||||||
placeActiveOrder(orderRequest: {
|
placeActiveOrder(
|
||||||
side: string;
|
orderRequest: InverseOrderRequest
|
||||||
symbol: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_type: string;
|
|
||||||
qty: number;
|
|
||||||
price?: number;
|
|
||||||
time_in_force: string;
|
|
||||||
take_profit?: number;
|
|
||||||
stop_loss?: number;
|
|
||||||
reduce_only?: boolean;
|
|
||||||
tp_trigger_by?: 'LastPrice' | 'MarkPrice' | 'IndexPrice';
|
|
||||||
sl_trigger_by?: 'LastPrice' | 'MarkPrice' | 'IndexPrice';
|
|
||||||
close_on_trigger?: boolean;
|
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/order/create', orderRequest);
|
return this.postPrivate('v2/private/order/create', orderRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveOrderList(params: {
|
getActiveOrderList(
|
||||||
symbol: string;
|
params: InverseActiveOrdersRequest
|
||||||
order_status?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
direction?: string;
|
|
||||||
limit?: number;
|
|
||||||
cursor?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('v2/private/order/list', params);
|
return this.getPrivate('v2/private/order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelActiveOrder(params: {
|
cancelActiveOrder(
|
||||||
symbol: string;
|
params: InverseCancelOrderRequest
|
||||||
order_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/order/cancel', params);
|
return this.postPrivate('v2/private/order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,25 +205,15 @@ export class InverseClient extends BaseRestClient {
|
|||||||
return this.postPrivate('v2/private/order/cancelAll', params);
|
return this.postPrivate('v2/private/order/cancelAll', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceActiveOrder(params: {
|
replaceActiveOrder(
|
||||||
order_id?: string;
|
params: InverseReplaceOrderRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
p_r_qty?: number;
|
|
||||||
p_r_price?: string;
|
|
||||||
take_profit?: number;
|
|
||||||
stop_loss?: number;
|
|
||||||
tp_trigger_by?: string;
|
|
||||||
sl_trigger_by?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/order/replace', params);
|
return this.postPrivate('v2/private/order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryActiveOrder(params: {
|
queryActiveOrder(
|
||||||
order_id?: string;
|
params: InverseGetOrderRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('v2/private/order', params);
|
return this.getPrivate('v2/private/order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,38 +221,22 @@ export class InverseClient extends BaseRestClient {
|
|||||||
* Conditional orders
|
* Conditional orders
|
||||||
*/
|
*/
|
||||||
|
|
||||||
placeConditionalOrder(params: {
|
placeConditionalOrder(
|
||||||
side: string;
|
params: InverseConditionalOrderRequest
|
||||||
symbol: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_type: string;
|
|
||||||
qty: string;
|
|
||||||
price?: string;
|
|
||||||
base_price: string;
|
|
||||||
stop_px: string;
|
|
||||||
time_in_force: string;
|
|
||||||
trigger_by?: string;
|
|
||||||
close_on_trigger?: boolean;
|
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/stop-order/create', params);
|
return this.postPrivate('v2/private/stop-order/create', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** get conditional order list. This may see delays, use queryConditionalOrder() for real-time queries */
|
/** get conditional order list. This may see delays, use queryConditionalOrder() for real-time queries */
|
||||||
getConditionalOrder(params: {
|
getConditionalOrder(
|
||||||
symbol: string;
|
params: InverseActiveConditionalOrderRequest
|
||||||
stop_order_status?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
direction?: string;
|
|
||||||
limit?: number;
|
|
||||||
cursor?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('v2/private/stop-order/list', params);
|
return this.getPrivate('v2/private/stop-order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelConditionalOrder(params: {
|
cancelConditionalOrder(
|
||||||
symbol: string;
|
params: InverseCancelConditionalOrderRequest
|
||||||
stop_order_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/stop-order/cancel', params);
|
return this.postPrivate('v2/private/stop-order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,22 +246,15 @@ export class InverseClient extends BaseRestClient {
|
|||||||
return this.postPrivate('v2/private/stop-order/cancelAll', params);
|
return this.postPrivate('v2/private/stop-order/cancelAll', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceConditionalOrder(params: {
|
replaceConditionalOrder(
|
||||||
stop_order_id?: string;
|
params: InverseReplaceConditionalOrderRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
p_r_qty?: number;
|
|
||||||
p_r_price?: string;
|
|
||||||
p_r_trigger_price?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/stop-order/replace', params);
|
return this.postPrivate('v2/private/stop-order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryConditionalOrder(params: {
|
queryConditionalOrder(
|
||||||
symbol: string;
|
params: InverseGetOrderRequest
|
||||||
stop_order_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('v2/private/stop-order', params);
|
return this.getPrivate('v2/private/stop-order', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,68 +268,45 @@ export class InverseClient extends BaseRestClient {
|
|||||||
return this.getPrivate('v2/private/position/list', params);
|
return this.getPrivate('v2/private/position/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
changePositionMargin(params: {
|
changePositionMargin(
|
||||||
symbol: string;
|
params: InverseChangePositionMarginRequest
|
||||||
margin: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('position/change-position-margin', params);
|
return this.postPrivate('position/change-position-margin', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTradingStop(params: {
|
setTradingStop(
|
||||||
symbol: string;
|
params: InverseSetTradingStopRequest
|
||||||
take_profit?: number;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
stop_loss?: number;
|
|
||||||
trailing_stop?: number;
|
|
||||||
tp_trigger_by?: string;
|
|
||||||
sl_trigger_by?: string;
|
|
||||||
new_trailing_active?: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/position/trading-stop', params);
|
return this.postPrivate('v2/private/position/trading-stop', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserLeverage(params: {
|
setUserLeverage(
|
||||||
symbol: string;
|
params: InverseSetLeverageRequest
|
||||||
leverage: number;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
leverage_only?: boolean;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/position/leverage/save', params);
|
return this.postPrivate('v2/private/position/leverage/save', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTradeRecords(params: {
|
getTradeRecords(
|
||||||
order_id?: string;
|
params: InverseGetTradeRecordsRequest
|
||||||
symbol: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
start_time?: number;
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
order?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('v2/private/execution/list', params);
|
return this.getPrivate('v2/private/execution/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosedPnl(params: {
|
getClosedPnl(
|
||||||
symbol: string;
|
params: InverseGetClosedPnlRequest
|
||||||
start_time?: number;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
end_time?: number;
|
|
||||||
exec_type?: string;
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('v2/private/trade/closed-pnl/list', params);
|
return this.getPrivate('v2/private/trade/closed-pnl/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSlTpPositionMode(params: {
|
setSlTpPositionMode(
|
||||||
symbol: string;
|
params: InverseSetSlTpPositionModeRequest
|
||||||
tp_sl_mode: 'Full' | 'Partial';
|
): Promise<APIResponseWithTime<any>> {
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/tpsl/switch-mode', params);
|
return this.postPrivate('v2/private/tpsl/switch-mode', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMarginType(params: {
|
setMarginType(
|
||||||
symbol: string;
|
params: InverseSetMarginTypeRequest
|
||||||
is_isolated: boolean;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
buy_leverage: number;
|
|
||||||
sell_leverage: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('v2/private/position/switch-isolated', params);
|
return this.postPrivate('v2/private/position/switch-isolated', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils';
|
||||||
import {
|
|
||||||
getRestBaseUrl,
|
|
||||||
RestClientOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM,
|
|
||||||
} from './util/requestUtils';
|
|
||||||
import {
|
import {
|
||||||
APIResponseWithTime,
|
APIResponseWithTime,
|
||||||
AssetExchangeRecordsReq,
|
AssetExchangeRecordsReq,
|
||||||
@@ -18,32 +13,12 @@ import {
|
|||||||
} from './types/shared';
|
} from './types/shared';
|
||||||
import BaseRestClient from './util/BaseRestClient';
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
export class InverseFuturesClient extends BaseRestClient {
|
|
||||||
/**
|
/**
|
||||||
* @public Creates an instance of the inverse futures REST API client.
|
* REST API client for Inverse Futures APIs (e.g. quarterly futures) (v2)
|
||||||
*
|
|
||||||
* @param {string} key - your API key
|
|
||||||
* @param {string} secret - your API secret
|
|
||||||
* @param {boolean} [useLivenet=false]
|
|
||||||
* @param {RestClientOptions} [restClientOptions={}] options to configure REST API connectivity
|
|
||||||
* @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
export class InverseFuturesClient extends BaseRestClient {
|
||||||
key?: string | undefined,
|
getClientType() {
|
||||||
secret?: string | undefined,
|
return REST_CLIENT_TYPE_ENUM.inverseFutures;
|
||||||
useLivenet: boolean = false,
|
|
||||||
restClientOptions: RestClientOptions = {},
|
|
||||||
requestOptions: AxiosRequestConfig = {}
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
key,
|
|
||||||
secret,
|
|
||||||
getRestBaseUrl(useLivenet, restClientOptions),
|
|
||||||
restClientOptions,
|
|
||||||
requestOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM.inverseFutures
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchServerTime(): Promise<number> {
|
async fetchServerTime(): Promise<number> {
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils';
|
||||||
import {
|
|
||||||
getRestBaseUrl,
|
|
||||||
RestClientOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM,
|
|
||||||
} from './util/requestUtils';
|
|
||||||
import {
|
import {
|
||||||
APIResponse,
|
APIResponse,
|
||||||
APIResponseWithTime,
|
APIResponseWithTime,
|
||||||
AssetExchangeRecordsReq,
|
AssetExchangeRecordsReq,
|
||||||
CoinParam,
|
CoinParam,
|
||||||
|
LinearCancelConditionalOrderRequest,
|
||||||
|
LinearCancelOrderRequest,
|
||||||
|
LinearConditionalOrderRequest,
|
||||||
|
LinearGetClosedPnlRequest,
|
||||||
|
LinearGetConditionalOrderRequest,
|
||||||
|
LinearGetOrderRequest,
|
||||||
|
LinearGetOrdersRequest,
|
||||||
|
LinearGetTradeRecordsRequest,
|
||||||
LinearOrder,
|
LinearOrder,
|
||||||
|
LinearQueryConditionalOrderRequest,
|
||||||
|
LinearReplaceConditionalOrderRequest,
|
||||||
|
LinearReplaceOrderRequest,
|
||||||
|
LinearSetAddReduceMarginRequest,
|
||||||
|
LinearSetAutoAddMarginRequest,
|
||||||
|
LinearSetMarginSwitchRequest,
|
||||||
|
LinearSetPositionModeRequest,
|
||||||
|
LinearSetPositionTpSlModeRequest,
|
||||||
|
LinearSetRiskLimitRequest,
|
||||||
|
LinearSetTradingStopRequest,
|
||||||
|
LinearSetUserLeverageRequest,
|
||||||
NewLinearOrder,
|
NewLinearOrder,
|
||||||
PerpPosition,
|
PerpPosition,
|
||||||
PerpPositionRoot,
|
PerpPositionRoot,
|
||||||
@@ -25,32 +39,12 @@ import {
|
|||||||
import { linearPositionModeEnum, positionTpSlModeEnum } from './constants/enum';
|
import { linearPositionModeEnum, positionTpSlModeEnum } from './constants/enum';
|
||||||
import BaseRestClient from './util/BaseRestClient';
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
export class LinearClient extends BaseRestClient {
|
|
||||||
/**
|
/**
|
||||||
* @public Creates an instance of the linear (USD Perps) REST API client.
|
* REST API client for linear/USD perpetual futures APIs (v2)
|
||||||
*
|
|
||||||
* @param {string} key - your API key
|
|
||||||
* @param {string} secret - your API secret
|
|
||||||
* @param {boolean} [useLivenet=false]
|
|
||||||
* @param {RestClientOptions} [restClientOptions={}] options to configure REST API connectivity
|
|
||||||
* @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
export class LinearClient extends BaseRestClient {
|
||||||
key?: string | undefined,
|
getClientType() {
|
||||||
secret?: string | undefined,
|
return REST_CLIENT_TYPE_ENUM.linear;
|
||||||
useLivenet: boolean = false,
|
|
||||||
restClientOptions: RestClientOptions = {},
|
|
||||||
requestOptions: AxiosRequestConfig = {}
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
key,
|
|
||||||
secret,
|
|
||||||
getRestBaseUrl(useLivenet, restClientOptions),
|
|
||||||
restClientOptions,
|
|
||||||
requestOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM.linear
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchServerTime(): Promise<number> {
|
async fetchServerTime(): Promise<number> {
|
||||||
@@ -203,23 +197,15 @@ export class LinearClient extends BaseRestClient {
|
|||||||
return this.postPrivate('private/linear/order/create', params);
|
return this.postPrivate('private/linear/order/create', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveOrderList(params: {
|
getActiveOrderList(
|
||||||
order_id?: string;
|
params: LinearGetOrdersRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
order?: string;
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
order_status?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('private/linear/order/list', params);
|
return this.getPrivate('private/linear/order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelActiveOrder(params: {
|
cancelActiveOrder(
|
||||||
symbol: string;
|
params: LinearCancelOrderRequest
|
||||||
order_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/order/cancel', params);
|
return this.postPrivate('private/linear/order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,25 +215,15 @@ export class LinearClient extends BaseRestClient {
|
|||||||
return this.postPrivate('private/linear/order/cancel-all', params);
|
return this.postPrivate('private/linear/order/cancel-all', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceActiveOrder(params: {
|
replaceActiveOrder(
|
||||||
order_id?: string;
|
params: LinearReplaceOrderRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
p_r_qty?: number;
|
|
||||||
p_r_price?: number;
|
|
||||||
take_profit?: number;
|
|
||||||
stop_loss?: number;
|
|
||||||
tp_trigger_by?: string;
|
|
||||||
sl_trigger_by?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/order/replace', params);
|
return this.postPrivate('private/linear/order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryActiveOrder(params: {
|
queryActiveOrder(
|
||||||
order_id?: string;
|
params: LinearGetOrderRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('private/linear/order/search', params);
|
return this.getPrivate('private/linear/order/search', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,44 +231,21 @@ export class LinearClient extends BaseRestClient {
|
|||||||
* Conditional orders
|
* Conditional orders
|
||||||
*/
|
*/
|
||||||
|
|
||||||
placeConditionalOrder(params: {
|
placeConditionalOrder(
|
||||||
side: string;
|
params: LinearConditionalOrderRequest
|
||||||
symbol: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_type: string;
|
|
||||||
qty: number;
|
|
||||||
price?: number;
|
|
||||||
base_price: number;
|
|
||||||
stop_px: number;
|
|
||||||
time_in_force: string;
|
|
||||||
trigger_by?: string;
|
|
||||||
close_on_trigger?: boolean;
|
|
||||||
order_link_id?: string;
|
|
||||||
reduce_only: boolean;
|
|
||||||
take_profit?: number;
|
|
||||||
stop_loss?: number;
|
|
||||||
tp_trigger_by?: string;
|
|
||||||
sl_trigger_by?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/stop-order/create', params);
|
return this.postPrivate('private/linear/stop-order/create', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConditionalOrder(params: {
|
getConditionalOrder(
|
||||||
stop_order_id?: string;
|
params: LinearGetConditionalOrderRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
stop_order_status?: string;
|
|
||||||
order?: string;
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('private/linear/stop-order/list', params);
|
return this.getPrivate('private/linear/stop-order/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelConditionalOrder(params: {
|
cancelConditionalOrder(
|
||||||
symbol: string;
|
params: LinearCancelConditionalOrderRequest
|
||||||
stop_order_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/stop-order/cancel', params);
|
return this.postPrivate('private/linear/stop-order/cancel', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,26 +255,15 @@ export class LinearClient extends BaseRestClient {
|
|||||||
return this.postPrivate('private/linear/stop-order/cancel-all', params);
|
return this.postPrivate('private/linear/stop-order/cancel-all', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceConditionalOrder(params: {
|
replaceConditionalOrder(
|
||||||
stop_order_id?: string;
|
params: LinearReplaceConditionalOrderRequest
|
||||||
order_link_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
symbol: string;
|
|
||||||
p_r_qty?: number;
|
|
||||||
p_r_price?: number;
|
|
||||||
p_r_trigger_price?: number;
|
|
||||||
take_profit?: number;
|
|
||||||
stop_loss?: number;
|
|
||||||
tp_trigger_by?: string;
|
|
||||||
sl_trigger_by?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/stop-order/replace', params);
|
return this.postPrivate('private/linear/stop-order/replace', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
queryConditionalOrder(params: {
|
queryConditionalOrder(
|
||||||
symbol: string;
|
params: LinearQueryConditionalOrderRequest
|
||||||
stop_order_id?: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
order_link_id?: string;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('private/linear/stop-order/search', params);
|
return this.getPrivate('private/linear/stop-order/search', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,33 +282,27 @@ export class LinearClient extends BaseRestClient {
|
|||||||
return this.getPrivate('private/linear/position/list', params);
|
return this.getPrivate('private/linear/position/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAutoAddMargin(params?: {
|
setAutoAddMargin(
|
||||||
symbol: string;
|
params?: LinearSetAutoAddMarginRequest
|
||||||
side: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
auto_add_margin: boolean;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate(
|
return this.postPrivate(
|
||||||
'private/linear/position/set-auto-add-margin',
|
'private/linear/position/set-auto-add-margin',
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMarginSwitch(params?: {
|
setMarginSwitch(
|
||||||
symbol: string;
|
params?: LinearSetMarginSwitchRequest
|
||||||
is_isolated: boolean;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
buy_leverage: number;
|
|
||||||
sell_leverage: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/position/switch-isolated', params);
|
return this.postPrivate('private/linear/position/switch-isolated', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch between one-way vs hedge mode. Use `linearPositionModeEnum` for the mode parameter.
|
* Switch between one-way vs hedge mode. Use `linearPositionModeEnum` for the mode parameter.
|
||||||
*/
|
*/
|
||||||
setPositionMode(params: {
|
setPositionMode(
|
||||||
symbol: string;
|
params: LinearSetPositionModeRequest
|
||||||
mode: typeof linearPositionModeEnum[keyof typeof linearPositionModeEnum];
|
): Promise<APIResponseWithTime<any>> {
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/position/switch-mode', params);
|
return this.postPrivate('private/linear/position/switch-mode', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,62 +310,39 @@ export class LinearClient extends BaseRestClient {
|
|||||||
* Switch TP/SL mode between full or partial. When set to Partial, TP/SL orders may have a quantity less than the position size.
|
* Switch TP/SL mode between full or partial. When set to Partial, TP/SL orders may have a quantity less than the position size.
|
||||||
* This is set with the setTradingStop() method. Use `positionTpSlModeEnum` for the tp_sl_mode parameter.
|
* This is set with the setTradingStop() method. Use `positionTpSlModeEnum` for the tp_sl_mode parameter.
|
||||||
*/
|
*/
|
||||||
setPositionTpSlMode(params: {
|
setPositionTpSlMode(
|
||||||
symbol: string;
|
params: LinearSetPositionTpSlModeRequest
|
||||||
tp_sl_mode: typeof positionTpSlModeEnum[keyof typeof positionTpSlModeEnum];
|
): Promise<APIResponseWithTime<any>> {
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/tpsl/switch-mode', params);
|
return this.postPrivate('private/linear/tpsl/switch-mode', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAddReduceMargin(params?: {
|
setAddReduceMargin(
|
||||||
symbol: string;
|
params?: LinearSetAddReduceMarginRequest
|
||||||
side: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
margin: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/position/add-margin', params);
|
return this.postPrivate('private/linear/position/add-margin', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserLeverage(params: {
|
setUserLeverage(
|
||||||
symbol: string;
|
params: LinearSetUserLeverageRequest
|
||||||
buy_leverage: number;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
sell_leverage: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/position/set-leverage', params);
|
return this.postPrivate('private/linear/position/set-leverage', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTradingStop(params: {
|
setTradingStop(
|
||||||
symbol: string;
|
params: LinearSetTradingStopRequest
|
||||||
side: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
take_profit?: number;
|
|
||||||
stop_loss?: number;
|
|
||||||
trailing_stop?: number;
|
|
||||||
tp_trigger_by?: string;
|
|
||||||
sl_trigger_by?: string;
|
|
||||||
sl_size?: number;
|
|
||||||
tp_size?: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/position/trading-stop', params);
|
return this.postPrivate('private/linear/position/trading-stop', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTradeRecords(params: {
|
getTradeRecords(
|
||||||
symbol: string;
|
params: LinearGetTradeRecordsRequest
|
||||||
start_time?: number;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
end_time?: number;
|
|
||||||
exec_type?: string;
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('private/linear/trade/execution/list', params);
|
return this.getPrivate('private/linear/trade/execution/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosedPnl(params: {
|
getClosedPnl(
|
||||||
symbol: string;
|
params: LinearGetClosedPnlRequest
|
||||||
start_time?: number;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
end_time?: number;
|
|
||||||
exec_type?: string;
|
|
||||||
page?: number;
|
|
||||||
limit?: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.getPrivate('private/linear/trade/closed-pnl/list', params);
|
return this.getPrivate('private/linear/trade/closed-pnl/list', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,11 +354,9 @@ export class LinearClient extends BaseRestClient {
|
|||||||
return this.getPrivate('public/linear/risk-limit', params);
|
return this.getPrivate('public/linear/risk-limit', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRiskLimit(params: {
|
setRiskLimit(
|
||||||
symbol: string;
|
params: LinearSetRiskLimitRequest
|
||||||
side: string;
|
): Promise<APIResponseWithTime<any>> {
|
||||||
risk_id: number;
|
|
||||||
}): Promise<APIResponseWithTime<any>> {
|
|
||||||
return this.postPrivate('private/linear/position/set-risk', params);
|
return this.postPrivate('private/linear/position/set-risk', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
300
src/spot-client-v3.ts
Normal file
300
src/spot-client-v3.ts
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
import {
|
||||||
|
APIResponseWithTime,
|
||||||
|
APIResponseV3,
|
||||||
|
SpotOrderQueryById,
|
||||||
|
OrderSide,
|
||||||
|
OrderTypeSpot,
|
||||||
|
SpotBalances,
|
||||||
|
KlineInterval,
|
||||||
|
NewSpotOrderV3,
|
||||||
|
SpotMyTradesRequest,
|
||||||
|
SpotLeveragedTokenPRHistoryRequest,
|
||||||
|
SpotCrossMarginBorrowingInfoRequest,
|
||||||
|
SpotCrossMarginRepaymentHistoryRequest,
|
||||||
|
SpotCancelOrderBatchRequest,
|
||||||
|
} from './types';
|
||||||
|
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API client for newer Spot V3 APIs.
|
||||||
|
*/
|
||||||
|
export class SpotClientV3 extends BaseRestClient {
|
||||||
|
getClientType() {
|
||||||
|
// Follows the same authentication mechanism as other v3 APIs (e.g. USDC)
|
||||||
|
return REST_CLIENT_TYPE_ENUM.v3;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Market Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Get all symbols */
|
||||||
|
getSymbols(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/symbols');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get orderbook for symbol */
|
||||||
|
getOrderBook(symbol: string, limit?: number): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/quote/depth', { symbol, limit });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get merged orderbook for symbol */
|
||||||
|
getMergedOrderBook(
|
||||||
|
symbol: string,
|
||||||
|
scale?: number,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/quote/depth/merged', {
|
||||||
|
symbol,
|
||||||
|
scale,
|
||||||
|
limit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get public trading records (raw trades) */
|
||||||
|
getTrades(symbol: string, limit?: number): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/quote/trades', { symbol, limit });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get candles/klines */
|
||||||
|
getCandles(
|
||||||
|
symbol: string,
|
||||||
|
interval: KlineInterval,
|
||||||
|
limit?: number,
|
||||||
|
startTime?: number,
|
||||||
|
endTime?: number
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/quote/kline', {
|
||||||
|
symbol,
|
||||||
|
interval,
|
||||||
|
limit,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get latest information for symbol (24hr ticker) */
|
||||||
|
get24hrTicker(symbol?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/quote/ticker/24hr', { symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get last traded price */
|
||||||
|
getLastTradedPrice(symbol?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/quote/ticker/price', { symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get best bid/ask price */
|
||||||
|
getBestBidAskPrice(symbol?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/quote/ticker/bookTicker', { symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Account Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** -> Order API */
|
||||||
|
|
||||||
|
/** Create order */
|
||||||
|
submitOrder(params: NewSpotOrderV3): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/spot/v3/private/order', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get active order state */
|
||||||
|
getOrder(params: SpotOrderQueryById): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/order', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel order */
|
||||||
|
cancelOrder(params: SpotOrderQueryById): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/spot/v3/private/cancel-order', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Batch cancel orders */
|
||||||
|
cancelOrderBatch(
|
||||||
|
params: SpotCancelOrderBatchRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
const orderTypes = params.orderTypes
|
||||||
|
? params.orderTypes.join(',')
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return this.postPrivate('/spot/v3/private/cancel-orders', {
|
||||||
|
...params,
|
||||||
|
orderTypes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Batch cancel up to 100 orders by ID */
|
||||||
|
cancelOrderBatchIDs(orderIds: string[]): Promise<APIResponseV3<any>> {
|
||||||
|
const orderIdsCsv = orderIds.join(',');
|
||||||
|
return this.postPrivate('/spot/v3/private/cancel-orders-by-ids', {
|
||||||
|
orderIds: orderIdsCsv,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get open orders */
|
||||||
|
getOpenOrders(
|
||||||
|
symbol?: string,
|
||||||
|
orderId?: string,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/open-orders', {
|
||||||
|
symbol,
|
||||||
|
orderId,
|
||||||
|
limit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get order history */
|
||||||
|
getPastOrders(
|
||||||
|
symbol?: string,
|
||||||
|
orderId?: string,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/history-orders', {
|
||||||
|
symbol,
|
||||||
|
orderId,
|
||||||
|
limit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get your trade history.
|
||||||
|
* If startTime is not specified, you can only query for records in the last 7 days.
|
||||||
|
* If you want to query for records older than 7 days, startTime is required.
|
||||||
|
*/
|
||||||
|
getMyTrades(params?: SpotMyTradesRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/my-trades', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Wallet Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Get Wallet Balance */
|
||||||
|
getBalances(): Promise<APIResponseV3<SpotBalances>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/account');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime> {
|
||||||
|
return this.get('/v2/public/time');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Leveraged Token Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Get all asset infos */
|
||||||
|
getLeveragedTokenAssetInfos(ltCode?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/spot/v3/public/infos', { ltCode });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get leveraged token market info */
|
||||||
|
getLeveragedTokenMarketInfo(ltCode: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/reference', { ltCode });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Purchase leveraged token */
|
||||||
|
purchaseLeveragedToken(
|
||||||
|
ltCode: string,
|
||||||
|
ltAmount: string,
|
||||||
|
serialNo?: string
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/spot/v3/private/purchase', {
|
||||||
|
ltCode,
|
||||||
|
ltAmount,
|
||||||
|
serialNo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Redeem leveraged token */
|
||||||
|
redeemLeveragedToken(
|
||||||
|
ltCode: string,
|
||||||
|
ltAmount: string,
|
||||||
|
serialNo?: string
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/spot/v3/private/redeem', {
|
||||||
|
ltCode,
|
||||||
|
ltAmount,
|
||||||
|
serialNo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get leveraged token purchase/redemption history */
|
||||||
|
getLeveragedTokenPRHistory(
|
||||||
|
params?: SpotLeveragedTokenPRHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/record', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Cross Margin Trading Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Borrow margin loan */
|
||||||
|
borrowCrossMarginLoan(
|
||||||
|
coin: string,
|
||||||
|
qty: string
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/spot/v3/private/cross-margin-loan', {
|
||||||
|
coin,
|
||||||
|
qty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Repay margin loan */
|
||||||
|
repayCrossMarginLoan(coin: string, qty: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/spot/v3/private/cross-margin-repay', {
|
||||||
|
coin,
|
||||||
|
qty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query borrowing info */
|
||||||
|
getCrossMarginBorrowingInfo(
|
||||||
|
params?: SpotCrossMarginBorrowingInfoRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/cross-margin-orders', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query account info */
|
||||||
|
getCrossMarginAccountInfo(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/cross-margin-account');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query interest & quota */
|
||||||
|
getCrossMarginInterestQuota(coin: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/spot/v3/private/cross-margin-loan-info', { coin });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query repayment history */
|
||||||
|
getCrossMarginRepaymentHistory(
|
||||||
|
params?: SpotCrossMarginRepaymentHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/spot/v3/private/cross-margin-repay-history',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { AxiosRequestConfig } from 'axios';
|
|
||||||
import {
|
import {
|
||||||
NewSpotOrder,
|
NewSpotOrder,
|
||||||
APIResponse,
|
APIResponse,
|
||||||
@@ -11,40 +10,15 @@ import {
|
|||||||
SpotSymbolInfo,
|
SpotSymbolInfo,
|
||||||
} from './types';
|
} from './types';
|
||||||
import BaseRestClient from './util/BaseRestClient';
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
import {
|
import { REST_CLIENT_TYPE_ENUM } from './util/requestUtils';
|
||||||
agentSource,
|
|
||||||
getRestBaseUrl,
|
|
||||||
RestClientOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM,
|
|
||||||
} from './util/requestUtils';
|
|
||||||
|
|
||||||
export class SpotClient extends BaseRestClient {
|
|
||||||
/**
|
/**
|
||||||
* @public Creates an instance of the Spot REST API client.
|
* @deprecated Use SpotV3Client instead, which leverages the newer v3 APIs
|
||||||
*
|
* REST API client for Spot APIs (v1)
|
||||||
* @param {string} key - your API key
|
|
||||||
* @param {string} secret - your API secret
|
|
||||||
* @param {boolean} [useLivenet=false]
|
|
||||||
* @param {RestClientOptions} [restClientOptions={}] options to configure REST API connectivity
|
|
||||||
* @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios
|
|
||||||
*/
|
*/
|
||||||
constructor(
|
export class SpotClient extends BaseRestClient {
|
||||||
key?: string | undefined,
|
getClientType() {
|
||||||
secret?: string | undefined,
|
return REST_CLIENT_TYPE_ENUM.spot;
|
||||||
useLivenet: boolean = false,
|
|
||||||
restClientOptions: RestClientOptions = {},
|
|
||||||
requestOptions: AxiosRequestConfig = {}
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
key,
|
|
||||||
secret,
|
|
||||||
getRestBaseUrl(useLivenet, restClientOptions),
|
|
||||||
restClientOptions,
|
|
||||||
requestOptions,
|
|
||||||
REST_CLIENT_TYPE_ENUM.spot
|
|
||||||
);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchServerTime(): Promise<number> {
|
fetchServerTime(): Promise<number> {
|
||||||
@@ -129,10 +103,7 @@ export class SpotClient extends BaseRestClient {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
submitOrder(params: NewSpotOrder): Promise<APIResponse<any>> {
|
submitOrder(params: NewSpotOrder): Promise<APIResponse<any>> {
|
||||||
return this.postPrivate('/spot/v1/order', {
|
return this.postPrivate('/spot/v1/order', params);
|
||||||
...params,
|
|
||||||
agentSource,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrder(params: SpotOrderQueryById): Promise<APIResponse<any>> {
|
getOrder(params: SpotOrderQueryById): Promise<APIResponse<any>> {
|
||||||
@@ -151,6 +122,7 @@ export class SpotClient extends BaseRestClient {
|
|||||||
const orderTypes = params.orderTypes
|
const orderTypes = params.orderTypes
|
||||||
? params.orderTypes.join(',')
|
? params.orderTypes.join(',')
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return this.deletePrivate('/spot/order/batch-cancel', {
|
return this.deletePrivate('/spot/order/batch-cancel', {
|
||||||
...params,
|
...params,
|
||||||
orderTypes,
|
orderTypes,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export * from './response';
|
export * from './response';
|
||||||
export * from './request';
|
export * from './request';
|
||||||
export * from './shared';
|
export * from './shared';
|
||||||
export * from './spot';
|
export * from './websockets';
|
||||||
|
|||||||
94
src/types/request/account-asset.ts
Normal file
94
src/types/request/account-asset.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
export type TransferAccountType =
|
||||||
|
| 'CONTRACT'
|
||||||
|
| 'SPOT'
|
||||||
|
| 'INVESTMENT'
|
||||||
|
| 'OPTION'
|
||||||
|
| 'UNIFIED';
|
||||||
|
|
||||||
|
export type TransferType = 'IN' | 'OUT';
|
||||||
|
|
||||||
|
export type TransferStatus = 'SUCCESS' | 'PENDING' | 'FAILED';
|
||||||
|
|
||||||
|
export type PageDirection = 'Prev' | 'Next';
|
||||||
|
|
||||||
|
export interface InternalTransferRequest {
|
||||||
|
transfer_id: string;
|
||||||
|
coin: string;
|
||||||
|
amount: string;
|
||||||
|
from_account_type: TransferAccountType;
|
||||||
|
to_account_type: TransferAccountType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubAccountTransferRequest {
|
||||||
|
transfer_id: string;
|
||||||
|
coin: string;
|
||||||
|
amount: string;
|
||||||
|
sub_user_id: string;
|
||||||
|
type: TransferType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransferQueryRequest {
|
||||||
|
transfer_id?: string;
|
||||||
|
coin?: string;
|
||||||
|
status?: TransferStatus;
|
||||||
|
start_time?: number;
|
||||||
|
end_time?: number;
|
||||||
|
direction?: PageDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnableUniversalTransferRequest {
|
||||||
|
/** A comma-separated list of subaccount UIDs, for example "123,45,14,26,46" */
|
||||||
|
transferable_sub_ids?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UniversalTransferRequest {
|
||||||
|
transfer_id: string;
|
||||||
|
coin: string;
|
||||||
|
amount: string;
|
||||||
|
from_member_id: string;
|
||||||
|
to_member_id: string;
|
||||||
|
from_account_type: TransferAccountType;
|
||||||
|
to_account_type: TransferAccountType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SupportedDepositListRequest {
|
||||||
|
coin?: string;
|
||||||
|
chain?: string;
|
||||||
|
page_index?: number;
|
||||||
|
page_size?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DepositRecordsRequest {
|
||||||
|
start_time?: number;
|
||||||
|
end_time?: number;
|
||||||
|
coin?: string;
|
||||||
|
cursor?: string;
|
||||||
|
direction?: PageDirection;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WithdrawalRecordsRequest {
|
||||||
|
withdraw_id?: number;
|
||||||
|
start_time?: number;
|
||||||
|
end_time?: number;
|
||||||
|
coin?: string;
|
||||||
|
cursor?: string;
|
||||||
|
direction?: PageDirection;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AccountAssetInformationRequest {
|
||||||
|
/** Account type. Default value: ACCOUNT_TYPE_SPOT */
|
||||||
|
account_type?: string;
|
||||||
|
coin?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WithdrawalRequest {
|
||||||
|
address: string;
|
||||||
|
amount: string;
|
||||||
|
coin: string;
|
||||||
|
chain: string;
|
||||||
|
tag?: string;
|
||||||
|
}
|
||||||
53
src/types/request/copy-trading.ts
Normal file
53
src/types/request/copy-trading.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { OrderSide } from '../shared';
|
||||||
|
import { USDCOrderType } from './usdc-shared';
|
||||||
|
|
||||||
|
export interface CopyTradingOrderRequest {
|
||||||
|
side: OrderSide;
|
||||||
|
symbol: string;
|
||||||
|
orderType: USDCOrderType;
|
||||||
|
price: string;
|
||||||
|
qty: string;
|
||||||
|
takeProfit?: string;
|
||||||
|
stopLoss?: string;
|
||||||
|
tpTriggerBy?: string;
|
||||||
|
slTriggerBy?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CopyTradingTradingStopRequest {
|
||||||
|
symbol: string;
|
||||||
|
parentOrderId: string;
|
||||||
|
takeProfit?: string;
|
||||||
|
stopLoss?: string;
|
||||||
|
tpTriggerBy?: string;
|
||||||
|
slTriggerBy?: string;
|
||||||
|
parentOrderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CopyTradingOrderListRequest {
|
||||||
|
symbol?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
copyTradeOrderType?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CopyTradingCancelOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CopyTradingCloseOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
parentOrderId?: string;
|
||||||
|
parentOrderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CopyTradingTransferRequest {
|
||||||
|
transferId: string;
|
||||||
|
coin: string;
|
||||||
|
amount: string;
|
||||||
|
fromAccountType: string;
|
||||||
|
toAccountType: string;
|
||||||
|
}
|
||||||
@@ -1 +1,9 @@
|
|||||||
export * from './usdt-perp';
|
export * from './account-asset';
|
||||||
|
export * from './copy-trading';
|
||||||
|
export * from './linear';
|
||||||
|
export * from './inverse';
|
||||||
|
export * from './spot';
|
||||||
|
export * from './usdc-perp';
|
||||||
|
export * from './usdc-options';
|
||||||
|
export * from './usdc-shared';
|
||||||
|
export * from './unified-margin';
|
||||||
|
|||||||
135
src/types/request/inverse.ts
Normal file
135
src/types/request/inverse.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
export interface InverseOrderRequest {
|
||||||
|
side: string;
|
||||||
|
symbol: string;
|
||||||
|
order_type: string;
|
||||||
|
qty: number;
|
||||||
|
price?: number;
|
||||||
|
time_in_force: string;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
reduce_only?: boolean;
|
||||||
|
tp_trigger_by?: 'LastPrice' | 'MarkPrice' | 'IndexPrice';
|
||||||
|
sl_trigger_by?: 'LastPrice' | 'MarkPrice' | 'IndexPrice';
|
||||||
|
close_on_trigger?: boolean;
|
||||||
|
order_link_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseActiveOrdersRequest {
|
||||||
|
symbol: string;
|
||||||
|
order_status?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseCancelOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseReplaceOrderRequest {
|
||||||
|
order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
p_r_qty?: number;
|
||||||
|
p_r_price?: string;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
tp_trigger_by?: string;
|
||||||
|
sl_trigger_by?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseGetOrderRequest {
|
||||||
|
order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseConditionalOrderRequest {
|
||||||
|
side: string;
|
||||||
|
symbol: string;
|
||||||
|
order_type: string;
|
||||||
|
qty: string;
|
||||||
|
price?: string;
|
||||||
|
base_price: string;
|
||||||
|
stop_px: string;
|
||||||
|
time_in_force: string;
|
||||||
|
trigger_by?: string;
|
||||||
|
close_on_trigger?: boolean;
|
||||||
|
order_link_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseActiveConditionalOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
stop_order_status?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseCancelConditionalOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
stop_order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseReplaceConditionalOrderRequest {
|
||||||
|
stop_order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
p_r_qty?: number;
|
||||||
|
p_r_price?: string;
|
||||||
|
p_r_trigger_price?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseChangePositionMarginRequest {
|
||||||
|
symbol: string;
|
||||||
|
margin: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseSetTradingStopRequest {
|
||||||
|
symbol: string;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
trailing_stop?: number;
|
||||||
|
tp_trigger_by?: string;
|
||||||
|
sl_trigger_by?: string;
|
||||||
|
new_trailing_active?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseSetLeverageRequest {
|
||||||
|
symbol: string;
|
||||||
|
leverage: number;
|
||||||
|
leverage_only?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseGetTradeRecordsRequest {
|
||||||
|
order_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
start_time?: number;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
order?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseGetClosedPnlRequest {
|
||||||
|
symbol: string;
|
||||||
|
start_time?: number;
|
||||||
|
end_time?: number;
|
||||||
|
exec_type?: string;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseSetSlTpPositionModeRequest {
|
||||||
|
symbol: string;
|
||||||
|
tp_sl_mode: 'Full' | 'Partial';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InverseSetMarginTypeRequest {
|
||||||
|
symbol: string;
|
||||||
|
is_isolated: boolean;
|
||||||
|
buy_leverage: number;
|
||||||
|
sell_leverage: number;
|
||||||
|
}
|
||||||
190
src/types/request/linear.ts
Normal file
190
src/types/request/linear.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import {
|
||||||
|
LinearPositionIdx,
|
||||||
|
linearPositionModeEnum,
|
||||||
|
positionTpSlModeEnum,
|
||||||
|
} from '../../constants/enum';
|
||||||
|
import { OrderSide } from '../shared';
|
||||||
|
|
||||||
|
export interface LinearGetOrdersRequest {
|
||||||
|
order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
order?: string;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
order_status?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearCancelOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearReplaceOrderRequest {
|
||||||
|
order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
p_r_qty?: number;
|
||||||
|
p_r_price?: number;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
tp_trigger_by?: string;
|
||||||
|
sl_trigger_by?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearGetOrderRequest {
|
||||||
|
order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LinearOrderType = 'Limit' | 'Market';
|
||||||
|
|
||||||
|
export type LinearTimeInForce =
|
||||||
|
| 'GoodTillCancel'
|
||||||
|
| 'ImmediateOrCancel'
|
||||||
|
| 'FillOrKill'
|
||||||
|
| 'PostOnly';
|
||||||
|
|
||||||
|
export interface NewLinearOrder {
|
||||||
|
side: OrderSide;
|
||||||
|
symbol: string;
|
||||||
|
order_type: LinearOrderType;
|
||||||
|
qty: number;
|
||||||
|
price?: number;
|
||||||
|
time_in_force: LinearTimeInForce;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
tp_trigger_by?: string;
|
||||||
|
sl_trigger_by?: string;
|
||||||
|
reduce_only: boolean;
|
||||||
|
close_on_trigger: boolean;
|
||||||
|
order_link_id?: string;
|
||||||
|
position_idx?: LinearPositionIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearConditionalOrderRequest {
|
||||||
|
side: string;
|
||||||
|
symbol: string;
|
||||||
|
order_type: string;
|
||||||
|
qty: number;
|
||||||
|
price?: number;
|
||||||
|
base_price: number;
|
||||||
|
stop_px: number;
|
||||||
|
time_in_force: string;
|
||||||
|
trigger_by?: string;
|
||||||
|
close_on_trigger?: boolean;
|
||||||
|
order_link_id?: string;
|
||||||
|
reduce_only: boolean;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
tp_trigger_by?: string;
|
||||||
|
sl_trigger_by?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearGetConditionalOrderRequest {
|
||||||
|
stop_order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
stop_order_status?: string;
|
||||||
|
order?: string;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearCancelConditionalOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
stop_order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearReplaceConditionalOrderRequest {
|
||||||
|
stop_order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
symbol: string;
|
||||||
|
p_r_qty?: number;
|
||||||
|
p_r_price?: number;
|
||||||
|
p_r_trigger_price?: number;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
tp_trigger_by?: string;
|
||||||
|
sl_trigger_by?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearQueryConditionalOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
stop_order_id?: string;
|
||||||
|
order_link_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetAutoAddMarginRequest {
|
||||||
|
symbol: string;
|
||||||
|
side: string;
|
||||||
|
auto_add_margin: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetMarginSwitchRequest {
|
||||||
|
symbol: string;
|
||||||
|
is_isolated: boolean;
|
||||||
|
buy_leverage: number;
|
||||||
|
sell_leverage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetPositionModeRequest {
|
||||||
|
symbol: string;
|
||||||
|
mode: typeof linearPositionModeEnum[keyof typeof linearPositionModeEnum];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetPositionTpSlModeRequest {
|
||||||
|
symbol: string;
|
||||||
|
tp_sl_mode: typeof positionTpSlModeEnum[keyof typeof positionTpSlModeEnum];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetAddReduceMarginRequest {
|
||||||
|
symbol: string;
|
||||||
|
side: string;
|
||||||
|
margin: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetUserLeverageRequest {
|
||||||
|
symbol: string;
|
||||||
|
buy_leverage: number;
|
||||||
|
sell_leverage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetTradingStopRequest {
|
||||||
|
symbol: string;
|
||||||
|
side: string;
|
||||||
|
take_profit?: number;
|
||||||
|
stop_loss?: number;
|
||||||
|
trailing_stop?: number;
|
||||||
|
tp_trigger_by?: string;
|
||||||
|
sl_trigger_by?: string;
|
||||||
|
sl_size?: number;
|
||||||
|
tp_size?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearGetTradeRecordsRequest {
|
||||||
|
symbol: string;
|
||||||
|
start_time?: number;
|
||||||
|
end_time?: number;
|
||||||
|
exec_type?: string;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearGetClosedPnlRequest {
|
||||||
|
symbol: string;
|
||||||
|
start_time?: number;
|
||||||
|
end_time?: number;
|
||||||
|
exec_type?: string;
|
||||||
|
page?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LinearSetRiskLimitRequest {
|
||||||
|
symbol: string;
|
||||||
|
side: string;
|
||||||
|
risk_id: number;
|
||||||
|
}
|
||||||
87
src/types/request/spot.ts
Normal file
87
src/types/request/spot.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { numberInString, OrderSide } from '../shared';
|
||||||
|
|
||||||
|
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
|
||||||
|
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
|
||||||
|
|
||||||
|
export interface NewSpotOrder {
|
||||||
|
symbol: string;
|
||||||
|
qty: number;
|
||||||
|
side: OrderSide;
|
||||||
|
type: OrderTypeSpot;
|
||||||
|
timeInForce?: OrderTimeInForce;
|
||||||
|
price?: number;
|
||||||
|
orderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NewSpotOrderV3 {
|
||||||
|
symbol: string;
|
||||||
|
orderQty: string;
|
||||||
|
side: OrderSide;
|
||||||
|
orderType: OrderTypeSpot;
|
||||||
|
timeInForce?: OrderTimeInForce;
|
||||||
|
orderPrice?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderCategory?: 0 | 1;
|
||||||
|
triggerPrice?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotCancelOrderBatchRequest {
|
||||||
|
symbol: string;
|
||||||
|
side?: OrderSide;
|
||||||
|
orderTypes: OrderTypeSpot[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotOrderQueryById {
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotSymbolInfo {
|
||||||
|
name: string;
|
||||||
|
alias: string;
|
||||||
|
baseCurrency: string;
|
||||||
|
quoteCurrency: string;
|
||||||
|
basePrecision: numberInString;
|
||||||
|
quotePrecision: numberInString;
|
||||||
|
minTradeQuantity: numberInString;
|
||||||
|
minTradeAmount: numberInString;
|
||||||
|
minPricePrecision: numberInString;
|
||||||
|
maxTradeQuantity: numberInString;
|
||||||
|
maxTradeAmount: numberInString;
|
||||||
|
category: numberInString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotMyTradesRequest {
|
||||||
|
symbol?: string;
|
||||||
|
orderId?: string;
|
||||||
|
limit?: string;
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
fromTradeId?: string;
|
||||||
|
toTradeId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotLeveragedTokenPRHistoryRequest {
|
||||||
|
ltCode?: string;
|
||||||
|
orderId?: string;
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
limit?: number;
|
||||||
|
orderType?: 1 | 2;
|
||||||
|
serialNo?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotCrossMarginBorrowingInfoRequest {
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
coin?: string;
|
||||||
|
status?: 0 | 1 | 2;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpotCrossMarginRepaymentHistoryRequest {
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
coin?: string;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
247
src/types/request/unified-margin.ts
Normal file
247
src/types/request/unified-margin.ts
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
import { KlineIntervalV3, OrderSide } from '../shared';
|
||||||
|
import { USDCOrderFilter, USDCTimeInForce } from './usdc-shared';
|
||||||
|
|
||||||
|
export type UMCategory = 'linear' | 'inverse' | 'option';
|
||||||
|
export type UMOrderType = 'Limit' | 'Market';
|
||||||
|
export type UMDirection = 'prev' | 'next';
|
||||||
|
|
||||||
|
export interface UMCandlesRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
interval: KlineIntervalV3;
|
||||||
|
start: number;
|
||||||
|
end: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMInstrumentInfoRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMFundingRateHistoryRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMOptionDeliveryPriceRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMPublicTradesRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
optionType?: 'Call' | 'Put';
|
||||||
|
limit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMOpenInterestRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
interval: '5min' | '15min' | '30min' | '1h' | '4h' | '1d';
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMOrderRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
side: OrderSide;
|
||||||
|
positionIdx?: '0';
|
||||||
|
orderType: UMOrderType;
|
||||||
|
qty: string;
|
||||||
|
price?: string;
|
||||||
|
basePrice?: string;
|
||||||
|
triggerPrice?: string;
|
||||||
|
triggerBy?: string;
|
||||||
|
iv?: string;
|
||||||
|
timeInForce: USDCTimeInForce;
|
||||||
|
orderLinkId?: string;
|
||||||
|
takeProfit?: number;
|
||||||
|
stopLoss?: number;
|
||||||
|
tpTriggerBy?: string;
|
||||||
|
slTriggerBy?: string;
|
||||||
|
reduceOnly?: boolean;
|
||||||
|
closeOnTrigger?: boolean;
|
||||||
|
mmp?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMModifyOrderRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
iv?: string;
|
||||||
|
triggerPrice?: string;
|
||||||
|
qty?: string;
|
||||||
|
price?: string;
|
||||||
|
takeProfit?: number;
|
||||||
|
stopLoss?: number;
|
||||||
|
tpTriggerBy?: string;
|
||||||
|
slTriggerBy?: string;
|
||||||
|
triggerBy?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMCancelOrderRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderFilter?: USDCOrderFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMActiveOrdersRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderFilter?: USDCOrderFilter;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMHistoricOrdersRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderStatus?: string;
|
||||||
|
orderFilter?: USDCOrderFilter;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMBatchOrder {
|
||||||
|
symbol: string;
|
||||||
|
side: OrderSide;
|
||||||
|
positionIdx?: '0';
|
||||||
|
orderType: UMOrderType;
|
||||||
|
qty: string;
|
||||||
|
price?: string;
|
||||||
|
iv?: string;
|
||||||
|
timeInForce: USDCTimeInForce;
|
||||||
|
orderLinkId?: string;
|
||||||
|
reduceOnly?: boolean;
|
||||||
|
closeOnTrigger?: boolean;
|
||||||
|
mmp?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMBatchOrderReplace {
|
||||||
|
symbol: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
iv?: string;
|
||||||
|
qty?: string;
|
||||||
|
price?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMBatchOrderCancel {
|
||||||
|
symbol: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMCancelAllOrdersRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
baseCoin?: string;
|
||||||
|
settleCoin?: string;
|
||||||
|
symbol?: string;
|
||||||
|
orderFilter?: USDCOrderFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMPositionsRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMSetTPSLRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
takeProfit?: string;
|
||||||
|
stopLoss?: string;
|
||||||
|
trailingStop?: string;
|
||||||
|
tpTriggerBy?: string;
|
||||||
|
slTriggerBy?: string;
|
||||||
|
activePrice?: string;
|
||||||
|
slSize?: string;
|
||||||
|
tpSize?: string;
|
||||||
|
positionIdx?: '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UM7DayTradingHistoryRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
execType?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMOptionsSettlementHistoryRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol?: string;
|
||||||
|
expDate?: string;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMPerpSettlementHistoryRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
symbol?: string;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMTransactionLogRequest {
|
||||||
|
category: UMCategory;
|
||||||
|
currency: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
type?: string;
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMExchangeCoinsRequest {
|
||||||
|
fromCoin?: string;
|
||||||
|
toCoin?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UMBorrowHistoryRequest {
|
||||||
|
currency: string;
|
||||||
|
startTime?: number;
|
||||||
|
endTime?: number;
|
||||||
|
direction?: UMDirection;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
134
src/types/request/usdc-options.ts
Normal file
134
src/types/request/usdc-options.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import { OrderSide } from '../shared';
|
||||||
|
import { USDCAPICategory, USDCOrderType, USDCTimeInForce } from './usdc-shared';
|
||||||
|
|
||||||
|
export interface USDCOptionsContractInfoRequest {
|
||||||
|
symbol?: string;
|
||||||
|
status?: 'WAITING_ONLINE' | 'ONLINE' | 'DELIVERING' | 'OFFLINE';
|
||||||
|
baseCoin?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsDeliveryPriceRequest {
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsRecentTradesRequest {
|
||||||
|
category: USDCAPICategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
optionType?: 'Call' | 'Put';
|
||||||
|
limit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsHistoricalVolatilityRequest {
|
||||||
|
baseCoin?: string;
|
||||||
|
period?: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderType: USDCOrderType;
|
||||||
|
side: OrderSide;
|
||||||
|
orderPrice?: string;
|
||||||
|
orderQty: string;
|
||||||
|
iv?: string;
|
||||||
|
timeInForce?: USDCTimeInForce;
|
||||||
|
orderLinkId?: string;
|
||||||
|
reduceOnly?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsModifyOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderPrice?: string;
|
||||||
|
orderQty?: string;
|
||||||
|
iv?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsCancelOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsCancelAllOrdersRequest {
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsActiveOrdersRealtimeRequest {
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsActiveOrdersRequest {
|
||||||
|
category: 'OPTION';
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsHistoricOrdersRequest {
|
||||||
|
category: 'OPTION';
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderStatus?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsOrderExecutionRequest {
|
||||||
|
category: 'OPTION';
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
startTime?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsDeliveryHistoryRequest {
|
||||||
|
symbol: string;
|
||||||
|
expDate?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsPositionsInfoExpiryRequest {
|
||||||
|
expDate?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCOptionsModifyMMPRequest {
|
||||||
|
currency: string;
|
||||||
|
windowMs: number;
|
||||||
|
frozenPeriodMs: number;
|
||||||
|
qtyLimit: string;
|
||||||
|
deltaLimit: string;
|
||||||
|
}
|
||||||
100
src/types/request/usdc-perp.ts
Normal file
100
src/types/request/usdc-perp.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { OrderSide } from '../shared';
|
||||||
|
import {
|
||||||
|
USDCAPICategory,
|
||||||
|
USDCOrderFilter,
|
||||||
|
USDCOrderType,
|
||||||
|
USDCTimeInForce,
|
||||||
|
} from './usdc-shared';
|
||||||
|
|
||||||
|
export interface USDCOpenInterestRequest {
|
||||||
|
symbol: string;
|
||||||
|
period: string;
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCLast500TradesRequest {
|
||||||
|
category: USDCAPICategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
limit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCSymbolDirectionLimit {
|
||||||
|
symbol?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCSymbolDirectionLimitCursor {
|
||||||
|
symbol?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCPerpOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderType: USDCOrderType;
|
||||||
|
orderFilter: USDCOrderFilter;
|
||||||
|
side: OrderSide;
|
||||||
|
orderPrice?: string;
|
||||||
|
orderQty: string;
|
||||||
|
timeInForce?: USDCTimeInForce;
|
||||||
|
orderLinkId?: string;
|
||||||
|
reduceOnly?: boolean;
|
||||||
|
closeOnTrigger?: boolean;
|
||||||
|
takeProfit?: string;
|
||||||
|
stopLoss?: string;
|
||||||
|
tptriggerby?: string;
|
||||||
|
slTriggerBy?: string;
|
||||||
|
basePrice?: string;
|
||||||
|
triggerPrice?: string;
|
||||||
|
triggerBy?: string;
|
||||||
|
mmp?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCPerpModifyOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderFilter: USDCOrderFilter;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderPrice?: string;
|
||||||
|
orderQty?: string;
|
||||||
|
takeProfit?: string;
|
||||||
|
stopLoss?: string;
|
||||||
|
tptriggerby?: string;
|
||||||
|
slTriggerBy?: string;
|
||||||
|
triggerPrice?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCPerpCancelOrderRequest {
|
||||||
|
symbol: string;
|
||||||
|
orderFilter: USDCOrderFilter;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCPerpActiveOrdersRequest {
|
||||||
|
category: 'PERPETUAL';
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderFilter?: USDCOrderFilter;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCPerpHistoricOrdersRequest {
|
||||||
|
category: 'PERPETUAL';
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
orderId?: string;
|
||||||
|
orderLinkId?: string;
|
||||||
|
orderStatus?: string;
|
||||||
|
orderFilter?: USDCOrderFilter;
|
||||||
|
direction?: string;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
39
src/types/request/usdc-shared.ts
Normal file
39
src/types/request/usdc-shared.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
export type USDCAPICategory = 'PERPETUAL' | 'OPTION';
|
||||||
|
|
||||||
|
export type USDCOrderType = 'Limit' | 'Market';
|
||||||
|
|
||||||
|
export type USDCTimeInForce =
|
||||||
|
| 'GoodTillCancel'
|
||||||
|
| 'ImmediateOrCancel'
|
||||||
|
| 'FillOrKill'
|
||||||
|
| 'PostOnly';
|
||||||
|
|
||||||
|
export type USDCOrderFilter = 'Order' | 'StopOrder';
|
||||||
|
|
||||||
|
export interface USDCKlineRequest {
|
||||||
|
symbol: string;
|
||||||
|
period: string;
|
||||||
|
startTime: number;
|
||||||
|
limit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCTransactionLogRequest {
|
||||||
|
type: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
category?: USDCAPICategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface USDCPositionsRequest {
|
||||||
|
category: USDCAPICategory;
|
||||||
|
symbol?: string;
|
||||||
|
baseCoin?: string;
|
||||||
|
expDate?: string;
|
||||||
|
direction?: string;
|
||||||
|
limit?: string;
|
||||||
|
cursor?: string;
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { LinearPositionIdx } from '../../constants/enum';
|
|
||||||
import { OrderSide } from '../shared';
|
|
||||||
|
|
||||||
export type LinearOrderType = 'Limit' | 'Market';
|
|
||||||
|
|
||||||
export type LinearTimeInForce =
|
|
||||||
| 'GoodTillCancel'
|
|
||||||
| 'ImmediateOrCancel'
|
|
||||||
| 'FillOrKill'
|
|
||||||
| 'PostOnly';
|
|
||||||
|
|
||||||
export interface NewLinearOrder {
|
|
||||||
side: OrderSide;
|
|
||||||
symbol: string;
|
|
||||||
order_type: LinearOrderType;
|
|
||||||
qty: number;
|
|
||||||
price?: number;
|
|
||||||
time_in_force: LinearTimeInForce;
|
|
||||||
take_profit?: number;
|
|
||||||
stop_loss?: number;
|
|
||||||
tp_trigger_by?: string;
|
|
||||||
sl_trigger_by?: string;
|
|
||||||
reduce_only: boolean;
|
|
||||||
close_on_trigger: boolean;
|
|
||||||
order_link_id?: string;
|
|
||||||
position_idx?: LinearPositionIdx;
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,20 @@
|
|||||||
|
import { InverseClient } from '../inverse-client';
|
||||||
|
import { LinearClient } from '../linear-client';
|
||||||
|
import { SpotClient } from '../spot-client';
|
||||||
|
import { SpotClientV3 } from '../spot-client-v3';
|
||||||
|
import { UnifiedMarginClient } from '../unified-margin-client';
|
||||||
|
import { USDCOptionClient } from '../usdc-option-client';
|
||||||
|
import { USDCPerpetualClient } from '../usdc-perpetual-client';
|
||||||
|
|
||||||
|
export type RESTClient =
|
||||||
|
| InverseClient
|
||||||
|
| LinearClient
|
||||||
|
| SpotClient
|
||||||
|
| SpotClientV3
|
||||||
|
| USDCOptionClient
|
||||||
|
| USDCPerpetualClient
|
||||||
|
| UnifiedMarginClient;
|
||||||
|
|
||||||
export type numberInString = string;
|
export type numberInString = string;
|
||||||
|
|
||||||
export type OrderSide = 'Buy' | 'Sell';
|
export type OrderSide = 'Buy' | 'Sell';
|
||||||
@@ -17,6 +34,21 @@ export type KlineInterval =
|
|||||||
| '1w'
|
| '1w'
|
||||||
| '1M';
|
| '1M';
|
||||||
|
|
||||||
|
export type KlineIntervalV3 =
|
||||||
|
| '1'
|
||||||
|
| '3'
|
||||||
|
| '5'
|
||||||
|
| '15'
|
||||||
|
| '30'
|
||||||
|
| '60'
|
||||||
|
| '120'
|
||||||
|
| '240'
|
||||||
|
| '360'
|
||||||
|
| '720'
|
||||||
|
| 'D'
|
||||||
|
| 'W'
|
||||||
|
| 'M';
|
||||||
|
|
||||||
export interface APIResponse<T> {
|
export interface APIResponse<T> {
|
||||||
ret_code: number;
|
ret_code: number;
|
||||||
ret_msg: 'OK' | string;
|
ret_msg: 'OK' | string;
|
||||||
@@ -25,7 +57,13 @@ export interface APIResponse<T> {
|
|||||||
result: T;
|
result: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface APIResponseWithTime<T> extends APIResponse<T> {
|
export interface APIResponseV3<T> {
|
||||||
|
retCode: number;
|
||||||
|
retMsg: 'OK' | string;
|
||||||
|
result: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface APIResponseWithTime<T = {}> extends APIResponse<T> {
|
||||||
/** UTC timestamp */
|
/** UTC timestamp */
|
||||||
time_now: numberInString;
|
time_now: numberInString;
|
||||||
}
|
}
|
||||||
@@ -37,15 +75,15 @@ export interface SymbolParam {
|
|||||||
symbol: string;
|
symbol: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SymbolLimitParam {
|
export interface SymbolLimitParam<TLimit = number> {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
limit?: number;
|
limit?: TLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SymbolPeriodLimitParam {
|
export interface SymbolPeriodLimitParam<TLimit = number> {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
period: string;
|
period: string;
|
||||||
limit?: number;
|
limit?: TLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SymbolFromLimitParam {
|
export interface SymbolFromLimitParam {
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import { numberInString, OrderSide } from './shared';
|
|
||||||
|
|
||||||
export type OrderTypeSpot = 'LIMIT' | 'MARKET' | 'LIMIT_MAKER';
|
|
||||||
export type OrderTimeInForce = 'GTC' | 'FOK' | 'IOC';
|
|
||||||
|
|
||||||
export interface NewSpotOrder {
|
|
||||||
symbol: string;
|
|
||||||
qty: number;
|
|
||||||
side: OrderSide;
|
|
||||||
type: OrderTypeSpot;
|
|
||||||
timeInForce?: OrderTimeInForce;
|
|
||||||
price?: number;
|
|
||||||
orderLinkId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SpotOrderQueryById {
|
|
||||||
orderId?: string;
|
|
||||||
orderLinkId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SpotSymbolInfo {
|
|
||||||
name: string;
|
|
||||||
alias: string;
|
|
||||||
baseCurrency: string;
|
|
||||||
quoteCurrency: string;
|
|
||||||
basePrecision: numberInString;
|
|
||||||
quotePrecision: numberInString;
|
|
||||||
minTradeQuantity: numberInString;
|
|
||||||
minTradeAmount: numberInString;
|
|
||||||
minPricePrecision: numberInString;
|
|
||||||
maxTradeQuantity: numberInString;
|
|
||||||
maxTradeAmount: numberInString;
|
|
||||||
category: numberInString;
|
|
||||||
}
|
|
||||||
111
src/types/websockets.ts
Normal file
111
src/types/websockets.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { RestClientOptions, WS_KEY_MAP } from '../util';
|
||||||
|
|
||||||
|
/** For spot markets, spotV3 is recommended */
|
||||||
|
export type APIMarket =
|
||||||
|
| 'inverse'
|
||||||
|
| 'linear'
|
||||||
|
| 'spot'
|
||||||
|
| 'spotv3'
|
||||||
|
| 'usdcOption'
|
||||||
|
| 'usdcPerp'
|
||||||
|
| 'unifiedPerp'
|
||||||
|
| 'unifiedOption';
|
||||||
|
|
||||||
|
// Same as inverse futures
|
||||||
|
export type WsPublicInverseTopic =
|
||||||
|
| 'orderBookL2_25'
|
||||||
|
| 'orderBookL2_200'
|
||||||
|
| 'trade'
|
||||||
|
| 'insurance'
|
||||||
|
| 'instrument_info'
|
||||||
|
| 'klineV2';
|
||||||
|
|
||||||
|
export type WsPublicUSDTPerpTopic =
|
||||||
|
| 'orderBookL2_25'
|
||||||
|
| 'orderBookL2_200'
|
||||||
|
| 'trade'
|
||||||
|
| 'insurance'
|
||||||
|
| 'instrument_info'
|
||||||
|
| 'kline';
|
||||||
|
|
||||||
|
export type WsPublicSpotV1Topic =
|
||||||
|
| 'trade'
|
||||||
|
| 'realtimes'
|
||||||
|
| 'kline'
|
||||||
|
| 'depth'
|
||||||
|
| 'mergedDepth'
|
||||||
|
| 'diffDepth';
|
||||||
|
|
||||||
|
export type WsPublicSpotV2Topic =
|
||||||
|
| 'depth'
|
||||||
|
| 'kline'
|
||||||
|
| 'trade'
|
||||||
|
| 'bookTicker'
|
||||||
|
| 'realtimes';
|
||||||
|
|
||||||
|
export type WsPublicTopics =
|
||||||
|
| WsPublicInverseTopic
|
||||||
|
| WsPublicUSDTPerpTopic
|
||||||
|
| WsPublicSpotV1Topic
|
||||||
|
| WsPublicSpotV2Topic
|
||||||
|
| string;
|
||||||
|
|
||||||
|
// Same as inverse futures
|
||||||
|
export type WsPrivateInverseTopic =
|
||||||
|
| 'position'
|
||||||
|
| 'execution'
|
||||||
|
| 'order'
|
||||||
|
| 'stop_order';
|
||||||
|
|
||||||
|
export type WsPrivateUSDTPerpTopic =
|
||||||
|
| 'position'
|
||||||
|
| 'execution'
|
||||||
|
| 'order'
|
||||||
|
| 'stop_order'
|
||||||
|
| 'wallet';
|
||||||
|
|
||||||
|
export type WsPrivateSpotTopic =
|
||||||
|
| 'outboundAccountInfo'
|
||||||
|
| 'executionReport'
|
||||||
|
| 'ticketInfo';
|
||||||
|
|
||||||
|
export type WsPrivateTopic =
|
||||||
|
| WsPrivateInverseTopic
|
||||||
|
| WsPrivateUSDTPerpTopic
|
||||||
|
| WsPrivateSpotTopic
|
||||||
|
| string;
|
||||||
|
|
||||||
|
export type WsTopic = WsPublicTopics | WsPrivateTopic;
|
||||||
|
|
||||||
|
/** This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets) */
|
||||||
|
export type WsKey = typeof WS_KEY_MAP[keyof typeof WS_KEY_MAP];
|
||||||
|
|
||||||
|
export interface WSClientConfigurableOptions {
|
||||||
|
key?: string;
|
||||||
|
secret?: string;
|
||||||
|
testnet?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API group this client should connect to.
|
||||||
|
*
|
||||||
|
* For the V3 APIs use `v3` as the market (spot/unified margin/usdc/account asset/copy trading)
|
||||||
|
*/
|
||||||
|
market: APIMarket;
|
||||||
|
|
||||||
|
pongTimeout?: number;
|
||||||
|
pingInterval?: number;
|
||||||
|
reconnectTimeout?: number;
|
||||||
|
restOptions?: RestClientOptions;
|
||||||
|
requestOptions?: any;
|
||||||
|
wsUrl?: string;
|
||||||
|
/** If true, fetch server time before trying to authenticate (disabled by default) */
|
||||||
|
fetchTimeOffsetBeforeAuth?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebsocketClientOptions extends WSClientConfigurableOptions {
|
||||||
|
testnet?: boolean;
|
||||||
|
market: APIMarket;
|
||||||
|
pongTimeout: number;
|
||||||
|
pingInterval: number;
|
||||||
|
reconnectTimeout: number;
|
||||||
|
}
|
||||||
396
src/unified-margin-client.ts
Normal file
396
src/unified-margin-client.ts
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
import {
|
||||||
|
APIResponseWithTime,
|
||||||
|
APIResponseV3,
|
||||||
|
UMCategory,
|
||||||
|
UMCandlesRequest,
|
||||||
|
UMInstrumentInfoRequest,
|
||||||
|
UMFundingRateHistoryRequest,
|
||||||
|
UMOptionDeliveryPriceRequest,
|
||||||
|
UMPublicTradesRequest,
|
||||||
|
UMOpenInterestRequest,
|
||||||
|
UMOrderRequest,
|
||||||
|
UMModifyOrderRequest,
|
||||||
|
UMCancelOrderRequest,
|
||||||
|
UMActiveOrdersRequest,
|
||||||
|
UMHistoricOrdersRequest,
|
||||||
|
UMBatchOrder,
|
||||||
|
UMBatchOrderReplace,
|
||||||
|
UMBatchOrderCancel,
|
||||||
|
UMCancelAllOrdersRequest,
|
||||||
|
UMPositionsRequest,
|
||||||
|
UMSetTPSLRequest,
|
||||||
|
UM7DayTradingHistoryRequest,
|
||||||
|
UMOptionsSettlementHistoryRequest,
|
||||||
|
UMPerpSettlementHistoryRequest,
|
||||||
|
UMTransactionLogRequest,
|
||||||
|
InternalTransferRequest,
|
||||||
|
UMExchangeCoinsRequest,
|
||||||
|
UMBorrowHistoryRequest,
|
||||||
|
} from './types';
|
||||||
|
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API client for Derivatives V3 unified margin APIs
|
||||||
|
*/
|
||||||
|
export class UnifiedMarginClient extends BaseRestClient {
|
||||||
|
getClientType() {
|
||||||
|
return REST_CLIENT_TYPE_ENUM.v3;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Market Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Query order book info. Each side has a depth of 25 orders. */
|
||||||
|
getOrderBook(
|
||||||
|
symbol: string,
|
||||||
|
category: string,
|
||||||
|
limit?: number
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/order-book/L2', {
|
||||||
|
category,
|
||||||
|
symbol,
|
||||||
|
limit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get candles/klines */
|
||||||
|
getCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a symbol price/statistics ticker */
|
||||||
|
getSymbolTicker(
|
||||||
|
category: UMCategory,
|
||||||
|
symbol?: string
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/tickers', { category, symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get trading rules per symbol/contract, incl price/amount/value/leverage filters */
|
||||||
|
getInstrumentInfo(
|
||||||
|
params: UMInstrumentInfoRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/instruments-info', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query mark price kline (like getCandles() but for mark price). */
|
||||||
|
getMarkPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/mark-price-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Index Price Kline */
|
||||||
|
getIndexPriceCandles(params: UMCandlesRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/index-price-kline', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The funding rate is generated every 8 hours at 00:00 UTC, 08:00 UTC and 16:00 UTC.
|
||||||
|
* For example, if a request is sent at 12:00 UTC, the funding rate generated earlier that day at 08:00 UTC will be sent.
|
||||||
|
*/
|
||||||
|
getFundingRateHistory(
|
||||||
|
params: UMFundingRateHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get(
|
||||||
|
'/derivatives/v3/public/funding/history-funding-rate',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get Risk Limit */
|
||||||
|
getRiskLimit(
|
||||||
|
category: UMCategory,
|
||||||
|
symbol: string
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/risk-limit/list', {
|
||||||
|
category,
|
||||||
|
symbol,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get option delivery price */
|
||||||
|
getOptionDeliveryPrice(
|
||||||
|
params: UMOptionDeliveryPriceRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/delivery-price', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get recent trades */
|
||||||
|
getTrades(params: UMPublicTradesRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/recent-trade', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the total amount of unsettled contracts.
|
||||||
|
* In other words, the total number of contracts held in open positions.
|
||||||
|
*/
|
||||||
|
getOpenInterest(params: UMOpenInterestRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/derivatives/v3/public/open-interest', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Unified Margin Account Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** -> Order API */
|
||||||
|
|
||||||
|
/** Place an order */
|
||||||
|
submitOrder(params: UMOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/order/create', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Active order parameters (such as quantity, price) and stop order parameters cannot be modified in one request at the same time. Please request modification separately. */
|
||||||
|
modifyOrder(params: UMModifyOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/order/replace', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel order */
|
||||||
|
cancelOrder(params: UMCancelOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/order/cancel', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Open Orders */
|
||||||
|
getActiveOrders(params: UMActiveOrdersRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/order/unfilled-orders', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query order history. As order creation/cancellation is asynchronous, the data returned from the interface may be delayed. To access order information in real-time, call getActiveOrders() */
|
||||||
|
getHistoricOrders(
|
||||||
|
params: UMHistoricOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/order/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API provides the batch order mode under the unified margin account.
|
||||||
|
* Max 10 per request
|
||||||
|
*/
|
||||||
|
batchSubmitOrders(
|
||||||
|
category: UMCategory,
|
||||||
|
orders: UMBatchOrder[]
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/order/create-batch', {
|
||||||
|
category,
|
||||||
|
request: orders,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface can modify the open order information in batches.
|
||||||
|
* Currently, it is not supported to modify the conditional order information.
|
||||||
|
* Please note that only unfilled or partial filled orders can be modified.
|
||||||
|
* If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type
|
||||||
|
*/
|
||||||
|
batchReplaceOrders(
|
||||||
|
category: UMCategory,
|
||||||
|
orders: UMBatchOrderReplace[]
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/order/replace-batch', {
|
||||||
|
category,
|
||||||
|
request: orders,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API provides batch cancellation under the unified margin account.
|
||||||
|
* Order cancellation of futures and options cannot be canceled in one request at the same time.
|
||||||
|
* If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type.
|
||||||
|
*/
|
||||||
|
batchCancelOrders(
|
||||||
|
category: UMCategory,
|
||||||
|
orders: UMBatchOrderCancel[]
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/order/cancel-batch', {
|
||||||
|
category,
|
||||||
|
request: orders,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API provides the cancellation of all open orders under the unified margin account.
|
||||||
|
* Order cancellation of futures and options cannot be canceled in one request at the same time.
|
||||||
|
* If both futures and options orders are in one request, only the orders matching the category will be operated according to the category type.
|
||||||
|
*/
|
||||||
|
cancelAllOrders(
|
||||||
|
params: UMCancelAllOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/order/cancel-all', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Positions API */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query my positions real-time. Accessing personal list of positions.
|
||||||
|
* Users can access their position holding information through this interface, such as the number of position holdings and wallet balance.
|
||||||
|
*/
|
||||||
|
getPositions(params: UMPositionsRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/position/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Leverage setting. */
|
||||||
|
setLeverage(
|
||||||
|
category: UMCategory,
|
||||||
|
symbol: string,
|
||||||
|
buyLeverage: number,
|
||||||
|
sellLeverage: number
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/position/set-leverage', {
|
||||||
|
category,
|
||||||
|
symbol,
|
||||||
|
buyLeverage,
|
||||||
|
sellLeverage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switching the TP/SL mode to the cross margin mode or selected positions.
|
||||||
|
* When you set the TP/SL mode on the selected positions, the quantity of take-profit or stop-loss orders can be smaller than the position size. Please use Trading-Stop endpoint.
|
||||||
|
*/
|
||||||
|
setTPSLMode(
|
||||||
|
category: UMCategory,
|
||||||
|
symbol: string,
|
||||||
|
tpSlMode: 1 | 0
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/position/tpsl/switch-mode', {
|
||||||
|
category,
|
||||||
|
symbol,
|
||||||
|
tpSlMode,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set risk limit */
|
||||||
|
setRiskLimit(
|
||||||
|
category: UMCategory,
|
||||||
|
symbol: string,
|
||||||
|
riskId: number,
|
||||||
|
positionIdx: number
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/unified/v3/private/position/set-risk-limit', {
|
||||||
|
category,
|
||||||
|
symbol,
|
||||||
|
riskId,
|
||||||
|
positionIdx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set position TP/SL and trailing stop.
|
||||||
|
* Pass the following parameters, then the system will create conditional orders.
|
||||||
|
* If the position is closed, the system will cancel these orders, and adjust the position size.
|
||||||
|
*/
|
||||||
|
setTPSL(params: UMSetTPSLRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/unified/v3/private/position/trading-stop',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the user's filled history, ranked by time in descending order.
|
||||||
|
* There might be multiple filled histories for an order.
|
||||||
|
*/
|
||||||
|
get7DayTradingHistory(
|
||||||
|
params: UM7DayTradingHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/execution/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query the settlement history, ranked by time in descending order. */
|
||||||
|
getOptionsSettlementHistory(
|
||||||
|
params: UMOptionsSettlementHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/delivery-record', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query session settlement records, only for USDC perpetual */
|
||||||
|
getUSDCPerpetualSettlementHistory(
|
||||||
|
params: UMPerpSettlementHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/settlement-record', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Account API */
|
||||||
|
|
||||||
|
/** Query wallet balance */
|
||||||
|
getBalances(coin?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/account/wallet/balance', {
|
||||||
|
coin,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade to unified margin account.
|
||||||
|
* WARNING: This is currently not reversable!
|
||||||
|
*/
|
||||||
|
upgradeToUnifiedMargin(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/unified/v3/private/account/upgrade-unified-account'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query trading history */
|
||||||
|
getTransactionLog(
|
||||||
|
params: UMTransactionLogRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/unified/v3/private/account/transaction-log',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fund transfer between accounts (v2) */
|
||||||
|
transferFunds(params: InternalTransferRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/asset/v1/private/transfer', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Exchange Coins */
|
||||||
|
getCoinExchangeHistory(
|
||||||
|
params?: UMExchangeCoinsRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/asset/v2/private/exchange/exchange-order-all',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get Borrow History */
|
||||||
|
getBorrowHistory(
|
||||||
|
params?: UMBorrowHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/unified/v3/private/account/borrow-history',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get Borrow Rate */
|
||||||
|
getBorrowRate(currency?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate('/unified/v3/private/account/borrow-rate', {
|
||||||
|
currency,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime> {
|
||||||
|
return this.get('/v2/public/time');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnnouncements(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/v2/public/announcement');
|
||||||
|
}
|
||||||
|
}
|
||||||
326
src/usdc-option-client.ts
Normal file
326
src/usdc-option-client.ts
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
import {
|
||||||
|
APIResponseWithTime,
|
||||||
|
APIResponseV3,
|
||||||
|
USDCOptionsActiveOrdersRealtimeRequest,
|
||||||
|
USDCOptionsActiveOrdersRequest,
|
||||||
|
USDCOptionsCancelAllOrdersRequest,
|
||||||
|
USDCOptionsCancelOrderRequest,
|
||||||
|
USDCOptionsContractInfoRequest,
|
||||||
|
USDCOptionsDeliveryHistoryRequest,
|
||||||
|
USDCOptionsDeliveryPriceRequest,
|
||||||
|
USDCOptionsHistoricalVolatilityRequest,
|
||||||
|
USDCOptionsHistoricOrdersRequest,
|
||||||
|
USDCOptionsModifyMMPRequest,
|
||||||
|
USDCOptionsModifyOrderRequest,
|
||||||
|
USDCOptionsOrderExecutionRequest,
|
||||||
|
USDCOptionsOrderRequest,
|
||||||
|
USDCOptionsPositionsInfoExpiryRequest,
|
||||||
|
USDCOptionsRecentTradesRequest,
|
||||||
|
USDCPositionsRequest,
|
||||||
|
USDCTransactionLogRequest,
|
||||||
|
} from './types';
|
||||||
|
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API client for USDC Option APIs
|
||||||
|
*/
|
||||||
|
export class USDCOptionClient extends BaseRestClient {
|
||||||
|
getClientType() {
|
||||||
|
return REST_CLIENT_TYPE_ENUM.v3;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Market Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Query order book info. Each side has a depth of 25 orders. */
|
||||||
|
getOrderBook(symbol: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/option/usdc/openapi/public/v1/order-book', { symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fetch trading rules (such as min/max qty). Query for all if blank. */
|
||||||
|
getContractInfo(
|
||||||
|
params?: USDCOptionsContractInfoRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/option/usdc/openapi/public/v1/symbols', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a symbol price/statistics ticker */
|
||||||
|
getSymbolTicker(symbol: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/option/usdc/openapi/public/v1/tick', { symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get delivery information */
|
||||||
|
getDeliveryPrice(
|
||||||
|
params?: USDCOptionsDeliveryPriceRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/option/usdc/openapi/public/v1/delivery-price', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returned records are Taker Buy in default. */
|
||||||
|
getLast500Trades(
|
||||||
|
params: USDCOptionsRecentTradesRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get(
|
||||||
|
'/option/usdc/openapi/public/v1/query-trade-latest',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data is in hourly.
|
||||||
|
* If time field is not passed, it returns the recent 1 hour data by default.
|
||||||
|
* It could be any timeframe by inputting startTime & endTime, but it must satisfy [endTime - startTime] <= 30 days.
|
||||||
|
* It returns all data in 2 years when startTime & endTime are not passed.
|
||||||
|
* Both startTime & endTime entered together or both are left blank
|
||||||
|
*/
|
||||||
|
getHistoricalVolatility(
|
||||||
|
params?: USDCOptionsHistoricalVolatilityRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get(
|
||||||
|
'/option/usdc/openapi/public/v1/query-historical-volatility',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Account Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** -> Order API */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place an order using the USDC Derivatives Account.
|
||||||
|
* The request status can be queried in real-time.
|
||||||
|
* The response parameters must be queried through a query or a WebSocket response.
|
||||||
|
*/
|
||||||
|
submitOrder(params: USDCOptionsOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/place-order',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each request supports a max. of four orders. The reduceOnly parameter should be separate and unique for each order in the request.
|
||||||
|
*/
|
||||||
|
batchSubmitOrders(
|
||||||
|
orderRequest: USDCOptionsOrderRequest[]
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/batch-place-orders',
|
||||||
|
{ orderRequest }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For Options, at least one of the three parameters — price, quantity or implied volatility — must be input. */
|
||||||
|
modifyOrder(
|
||||||
|
params: USDCOptionsModifyOrderRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/replace-order',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Each request supports a max. of four orders. The reduceOnly parameter should be separate and unique for each order in the request. */
|
||||||
|
batchModifyOrders(
|
||||||
|
replaceOrderRequest: USDCOptionsModifyOrderRequest[]
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/batch-replace-orders',
|
||||||
|
{ replaceOrderRequest }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel order */
|
||||||
|
cancelOrder(
|
||||||
|
params: USDCOptionsCancelOrderRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/cancel-order',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Batch cancel orders */
|
||||||
|
batchCancelOrders(
|
||||||
|
cancelRequest: USDCOptionsCancelOrderRequest[]
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/batch-cancel-orders',
|
||||||
|
{ cancelRequest }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This is used to cancel all active orders. The real-time response indicates whether the request is successful, depending on retCode. */
|
||||||
|
cancelActiveOrders(
|
||||||
|
params?: USDCOptionsCancelAllOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/cancel-all',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Unfilled/Partially Filled Orders(real-time), up to last 7 days of partially filled/unfilled orders */
|
||||||
|
getActiveRealtimeOrders(
|
||||||
|
params?: USDCOptionsActiveOrdersRealtimeRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/trade/query-active-orders',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Unfilled/Partially Filled Orders */
|
||||||
|
getActiveOrders(
|
||||||
|
params: USDCOptionsActiveOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-active-orders',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query order history. The endpoint only supports up to 30 days of queried records */
|
||||||
|
getHistoricOrders(
|
||||||
|
params: USDCOptionsHistoricOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-order-history',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query trade history. The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */
|
||||||
|
getOrderExecutionHistory(
|
||||||
|
params: USDCOptionsOrderExecutionRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/execution-list',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Account API */
|
||||||
|
|
||||||
|
/** The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */
|
||||||
|
getTransactionLog(
|
||||||
|
params: USDCTransactionLogRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-transaction-log',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wallet info for USDC account. */
|
||||||
|
getBalances(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-wallet-balance'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asset Info */
|
||||||
|
getAssetInfo(baseCoin?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-asset-info',
|
||||||
|
{ baseCoin }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If USDC derivatives account balance is greater than X, you can open PORTFOLIO_MARGIN, and if it is less than Y, it will automatically close PORTFOLIO_MARGIN and change back to REGULAR_MARGIN. X and Y will be adjusted according to operational requirements.
|
||||||
|
* Rest API returns the result of checking prerequisites. You could get the real status of margin mode change by subscribing margin mode.
|
||||||
|
*/
|
||||||
|
setMarginMode(
|
||||||
|
newMarginMode: 'REGULAR_MARGIN' | 'PORTFOLIO_MARGIN'
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/private/asset/account/setMarginMode',
|
||||||
|
{ setMarginMode: newMarginMode }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query margin mode for USDC account. */
|
||||||
|
getMarginMode(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-margin-info'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Positions API */
|
||||||
|
|
||||||
|
/** Query my positions */
|
||||||
|
getPositions(params: USDCPositionsRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-position',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Delivery History */
|
||||||
|
getDeliveryHistory(
|
||||||
|
params: USDCOptionsDeliveryHistoryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-delivery-list',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Positions Info Upon Expiry */
|
||||||
|
getPositionsInfoUponExpiry(
|
||||||
|
params?: USDCOptionsPositionsInfoExpiryRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-position-exp-date',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Market Maker Protection */
|
||||||
|
|
||||||
|
/** modifyMMP */
|
||||||
|
modifyMMP(params: USDCOptionsModifyMMPRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/mmp-modify',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** resetMMP */
|
||||||
|
resetMMP(currency: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/option/usdc/openapi/private/v1/mmp-reset', {
|
||||||
|
currency,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** queryMMPState */
|
||||||
|
queryMMPState(baseCoin: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/option/usdc/openapi/private/v1/get-mmp-state', {
|
||||||
|
baseCoin,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime> {
|
||||||
|
return this.get('/v2/public/time');
|
||||||
|
}
|
||||||
|
}
|
||||||
308
src/usdc-perpetual-client.ts
Normal file
308
src/usdc-perpetual-client.ts
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
import {
|
||||||
|
APIResponseWithTime,
|
||||||
|
SymbolLimitParam,
|
||||||
|
SymbolPeriodLimitParam,
|
||||||
|
APIResponseV3,
|
||||||
|
USDCKlineRequest,
|
||||||
|
USDCLast500TradesRequest,
|
||||||
|
USDCOpenInterestRequest,
|
||||||
|
USDCOrderFilter,
|
||||||
|
USDCPerpActiveOrdersRequest,
|
||||||
|
USDCPerpCancelOrderRequest,
|
||||||
|
USDCPerpHistoricOrdersRequest,
|
||||||
|
USDCPerpModifyOrderRequest,
|
||||||
|
USDCPerpOrderRequest,
|
||||||
|
USDCPositionsRequest,
|
||||||
|
USDCSymbolDirectionLimit,
|
||||||
|
USDCSymbolDirectionLimitCursor,
|
||||||
|
USDCTransactionLogRequest,
|
||||||
|
} from './types';
|
||||||
|
import { REST_CLIENT_TYPE_ENUM } from './util';
|
||||||
|
import BaseRestClient from './util/BaseRestClient';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST API client for USDC Perpetual APIs
|
||||||
|
*/
|
||||||
|
export class USDCPerpetualClient extends BaseRestClient {
|
||||||
|
getClientType() {
|
||||||
|
return REST_CLIENT_TYPE_ENUM.v3;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchServerTime(): Promise<number> {
|
||||||
|
const res = await this.getServerTime();
|
||||||
|
return Number(res.time_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Market Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getOrderBook(symbol: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/order-book', { symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Fetch trading rules (such as min/max qty). Query for all if blank. */
|
||||||
|
getContractInfo(
|
||||||
|
params?: USDCSymbolDirectionLimit
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/symbols', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a symbol price/statistics ticker */
|
||||||
|
getSymbolTicker(symbol: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/tick', { symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
getCandles(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/kline/list', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMarkPrice(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get(
|
||||||
|
'/perpetual/usdc/openapi/public/v1/mark-price-kline',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexPrice(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get(
|
||||||
|
'/perpetual/usdc/openapi/public/v1/index-price-kline',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexPremium(params: USDCKlineRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get(
|
||||||
|
'/perpetual/usdc/openapi/public/v1/premium-index-kline',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOpenInterest(
|
||||||
|
params: USDCOpenInterestRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/open-interest', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLargeOrders(
|
||||||
|
params: SymbolLimitParam<string>
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/big-deal', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLongShortRatio(
|
||||||
|
params: SymbolPeriodLimitParam<string>
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/account-ratio', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLast500Trades(
|
||||||
|
params: USDCLast500TradesRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get(
|
||||||
|
'/option/usdc/openapi/public/v1/query-trade-latest',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Account Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** -> Order API */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place an order using the USDC Derivatives Account.
|
||||||
|
* The request status can be queried in real-time.
|
||||||
|
* The response parameters must be queried through a query or a WebSocket response.
|
||||||
|
*/
|
||||||
|
submitOrder(params: USDCPerpOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/perpetual/usdc/openapi/private/v1/place-order',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Active order parameters (such as quantity, price) and stop order parameters cannot be modified in one request at the same time. Please request modification separately. */
|
||||||
|
modifyOrder(params: USDCPerpModifyOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/perpetual/usdc/openapi/private/v1/replace-order',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel order */
|
||||||
|
cancelOrder(params: USDCPerpCancelOrderRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/perpetual/usdc/openapi/private/v1/cancel-order',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel all active orders. The real-time response indicates whether the request is successful, depending on retCode. */
|
||||||
|
cancelActiveOrders(
|
||||||
|
symbol: string,
|
||||||
|
orderFilter: USDCOrderFilter
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate('/perpetual/usdc/openapi/private/v1/cancel-all', {
|
||||||
|
symbol,
|
||||||
|
orderFilter,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Unfilled/Partially Filled Orders */
|
||||||
|
getActiveOrders(
|
||||||
|
params: USDCPerpActiveOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-active-orders',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query order history. The endpoint only supports up to 30 days of queried records */
|
||||||
|
getHistoricOrders(
|
||||||
|
params: USDCPerpHistoricOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-order-history',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query trade history. The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */
|
||||||
|
getOrderExecutionHistory(
|
||||||
|
params: USDCPerpActiveOrdersRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/execution-list',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Account API */
|
||||||
|
|
||||||
|
/** The endpoint only supports up to 30 days of queried records. An error will be returned if startTime is more than 30 days. */
|
||||||
|
getTransactionLog(
|
||||||
|
params: USDCTransactionLogRequest
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-transaction-log',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wallet info for USDC account. */
|
||||||
|
getBalances(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-wallet-balance'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asset Info */
|
||||||
|
getAssetInfo(baseCoin?: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-asset-info',
|
||||||
|
{ baseCoin }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If USDC derivatives account balance is greater than X, you can open PORTFOLIO_MARGIN, and if it is less than Y, it will automatically close PORTFOLIO_MARGIN and change back to REGULAR_MARGIN. X and Y will be adjusted according to operational requirements.
|
||||||
|
* Rest API returns the result of checking prerequisites. You could get the real status of margin mode change by subscribing margin mode.
|
||||||
|
*/
|
||||||
|
setMarginMode(
|
||||||
|
newMarginMode: 'REGULAR_MARGIN' | 'PORTFOLIO_MARGIN'
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/private/asset/account/setMarginMode',
|
||||||
|
{ setMarginMode: newMarginMode }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query margin mode for USDC account. */
|
||||||
|
getMarginMode(): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-margin-info'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Positions API */
|
||||||
|
|
||||||
|
/** Query my positions */
|
||||||
|
getPositions(params: USDCPositionsRequest): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/query-position',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Only for REGULAR_MARGIN */
|
||||||
|
setLeverage(symbol: string, leverage: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/perpetual/usdc/openapi/private/v1/position/leverage/save',
|
||||||
|
{ symbol, leverage }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query Settlement History */
|
||||||
|
getSettlementHistory(
|
||||||
|
params?: USDCSymbolDirectionLimitCursor
|
||||||
|
): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/option/usdc/openapi/private/v1/session-settlement',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Risk Limit API */
|
||||||
|
|
||||||
|
/** Query risk limit */
|
||||||
|
getRiskLimit(symbol: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.getPrivate(
|
||||||
|
'/perpetual/usdc/openapi/public/v1/risk-limit/list',
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set risk limit */
|
||||||
|
setRiskLimit(symbol: string, riskId: number): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/perpetual/usdc/openapi/private/v1/position/set-risk-limit',
|
||||||
|
{ symbol, riskId }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -> Funding API */
|
||||||
|
|
||||||
|
/** Funding settlement occurs every 8 hours at 00:00 UTC, 08:00 UTC and 16:00 UTC. The current interval's fund fee settlement is based on the previous interval's fund rate. For example, at 16:00, the settlement is based on the fund rate generated at 8:00. The fund rate generated at 16:00 will be used at 0:00 the next day. */
|
||||||
|
getLastFundingRate(symbol: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.get('/perpetual/usdc/openapi/public/v1/prev-funding-rate', {
|
||||||
|
symbol,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get predicted funding rate and my predicted funding fee */
|
||||||
|
getPredictedFundingRate(symbol: string): Promise<APIResponseV3<any>> {
|
||||||
|
return this.postPrivate(
|
||||||
|
'/perpetual/usdc/openapi/private/v1/predicted-funding',
|
||||||
|
{ symbol }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* API Data Endpoints
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
getServerTime(): Promise<APIResponseWithTime> {
|
||||||
|
return this.get('/v2/public/time');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,8 @@ import {
|
|||||||
serializeParams,
|
serializeParams,
|
||||||
RestClientType,
|
RestClientType,
|
||||||
REST_CLIENT_TYPE_ENUM,
|
REST_CLIENT_TYPE_ENUM,
|
||||||
agentSource,
|
APIID,
|
||||||
|
getRestBaseUrl,
|
||||||
} from './requestUtils';
|
} from './requestUtils';
|
||||||
|
|
||||||
// axios.interceptors.request.use((request) => {
|
// axios.interceptors.request.use((request) => {
|
||||||
@@ -20,22 +21,32 @@ import {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
interface SignedRequestContext {
|
interface SignedRequestContext {
|
||||||
timestamp: number;
|
timestamp?: number;
|
||||||
api_key?: string;
|
api_key?: string;
|
||||||
recv_window?: number;
|
recv_window?: number;
|
||||||
// spot is diff from the rest...
|
// spot v1 is diff from the rest...
|
||||||
recvWindow?: number;
|
recvWindow?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SignedRequest<T> {
|
interface SignedRequest<T> {
|
||||||
originalParams: T & SignedRequestContext;
|
originalParams: T & SignedRequestContext;
|
||||||
paramsWithSign?: T & SignedRequestContext & { sign: string };
|
paramsWithSign?: T & SignedRequestContext & { sign: string };
|
||||||
|
serializedParams: string;
|
||||||
sign: string;
|
sign: string;
|
||||||
|
timestamp: number;
|
||||||
|
recvWindow: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UnsignedRequest<T> {
|
||||||
|
originalParams: T;
|
||||||
|
paramsWithSign: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignMethod = 'keyInBody' | 'usdc';
|
||||||
|
|
||||||
export default abstract class BaseRestClient {
|
export default abstract class BaseRestClient {
|
||||||
private timeOffset: number | null;
|
private timeOffset: number | null = null;
|
||||||
private syncTimePromise: null | Promise<any>;
|
private syncTimePromise: null | Promise<any> = null;
|
||||||
private options: RestClientOptions;
|
private options: RestClientOptions;
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
private globalRequestOptions: AxiosRequestConfig;
|
private globalRequestOptions: AxiosRequestConfig;
|
||||||
@@ -43,49 +54,51 @@ export default abstract class BaseRestClient {
|
|||||||
private secret: string | undefined;
|
private secret: string | undefined;
|
||||||
private clientType: RestClientType;
|
private clientType: RestClientType;
|
||||||
|
|
||||||
/** Function that calls exchange API to query & resolve server time, used by time sync */
|
/** Function that calls exchange API to query & resolve server time, used by time sync, disabled by default */
|
||||||
abstract fetchServerTime(): Promise<number>;
|
abstract fetchServerTime(): Promise<number>;
|
||||||
|
|
||||||
constructor(
|
/** Defines the client type (affecting how requests & signatures behave) */
|
||||||
key: string | undefined,
|
abstract getClientType(): RestClientType;
|
||||||
secret: string | undefined,
|
|
||||||
baseUrl: string,
|
|
||||||
options: RestClientOptions = {},
|
|
||||||
requestOptions: AxiosRequestConfig = {},
|
|
||||||
clientType: RestClientType
|
|
||||||
) {
|
|
||||||
this.timeOffset = null;
|
|
||||||
this.syncTimePromise = null;
|
|
||||||
|
|
||||||
this.clientType = clientType;
|
/**
|
||||||
|
* Create an instance of the REST client. Pass API credentials in the object in the first parameter.
|
||||||
|
* @param {RestClientOptions} [restClientOptions={}] options to configure REST API connectivity
|
||||||
|
* @param {AxiosRequestConfig} [networkOptions={}] HTTP networking options for axios
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
restOptions: RestClientOptions = {},
|
||||||
|
networkOptions: AxiosRequestConfig = {}
|
||||||
|
) {
|
||||||
|
this.clientType = this.getClientType();
|
||||||
|
|
||||||
this.options = {
|
this.options = {
|
||||||
recv_window: 5000,
|
recv_window: 5000,
|
||||||
|
/** Throw errors if any request params are empty */
|
||||||
/** Throw errors if any params are undefined */
|
|
||||||
strict_param_validation: false,
|
strict_param_validation: false,
|
||||||
/** Disable time sync by default */
|
/** Disable time sync by default */
|
||||||
enable_time_sync: false,
|
enable_time_sync: false,
|
||||||
/** How often to sync time drift with bybit servers (if time sync is enabled) */
|
/** How often to sync time drift with bybit servers (if time sync is enabled) */
|
||||||
sync_interval_ms: 3600000,
|
sync_interval_ms: 3600000,
|
||||||
...options,
|
...restOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.globalRequestOptions = {
|
this.globalRequestOptions = {
|
||||||
// in ms == 5 minutes by default
|
// in ms == 5 minutes by default
|
||||||
timeout: 1000 * 60 * 5,
|
timeout: 1000 * 60 * 5,
|
||||||
// custom request options based on axios specs - see: https://github.com/axios/axios#request-config
|
// custom request options based on axios specs - see: https://github.com/axios/axios#request-config
|
||||||
...requestOptions,
|
...networkOptions,
|
||||||
headers: {
|
headers: {
|
||||||
'x-referer': 'bybitapinode',
|
'x-referer': APIID,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = getRestBaseUrl(!!this.options.testnet, restOptions);
|
||||||
|
this.key = this.options.key;
|
||||||
|
this.secret = this.options.secret;
|
||||||
|
|
||||||
if (key && !secret) {
|
if (this.key && !this.secret) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'API Key & Secret are both required for private enpoints'
|
'API Key & Secret are both required for private endpoints'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,9 +106,6 @@ export default abstract class BaseRestClient {
|
|||||||
this.syncTime();
|
this.syncTime();
|
||||||
setInterval(this.syncTime.bind(this), +this.options.sync_interval_ms!);
|
setInterval(this.syncTime.bind(this), +this.options.sync_interval_ms!);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.key = key;
|
|
||||||
this.secret = secret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isSpotClient() {
|
private isSpotClient() {
|
||||||
@@ -106,14 +116,14 @@ export default abstract class BaseRestClient {
|
|||||||
return this._call('GET', endpoint, params, true);
|
return this._call('GET', endpoint, params, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
post(endpoint: string, params?: any) {
|
|
||||||
return this._call('POST', endpoint, params, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPrivate(endpoint: string, params?: any) {
|
getPrivate(endpoint: string, params?: any) {
|
||||||
return this._call('GET', endpoint, params, false);
|
return this._call('GET', endpoint, params, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post(endpoint: string, params?: any) {
|
||||||
|
return this._call('POST', endpoint, params, true);
|
||||||
|
}
|
||||||
|
|
||||||
postPrivate(endpoint: string, params?: any) {
|
postPrivate(endpoint: string, params?: any) {
|
||||||
return this._call('POST', endpoint, params, false);
|
return this._call('POST', endpoint, params, false);
|
||||||
}
|
}
|
||||||
@@ -122,7 +132,24 @@ export default abstract class BaseRestClient {
|
|||||||
return this._call('DELETE', endpoint, params, false);
|
return this._call('DELETE', endpoint, params, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async prepareSignParams(params?: any, isPublicApi?: boolean) {
|
private async prepareSignParams<TParams = any>(
|
||||||
|
method: Method,
|
||||||
|
signMethod: SignMethod,
|
||||||
|
params?: TParams,
|
||||||
|
isPublicApi?: true
|
||||||
|
): Promise<UnsignedRequest<TParams>>;
|
||||||
|
private async prepareSignParams<TParams = any>(
|
||||||
|
method: Method,
|
||||||
|
signMethod: SignMethod,
|
||||||
|
params?: TParams,
|
||||||
|
isPublicApi?: false | undefined
|
||||||
|
): Promise<SignedRequest<TParams>>;
|
||||||
|
private async prepareSignParams<TParams = any>(
|
||||||
|
method: Method,
|
||||||
|
signMethod: SignMethod,
|
||||||
|
params?: TParams,
|
||||||
|
isPublicApi?: boolean
|
||||||
|
) {
|
||||||
if (isPublicApi) {
|
if (isPublicApi) {
|
||||||
return {
|
return {
|
||||||
originalParams: params,
|
originalParams: params,
|
||||||
@@ -138,7 +165,85 @@ export default abstract class BaseRestClient {
|
|||||||
await this.syncTime();
|
await this.syncTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.signRequest(params);
|
return this.signRequest(params, method, signMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an axios request object. Handles signing process automatically if this is a private API call */
|
||||||
|
private async buildRequest(
|
||||||
|
method: Method,
|
||||||
|
url: string,
|
||||||
|
params?: any,
|
||||||
|
isPublicApi?: boolean
|
||||||
|
): Promise<AxiosRequestConfig> {
|
||||||
|
const options: AxiosRequestConfig = {
|
||||||
|
...this.globalRequestOptions,
|
||||||
|
url: url,
|
||||||
|
method: method,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const key in params) {
|
||||||
|
if (typeof params[key] === 'undefined') {
|
||||||
|
delete params[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPublicApi) {
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
params: params,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// USDC endpoints, unified margin and a few others use a different way of authenticating requests (headers instead of params)
|
||||||
|
if (this.clientType === REST_CLIENT_TYPE_ENUM.v3) {
|
||||||
|
if (!options.headers) {
|
||||||
|
options.headers = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const signResult = await this.prepareSignParams(
|
||||||
|
method,
|
||||||
|
'usdc',
|
||||||
|
params,
|
||||||
|
isPublicApi
|
||||||
|
);
|
||||||
|
|
||||||
|
options.headers['X-BAPI-SIGN-TYPE'] = 2;
|
||||||
|
options.headers['X-BAPI-API-KEY'] = this.key;
|
||||||
|
options.headers['X-BAPI-TIMESTAMP'] = signResult.timestamp;
|
||||||
|
options.headers['X-BAPI-SIGN'] = signResult.sign;
|
||||||
|
options.headers['X-BAPI-RECV-WINDOW'] = signResult.recvWindow;
|
||||||
|
|
||||||
|
if (method === 'GET') {
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
params: signResult.originalParams,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
data: signResult.originalParams,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const signResult = await this.prepareSignParams(
|
||||||
|
method,
|
||||||
|
'keyInBody',
|
||||||
|
params,
|
||||||
|
isPublicApi
|
||||||
|
);
|
||||||
|
|
||||||
|
if (method === 'GET' || this.isSpotClient()) {
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
params: signResult.paramsWithSign,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
data: signResult.paramsWithSign,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,27 +255,20 @@ export default abstract class BaseRestClient {
|
|||||||
params?: any,
|
params?: any,
|
||||||
isPublicApi?: boolean
|
isPublicApi?: boolean
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const options = {
|
// Sanity check to make sure it's only ever prefixed by one forward slash
|
||||||
...this.globalRequestOptions,
|
const requestUrl = [this.baseUrl, endpoint].join(
|
||||||
url: [this.baseUrl, endpoint].join(endpoint.startsWith('/') ? '' : '/'),
|
endpoint.startsWith('/') ? '' : '/'
|
||||||
method: method,
|
);
|
||||||
json: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const key in params) {
|
// Build a request and handle signature process
|
||||||
if (typeof params[key] === 'undefined') {
|
const options = await this.buildRequest(
|
||||||
delete params[key];
|
method,
|
||||||
}
|
requestUrl,
|
||||||
}
|
params,
|
||||||
|
isPublicApi
|
||||||
const signResult = await this.prepareSignParams(params, isPublicApi);
|
);
|
||||||
|
|
||||||
if (method === 'GET' || this.isSpotClient()) {
|
|
||||||
options.params = signResult.paramsWithSign;
|
|
||||||
} else {
|
|
||||||
options.data = signResult.paramsWithSign;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Dispatch request
|
||||||
return axios(options)
|
return axios(options)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status == 200) {
|
if (response.status == 200) {
|
||||||
@@ -215,37 +313,70 @@ export default abstract class BaseRestClient {
|
|||||||
/**
|
/**
|
||||||
* @private sign request and set recv window
|
* @private sign request and set recv window
|
||||||
*/
|
*/
|
||||||
private async signRequest<T extends Object>(
|
private async signRequest<T = {}>(
|
||||||
data: T & SignedRequestContext
|
data: T,
|
||||||
|
method: Method,
|
||||||
|
signMethod: SignMethod
|
||||||
): Promise<SignedRequest<T>> {
|
): Promise<SignedRequest<T>> {
|
||||||
|
const timestamp = Date.now() + (this.timeOffset || 0);
|
||||||
|
|
||||||
const res: SignedRequest<T> = {
|
const res: SignedRequest<T> = {
|
||||||
originalParams: {
|
originalParams: {
|
||||||
...data,
|
...data,
|
||||||
api_key: this.key,
|
|
||||||
timestamp: Date.now() + (this.timeOffset || 0),
|
|
||||||
},
|
},
|
||||||
sign: '',
|
sign: '',
|
||||||
|
timestamp,
|
||||||
|
recvWindow: 0,
|
||||||
|
serializedParams: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!this.key || !this.secret) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
const key = this.key;
|
||||||
|
const recvWindow =
|
||||||
|
res.originalParams.recv_window || this.options.recv_window || 5000;
|
||||||
|
const strictParamValidation = this.options.strict_param_validation;
|
||||||
|
|
||||||
|
// In case the parent function needs it (e.g. USDC uses a header)
|
||||||
|
res.recvWindow = recvWindow;
|
||||||
|
|
||||||
|
// usdc is different for some reason
|
||||||
|
if (signMethod === 'usdc') {
|
||||||
|
const signRequestParams =
|
||||||
|
method === 'GET'
|
||||||
|
? serializeParams(res.originalParams, strictParamValidation)
|
||||||
|
: JSON.stringify(res.originalParams);
|
||||||
|
|
||||||
|
const paramsStr = timestamp + key + recvWindow + signRequestParams;
|
||||||
|
res.sign = await signMessage(paramsStr, this.secret);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// spot/v2 derivatives
|
||||||
|
if (signMethod === 'keyInBody') {
|
||||||
|
res.originalParams.api_key = key;
|
||||||
|
res.originalParams.timestamp = timestamp;
|
||||||
|
|
||||||
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen.
|
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen.
|
||||||
if (this.options.recv_window && !res.originalParams.recv_window) {
|
if (recvWindow) {
|
||||||
if (this.isSpotClient()) {
|
if (this.isSpotClient()) {
|
||||||
res.originalParams.recvWindow = this.options.recv_window;
|
res.originalParams.recvWindow = recvWindow;
|
||||||
} else {
|
} else {
|
||||||
res.originalParams.recv_window = this.options.recv_window;
|
res.originalParams.recv_window = recvWindow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.key && this.secret) {
|
res.serializedParams = serializeParams(
|
||||||
const serializedParams = serializeParams(
|
|
||||||
res.originalParams,
|
res.originalParams,
|
||||||
this.options.strict_param_validation
|
strictParamValidation
|
||||||
);
|
);
|
||||||
res.sign = await signMessage(serializedParams, this.secret);
|
res.sign = await signMessage(res.serializedParams, this.secret);
|
||||||
res.paramsWithSign = {
|
res.paramsWithSign = {
|
||||||
...res.originalParams,
|
...res.originalParams,
|
||||||
sign: res.sign,
|
sign: res.sign,
|
||||||
};
|
};
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -1,16 +1,39 @@
|
|||||||
import { WsConnectionState } from '../websocket-client';
|
|
||||||
import { DefaultLogger } from '../logger';
|
|
||||||
|
|
||||||
import WebSocket from 'isomorphic-ws';
|
import WebSocket from 'isomorphic-ws';
|
||||||
|
import { WsKey } from '../types';
|
||||||
|
|
||||||
|
import { DefaultLogger } from './logger';
|
||||||
|
|
||||||
|
export enum WsConnectionStateEnum {
|
||||||
|
INITIAL = 0,
|
||||||
|
CONNECTING = 1,
|
||||||
|
CONNECTED = 2,
|
||||||
|
CLOSING = 3,
|
||||||
|
RECONNECTING = 4,
|
||||||
|
// ERROR = 5,
|
||||||
|
}
|
||||||
|
/** A "topic" is always a string */
|
||||||
type WsTopic = string;
|
type WsTopic = string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "Set" is used to ensure we only subscribe to a topic once (tracking a list of unique topics we're expected to be connected to)
|
||||||
|
* Note: Accurate duplicate tracking only works for plaintext topics. E.g. JSON objects may not be seen as duplicates if keys are in different orders. If that's needed, check the FTX implementation.
|
||||||
|
*/
|
||||||
type WsTopicList = Set<WsTopic>;
|
type WsTopicList = Set<WsTopic>;
|
||||||
|
|
||||||
interface WsStoredState {
|
interface WsStoredState {
|
||||||
|
/** The currently active websocket connection */
|
||||||
ws?: WebSocket;
|
ws?: WebSocket;
|
||||||
connectionState?: WsConnectionState;
|
/** The current lifecycle state of the connection (enum) */
|
||||||
|
connectionState?: WsConnectionStateEnum;
|
||||||
|
/** A timer that will send an upstream heartbeat (ping) when it expires */
|
||||||
activePingTimer?: ReturnType<typeof setTimeout> | undefined;
|
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;
|
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;
|
||||||
|
/**
|
||||||
|
* All the topics we are expected to be subscribed to (and we automatically resubscribe to if the connection drops)
|
||||||
|
*/
|
||||||
subscribedTopics: WsTopicList;
|
subscribedTopics: WsTopicList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,7 +46,10 @@ export default class WsStore {
|
|||||||
this.wsState = {};
|
this.wsState = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
get(key: string, createIfMissing?: boolean): WsStoredState | undefined {
|
/** Get WS stored state for key, optionally create if missing */
|
||||||
|
get(key: WsKey, createIfMissing?: true): WsStoredState;
|
||||||
|
get(key: WsKey, createIfMissing?: false): WsStoredState | undefined;
|
||||||
|
get(key: WsKey, createIfMissing?: boolean): WsStoredState | undefined {
|
||||||
if (this.wsState[key]) {
|
if (this.wsState[key]) {
|
||||||
return this.wsState[key];
|
return this.wsState[key];
|
||||||
}
|
}
|
||||||
@@ -33,11 +59,11 @@ export default class WsStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getKeys(): string[] {
|
getKeys(): WsKey[] {
|
||||||
return Object.keys(this.wsState);
|
return Object.keys(this.wsState) as WsKey[];
|
||||||
}
|
}
|
||||||
|
|
||||||
create(key: string): WsStoredState | undefined {
|
create(key: WsKey): WsStoredState | undefined {
|
||||||
if (this.hasExistingActiveConnection(key)) {
|
if (this.hasExistingActiveConnection(key)) {
|
||||||
this.logger.warning(
|
this.logger.warning(
|
||||||
'WsStore setConnection() overwriting existing open connection: ',
|
'WsStore setConnection() overwriting existing open connection: ',
|
||||||
@@ -46,12 +72,12 @@ export default class WsStore {
|
|||||||
}
|
}
|
||||||
this.wsState[key] = {
|
this.wsState[key] = {
|
||||||
subscribedTopics: new Set(),
|
subscribedTopics: new Set(),
|
||||||
connectionState: WsConnectionState.READY_STATE_INITIAL,
|
connectionState: WsConnectionStateEnum.INITIAL,
|
||||||
};
|
};
|
||||||
return this.get(key);
|
return this.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(key: string) {
|
delete(key: WsKey) {
|
||||||
if (this.hasExistingActiveConnection(key)) {
|
if (this.hasExistingActiveConnection(key)) {
|
||||||
const ws = this.getWs(key);
|
const ws = this.getWs(key);
|
||||||
this.logger.warning(
|
this.logger.warning(
|
||||||
@@ -65,15 +91,15 @@ export default class WsStore {
|
|||||||
|
|
||||||
/* connection websocket */
|
/* connection websocket */
|
||||||
|
|
||||||
hasExistingActiveConnection(key: string) {
|
hasExistingActiveConnection(key: WsKey) {
|
||||||
return this.get(key) && this.isWsOpen(key);
|
return this.get(key) && this.isWsOpen(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
getWs(key: string): WebSocket | undefined {
|
getWs(key: WsKey): WebSocket | undefined {
|
||||||
return this.get(key)?.ws;
|
return this.get(key)?.ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
setWs(key: string, wsConnection: WebSocket): WebSocket {
|
setWs(key: WsKey, wsConnection: WebSocket): WebSocket {
|
||||||
if (this.isWsOpen(key)) {
|
if (this.isWsOpen(key)) {
|
||||||
this.logger.warning(
|
this.logger.warning(
|
||||||
'WsStore setConnection() overwriting existing open connection: ',
|
'WsStore setConnection() overwriting existing open connection: ',
|
||||||
@@ -86,7 +112,7 @@ export default class WsStore {
|
|||||||
|
|
||||||
/* connection state */
|
/* connection state */
|
||||||
|
|
||||||
isWsOpen(key: string): boolean {
|
isWsOpen(key: WsKey): boolean {
|
||||||
const existingConnection = this.getWs(key);
|
const existingConnection = this.getWs(key);
|
||||||
return (
|
return (
|
||||||
!!existingConnection &&
|
!!existingConnection &&
|
||||||
@@ -94,37 +120,37 @@ export default class WsStore {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConnectionState(key: string): WsConnectionState {
|
getConnectionState(key: WsKey): WsConnectionStateEnum {
|
||||||
return this.get(key, true)!.connectionState!;
|
return this.get(key, true)!.connectionState!;
|
||||||
}
|
}
|
||||||
|
|
||||||
setConnectionState(key: string, state: WsConnectionState) {
|
setConnectionState(key: WsKey, state: WsConnectionStateEnum) {
|
||||||
this.get(key, true)!.connectionState = state;
|
this.get(key, true)!.connectionState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnectionState(key: string, state: WsConnectionState): boolean {
|
isConnectionState(key: WsKey, state: WsConnectionStateEnum): boolean {
|
||||||
return this.getConnectionState(key) === state;
|
return this.getConnectionState(key) === state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* subscribed topics */
|
/* subscribed topics */
|
||||||
|
|
||||||
getTopics(key: string): WsTopicList {
|
getTopics(key: WsKey): WsTopicList {
|
||||||
return this.get(key, true)!.subscribedTopics;
|
return this.get(key, true).subscribedTopics;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTopicsByKey(): Record<string, WsTopicList> {
|
getTopicsByKey(): Record<string, WsTopicList> {
|
||||||
const result = {};
|
const result = {};
|
||||||
for (const refKey in this.wsState) {
|
for (const refKey in this.wsState) {
|
||||||
result[refKey] = this.getTopics(refKey);
|
result[refKey] = this.getTopics(refKey as WsKey);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
addTopic(key: string, topic: WsTopic) {
|
addTopic(key: WsKey, topic: WsTopic) {
|
||||||
return this.getTopics(key).add(topic);
|
return this.getTopics(key).add(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTopic(key: string, topic: WsTopic) {
|
deleteTopic(key: WsKey, topic: WsTopic) {
|
||||||
return this.getTopics(key).delete(topic);
|
return this.getTopics(key).delete(topic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/util/index.ts
Normal file
5
src/util/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export * from './BaseRestClient';
|
||||||
|
export * from './requestUtils';
|
||||||
|
export * from './WsStore';
|
||||||
|
export * from './logger';
|
||||||
|
export * from './websocket-util';
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createHmac } from 'crypto';
|
import { createHmac } from 'crypto';
|
||||||
|
|
||||||
|
/** This is async because the browser version uses a promise (browser-support) */
|
||||||
export async function signMessage(
|
export async function signMessage(
|
||||||
message: string,
|
message: string,
|
||||||
secret: string
|
secret: string
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
export interface RestClientOptions {
|
export interface RestClientOptions {
|
||||||
|
/** Your API key */
|
||||||
|
key?: string;
|
||||||
|
|
||||||
|
/** Your API secret */
|
||||||
|
secret?: string;
|
||||||
|
|
||||||
|
/** Set to `true` to connect to testnet. Uses the live environment by default. */
|
||||||
|
testnet?: boolean;
|
||||||
|
|
||||||
/** Override the max size of the request window (in ms) */
|
/** Override the max size of the request window (in ms) */
|
||||||
recv_window?: number;
|
recv_window?: number;
|
||||||
|
|
||||||
/** @deprecated Time sync is now disabled by default. To re-enable it, use enable_time_sync instead. */
|
|
||||||
disable_time_sync?: boolean;
|
|
||||||
|
|
||||||
/** Disabled by default. This can help on machines with consistent latency problems. */
|
/** Disabled by default. This can help on machines with consistent latency problems. */
|
||||||
enable_time_sync?: boolean;
|
enable_time_sync?: boolean;
|
||||||
|
|
||||||
@@ -43,10 +49,10 @@ export function serializeParams(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getRestBaseUrl(
|
export function getRestBaseUrl(
|
||||||
useLivenet: boolean,
|
useTestnet: boolean,
|
||||||
restInverseOptions: RestClientOptions
|
restInverseOptions: RestClientOptions
|
||||||
) {
|
): string {
|
||||||
const baseUrlsInverse = {
|
const exchangeBaseUrls = {
|
||||||
livenet: 'https://api.bybit.com',
|
livenet: 'https://api.bybit.com',
|
||||||
testnet: 'https://api-testnet.bybit.com',
|
testnet: 'https://api-testnet.bybit.com',
|
||||||
};
|
};
|
||||||
@@ -55,48 +61,49 @@ export function getRestBaseUrl(
|
|||||||
return restInverseOptions.baseUrl;
|
return restInverseOptions.baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useLivenet === true) {
|
if (useTestnet) {
|
||||||
return baseUrlsInverse.livenet;
|
return exchangeBaseUrls.testnet;
|
||||||
}
|
|
||||||
return baseUrlsInverse.testnet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPublicEndpoint(endpoint: string): boolean {
|
return exchangeBaseUrls.livenet;
|
||||||
const publicPrefixes = [
|
}
|
||||||
'v2/public',
|
|
||||||
'public/linear',
|
|
||||||
'spot/quote/v1',
|
|
||||||
'spot/v1/symbols',
|
|
||||||
'spot/v1/time',
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const prefix of publicPrefixes) {
|
export function isWsPong(msg: any): boolean {
|
||||||
if (endpoint.startsWith(prefix)) {
|
if (!msg) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (msg.pong || msg.ping) {
|
||||||
export function isWsPong(response: any) {
|
|
||||||
if (response.pong || response.ping) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg['op'] === 'pong') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg['ret_msg'] === 'pong') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
response.request &&
|
msg.request &&
|
||||||
response.request.op === 'ping' &&
|
msg.request.op === 'ping' &&
|
||||||
response.ret_msg === 'pong' &&
|
msg.ret_msg === 'pong' &&
|
||||||
response.success === true
|
msg.success === true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const agentSource = 'bybitapinode';
|
export const APIID = 'bybitapinode';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to switch how authentication/requests work under the hood (primarily for SPOT since it's different there)
|
||||||
|
*/
|
||||||
export const REST_CLIENT_TYPE_ENUM = {
|
export const REST_CLIENT_TYPE_ENUM = {
|
||||||
|
accountAsset: 'accountAsset',
|
||||||
inverse: 'inverse',
|
inverse: 'inverse',
|
||||||
inverseFutures: 'inverseFutures',
|
inverseFutures: 'inverseFutures',
|
||||||
linear: 'linear',
|
linear: 'linear',
|
||||||
spot: 'spot',
|
spot: 'spot',
|
||||||
|
v3: 'v3',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type RestClientType =
|
export type RestClientType =
|
||||||
|
|||||||
286
src/util/websocket-util.ts
Normal file
286
src/util/websocket-util.ts
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
import { APIMarket, WsKey } from '../types';
|
||||||
|
|
||||||
|
interface NetworkMapV3 {
|
||||||
|
livenet: string;
|
||||||
|
livenet2?: string;
|
||||||
|
testnet: string;
|
||||||
|
testnet2?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicPrivateNetwork = 'public' | 'private';
|
||||||
|
|
||||||
|
export const WS_BASE_URL_MAP: Record<
|
||||||
|
APIMarket | 'unifiedPerpUSDT' | 'unifiedPerpUSDC',
|
||||||
|
Record<PublicPrivateNetwork, NetworkMapV3>
|
||||||
|
> = {
|
||||||
|
inverse: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/realtime',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/realtime',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/realtime',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/realtime',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
linear: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/realtime_public',
|
||||||
|
livenet2: 'wss://stream.bytick.com/realtime_public',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/realtime_public',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/realtime_private',
|
||||||
|
livenet2: 'wss://stream.bytick.com/realtime_private',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/realtime_private',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spot: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/spot/quote/ws/v1',
|
||||||
|
livenet2: 'wss://stream.bybit.com/spot/quote/ws/v2',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/spot/quote/ws/v1',
|
||||||
|
testnet2: 'wss://stream-testnet.bybit.com/spot/quote/ws/v2',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/spot/ws',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/spot/ws',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spotv3: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/spot/public/v3',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/spot/public/v3',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/spot/private/v3',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/spot/private/v3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
usdcOption: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/trade/option/usdc/public/v1',
|
||||||
|
livenet2: 'wss://stream.bytick.com/trade/option/usdc/public/v1',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/public/v1',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/trade/option/usdc/private/v1',
|
||||||
|
livenet2: 'wss://stream.bytick.com/trade/option/usdc/private/v1',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/private/v1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
usdcPerp: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/perpetual/ws/v1/realtime_public',
|
||||||
|
livenet2: 'wss://stream.bytick.com/perpetual/ws/v1/realtime_public',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/perpetual/ws/v1/realtime_public',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/trade/option/usdc/private/v1',
|
||||||
|
livenet2: 'wss://stream.bytick.com/trade/option/usdc/private/v1',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/trade/option/usdc/private/v1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unifiedOption: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/option/usdc/public/v3',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/option/usdc/public/v3',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/unified/private/v3',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/unified/private/v3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unifiedPerp: {
|
||||||
|
public: {
|
||||||
|
livenet: 'useBaseSpecificEndpoint',
|
||||||
|
testnet: 'useBaseSpecificEndpoint',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'wss://stream.bybit.com/unified/private/v3',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/unified/private/v3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unifiedPerpUSDT: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/contract/usdt/public/v3',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/contract/usdt/public/v3',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'useUnifiedEndpoint',
|
||||||
|
testnet: 'useUnifiedEndpoint',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unifiedPerpUSDC: {
|
||||||
|
public: {
|
||||||
|
livenet: 'wss://stream.bybit.com/contract/usdc/public/v3',
|
||||||
|
testnet: 'wss://stream-testnet.bybit.com/contract/usdc/public/v3',
|
||||||
|
},
|
||||||
|
private: {
|
||||||
|
livenet: 'useUnifiedEndpoint',
|
||||||
|
testnet: 'useUnifiedEndpoint',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WS_KEY_MAP = {
|
||||||
|
inverse: 'inverse',
|
||||||
|
linearPrivate: 'linearPrivate',
|
||||||
|
linearPublic: 'linearPublic',
|
||||||
|
spotPrivate: 'spotPrivate',
|
||||||
|
spotPublic: 'spotPublic',
|
||||||
|
spotV3Private: 'spotV3Private',
|
||||||
|
spotV3Public: 'spotV3Public',
|
||||||
|
usdcOptionPrivate: 'usdcOptionPrivate',
|
||||||
|
usdcOptionPublic: 'usdcOptionPublic',
|
||||||
|
usdcPerpPrivate: 'usdcPerpPrivate',
|
||||||
|
usdcPerpPublic: 'usdcPerpPublic',
|
||||||
|
unifiedPrivate: 'unifiedPrivate',
|
||||||
|
unifiedOptionPublic: 'unifiedOptionPublic',
|
||||||
|
unifiedPerpUSDTPublic: 'unifiedPerpUSDTPublic',
|
||||||
|
unifiedPerpUSDCPublic: 'unifiedPerpUSDCPublic',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [
|
||||||
|
WS_KEY_MAP.spotV3Private,
|
||||||
|
WS_KEY_MAP.usdcOptionPrivate,
|
||||||
|
WS_KEY_MAP.usdcPerpPrivate,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PUBLIC_WS_KEYS = [
|
||||||
|
WS_KEY_MAP.linearPublic,
|
||||||
|
WS_KEY_MAP.spotPublic,
|
||||||
|
WS_KEY_MAP.spotV3Public,
|
||||||
|
WS_KEY_MAP.usdcOptionPublic,
|
||||||
|
WS_KEY_MAP.usdcPerpPublic,
|
||||||
|
WS_KEY_MAP.unifiedOptionPublic,
|
||||||
|
WS_KEY_MAP.unifiedPerpUSDTPublic,
|
||||||
|
WS_KEY_MAP.unifiedPerpUSDCPublic,
|
||||||
|
] as string[];
|
||||||
|
|
||||||
|
/** Used to automatically determine if a sub request should be to the public or private ws (when there's two) */
|
||||||
|
const PRIVATE_TOPICS = [
|
||||||
|
'position',
|
||||||
|
'execution',
|
||||||
|
'order',
|
||||||
|
'stop_order',
|
||||||
|
'wallet',
|
||||||
|
'outboundAccountInfo',
|
||||||
|
'executionReport',
|
||||||
|
'ticketInfo',
|
||||||
|
// copy trading apis
|
||||||
|
'copyTradePosition',
|
||||||
|
'copyTradeOrder',
|
||||||
|
'copyTradeExecution',
|
||||||
|
'copyTradeWallet',
|
||||||
|
// usdc options
|
||||||
|
'user.openapi.option.position',
|
||||||
|
'user.openapi.option.trade',
|
||||||
|
'user.order',
|
||||||
|
'user.openapi.option.order',
|
||||||
|
'user.service',
|
||||||
|
'user.openapi.greeks',
|
||||||
|
'user.mmp.event',
|
||||||
|
// usdc perps
|
||||||
|
'user.openapi.perp.position',
|
||||||
|
'user.openapi.perp.trade',
|
||||||
|
'user.openapi.perp.order',
|
||||||
|
'user.service',
|
||||||
|
// unified margin
|
||||||
|
'user.position.unifiedAccount',
|
||||||
|
'user.execution.unifiedAccount',
|
||||||
|
'user.order.unifiedAccount',
|
||||||
|
'user.wallet.unifiedAccount',
|
||||||
|
'user.greeks.unifiedAccount',
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getWsKeyForTopic(
|
||||||
|
market: APIMarket,
|
||||||
|
topic: string,
|
||||||
|
isPrivate?: boolean
|
||||||
|
): WsKey {
|
||||||
|
const isPrivateTopic = isPrivate === true || PRIVATE_TOPICS.includes(topic);
|
||||||
|
switch (market) {
|
||||||
|
case 'inverse': {
|
||||||
|
return WS_KEY_MAP.inverse;
|
||||||
|
}
|
||||||
|
case 'linear': {
|
||||||
|
return isPrivateTopic
|
||||||
|
? WS_KEY_MAP.linearPrivate
|
||||||
|
: WS_KEY_MAP.linearPublic;
|
||||||
|
}
|
||||||
|
case 'spot': {
|
||||||
|
return isPrivateTopic ? WS_KEY_MAP.spotPrivate : WS_KEY_MAP.spotPublic;
|
||||||
|
}
|
||||||
|
case 'spotv3': {
|
||||||
|
return isPrivateTopic
|
||||||
|
? WS_KEY_MAP.spotV3Private
|
||||||
|
: WS_KEY_MAP.spotV3Public;
|
||||||
|
}
|
||||||
|
case 'usdcOption': {
|
||||||
|
return isPrivateTopic
|
||||||
|
? WS_KEY_MAP.usdcOptionPrivate
|
||||||
|
: WS_KEY_MAP.usdcOptionPublic;
|
||||||
|
}
|
||||||
|
case 'usdcPerp': {
|
||||||
|
return isPrivateTopic
|
||||||
|
? WS_KEY_MAP.usdcPerpPrivate
|
||||||
|
: WS_KEY_MAP.usdcPerpPublic;
|
||||||
|
}
|
||||||
|
case 'unifiedOption': {
|
||||||
|
return isPrivateTopic
|
||||||
|
? WS_KEY_MAP.unifiedPrivate
|
||||||
|
: WS_KEY_MAP.unifiedOptionPublic;
|
||||||
|
}
|
||||||
|
case 'unifiedPerp': {
|
||||||
|
if (isPrivateTopic) {
|
||||||
|
return WS_KEY_MAP.unifiedPrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const upperTopic = topic.toUpperCase();
|
||||||
|
if (upperTopic.indexOf('USDT') !== -1) {
|
||||||
|
return WS_KEY_MAP.unifiedPerpUSDTPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upperTopic.indexOf('USDC') !== -1) {
|
||||||
|
return WS_KEY_MAP.unifiedPerpUSDCPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Failed to determine wskey for unified perps topic: "${topic}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw neverGuard(market, `getWsKeyForTopic(): Unhandled market`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUsdcWsKeyForTopic(
|
||||||
|
topic: string,
|
||||||
|
subGroup: 'option' | 'perp'
|
||||||
|
): WsKey {
|
||||||
|
const isPrivateTopic = PRIVATE_TOPICS.includes(topic);
|
||||||
|
if (subGroup === 'option') {
|
||||||
|
return isPrivateTopic
|
||||||
|
? WS_KEY_MAP.usdcOptionPrivate
|
||||||
|
: WS_KEY_MAP.usdcOptionPublic;
|
||||||
|
}
|
||||||
|
return isPrivateTopic
|
||||||
|
? WS_KEY_MAP.usdcOptionPrivate
|
||||||
|
: WS_KEY_MAP.usdcOptionPublic;
|
||||||
|
// return isPrivateTopic
|
||||||
|
// ? WS_KEY_MAP.usdcPerpPrivate
|
||||||
|
// : WS_KEY_MAP.usdcPerpPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WS_ERROR_ENUM = {
|
||||||
|
NOT_AUTHENTICATED_SPOT_V3: '-1004',
|
||||||
|
API_ERROR_GENERIC: '10001',
|
||||||
|
API_SIGN_AUTH_FAILED: '10003',
|
||||||
|
USDC_OPTION_AUTH_FAILED: '3303006',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function neverGuard(x: never, msg: string): Error {
|
||||||
|
return new Error(`Unhandled value exception "x", ${msg}`);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
70
test/account-asset/private.read.test.ts
Normal file
70
test/account-asset/private.read.test.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { AccountAssetClient } from '../../src/';
|
||||||
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Account Asset REST API GET Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new AccountAssetClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getInternalTransfers()', async () => {
|
||||||
|
expect(await api.getInternalTransfers()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSubAccountTransfers()', async () => {
|
||||||
|
expect(await api.getSubAccountTransfers()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSubAccounts()', async () => {
|
||||||
|
expect(await api.getSubAccounts()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getUniversalTransfers()', async () => {
|
||||||
|
expect(await api.getInternalTransfers()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getDepositRecords()', async () => {
|
||||||
|
expect(await api.getDepositRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getWithdrawRecords()', async () => {
|
||||||
|
expect(await api.getWithdrawRecords()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCoinInformation()', async () => {
|
||||||
|
expect(await api.getCoinInformation()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAssetInformation()', async () => {
|
||||||
|
expect(await api.getAssetInformation()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getDepositAddress()', async () => {
|
||||||
|
expect(await api.getDepositAddress('BTC')).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
23
test/account-asset/public.read.test.ts
Normal file
23
test/account-asset/public.read.test.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { AccountAssetClient } from '../../src';
|
||||||
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Account Asset REST API Endpoints', () => {
|
||||||
|
const API_KEY = undefined;
|
||||||
|
const API_SECRET = undefined;
|
||||||
|
|
||||||
|
const api = new AccountAssetClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSupportedDepositList()', async () => {
|
||||||
|
expect(await api.getSupportedDepositList()).toMatchObject(
|
||||||
|
successResponseObject()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getServerTime()', async () => {
|
||||||
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
});
|
||||||
38
test/copy-trading/private.read.test.ts
Normal file
38
test/copy-trading/private.read.test.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { API_ERROR_CODE, CopyTradingClient } from '../../src';
|
||||||
|
|
||||||
|
describe('Private Copy Trading REST API GET Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new CopyTradingClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't have copy trading properly enabled on the test account, so testing is very light
|
||||||
|
// (just make sure auth works and endpoint doesn't throw)
|
||||||
|
|
||||||
|
it('getActiveOrders()', async () => {
|
||||||
|
expect(await api.getActiveOrders()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.INCORRECT_API_KEY_PERMISSIONS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPositions()', async () => {
|
||||||
|
expect(await api.getPositions()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.INCORRECT_API_KEY_PERMISSIONS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBalances()', async () => {
|
||||||
|
expect(await api.getBalances()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.INCORRECT_API_KEY_PERMISSIONS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
29
test/copy-trading/public.read.test.ts
Normal file
29
test/copy-trading/public.read.test.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { CopyTradingClient } from '../../src';
|
||||||
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Copy Trading REST API Endpoints', () => {
|
||||||
|
const API_KEY = undefined;
|
||||||
|
const API_SECRET = undefined;
|
||||||
|
|
||||||
|
const api = new CopyTradingClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSymbols()', async () => {
|
||||||
|
expect(await api.getSymbols()).toMatchObject({
|
||||||
|
result: {
|
||||||
|
list: expect.any(Array),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getServerTime()', async () => {
|
||||||
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAnnouncements()', async () => {
|
||||||
|
expect(await api.getAnnouncements()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import { InverseFuturesClient } from '../../src/inverse-futures-client';
|
import { InverseFuturesClient } from '../../src/inverse-futures-client';
|
||||||
import { successResponseList, successResponseObject } from '../response.util';
|
import { successResponseList, successResponseObject } from '../response.util';
|
||||||
|
|
||||||
describe('Public Inverse-Futures REST API GET Endpoints', () => {
|
describe('Private Inverse-Futures REST API GET Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
const api = new InverseFuturesClient(API_KEY, API_SECRET, useLivenet);
|
const api = new InverseFuturesClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
const symbol = 'BTCUSDU22';
|
const symbol = 'BTCUSDU22';
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { API_ERROR_CODE, InverseFuturesClient } from '../../src';
|
|||||||
import { successResponseObject } from '../response.util';
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
@@ -11,7 +10,11 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new InverseFuturesClient(API_KEY, API_SECRET, useLivenet);
|
const api = new InverseFuturesClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
const symbol = 'BTCUSDU22';
|
const symbol = 'BTCUSDU22';
|
||||||
@@ -30,7 +33,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
||||||
ret_msg: 'position idx not match position mode',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -41,7 +43,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: 'order not exists or too late to cancel',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -63,7 +64,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: 'order not exists or too late to replace',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -82,7 +82,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
||||||
ret_msg: 'position idx not match position mode',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,7 +93,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: 'Order not exists',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,7 +113,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: 'order not exists or too late to replace',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,7 +124,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
||||||
ret_msg: 'position idx not match position mode',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -139,7 +135,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
ret_code: API_ERROR_CODE.POSITION_IDX_NOT_MATCH_POSITION_MODE,
|
||||||
ret_msg: 'position idx not match position mode',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -152,7 +147,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.LEVERAGE_NOT_MODIFIED,
|
ret_code: API_ERROR_CODE.LEVERAGE_NOT_MODIFIED,
|
||||||
ret_msg: 'leverage not modified',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -164,7 +158,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.POSITION_MODE_NOT_MODIFIED,
|
ret_code: API_ERROR_CODE.POSITION_MODE_NOT_MODIFIED,
|
||||||
ret_msg: 'position mode not modified',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -178,7 +171,6 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ISOLATED_NOT_MODIFIED,
|
ret_code: API_ERROR_CODE.ISOLATED_NOT_MODIFIED,
|
||||||
ret_msg: 'Isolated not modified',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import {
|
|||||||
successResponseObject,
|
successResponseObject,
|
||||||
} from '../response.util';
|
} from '../response.util';
|
||||||
|
|
||||||
describe('Public Inverse Futures REST API Endpoints', () => {
|
describe('Public Inverse-Futures REST API Endpoints', () => {
|
||||||
const useLivenet = true;
|
const api = new InverseFuturesClient();
|
||||||
const api = new InverseFuturesClient(undefined, undefined, useLivenet);
|
|
||||||
|
|
||||||
const symbol = 'BTCUSD';
|
const symbol = 'BTCUSD';
|
||||||
const interval = '15';
|
const interval = '15';
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { InverseClient } from '../../src/inverse-client';
|
import { InverseClient } from '../../src/';
|
||||||
import { successResponseList, successResponseObject } from '../response.util';
|
import { successResponseList, successResponseObject } from '../response.util';
|
||||||
|
|
||||||
describe('Private Inverse REST API Endpoints', () => {
|
describe('Private Inverse REST API GET Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
@@ -11,7 +10,11 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new InverseClient(API_KEY, API_SECRET, useLivenet);
|
const api = new InverseClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
const symbol = 'BTCUSD';
|
const symbol = 'BTCUSD';
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { API_ERROR_CODE } from '../../src';
|
|||||||
import { InverseClient } from '../../src/inverse-client';
|
import { InverseClient } from '../../src/inverse-client';
|
||||||
import { successResponseObject } from '../response.util';
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
describe('Private Inverse REST API Endpoints', () => {
|
describe('Private Inverse REST API POST Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
@@ -12,7 +11,11 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new InverseClient(API_KEY, API_SECRET, useLivenet);
|
const api = new InverseClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
const symbol = 'BTCUSD';
|
const symbol = 'BTCUSD';
|
||||||
|
|
||||||
@@ -40,7 +43,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: 'order not exists or too late to cancel',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -62,7 +64,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: 'order not exists or too late to replace',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,7 +82,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.INSUFFICIENT_BALANCE,
|
ret_code: API_ERROR_CODE.INSUFFICIENT_BALANCE,
|
||||||
ret_msg: 'Insufficient wallet balance',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -93,7 +93,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: expect.any(String),
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,7 +113,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
ret_code: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
ret_msg: 'order not exists or too late to replace',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -126,7 +124,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.POSITION_IS_CROSS_MARGIN,
|
ret_code: API_ERROR_CODE.POSITION_IS_CROSS_MARGIN,
|
||||||
ret_msg: expect.any(String),
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -138,7 +135,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.CANNOT_SET_TRADING_STOP_FOR_ZERO_POS,
|
ret_code: API_ERROR_CODE.CANNOT_SET_TRADING_STOP_FOR_ZERO_POS,
|
||||||
ret_msg: 'can not set tp/sl/ts for zero position',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -162,7 +158,6 @@ describe('Private Inverse REST API Endpoints', () => {
|
|||||||
})
|
})
|
||||||
).toMatchObject({
|
).toMatchObject({
|
||||||
ret_code: API_ERROR_CODE.SAME_SLTP_MODE,
|
ret_code: API_ERROR_CODE.SAME_SLTP_MODE,
|
||||||
ret_msg: 'same tp sl mode2',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
} from '../response.util';
|
} from '../response.util';
|
||||||
|
|
||||||
describe('Public Inverse REST API Endpoints', () => {
|
describe('Public Inverse REST API Endpoints', () => {
|
||||||
const useLivenet = true;
|
const api = new InverseClient();
|
||||||
const api = new InverseClient(undefined, undefined, useLivenet);
|
|
||||||
|
|
||||||
const symbol = 'BTCUSD';
|
const symbol = 'BTCUSD';
|
||||||
const interval = '15';
|
const interval = '15';
|
||||||
|
|||||||
99
test/inverse/ws.private.test.ts
Normal file
99
test/inverse/ws.private.test.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Private Inverse Perps Websocket Client', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'inverse',
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('with invalid credentials', () => {
|
||||||
|
it('should fail to open a connection if keys/signature are incorrect', async () => {
|
||||||
|
const badClient = new WebsocketClient(
|
||||||
|
{
|
||||||
|
...wsClientOptions,
|
||||||
|
key: 'bad',
|
||||||
|
secret: 'bad',
|
||||||
|
},
|
||||||
|
getSilentLogger('expect401')
|
||||||
|
);
|
||||||
|
|
||||||
|
const wsOpenPromise = waitForSocketEvent(badClient, 'open', 2500);
|
||||||
|
|
||||||
|
badClient.connectPrivate();
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatch('Failed to receive');
|
||||||
|
} catch (e) {
|
||||||
|
// console.error()
|
||||||
|
expect(e?.message).toStrictEqual('Unexpected server response: 401');
|
||||||
|
}
|
||||||
|
badClient.closeAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with valid API credentails', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
// await promiseSleep(2000);
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.inverse,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to private wallet events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
const wsTopic = 'wallet';
|
||||||
|
// No easy way to trigger a private event (other than executing trades)
|
||||||
|
// expect(wsUpdatePromise).resolves.toMatchObject({
|
||||||
|
// topic: wsTopic,
|
||||||
|
// data: expect.any(Array),
|
||||||
|
// });
|
||||||
|
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
request: {
|
||||||
|
args: [wsTopic],
|
||||||
|
op: 'subscribe',
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
77
test/inverse/ws.public.test.ts
Normal file
77
test/inverse/ws.public.test.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import {
|
||||||
|
LinearClient,
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
promiseSleep,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Public Inverse Perps Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'inverse',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(wsClientOptions, getSilentLogger('public'));
|
||||||
|
wsClient.connectPublic();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.inverse,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public orderBookL2_25 events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
const wsTopic = 'orderBookL2_25.BTCUSD';
|
||||||
|
expect(wsResponsePromise).resolves.toMatchObject({
|
||||||
|
request: {
|
||||||
|
args: [wsTopic],
|
||||||
|
op: 'subscribe',
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
expect(wsUpdatePromise).resolves.toMatchObject({
|
||||||
|
topic: wsTopic,
|
||||||
|
data: expect.any(Array),
|
||||||
|
});
|
||||||
|
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await wsResponsePromise;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`Wait for "${wsTopic}" subscription response exception: `,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await wsUpdatePromise;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Wait for "${wsTopic}" event exception: `, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import { LinearClient } from '../../src/linear-client';
|
import { LinearClient } from '../../src/linear-client';
|
||||||
import { successResponseList, successResponseObject } from '../response.util';
|
import { successResponseList, successResponseObject } from '../response.util';
|
||||||
|
|
||||||
describe('Public Linear REST API GET Endpoints', () => {
|
describe('Private Linear REST API GET Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
@@ -11,7 +10,11 @@ describe('Public Linear REST API GET Endpoints', () => {
|
|||||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new LinearClient(API_KEY, API_SECRET, useLivenet);
|
const api = new LinearClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
const symbol = 'BTCUSDT';
|
const symbol = 'BTCUSDT';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { API_ERROR_CODE, LinearClient } from '../../src';
|
import { API_ERROR_CODE, LinearClient } from '../../src';
|
||||||
import { successResponseObject } from '../response.util';
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
describe('Private Linear REST API POST Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
@@ -11,7 +10,11 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new LinearClient(API_KEY, API_SECRET, useLivenet);
|
const api = new LinearClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
const symbol = 'BTCUSDT';
|
const symbol = 'BTCUSDT';
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
} from '../response.util';
|
} from '../response.util';
|
||||||
|
|
||||||
describe('Public Linear REST API Endpoints', () => {
|
describe('Public Linear REST API Endpoints', () => {
|
||||||
const useLivenet = true;
|
const api = new LinearClient();
|
||||||
const api = new LinearClient(undefined, undefined, useLivenet);
|
|
||||||
|
|
||||||
const symbol = 'BTCUSDT';
|
const symbol = 'BTCUSDT';
|
||||||
const interval = '15';
|
const interval = '15';
|
||||||
|
|||||||
108
test/linear/ws.private.test.ts
Normal file
108
test/linear/ws.private.test.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Private Linear Perps Websocket Client', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'linear',
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('with invalid credentials', () => {
|
||||||
|
it('should fail to open a connection if keys/signature are incorrect', async () => {
|
||||||
|
const badClient = new WebsocketClient(
|
||||||
|
{
|
||||||
|
...wsClientOptions,
|
||||||
|
key: 'bad',
|
||||||
|
secret: 'bad',
|
||||||
|
},
|
||||||
|
getSilentLogger('expect401')
|
||||||
|
);
|
||||||
|
|
||||||
|
const wsOpenPromise = waitForSocketEvent(badClient, 'open', 2500);
|
||||||
|
badClient.connectPrivate();
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatch('Failed to receive');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e?.message).toStrictEqual('Unexpected server response: 401');
|
||||||
|
}
|
||||||
|
badClient.closeAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with valid API credentails', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'linear',
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.linearPrivate,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to private wallet events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
const wsTopic = 'wallet';
|
||||||
|
expect(wsResponsePromise).resolves.toMatchObject({
|
||||||
|
request: {
|
||||||
|
args: [wsTopic],
|
||||||
|
op: 'subscribe',
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// No easy way to trigger a private event (other than executing trades)
|
||||||
|
// expect(wsUpdatePromise).resolves.toMatchObject({
|
||||||
|
// topic: wsTopic,
|
||||||
|
// data: expect.any(Array),
|
||||||
|
// });
|
||||||
|
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
await Promise.all([wsResponsePromise]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
67
test/linear/ws.public.test.ts
Normal file
67
test/linear/ws.public.test.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Public Linear Perps Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'linear',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(wsClientOptions, getSilentLogger('public'));
|
||||||
|
wsClient.connectPublic();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.linearPublic,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public orderBookL2_25 events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
const wsTopic = 'orderBookL2_25.BTCUSDT';
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
request: {
|
||||||
|
args: [wsTopic],
|
||||||
|
op: 'subscribe',
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsUpdatePromise).toMatchObject({
|
||||||
|
topic: wsTopic,
|
||||||
|
data: {
|
||||||
|
order_book: expect.any(Array),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Wait for "${wsTopic}" event exception: `, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,19 +1,46 @@
|
|||||||
|
import { API_ERROR_CODE } from '../src';
|
||||||
|
|
||||||
|
const SUCCESS_MSG_REGEX = /OK|SUCCESS|success|success\.|Request accepted|/gim;
|
||||||
|
|
||||||
export function successResponseList(successMsg: string | null = 'OK') {
|
export function successResponseList(successMsg: string | null = 'OK') {
|
||||||
return {
|
return {
|
||||||
result: expect.any(Array),
|
result: expect.any(Array),
|
||||||
ret_code: 0,
|
ret_code: API_ERROR_CODE.SUCCESS,
|
||||||
ret_msg: successMsg,
|
ret_msg: successMsg,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function successResponseListV3() {
|
||||||
|
return {
|
||||||
|
result: {
|
||||||
|
list: expect.any(Array),
|
||||||
|
},
|
||||||
|
...successEmptyResponseObjectV3(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function successResponseObject(successMsg: string | null = 'OK') {
|
export function successResponseObject(successMsg: string | null = 'OK') {
|
||||||
return {
|
return {
|
||||||
result: expect.any(Object),
|
result: expect.any(Object),
|
||||||
ret_code: 0,
|
ret_code: API_ERROR_CODE.SUCCESS,
|
||||||
ret_msg: successMsg,
|
ret_msg: successMsg,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function successResponseObjectV3() {
|
||||||
|
return {
|
||||||
|
result: expect.any(Object),
|
||||||
|
...successEmptyResponseObjectV3(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function successEmptyResponseObjectV3() {
|
||||||
|
return {
|
||||||
|
retCode: API_ERROR_CODE.SUCCESS,
|
||||||
|
retMsg: expect.stringMatching(SUCCESS_MSG_REGEX),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function errorResponseObject(
|
export function errorResponseObject(
|
||||||
result: null | any = null,
|
result: null | any = null,
|
||||||
ret_code: number,
|
ret_code: number,
|
||||||
@@ -26,6 +53,16 @@ export function errorResponseObject(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function errorResponseObjectV3(
|
||||||
|
result: null | any = null,
|
||||||
|
retCode: number
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
retCode: retCode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function notAuthenticatedError() {
|
export function notAuthenticatedError() {
|
||||||
return new Error('Private endpoints require api and private keys set');
|
return new Error('Private endpoints require api and private keys set');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { SpotClient } from '../../src';
|
import { SpotClient } from '../../src';
|
||||||
import {
|
import { errorResponseObject, successResponseList } from '../response.util';
|
||||||
errorResponseObject,
|
|
||||||
notAuthenticatedError,
|
|
||||||
successResponseList,
|
|
||||||
successResponseObject,
|
|
||||||
} from '../response.util';
|
|
||||||
|
|
||||||
describe('Private Spot REST API Endpoints', () => {
|
describe('Private Spot REST API GET Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
@@ -16,10 +10,11 @@ describe('Private Spot REST API Endpoints', () => {
|
|||||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new SpotClient(API_KEY, API_SECRET, useLivenet);
|
const api = new SpotClient({
|
||||||
|
key: API_KEY,
|
||||||
const symbol = 'BTCUSDT';
|
secret: API_SECRET,
|
||||||
const interval = '15m';
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
it('getOrder()', async () => {
|
it('getOrder()', async () => {
|
||||||
// No auth error == test pass
|
// No auth error == test pass
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import { API_ERROR_CODE, SpotClient } from '../../src';
|
import { API_ERROR_CODE, SpotClient } from '../../src';
|
||||||
import { successResponseObject } from '../response.util';
|
import { successResponseObject } from '../response.util';
|
||||||
|
|
||||||
describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
describe('Private Spot REST API POST Endpoints', () => {
|
||||||
const useLivenet = true;
|
|
||||||
const API_KEY = process.env.API_KEY_COM;
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
const API_SECRET = process.env.API_SECRET_COM;
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
@@ -11,7 +10,11 @@ describe('Private Inverse-Futures REST API POST Endpoints', () => {
|
|||||||
expect(API_SECRET).toStrictEqual(expect.any(String));
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
});
|
});
|
||||||
|
|
||||||
const api = new SpotClient(API_KEY, API_SECRET, useLivenet);
|
const api = new SpotClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
// Warning: if some of these start to fail with 10001 params error, it's probably that this future expired and a newer one exists with a different symbol!
|
||||||
const symbol = 'BTCUSDT';
|
const symbol = 'BTCUSDT';
|
||||||
90
test/spot/private.v3.read.test.ts
Normal file
90
test/spot/private.v3.read.test.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { API_ERROR_CODE, SpotClientV3 } from '../../src';
|
||||||
|
import {
|
||||||
|
successEmptyResponseObjectV3,
|
||||||
|
successResponseListV3,
|
||||||
|
successResponseObjectV3,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Spot REST API GET Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new SpotClientV3({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const interval = '15m';
|
||||||
|
const ltCode = 'BTC3S';
|
||||||
|
|
||||||
|
it('getOrder()', async () => {
|
||||||
|
// No auth error == test pass
|
||||||
|
expect(await api.getOrder({ orderId: '123123' })).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ORDER_NOT_FOUND_SPOT_V3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOpenOrders()', async () => {
|
||||||
|
expect(await api.getOpenOrders()).toMatchObject(successResponseListV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPastOrders()', async () => {
|
||||||
|
expect(await api.getPastOrders()).toMatchObject(successResponseListV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMyTrades()', async () => {
|
||||||
|
expect(await api.getMyTrades()).toMatchObject(successResponseListV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBalances()', async () => {
|
||||||
|
expect(await api.getBalances()).toMatchObject({
|
||||||
|
result: {
|
||||||
|
balances: expect.any(Array),
|
||||||
|
},
|
||||||
|
...successEmptyResponseObjectV3(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLeveragedTokenMarketInfo()', async () => {
|
||||||
|
expect(await api.getLeveragedTokenMarketInfo(ltCode)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLeveragedTokenPRHistory()', async () => {
|
||||||
|
expect(await api.getLeveragedTokenPRHistory()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ORDER_NOT_FOUND_LEVERAGED_TOKEN,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCrossMarginBorrowingInfo()', async () => {
|
||||||
|
expect(await api.getCrossMarginBorrowingInfo()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCrossMarginAccountInfo()', async () => {
|
||||||
|
expect(await api.getCrossMarginAccountInfo()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.QUERY_ACCOUNT_INFO_ERROR,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCrossMarginInterestQuota()', async () => {
|
||||||
|
expect(await api.getCrossMarginInterestQuota('USDT')).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCrossMarginRepaymentHistory()', async () => {
|
||||||
|
expect(await api.getCrossMarginRepaymentHistory()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
79
test/spot/private.v3.write.test.ts
Normal file
79
test/spot/private.v3.write.test.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { API_ERROR_CODE, SpotClientV3 } from '../../src';
|
||||||
|
import { successResponseObjectV3 } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Spot REST API POST Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new SpotClientV3({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const ltCode = 'BTC3S';
|
||||||
|
|
||||||
|
// These tests are primarily check auth is working by expecting balance or order not found style errors
|
||||||
|
|
||||||
|
it('submitOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.submitOrder({
|
||||||
|
side: 'Buy',
|
||||||
|
symbol,
|
||||||
|
orderQty: '10000',
|
||||||
|
orderType: 'MARKET',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.BALANCE_INSUFFICIENT_SPOT_V3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelOrder({
|
||||||
|
orderId: '1231231',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ORDER_NOT_FOUND_SPOT_V3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelOrderBatch()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelOrderBatch({
|
||||||
|
symbol,
|
||||||
|
orderTypes: ['LIMIT', 'LIMIT_MAKER'],
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('purchaseLeveragedToken()', async () => {
|
||||||
|
expect(await api.purchaseLeveragedToken(ltCode, '1')).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.EXCEEDED_UPPER_LIMIT_LEVERAGED_TOKEN,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('redeemLeveragedToken()', async () => {
|
||||||
|
expect(await api.redeemLeveragedToken(ltCode, '1')).toMatchObject({
|
||||||
|
retCode: 12426, // unknown error code, not listed in docs yet
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('borrowCrossMarginLoan()', async () => {
|
||||||
|
expect(await api.borrowCrossMarginLoan('USDT', '1')).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.CROSS_MARGIN_USER_NOT_FOUND,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('repayCrossMarginLoan()', async () => {
|
||||||
|
expect(await api.repayCrossMarginLoan('USDT', '1')).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.CROSS_MARGIN_REPAYMENT_NOT_REQUIRED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,8 +6,7 @@ import {
|
|||||||
} from '../response.util';
|
} from '../response.util';
|
||||||
|
|
||||||
describe('Public Spot REST API Endpoints', () => {
|
describe('Public Spot REST API Endpoints', () => {
|
||||||
const useLivenet = true;
|
const api = new SpotClient();
|
||||||
const api = new SpotClient(undefined, undefined, useLivenet);
|
|
||||||
|
|
||||||
const symbol = 'BTCUSDT';
|
const symbol = 'BTCUSDT';
|
||||||
const interval = '15m';
|
const interval = '15m';
|
||||||
71
test/spot/public.v3.test.ts
Normal file
71
test/spot/public.v3.test.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { SpotClientV3 } from '../../src';
|
||||||
|
import {
|
||||||
|
notAuthenticatedError,
|
||||||
|
successResponseObjectV3,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Spot REST API Endpoints', () => {
|
||||||
|
const api = new SpotClientV3();
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const interval = '15m';
|
||||||
|
const timestampOneHourAgo = new Date().getTime() / 1000 - 1000 * 60 * 60;
|
||||||
|
const from = Number(timestampOneHourAgo.toFixed(0));
|
||||||
|
|
||||||
|
it('should throw for unauthenticated private calls', async () => {
|
||||||
|
expect(() => api.getOpenOrders()).rejects.toMatchObject(
|
||||||
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
|
expect(() => api.getBalances()).rejects.toMatchObject(
|
||||||
|
notAuthenticatedError()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSymbols()', async () => {
|
||||||
|
expect(await api.getSymbols()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOrderBook()', async () => {
|
||||||
|
expect(await api.getOrderBook(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMergedOrderBook()', async () => {
|
||||||
|
expect(await api.getMergedOrderBook(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTrades()', async () => {
|
||||||
|
expect(await api.getTrades(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCandles()', async () => {
|
||||||
|
expect(await api.getCandles(symbol, interval)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get24hrTicker()', async () => {
|
||||||
|
expect(await api.get24hrTicker()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastTradedPrice()', async () => {
|
||||||
|
expect(await api.getLastTradedPrice()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBestBidAskPrice()', async () => {
|
||||||
|
expect(await api.getBestBidAskPrice()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetchServertime() returns number', async () => {
|
||||||
|
expect(await api.fetchServerTime()).toStrictEqual(expect.any(Number));
|
||||||
|
});
|
||||||
|
});
|
||||||
66
test/spot/ws.private.v1.test.ts
Normal file
66
test/spot/ws.private.v1.test.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
promiseSleep,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Private Spot V1 Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'spot',
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
logAllEvents(wsClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: how to detect if auth failed for the v1 spot ws
|
||||||
|
it('should open a private ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
// wsKey: WS_KEY_MAP.spotPrivate,
|
||||||
|
// also opens public conn automatically, which can confuse the test
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
// expect(wsUpdatePromise).resolves.toMatchObject({
|
||||||
|
// topic: 'wsTopic',
|
||||||
|
// data: expect.any(Array),
|
||||||
|
// });
|
||||||
|
|
||||||
|
// await Promise.all([wsUpdatePromise]);
|
||||||
|
// await promiseSleep(4000);
|
||||||
|
});
|
||||||
|
});
|
||||||
112
test/spot/ws.private.v3.test.ts
Normal file
112
test/spot/ws.private.v3.test.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_ERROR_ENUM,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Private Spot V3 Websocket Client', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'spotv3',
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
};
|
||||||
|
const wsTopic = `outboundAccountInfo`;
|
||||||
|
|
||||||
|
describe('with invalid credentials', () => {
|
||||||
|
it('should reject private subscribe if keys/signature are incorrect', async () => {
|
||||||
|
const badClient = new WebsocketClient(
|
||||||
|
{
|
||||||
|
...wsClientOptions,
|
||||||
|
key: 'bad',
|
||||||
|
secret: 'bad',
|
||||||
|
},
|
||||||
|
getSilentLogger('expect401')
|
||||||
|
);
|
||||||
|
|
||||||
|
// const wsOpenPromise = waitForSocketEvent(badClient, 'open');
|
||||||
|
const wsResponsePromise = waitForSocketEvent(badClient, 'response');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
badClient.connectPrivate();
|
||||||
|
badClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
expect(wsResponsePromise).rejects.toMatchObject({
|
||||||
|
ret_code: WS_ERROR_ENUM.API_SIGN_AUTH_FAILED,
|
||||||
|
ret_msg: expect.any(String),
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all([wsResponsePromise]);
|
||||||
|
} catch (e) {
|
||||||
|
// console.error()
|
||||||
|
}
|
||||||
|
badClient.closeAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with valid API credentails', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a private ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.spotV3Private,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
op: 'auth',
|
||||||
|
success: true,
|
||||||
|
req_id: `${WS_KEY_MAP.spotV3Private}-auth`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to private outboundAccountInfo events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
|
||||||
|
// expect(wsUpdatePromise).resolves.toStrictEqual('');
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
op: 'subscribe',
|
||||||
|
success: true,
|
||||||
|
ret_msg: '',
|
||||||
|
req_id: wsTopic,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
70
test/spot/ws.public.v1.test.ts
Normal file
70
test/spot/ws.public.v1.test.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
fullLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Public Spot V1 Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'spot',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
wsClient.connectPublic();
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.spotPublic,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public orderbook events', async () => {
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
expect(wsUpdatePromise).resolves.toMatchObject({
|
||||||
|
symbol: symbol,
|
||||||
|
symbolName: symbol,
|
||||||
|
topic: 'diffDepth',
|
||||||
|
params: {
|
||||||
|
realtimeInterval: '24h',
|
||||||
|
binary: 'false',
|
||||||
|
},
|
||||||
|
data: expect.any(Array),
|
||||||
|
});
|
||||||
|
|
||||||
|
wsClient.subscribePublicSpotOrderbook(symbol, 'delta');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await wsUpdatePromise;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Wait for spot v1 orderbook event exception: `, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
90
test/spot/ws.public.v3.test.ts
Normal file
90
test/spot/ws.public.v3.test.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
fullLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Public Spot V3 Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'spotv3',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
wsClient.connectPublic();
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.spotV3Public,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public orderbook events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const wsTopic = `orderbook.40.${symbol}`;
|
||||||
|
|
||||||
|
expect(wsResponsePromise).resolves.toMatchObject({
|
||||||
|
op: 'subscribe',
|
||||||
|
success: true,
|
||||||
|
ret_msg: 'subscribe',
|
||||||
|
req_id: wsTopic,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wsUpdatePromise).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
a: expect.any(Array),
|
||||||
|
b: expect.any(Array),
|
||||||
|
s: symbol,
|
||||||
|
t: expect.any(Number),
|
||||||
|
},
|
||||||
|
topic: wsTopic,
|
||||||
|
ts: expect.any(Number),
|
||||||
|
type: 'delta',
|
||||||
|
});
|
||||||
|
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await wsResponsePromise;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`Wait for "${wsTopic}" subscription response exception: `,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await wsUpdatePromise;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Wait for "${wsTopic}" event exception: `, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
93
test/unified-margin/private.read.test.ts
Normal file
93
test/unified-margin/private.read.test.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import { API_ERROR_CODE, UnifiedMarginClient } from '../../src';
|
||||||
|
import { successResponseObjectV3 } from '../response.util';
|
||||||
|
|
||||||
|
describe('Private Unified Margin REST API GET Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new UnifiedMarginClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const category = 'linear';
|
||||||
|
|
||||||
|
it('getActiveOrders()', async () => {
|
||||||
|
expect(await api.getActiveOrders({ category })).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getHistoricOrders()', async () => {
|
||||||
|
expect(await api.getHistoricOrders({ category })).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPositions()', async () => {
|
||||||
|
expect(await api.getPositions({ category })).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get7DayTradingHistory()', async () => {
|
||||||
|
expect(await api.get7DayTradingHistory({ category, symbol })).toMatchObject(
|
||||||
|
{
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOptionsSettlementHistory()', async () => {
|
||||||
|
expect(await api.getOptionsSettlementHistory({ category })).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getUSDCPerpetualSettlementHistory()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getUSDCPerpetualSettlementHistory({ category })
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBalances()', async () => {
|
||||||
|
expect(await api.getBalances()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTransactionLog()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getTransactionLog({ category, currency: 'USDT' })
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCoinExchangeHistory()', async () => {
|
||||||
|
expect(await api.getCoinExchangeHistory()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBorrowHistory()', async () => {
|
||||||
|
expect(await api.getBorrowHistory()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBorrowRate()', async () => {
|
||||||
|
expect(await api.getBorrowRate()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
190
test/unified-margin/private.write.test.ts
Normal file
190
test/unified-margin/private.write.test.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import { API_ERROR_CODE, UnifiedMarginClient } from '../../src';
|
||||||
|
|
||||||
|
describe('Private Unified Margin REST API POST Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new UnifiedMarginClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const category = 'linear';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* While it may seem silly, these tests simply validate the request is processed at all.
|
||||||
|
* Something very wrong would be a sign error or complaints about the endpoint/request method/server error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
it('submitOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.submitOrder({
|
||||||
|
symbol,
|
||||||
|
category,
|
||||||
|
side: 'Sell',
|
||||||
|
orderType: 'Limit',
|
||||||
|
qty: '1',
|
||||||
|
price: '20000',
|
||||||
|
orderLinkId: Date.now().toString(),
|
||||||
|
timeInForce: 'GoodTillCancel',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modifyOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.modifyOrder({
|
||||||
|
symbol,
|
||||||
|
category,
|
||||||
|
orderId: 'somethingFake',
|
||||||
|
price: '20000',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelOrder({
|
||||||
|
symbol,
|
||||||
|
category,
|
||||||
|
orderId: 'somethingFake1',
|
||||||
|
orderFilter: 'Order',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('batchSubmitOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.batchSubmitOrders(category, [
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
side: 'Buy',
|
||||||
|
orderType: 'Limit',
|
||||||
|
qty: '1',
|
||||||
|
price: '10000',
|
||||||
|
timeInForce: 'FillOrKill',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
side: 'Buy',
|
||||||
|
orderType: 'Limit',
|
||||||
|
qty: '1',
|
||||||
|
price: '10001',
|
||||||
|
timeInForce: 'FillOrKill',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
side: 'Buy',
|
||||||
|
orderType: 'Limit',
|
||||||
|
qty: '1',
|
||||||
|
price: '10002',
|
||||||
|
timeInForce: 'FillOrKill',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('batchReplaceOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.batchReplaceOrders(category, [
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderLinkId: 'somethingFake1',
|
||||||
|
qty: '4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderLinkId: 'somethingFake2',
|
||||||
|
qty: '5',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderLinkId: 'somethingFake3',
|
||||||
|
qty: '6',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('batchCancelOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.batchCancelOrders(category, [
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderLinkId: 'somethingFake1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderLinkId: 'somethingFake2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderLinkId: 'somethingFake3',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelAllOrders()', async () => {
|
||||||
|
expect(await api.cancelAllOrders({ category })).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setLeverage()', async () => {
|
||||||
|
expect(await api.setLeverage(category, symbol, 5, 5)).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setTPSLMode()', async () => {
|
||||||
|
expect(await api.setTPSLMode(category, symbol, 1)).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setRiskLimit()', async () => {
|
||||||
|
expect(await api.setRiskLimit(category, symbol, 1, 0)).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setTPSL()', async () => {
|
||||||
|
expect(await api.setTPSL({ category, symbol })).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_UNIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transferFunds()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.transferFunds({
|
||||||
|
amount: '1',
|
||||||
|
coin: 'USDT',
|
||||||
|
from_account_type: 'SPOT',
|
||||||
|
to_account_type: 'CONTRACT',
|
||||||
|
transfer_id: 'testtransfer',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
ret_code: API_ERROR_CODE.INVALID_API_KEY_OR_PERMISSIONS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
107
test/unified-margin/public.read.test.ts
Normal file
107
test/unified-margin/public.read.test.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { UnifiedMarginClient, UMCandlesRequest } from '../../src';
|
||||||
|
import {
|
||||||
|
successResponseObject,
|
||||||
|
successResponseObjectV3,
|
||||||
|
} from '../response.util';
|
||||||
|
|
||||||
|
describe('Public Unified Margin REST API Endpoints', () => {
|
||||||
|
const API_KEY = undefined;
|
||||||
|
const API_SECRET = undefined;
|
||||||
|
|
||||||
|
const api = new UnifiedMarginClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCUSDT';
|
||||||
|
const category = 'linear';
|
||||||
|
const start = Number((Date.now() / 1000).toFixed(0));
|
||||||
|
const end = start + 1000 * 60 * 60 * 24;
|
||||||
|
const interval = '1';
|
||||||
|
|
||||||
|
const candleRequest: UMCandlesRequest = {
|
||||||
|
category,
|
||||||
|
symbol,
|
||||||
|
interval,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
};
|
||||||
|
|
||||||
|
it('getOrderBook()', async () => {
|
||||||
|
expect(await api.getOrderBook(symbol, category)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCandles()', async () => {
|
||||||
|
expect(await api.getCandles(candleRequest)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSymbolTicker()', async () => {
|
||||||
|
expect(await api.getSymbolTicker(category)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getInstrumentInfo()', async () => {
|
||||||
|
expect(await api.getInstrumentInfo({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMarkPrice()', async () => {
|
||||||
|
expect(await api.getMarkPriceCandles(candleRequest)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getIndexPrice()', async () => {
|
||||||
|
expect(await api.getIndexPriceCandles(candleRequest)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastFundingRate()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getFundingRateHistory({
|
||||||
|
category,
|
||||||
|
symbol,
|
||||||
|
})
|
||||||
|
).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getRiskLimit()', async () => {
|
||||||
|
expect(await api.getRiskLimit(category, symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOptionDeliveryPrice()', async () => {
|
||||||
|
expect(await api.getOptionDeliveryPrice({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTrades()', async () => {
|
||||||
|
expect(await api.getTrades({ category, symbol })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOpenInterest()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.getOpenInterest({ symbol, category, interval: '5min' })
|
||||||
|
).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getServerTime()', async () => {
|
||||||
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAnnouncements()', async () => {
|
||||||
|
expect(await api.getAnnouncements()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
});
|
||||||
77
test/unified-margin/ws.private.test.ts
Normal file
77
test/unified-margin/ws.private.test.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
fullLogger,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Private Unified Margin Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'unifiedPerp',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccessNoAuth')
|
||||||
|
// fullLogger
|
||||||
|
);
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.unifiedPrivate,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should work, but don't have unified margin activated - needs extra testing
|
||||||
|
// {"conn_id": "064443fffef10442-0000798d-0000a9f6-ba32aeee49712540-1752c4f4", "ret_msg": "3303001", "success": false, "type": "COMMAND_RESP", "wsKey": "unifiedPrivate"}
|
||||||
|
|
||||||
|
it.skip('should subscribe to public private unified account events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
// USDT should be detected and automatically routed through the USDT connection
|
||||||
|
const topic = 'user.position.unifiedAccount';
|
||||||
|
wsClient.subscribe(topic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
op: 'subscribe',
|
||||||
|
req_id: topic,
|
||||||
|
success: true,
|
||||||
|
wsKey: WS_KEY_MAP.unifiedPrivate,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// sub failed
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsUpdatePromise).toStrictEqual('');
|
||||||
|
} catch (e) {
|
||||||
|
// no data
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
67
test/unified-margin/ws.public.option.test.ts
Normal file
67
test/unified-margin/ws.public.option.test.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Public Unified Margin Websocket Client (Options)', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'unifiedOption',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccessNoAuth')
|
||||||
|
);
|
||||||
|
wsClient.connectPublic();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.unifiedOptionPublic,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public orderbook events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
wsClient.subscribe('orderbook.25.BTCUSDT');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
success: true,
|
||||||
|
type: 'COMMAND_RESP',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// sub failed
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// expect(await wsUpdatePromise).toStrictEqual('asdfasdf');
|
||||||
|
// } catch (e) {
|
||||||
|
// // no data
|
||||||
|
// expect(e).toBeFalsy();
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
});
|
||||||
85
test/unified-margin/ws.public.perp.usdc.test.ts
Normal file
85
test/unified-margin/ws.public.perp.usdc.test.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
fullLogger,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Public Unified Margin Websocket Client (Perps - USDC)', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'unifiedPerp',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccessNoAuth')
|
||||||
|
// fullLogger
|
||||||
|
);
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
wsClient.connectPublic();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: expect.stringContaining('unifiedPerpUSD'),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: are there USDC topics? This doesn't seem to work
|
||||||
|
it.skip('should subscribe to public orderbook events through USDC connection', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
// USDT should be detected and automatically routed through the USDT connection
|
||||||
|
wsClient.subscribe('orderbook.25.BTCUSDC');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
op: 'subscribe',
|
||||||
|
req_id: 'orderbook.25.BTCUSDC',
|
||||||
|
success: true,
|
||||||
|
wsKey: WS_KEY_MAP.unifiedPerpUSDTPublic,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// sub failed
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsUpdatePromise).toMatchObject({
|
||||||
|
data: {
|
||||||
|
a: expect.any(Array),
|
||||||
|
b: expect.any(Array),
|
||||||
|
s: 'BTCUSDT',
|
||||||
|
u: expect.any(Number),
|
||||||
|
},
|
||||||
|
topic: 'orderbook.25.BTCUSDC',
|
||||||
|
ts: expect.any(Number),
|
||||||
|
type: 'snapshot',
|
||||||
|
wsKey: WS_KEY_MAP.unifiedPerpUSDTPublic,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// no data
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
85
test/unified-margin/ws.public.perp.usdt.test.ts
Normal file
85
test/unified-margin/ws.public.perp.usdt.test.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
fullLogger,
|
||||||
|
} from '../ws.util';
|
||||||
|
|
||||||
|
describe('Public Unified Margin Websocket Client (Perps - USDT)', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'unifiedPerp',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccessNoAuth')
|
||||||
|
// fullLogger
|
||||||
|
);
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
wsClient.connectPublic();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: expect.stringContaining('unifiedPerpUSD'),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public orderbook events through USDT connection', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
// USDT should be detected and automatically routed through the USDT connection
|
||||||
|
const topic = 'orderbook.25.BTCUSDT';
|
||||||
|
wsClient.subscribe(topic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
op: 'subscribe',
|
||||||
|
req_id: topic,
|
||||||
|
success: true,
|
||||||
|
wsKey: WS_KEY_MAP.unifiedPerpUSDTPublic,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// sub failed
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsUpdatePromise).toMatchObject({
|
||||||
|
data: {
|
||||||
|
a: expect.any(Array),
|
||||||
|
b: expect.any(Array),
|
||||||
|
s: 'BTCUSDT',
|
||||||
|
u: expect.any(Number),
|
||||||
|
},
|
||||||
|
topic: topic,
|
||||||
|
ts: expect.any(Number),
|
||||||
|
type: 'snapshot',
|
||||||
|
wsKey: WS_KEY_MAP.unifiedPerpUSDTPublic,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// no data
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
80
test/usdc/options/private.read.test.ts
Normal file
80
test/usdc/options/private.read.test.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { USDCOptionClient } from '../../../src';
|
||||||
|
import { successResponseObjectV3 } from '../../response.util';
|
||||||
|
|
||||||
|
describe('Private USDC Options REST API GET Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
const symbol = 'BTC-30SEP22-400000-C';
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new USDCOptionClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
const category = 'OPTION';
|
||||||
|
|
||||||
|
it('getActiveRealtimeOrders()', async () => {
|
||||||
|
expect(await api.getActiveRealtimeOrders()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getActiveOrders()', async () => {
|
||||||
|
expect(await api.getActiveOrders({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getHistoricOrders()', async () => {
|
||||||
|
expect(await api.getHistoricOrders({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOrderExecutionHistory()', async () => {
|
||||||
|
expect(await api.getOrderExecutionHistory({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTransactionLog()', async () => {
|
||||||
|
expect(await api.getTransactionLog({ type: 'TRADE' })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBalances()', async () => {
|
||||||
|
expect(await api.getBalances()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAssetInfo()', async () => {
|
||||||
|
expect(await api.getAssetInfo()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMarginMode()', async () => {
|
||||||
|
expect(await api.getMarginMode()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPositions()', async () => {
|
||||||
|
expect(await api.getPositions({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getDeliveryHistory()', async () => {
|
||||||
|
expect(await api.getDeliveryHistory({ symbol })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPositionsInfoUponExpiry()', async () => {
|
||||||
|
expect(await api.getPositionsInfoUponExpiry()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
161
test/usdc/options/private.write.test.ts
Normal file
161
test/usdc/options/private.write.test.ts
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
import { API_ERROR_CODE, USDCOptionClient } from '../../../src';
|
||||||
|
import { successResponseObjectV3 } from '../../response.util';
|
||||||
|
|
||||||
|
describe('Private USDC Options REST API POST Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new USDCOptionClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const currency = 'USDC';
|
||||||
|
const symbol = 'BTC-30SEP22-400000-C';
|
||||||
|
|
||||||
|
it('submitOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.submitOrder({
|
||||||
|
symbol,
|
||||||
|
orderType: 'Limit',
|
||||||
|
side: 'Sell',
|
||||||
|
orderQty: '1000',
|
||||||
|
orderPrice: '40',
|
||||||
|
orderLinkId: Date.now().toString(),
|
||||||
|
timeInForce: 'GoodTillCancel',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ACCOUNT_NOT_EXIST,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('batchSubmitOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.batchSubmitOrders([
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderType: 'Limit',
|
||||||
|
side: 'Sell',
|
||||||
|
orderQty: '1000',
|
||||||
|
orderPrice: '40',
|
||||||
|
orderLinkId: Date.now().toString(),
|
||||||
|
timeInForce: 'GoodTillCancel',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderType: 'Limit',
|
||||||
|
side: 'Sell',
|
||||||
|
orderQty: '1000',
|
||||||
|
orderPrice: '40',
|
||||||
|
orderLinkId: Date.now().toString(),
|
||||||
|
timeInForce: 'GoodTillCancel',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toMatchObject({
|
||||||
|
result: [
|
||||||
|
{ errorCode: API_ERROR_CODE.ACCOUNT_NOT_EXIST },
|
||||||
|
{ errorCode: API_ERROR_CODE.ACCOUNT_NOT_EXIST },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modifyOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.modifyOrder({
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ORDER_NOT_EXIST,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('batchModifyOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.batchModifyOrders([
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake2',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toMatchObject({
|
||||||
|
result: [
|
||||||
|
{ errorCode: API_ERROR_CODE.ORDER_NOT_EXIST },
|
||||||
|
{ errorCode: API_ERROR_CODE.ORDER_NOT_EXIST },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelOrder({
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake1',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ORDER_NOT_EXIST,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('batchCancelOrders()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.batchCancelOrders([
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake2',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
).toMatchObject({
|
||||||
|
result: [
|
||||||
|
{ errorCode: API_ERROR_CODE.ORDER_NOT_EXIST },
|
||||||
|
{ errorCode: API_ERROR_CODE.ORDER_NOT_EXIST },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelActiveOrders()', async () => {
|
||||||
|
expect(await api.cancelActiveOrders()).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.NO_ACTIVE_ORDER,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setMarginMode()', async () => {
|
||||||
|
expect(await api.setMarginMode('REGULAR_MARGIN')).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modifyMMP()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.modifyMMP({
|
||||||
|
currency,
|
||||||
|
windowMs: 0,
|
||||||
|
frozenPeriodMs: 100,
|
||||||
|
qtyLimit: '100',
|
||||||
|
deltaLimit: '1',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.INCORRECT_MMP_PARAMETERS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('resetMMP()', async () => {
|
||||||
|
expect(await api.resetMMP(currency)).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.INSTITION_MMP_PROFILE_NOT_FOUND,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
test/usdc/options/public.read.test.ts
Normal file
57
test/usdc/options/public.read.test.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { USDCOptionClient } from '../../../src';
|
||||||
|
import {
|
||||||
|
successResponseObject,
|
||||||
|
successResponseObjectV3,
|
||||||
|
} from '../../response.util';
|
||||||
|
|
||||||
|
describe('Public USDC Options REST API Endpoints', () => {
|
||||||
|
const API_KEY = undefined;
|
||||||
|
const API_SECRET = undefined;
|
||||||
|
|
||||||
|
const api = new USDCOptionClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
const symbol = 'BTC-30SEP22-400000-C';
|
||||||
|
|
||||||
|
it('getOrderBook()', async () => {
|
||||||
|
expect(await api.getOrderBook(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getContractInfo()', async () => {
|
||||||
|
expect(await api.getContractInfo()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSymbolTicker()', async () => {
|
||||||
|
expect(await api.getSymbolTicker(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getDeliveryPrice()', async () => {
|
||||||
|
expect(await api.getDeliveryPrice()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLast500Trades()', async () => {
|
||||||
|
expect(await api.getLast500Trades({ category: 'OPTION' })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getHistoricalVolatility()', async () => {
|
||||||
|
expect(await api.getHistoricalVolatility()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getServerTime()', async () => {
|
||||||
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
});
|
||||||
149
test/usdc/options/ws.private.test.ts
Normal file
149
test/usdc/options/ws.private.test.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_ERROR_ENUM,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../../src';
|
||||||
|
import {
|
||||||
|
fullLogger,
|
||||||
|
getSilentLogger,
|
||||||
|
logAllEvents,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../../ws.util';
|
||||||
|
|
||||||
|
describe('Private USDC Option Websocket Client', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'usdcOption',
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
};
|
||||||
|
|
||||||
|
const wsTopic = `user.openapi.option.position`;
|
||||||
|
|
||||||
|
describe('with invalid credentials', () => {
|
||||||
|
it('should reject private subscribe if keys/signature are incorrect', async () => {
|
||||||
|
const badClient = new WebsocketClient(
|
||||||
|
{
|
||||||
|
...wsClientOptions,
|
||||||
|
key: 'bad',
|
||||||
|
secret: 'bad',
|
||||||
|
reconnectTimeout: 10000,
|
||||||
|
},
|
||||||
|
// fullLogger
|
||||||
|
getSilentLogger('expect401')
|
||||||
|
);
|
||||||
|
// logAllEvents(badClient);
|
||||||
|
|
||||||
|
// const wsOpenPromise = waitForSocketEvent(badClient, 'open');
|
||||||
|
const wsResponsePromise = waitForSocketEvent(badClient, 'response');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
badClient.connectPrivate();
|
||||||
|
|
||||||
|
const responsePartial = {
|
||||||
|
ret_msg: WS_ERROR_ENUM.USDC_OPTION_AUTH_FAILED,
|
||||||
|
success: false,
|
||||||
|
type: 'AUTH_RESP',
|
||||||
|
};
|
||||||
|
expect(wsResponsePromise).rejects.toMatchObject(responsePartial);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all([wsResponsePromise]);
|
||||||
|
} catch (e) {
|
||||||
|
// console.error()
|
||||||
|
expect(e).toMatchObject(responsePartial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// badClient.subscribe(wsTopic);
|
||||||
|
badClient.removeAllListeners();
|
||||||
|
badClient.closeAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with valid API credentails', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a private ws connection', async () => {
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.usdcOptionPrivate,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
ret_msg: '0',
|
||||||
|
success: true,
|
||||||
|
type: 'AUTH_RESP',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Wait for "${wsTopic}" event exception: `, e);
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should subscribe to private "${wsTopic}" events`, async () => {
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
// expect(wsUpdatePromise).resolves.toStrictEqual('');
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
data: {
|
||||||
|
failTopics: [],
|
||||||
|
successTopics: [wsTopic],
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
type: 'COMMAND_RESP',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`Wait for "${wsTopic}" subscription response exception: `,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
expect(await wsUpdatePromise).toMatchObject({
|
||||||
|
creationTime: expect.any(Number),
|
||||||
|
data: {
|
||||||
|
baseLine: expect.any(Number),
|
||||||
|
dataType: expect.any(String),
|
||||||
|
result: expect.any(Array),
|
||||||
|
version: expect.any(Number),
|
||||||
|
},
|
||||||
|
topic: wsTopic,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
76
test/usdc/options/ws.public.test.ts
Normal file
76
test/usdc/options/ws.public.test.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../../ws.util';
|
||||||
|
|
||||||
|
describe('Public USDC Option Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'usdcOption',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccessNoAuth')
|
||||||
|
);
|
||||||
|
wsClient.connectPublic();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.usdcOptionPublic,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public trade events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
wsClient.subscribe([
|
||||||
|
'recenttrades.BTC',
|
||||||
|
'recenttrades.ETH',
|
||||||
|
'recenttrades.SOL',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
failTopics: [],
|
||||||
|
successTopics: expect.any(Array),
|
||||||
|
},
|
||||||
|
type: 'COMMAND_RESP',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// sub failed
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes a while to get an event from USDC options - testing this manually for now
|
||||||
|
// try {
|
||||||
|
// expect(await wsUpdatePromise).toStrictEqual('asdfasdf');
|
||||||
|
// } catch (e) {
|
||||||
|
// // no data
|
||||||
|
// expect(e).toBeFalsy();
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
});
|
||||||
75
test/usdc/perpetual/private.read.test.ts
Normal file
75
test/usdc/perpetual/private.read.test.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { USDCPerpetualClient } from '../../../src';
|
||||||
|
import { successResponseObjectV3 } from '../../response.util';
|
||||||
|
|
||||||
|
describe('Private USDC Perp REST API GET Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new USDCPerpetualClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCPERP';
|
||||||
|
const category = 'PERPETUAL';
|
||||||
|
|
||||||
|
it('getActiveOrders()', async () => {
|
||||||
|
expect(await api.getActiveOrders({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getHistoricOrders()', async () => {
|
||||||
|
expect(await api.getHistoricOrders({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOrderExecutionHistory()', async () => {
|
||||||
|
expect(await api.getOrderExecutionHistory({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTransactionLog()', async () => {
|
||||||
|
expect(await api.getTransactionLog({ type: 'TRADE' })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBalances()', async () => {
|
||||||
|
expect(await api.getBalances()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAssetInfo()', async () => {
|
||||||
|
expect(await api.getAssetInfo()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMarginMode()', async () => {
|
||||||
|
expect(await api.getMarginMode()).toMatchObject(successResponseObjectV3());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPositions()', async () => {
|
||||||
|
expect(await api.getPositions({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSettlementHistory()', async () => {
|
||||||
|
expect(await api.getSettlementHistory({ symbol })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPredictedFundingRate()', async () => {
|
||||||
|
expect(await api.getPredictedFundingRate(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
89
test/usdc/perpetual/private.write.test.ts
Normal file
89
test/usdc/perpetual/private.write.test.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { API_ERROR_CODE, USDCPerpetualClient } from '../../../src';
|
||||||
|
import {
|
||||||
|
successEmptyResponseObjectV3,
|
||||||
|
successResponseObjectV3,
|
||||||
|
} from '../../response.util';
|
||||||
|
|
||||||
|
describe('Private USDC Perp REST API POST Endpoints', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = new USDCPerpetualClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCPERP';
|
||||||
|
|
||||||
|
it('submitOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.submitOrder({
|
||||||
|
symbol,
|
||||||
|
side: 'Sell',
|
||||||
|
orderType: 'Limit',
|
||||||
|
orderFilter: 'Order',
|
||||||
|
orderQty: '1',
|
||||||
|
orderPrice: '20000',
|
||||||
|
orderLinkId: Date.now().toString(),
|
||||||
|
timeInForce: 'GoodTillCancel',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.INSUFFICIENT_BALANCE_FOR_ORDER_COST,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modifyOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.modifyOrder({
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake',
|
||||||
|
orderPrice: '20000',
|
||||||
|
orderFilter: 'Order',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelOrder()', async () => {
|
||||||
|
expect(
|
||||||
|
await api.cancelOrder({
|
||||||
|
symbol,
|
||||||
|
orderId: 'somethingFake1',
|
||||||
|
orderFilter: 'Order',
|
||||||
|
})
|
||||||
|
).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.ORDER_NOT_FOUND_OR_TOO_LATE,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancelActiveOrders()', async () => {
|
||||||
|
expect(await api.cancelActiveOrders(symbol, 'Order')).toMatchObject(
|
||||||
|
successEmptyResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setMarginMode()', async () => {
|
||||||
|
expect(await api.setMarginMode('REGULAR_MARGIN')).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setLeverage()', async () => {
|
||||||
|
expect(await api.setLeverage(symbol, '10')).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.LEVERAGE_NOT_MODIFIED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setRiskLimit()', async () => {
|
||||||
|
expect(await api.setRiskLimit(symbol, 1)).toMatchObject({
|
||||||
|
retCode: API_ERROR_CODE.RISK_LIMIT_NOT_EXISTS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
98
test/usdc/perpetual/public.read.test.ts
Normal file
98
test/usdc/perpetual/public.read.test.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { USDCKlineRequest, USDCPerpetualClient } from '../../../src';
|
||||||
|
import {
|
||||||
|
successResponseObject,
|
||||||
|
successResponseObjectV3,
|
||||||
|
} from '../../response.util';
|
||||||
|
|
||||||
|
describe('Public USDC Perp REST API Endpoints', () => {
|
||||||
|
const API_KEY = undefined;
|
||||||
|
const API_SECRET = undefined;
|
||||||
|
|
||||||
|
const api = new USDCPerpetualClient({
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
testnet: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbol = 'BTCPERP';
|
||||||
|
const category = 'PERPETUAL';
|
||||||
|
const startTime = Number((Date.now() / 1000).toFixed(0));
|
||||||
|
|
||||||
|
const candleRequest: USDCKlineRequest = { symbol, period: '1m', startTime };
|
||||||
|
|
||||||
|
it('getOrderBook()', async () => {
|
||||||
|
expect(await api.getOrderBook(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getContractInfo()', async () => {
|
||||||
|
expect(await api.getContractInfo()).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSymbolTicker()', async () => {
|
||||||
|
expect(await api.getSymbolTicker(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getCandles()', async () => {
|
||||||
|
expect(await api.getCandles(candleRequest)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getMarkPrice()', async () => {
|
||||||
|
expect(await api.getMarkPrice(candleRequest)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getIndexPrice()', async () => {
|
||||||
|
expect(await api.getIndexPrice(candleRequest)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getIndexPremium()', async () => {
|
||||||
|
expect(await api.getIndexPremium(candleRequest)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getOpenInterest()', async () => {
|
||||||
|
expect(await api.getOpenInterest({ symbol, period: '1m' })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLargeOrders()', async () => {
|
||||||
|
expect(await api.getLargeOrders({ symbol })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLongShortRatio()', async () => {
|
||||||
|
expect(await api.getLongShortRatio({ symbol, period: '1m' })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLast500Trades()', async () => {
|
||||||
|
expect(await api.getLast500Trades({ category })).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLastFundingRate()', async () => {
|
||||||
|
expect(await api.getLastFundingRate(symbol)).toMatchObject(
|
||||||
|
successResponseObjectV3()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getServerTime()', async () => {
|
||||||
|
expect(await api.getServerTime()).toMatchObject(successResponseObject());
|
||||||
|
});
|
||||||
|
});
|
||||||
148
test/usdc/perpetual/ws.private.test.ts
Normal file
148
test/usdc/perpetual/ws.private.test.ts
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_ERROR_ENUM,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../../src';
|
||||||
|
import {
|
||||||
|
fullLogger,
|
||||||
|
getSilentLogger,
|
||||||
|
logAllEvents,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../../ws.util';
|
||||||
|
|
||||||
|
describe('Private USDC Perp Websocket Client', () => {
|
||||||
|
const API_KEY = process.env.API_KEY_COM;
|
||||||
|
const API_SECRET = process.env.API_SECRET_COM;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'usdcPerp',
|
||||||
|
key: API_KEY,
|
||||||
|
secret: API_SECRET,
|
||||||
|
};
|
||||||
|
|
||||||
|
const wsTopic = `user.openapi.perp.position`;
|
||||||
|
|
||||||
|
describe('with invalid credentials', () => {
|
||||||
|
it('should reject private subscribe if keys/signature are incorrect', async () => {
|
||||||
|
const badClient = new WebsocketClient(
|
||||||
|
{
|
||||||
|
...wsClientOptions,
|
||||||
|
key: 'bad',
|
||||||
|
secret: 'bad',
|
||||||
|
reconnectTimeout: 10000,
|
||||||
|
},
|
||||||
|
// fullLogger
|
||||||
|
getSilentLogger('expect401')
|
||||||
|
);
|
||||||
|
// logAllEvents(badClient);
|
||||||
|
|
||||||
|
// const wsOpenPromise = waitForSocketEvent(badClient, 'open');
|
||||||
|
const wsResponsePromise = waitForSocketEvent(badClient, 'response');
|
||||||
|
// const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
badClient.connectPrivate();
|
||||||
|
|
||||||
|
const responsePartial = {
|
||||||
|
ret_msg: WS_ERROR_ENUM.USDC_OPTION_AUTH_FAILED,
|
||||||
|
success: false,
|
||||||
|
type: 'AUTH_RESP',
|
||||||
|
};
|
||||||
|
expect(wsResponsePromise).rejects.toMatchObject(responsePartial);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all([wsResponsePromise]);
|
||||||
|
} catch (e) {
|
||||||
|
// console.error()
|
||||||
|
expect(e).toMatchObject(responsePartial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// badClient.subscribe(wsTopic);
|
||||||
|
badClient.removeAllListeners();
|
||||||
|
badClient.closeAll();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with valid API credentails', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
it('should have api credentials to test with', () => {
|
||||||
|
expect(API_KEY).toStrictEqual(expect.any(String));
|
||||||
|
expect(API_SECRET).toStrictEqual(expect.any(String));
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccess')
|
||||||
|
);
|
||||||
|
wsClient.connectPrivate();
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a private ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.usdcPerpPrivate,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
ret_msg: '0',
|
||||||
|
success: true,
|
||||||
|
type: 'AUTH_RESP',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Wait for "${wsTopic}" event exception: `, e);
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should subscribe to private "${wsTopic}" events`, async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
// expect(wsUpdatePromise).resolves.toStrictEqual('');
|
||||||
|
wsClient.subscribe(wsTopic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
data: {
|
||||||
|
failTopics: [],
|
||||||
|
successTopics: [wsTopic],
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
type: 'COMMAND_RESP',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
`Wait for "${wsTopic}" subscription response exception: `,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
expect(await wsUpdatePromise).toMatchObject({
|
||||||
|
creationTime: expect.any(Number),
|
||||||
|
data: {
|
||||||
|
baseLine: expect.any(Number),
|
||||||
|
dataType: expect.any(String),
|
||||||
|
result: expect.any(Array),
|
||||||
|
version: expect.any(Number),
|
||||||
|
},
|
||||||
|
topic: wsTopic,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
76
test/usdc/perpetual/ws.public.test.ts
Normal file
76
test/usdc/perpetual/ws.public.test.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import {
|
||||||
|
WebsocketClient,
|
||||||
|
WSClientConfigurableOptions,
|
||||||
|
WS_KEY_MAP,
|
||||||
|
} from '../../../src';
|
||||||
|
import {
|
||||||
|
logAllEvents,
|
||||||
|
getSilentLogger,
|
||||||
|
waitForSocketEvent,
|
||||||
|
WS_OPEN_EVENT_PARTIAL,
|
||||||
|
} from '../../ws.util';
|
||||||
|
|
||||||
|
describe('Public USDC Perp Websocket Client', () => {
|
||||||
|
let wsClient: WebsocketClient;
|
||||||
|
|
||||||
|
const wsClientOptions: WSClientConfigurableOptions = {
|
||||||
|
market: 'usdcPerp',
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
wsClient = new WebsocketClient(
|
||||||
|
wsClientOptions,
|
||||||
|
getSilentLogger('expectSuccessNoAuth')
|
||||||
|
);
|
||||||
|
wsClient.connectPublic();
|
||||||
|
// logAllEvents(wsClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
wsClient.closeAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should open a public ws connection', async () => {
|
||||||
|
const wsOpenPromise = waitForSocketEvent(wsClient, 'open');
|
||||||
|
|
||||||
|
expect(await wsOpenPromise).toMatchObject({
|
||||||
|
event: WS_OPEN_EVENT_PARTIAL,
|
||||||
|
wsKey: WS_KEY_MAP.usdcPerpPublic,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should subscribe to public trade events', async () => {
|
||||||
|
const wsResponsePromise = waitForSocketEvent(wsClient, 'response');
|
||||||
|
const wsUpdatePromise = waitForSocketEvent(wsClient, 'update');
|
||||||
|
|
||||||
|
const topic = 'orderBook_200.100ms.BTCPERP';
|
||||||
|
wsClient.subscribe(topic);
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsResponsePromise).toMatchObject({
|
||||||
|
success: true,
|
||||||
|
ret_msg: '',
|
||||||
|
request: {
|
||||||
|
op: 'subscribe',
|
||||||
|
args: [topic],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// sub failed
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
expect(await wsUpdatePromise).toMatchObject({
|
||||||
|
crossSeq: expect.any(String),
|
||||||
|
data: { orderBook: expect.any(Array) },
|
||||||
|
timestampE6: expect.any(String),
|
||||||
|
topic: topic,
|
||||||
|
type: 'snapshot',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// no data
|
||||||
|
expect(e).toBeFalsy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
132
test/ws.util.ts
Normal file
132
test/ws.util.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { WebsocketClient, WsClientEvent } from '../src';
|
||||||
|
|
||||||
|
export function getSilentLogger(logHint?: string) {
|
||||||
|
return {
|
||||||
|
silly: () => {},
|
||||||
|
debug: () => {},
|
||||||
|
notice: () => {},
|
||||||
|
info: () => {},
|
||||||
|
warning: () => {},
|
||||||
|
error: () => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fullLogger = {
|
||||||
|
silly: (...params) => console.log('silly', ...params),
|
||||||
|
debug: (...params) => console.log('debug', ...params),
|
||||||
|
notice: (...params) => console.log('notice', ...params),
|
||||||
|
info: (...params) => console.info('info', ...params),
|
||||||
|
warning: (...params) => console.warn('warning', ...params),
|
||||||
|
error: (...params) => console.error('error', ...params),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WS_OPEN_EVENT_PARTIAL = {
|
||||||
|
type: 'open',
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Resolves a promise if an event is seen before a timeout (defaults to 4.5 seconds) */
|
||||||
|
export function waitForSocketEvent(
|
||||||
|
wsClient: WebsocketClient,
|
||||||
|
event: WsClientEvent,
|
||||||
|
timeoutMs: number = 4.5 * 1000
|
||||||
|
) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
reject(
|
||||||
|
`Failed to receive "${event}" event before timeout. Check that these are correct: topic, api keys (if private), signature process (if private)`
|
||||||
|
);
|
||||||
|
}, timeoutMs);
|
||||||
|
|
||||||
|
let resolvedOnce = false;
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolvedOnce = true;
|
||||||
|
wsClient.removeListener(event, (e) => resolver(e));
|
||||||
|
wsClient.removeListener('error', (e) => rejector(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolver(event) {
|
||||||
|
resolve(event);
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
function rejector(event) {
|
||||||
|
if (!resolvedOnce) {
|
||||||
|
reject(event);
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
wsClient.on(event, (e) => resolver(e));
|
||||||
|
wsClient.on('error', (e) => rejector(e));
|
||||||
|
|
||||||
|
// if (event !== 'close') {
|
||||||
|
// wsClient.on('close', (event) => {
|
||||||
|
// clearTimeout(timeout);
|
||||||
|
|
||||||
|
// if (!resolvedOnce) {
|
||||||
|
// reject(event);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listenToSocketEvents(wsClient: WebsocketClient) {
|
||||||
|
const retVal: Record<
|
||||||
|
'update' | 'open' | 'response' | 'close' | 'error',
|
||||||
|
typeof jest.fn
|
||||||
|
> = {
|
||||||
|
open: jest.fn(),
|
||||||
|
response: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
close: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
wsClient.on('open', retVal.open);
|
||||||
|
wsClient.on('response', retVal.response);
|
||||||
|
wsClient.on('update', retVal.update);
|
||||||
|
wsClient.on('close', retVal.close);
|
||||||
|
wsClient.on('error', retVal.error);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...retVal,
|
||||||
|
cleanup: () => {
|
||||||
|
wsClient.removeListener('open', retVal.open);
|
||||||
|
wsClient.removeListener('response', retVal.response);
|
||||||
|
wsClient.removeListener('update', retVal.update);
|
||||||
|
wsClient.removeListener('close', retVal.close);
|
||||||
|
wsClient.removeListener('error', retVal.error);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logAllEvents(wsClient: WebsocketClient) {
|
||||||
|
wsClient.on('update', (data) => {
|
||||||
|
// console.log('wsUpdate: ', JSON.stringify(data, null, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
wsClient.on('open', (data) => {
|
||||||
|
console.log('wsOpen: ', data.wsKey);
|
||||||
|
});
|
||||||
|
wsClient.on('response', (data) => {
|
||||||
|
console.log('wsResponse ', JSON.stringify(data, null, 2));
|
||||||
|
});
|
||||||
|
wsClient.on('reconnect', ({ wsKey }) => {
|
||||||
|
console.log('wsReconnecting ', wsKey);
|
||||||
|
});
|
||||||
|
wsClient.on('reconnected', (data) => {
|
||||||
|
console.log('wsReconnected ', data?.wsKey);
|
||||||
|
});
|
||||||
|
wsClient.on('close', (data) => {
|
||||||
|
// console.log('wsClose: ', data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function promiseSleep(ms: number) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user