v3.9.9: feat() support demo trading apis

This commit is contained in:
tiagosiebler
2024-04-09 17:47:46 +01:00
parent 185f02aa10
commit cc362c469b
9 changed files with 160 additions and 22 deletions

View File

@@ -35,6 +35,7 @@ Check out my related projects:
- [binance](https://www.npmjs.com/package/binance) - [binance](https://www.npmjs.com/package/binance)
- [okx-api](https://www.npmjs.com/package/okx-api) - [okx-api](https://www.npmjs.com/package/okx-api)
- [bitget-api](https://www.npmjs.com/package/bitget-api) - [bitget-api](https://www.npmjs.com/package/bitget-api)
- [bitmart-api](https://www.npmjs.com/package/bitmart-api)
- Try my misc utilities: - Try my misc utilities:
- [orderbooks](https://www.npmjs.com/package/orderbooks) - [orderbooks](https://www.npmjs.com/package/orderbooks)
- [accountstate](https://www.npmjs.com/package/accountstate) - [accountstate](https://www.npmjs.com/package/accountstate)

115
examples/demo-trading.ts Normal file
View File

@@ -0,0 +1,115 @@
import { RestClientV5, WebsocketClient } from '../src/index';
// or
// import { RestClientV5 } from 'bybit-api';
const key = process.env.API_KEY_COM;
const secret = process.env.API_SECRET_COM;
const restClient = new RestClientV5({
key: key,
secret: secret,
parseAPIRateLimits: true,
/**
* Set this to true to enable demo trading:
*/
demoTrading: true,
});
const wsClient = new WebsocketClient({
market: 'v5',
/**
* Set this to true to enable demo trading for the private account data WS
* Topics: order,execution,position,wallet,greeks
*/
demoTrading: true,
});
function setWsClientEventListeners(
websocketClient: WebsocketClient,
accountRef: string,
): void {
websocketClient.on('update', (data) => {
console.log(new Date(), accountRef, 'data ', JSON.stringify(data));
// console.log('raw message received ', JSON.stringify(data, null, 2));
});
websocketClient.on('open', (data) => {
console.log(new Date(), accountRef, 'connection opened open:', data.wsKey);
});
websocketClient.on('response', (data) => {
console.log(
new Date(),
accountRef,
'log response: ',
JSON.stringify(data, null, 2),
);
});
websocketClient.on('reconnect', ({ wsKey }) => {
console.log(
new Date(),
accountRef,
'ws automatically reconnecting.... ',
wsKey,
);
});
websocketClient.on('reconnected', (data) => {
console.log(new Date(), accountRef, 'ws has reconnected ', data?.wsKey);
});
websocketClient.on('error', (data) => {
console.error(new Date(), accountRef, 'ws exception: ', data);
});
}
(async () => {
try {
setWsClientEventListeners(wsClient, 'demoAcc');
const balResponse1 = await restClient.getWalletBalance({
accountType: 'CONTRACT',
});
console.log('balResponse1: ', JSON.stringify(balResponse1, null, 2));
const demoFunds = await restClient.requestDemoTradingFunds();
console.log(`requested demo funds: `, demoFunds);
const balResponse2 = await restClient.getWalletBalance({
accountType: 'CONTRACT',
});
console.log('balResponse2: ', JSON.stringify(balResponse2, null, 2));
/** Simple examples for private REST API calls with bybit's V5 REST APIs */
const response = await restClient.getPositionInfo({
category: 'linear',
symbol: 'BTCUSDT',
});
console.log('response:', response);
// Trade USDT linear perps
const buyOrderResult = await restClient.submitOrder({
category: 'linear',
symbol: 'BTCUSDT',
orderType: 'Market',
qty: '1',
side: 'Buy',
});
console.log('buyOrderResult:', buyOrderResult);
const sellOrderResult = await restClient.submitOrder({
category: 'linear',
symbol: 'BTCUSDT',
orderType: 'Market',
qty: '1',
side: 'Sell',
});
console.log('sellOrderResult:', sellOrderResult);
const balResponse3 = await restClient.getWalletBalance({
accountType: 'CONTRACT',
});
console.log('balResponse2: ', JSON.stringify(balResponse3, null, 2));
} catch (e) {
console.error('request failed: ', e);
}
})();

16
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "bybit-api", "name": "bybit-api",
"version": "3.9.7", "version": "3.9.9",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "bybit-api", "name": "bybit-api",
"version": "3.9.7", "version": "3.9.9",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"axios": "^1.6.6", "axios": "^1.6.6",
@@ -3230,9 +3230,9 @@
"dev": true "dev": true
}, },
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.15.5", "version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@@ -9303,9 +9303,9 @@
"dev": true "dev": true
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.15.5", "version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
}, },
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "bybit-api", "name": "bybit-api",
"version": "3.9.8", "version": "3.9.9",
"description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",

View File

@@ -166,6 +166,10 @@ export class RestClientV5 extends BaseRestClient {
return this.get('/v3/public/time'); return this.get('/v3/public/time');
} }
requestDemoTradingFunds(): Promise<{}> {
return this.postPrivate('/v5/account/demo-apply/money');
}
/** /**
* *
****** Market APIs ****** Market APIs

View File

@@ -88,6 +88,13 @@ export interface WSClientConfigurableOptions {
secret?: string; secret?: string;
testnet?: boolean; testnet?: boolean;
/**
* Set to `true` to connect to Bybit's V5 demo trading: https://bybit-exchange.github.io/docs/v5/demo
*
* Only the "V5" "market" is supported here.
*/
demoTrading?: boolean;
/** /**
* The API group this client should connect to. * The API group this client should connect to.
* *
@@ -109,7 +116,6 @@ export interface WSClientConfigurableOptions {
} }
export interface WebsocketClientOptions extends WSClientConfigurableOptions { export interface WebsocketClientOptions extends WSClientConfigurableOptions {
testnet?: boolean;
market: APIMarket; market: APIMarket;
pongTimeout: number; pongTimeout: number;
pingInterval: number; pingInterval: number;

View File

@@ -13,6 +13,11 @@ export interface RestClientOptions {
/** Set to `true` to connect to testnet. Uses the live environment by default. */ /** Set to `true` to connect to testnet. Uses the live environment by default. */
testnet?: boolean; testnet?: boolean;
/**
* Set to `true` to use Bybit's V5 demo trading: https://bybit-exchange.github.io/docs/v5/demo
*/
demoTrading?: 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;
@@ -92,15 +97,20 @@ export function serializeParams(
export function getRestBaseUrl( export function getRestBaseUrl(
useTestnet: boolean, useTestnet: boolean,
restInverseOptions: RestClientOptions, restClientOptions: RestClientOptions,
): string { ): string {
const exchangeBaseUrls = { const exchangeBaseUrls = {
livenet: 'https://api.bybit.com', livenet: 'https://api.bybit.com',
testnet: 'https://api-testnet.bybit.com', testnet: 'https://api-testnet.bybit.com',
demoLivenet: 'https://api-demo.bybit.com',
}; };
if (restInverseOptions.baseUrl) { if (restClientOptions.baseUrl) {
return restInverseOptions.baseUrl; return restClientOptions.baseUrl;
}
if (restClientOptions.demoTrading) {
return exchangeBaseUrls.demoLivenet;
} }
if (useTestnet) { if (useTestnet) {

View File

@@ -1,6 +1,6 @@
import WebSocket from 'isomorphic-ws'; import WebSocket from 'isomorphic-ws';
import { APIMarket, CategoryV5, WsKey } from '../types'; import { APIMarket, CategoryV5, WebsocketClientOptions, WsKey } from '../types';
import { DefaultLogger } from './logger'; import { DefaultLogger } from './logger';
interface NetworkMapV3 { interface NetworkMapV3 {
@@ -398,14 +398,21 @@ export function getWsKeyForTopic(
export function getWsUrl( export function getWsUrl(
wsKey: WsKey, wsKey: WsKey,
wsUrl: string | undefined, wsClientOptions: WebsocketClientOptions,
isTestnet: boolean,
logger: typeof DefaultLogger, logger: typeof DefaultLogger,
): string { ): string {
const wsUrl = wsClientOptions.wsUrl;
if (wsUrl) { if (wsUrl) {
return wsUrl; return wsUrl;
} }
// https://bybit-exchange.github.io/docs/v5/demo
const isDemoTrading = wsClientOptions.demoTrading;
if (isDemoTrading) {
return 'wss://stream-demo.bybit.com/v5/private';
}
const isTestnet = wsClientOptions.testnet;
const networkKey = isTestnet ? 'testnet' : 'livenet'; const networkKey = isTestnet ? 'testnet' : 'livenet';
switch (wsKey) { switch (wsKey) {

View File

@@ -623,12 +623,7 @@ export class WebsocketClient extends EventEmitter {
} }
const authParams = await this.getAuthParams(wsKey); const authParams = await this.getAuthParams(wsKey);
const url = getWsUrl( const url = getWsUrl(wsKey, this.options, this.logger);
wsKey,
this.options.wsUrl,
this.isTestnet(),
this.logger,
);
const ws = this.connectToWsUrl(url + authParams, wsKey); const ws = this.connectToWsUrl(url + authParams, wsKey);
return this.wsStore.setWs(wsKey, ws); return this.wsStore.setWs(wsKey, ws);