Merge pull request #334 from tiagosiebler/demotrr
v3.10.0: feat() add v5 demo trading
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
125
examples/demo-trading.ts
Normal file
125
examples/demo-trading.ts
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This example demonstrates how to use Bybit's demo trading functionality, both for REST and WS.
|
||||||
|
*
|
||||||
|
* Refer to the API docs for more information: https://bybit-exchange.github.io/docs/v5/demo
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
16
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "3.9.7",
|
"version": "3.10.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "3.9.7",
|
"version": "3.10.0",
|
||||||
"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",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bybit-api",
|
"name": "bybit-api",
|
||||||
"version": "3.9.8",
|
"version": "3.10.0",
|
||||||
"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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user