@pear-protocol/exchanges-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +187 -0
  2. package/dist/account/account-manager.d.ts +34 -0
  3. package/dist/account/account-manager.js +140 -0
  4. package/dist/account/exchange-account.d.ts +36 -0
  5. package/dist/account/exchange-account.js +185 -0
  6. package/dist/account/exchanges/base.d.ts +49 -0
  7. package/dist/account/exchanges/base.js +63 -0
  8. package/dist/account/exchanges/binance/const.d.ts +10 -0
  9. package/dist/account/exchanges/binance/const.js +10 -0
  10. package/dist/account/exchanges/binance/mapper.d.ts +9 -0
  11. package/dist/account/exchanges/binance/mapper.js +96 -0
  12. package/dist/account/exchanges/binance/orchestrator.d.ts +35 -0
  13. package/dist/account/exchanges/binance/orchestrator.js +232 -0
  14. package/dist/account/exchanges/binance/rest.d.ts +13 -0
  15. package/dist/account/exchanges/binance/rest.js +81 -0
  16. package/dist/account/exchanges/binance/types.d.ts +77 -0
  17. package/dist/account/exchanges/binance/types.js +1 -0
  18. package/dist/account/exchanges/binance/ws.d.ts +21 -0
  19. package/dist/account/exchanges/binance/ws.js +85 -0
  20. package/dist/account/exchanges/bybit/const.d.ts +7 -0
  21. package/dist/account/exchanges/bybit/const.js +7 -0
  22. package/dist/account/exchanges/bybit/mapper.d.ts +11 -0
  23. package/dist/account/exchanges/bybit/mapper.js +106 -0
  24. package/dist/account/exchanges/bybit/orchestrator.d.ts +23 -0
  25. package/dist/account/exchanges/bybit/orchestrator.js +159 -0
  26. package/dist/account/exchanges/bybit/rest.d.ts +11 -0
  27. package/dist/account/exchanges/bybit/rest.js +110 -0
  28. package/dist/account/exchanges/bybit/types.d.ts +59 -0
  29. package/dist/account/exchanges/bybit/types.js +1 -0
  30. package/dist/account/exchanges/bybit/ws.d.ts +18 -0
  31. package/dist/account/exchanges/bybit/ws.js +74 -0
  32. package/dist/account/exchanges/index.d.ts +8 -0
  33. package/dist/account/exchanges/index.js +16 -0
  34. package/dist/account/index.d.ts +8 -0
  35. package/dist/account/index.js +4 -0
  36. package/dist/account/types.d.ts +81 -0
  37. package/dist/account/types.js +1 -0
  38. package/dist/account/utils.d.ts +7 -0
  39. package/dist/account/utils.js +15 -0
  40. package/dist/credentials.d.ts +28 -0
  41. package/dist/credentials.js +46 -0
  42. package/dist/index.d.ts +22 -0
  43. package/dist/index.js +21 -0
  44. package/dist/utils/hmac.d.ts +4 -0
  45. package/dist/utils/hmac.js +17 -0
  46. package/dist/utils/ws.d.ts +7 -0
  47. package/dist/utils/ws.js +25 -0
  48. package/package.json +36 -0
@@ -0,0 +1,7 @@
1
+ declare const BASE_URL = "https://api.bybit.com";
2
+ declare const BASE_URL_DEMO = "https://api-demo.bybit.com";
3
+ declare const WS_URL = "wss://stream.bybit.com/v5/private";
4
+ declare const WS_URL_DEMO = "wss://stream-demo.bybit.com/v5/private";
5
+ declare const DEFAULT_PING_INTERVAL_MS = 20000;
6
+
7
+ export { BASE_URL, BASE_URL_DEMO, DEFAULT_PING_INTERVAL_MS, WS_URL, WS_URL_DEMO };
@@ -0,0 +1,7 @@
1
+ const BASE_URL = "https://api.bybit.com";
2
+ const BASE_URL_DEMO = "https://api-demo.bybit.com";
3
+ const WS_URL = "wss://stream.bybit.com/v5/private";
4
+ const WS_URL_DEMO = "wss://stream-demo.bybit.com/v5/private";
5
+ const DEFAULT_PING_INTERVAL_MS = 2e4;
6
+
7
+ export { BASE_URL, BASE_URL_DEMO, DEFAULT_PING_INTERVAL_MS, WS_URL, WS_URL_DEMO };
@@ -0,0 +1,11 @@
1
+ import { AccountBalance, AccountPosition } from '../../types.js';
2
+ import { BybitRawWallet, BybitRawCoin, BybitRawPosition } from './types.js';
3
+ import '@pear-protocol/types';
4
+
5
+ declare function buildBalance(raw: BybitRawWallet, coins: Map<string, BybitRawCoin>, marginMode: string, collateralRatios: Map<string, string>): AccountBalance;
6
+ declare function mergeRawCoins(existing: Map<string, BybitRawCoin>, incoming: BybitRawCoin[]): Map<string, BybitRawCoin>;
7
+ declare function mapSinglePosition(p: BybitRawPosition, size: string): AccountPosition;
8
+ declare function mapPositions(raw: BybitRawPosition[]): Map<string, AccountPosition>;
9
+ declare function mapPositionUpdates(raw: BybitRawPosition[]): Map<string, AccountPosition>;
10
+
11
+ export { buildBalance, mapPositionUpdates, mapPositions, mapSinglePosition, mergeRawCoins };
@@ -0,0 +1,106 @@
1
+ import '../../types';
2
+ import { positionKey } from '../../utils';
3
+
4
+ function buildBalance(raw, coins, marginMode, collateralRatios) {
5
+ const free = {};
6
+ const used = {};
7
+ const total = {};
8
+ const assets = [];
9
+ const isPortfolio = marginMode === "portfolio";
10
+ for (const [, c] of coins) {
11
+ const equity = c.equity ?? "0";
12
+ const positionInitialMargin = c.totalPositionIM === "" ? "0" : c.totalPositionIM ?? "0";
13
+ const orderInitialMargin = c.totalOrderIM === "" ? "0" : c.totalOrderIM ?? "0";
14
+ const positionMaintenanceMargin = c.totalPositionMM === "" ? "0" : c.totalPositionMM ?? "0";
15
+ const locked = c.locked ?? "0";
16
+ const equityNum = parseFloat(equity);
17
+ const positionInitialMarginNum = parseFloat(positionInitialMargin);
18
+ const orderInitialMarginNum = parseFloat(orderInitialMargin);
19
+ const lockedNum = parseFloat(locked);
20
+ if (equityNum === 0 && parseFloat(c.walletBalance ?? "0") === 0) continue;
21
+ let coinFree;
22
+ let coinUsed;
23
+ if (isPortfolio) {
24
+ coinFree = (equityNum - lockedNum).toString();
25
+ coinUsed = "0";
26
+ } else {
27
+ coinFree = (equityNum - positionInitialMarginNum - orderInitialMarginNum - lockedNum).toString();
28
+ coinUsed = (positionInitialMarginNum + orderInitialMarginNum).toString();
29
+ }
30
+ free[c.coin] = coinFree;
31
+ used[c.coin] = coinUsed;
32
+ total[c.coin] = equity;
33
+ assets.push({
34
+ asset: c.coin,
35
+ free: coinFree,
36
+ used: coinUsed,
37
+ total: equity,
38
+ walletBalance: c.walletBalance ?? "0",
39
+ unrealizedPnl: c.unrealisedPnl ?? "0",
40
+ usdValue: c.usdValue ?? "0",
41
+ initialMargin: isPortfolio ? "0" : (positionInitialMarginNum + orderInitialMarginNum).toString(),
42
+ maintenanceMargin: positionMaintenanceMargin,
43
+ isCollateral: (c.marginCollateral ?? false) && (c.collateralSwitch ?? false),
44
+ collateralRatio: collateralRatios.get(c.coin) ?? "1.0000",
45
+ borrowedAmount: c.borrowAmount ?? "0"
46
+ });
47
+ }
48
+ return {
49
+ exchange: "bybit",
50
+ accountType: "UNIFIED",
51
+ marginMode,
52
+ free,
53
+ used,
54
+ total,
55
+ availableToTrade: raw.totalAvailableBalance ?? "0",
56
+ totalEquity: raw.totalEquity ?? "0",
57
+ walletBalance: raw.totalWalletBalance ?? "0",
58
+ unrealizedPnl: raw.totalPerpUPL ?? "0",
59
+ initialMarginUsed: raw.totalInitialMargin ?? "0",
60
+ maintenanceMargin: raw.totalMaintenanceMargin ?? "0",
61
+ marginRatio: raw.accountMMRate ?? "0.0000",
62
+ assets,
63
+ timestamp: Date.now()
64
+ };
65
+ }
66
+ function mergeRawCoins(existing, incoming) {
67
+ const merged = new Map(existing);
68
+ for (const c of incoming) {
69
+ merged.set(c.coin, c);
70
+ }
71
+ return merged;
72
+ }
73
+ function mapSinglePosition(p, size) {
74
+ const side = p.side === "Buy" ? "long" : p.side === "Sell" ? "short" : "both";
75
+ return {
76
+ symbol: p.symbol ?? "",
77
+ side,
78
+ size: size === "0" || size === "" ? "0" : side === "short" ? `-${size}` : size,
79
+ entryPrice: p.avgPrice ?? "0",
80
+ unrealizedPnl: p.unrealisedPnl ?? "0",
81
+ leverage: p.leverage ?? "0",
82
+ marginType: p.tradeMode === "1" ? "isolated" : "cross",
83
+ liquidationPrice: p.liqPrice || null
84
+ };
85
+ }
86
+ function mapPositions(raw) {
87
+ const positions = /* @__PURE__ */ new Map();
88
+ for (const p of raw) {
89
+ const size = p.size ?? "0";
90
+ if (size === "0" || size === "") continue;
91
+ const mapped = mapSinglePosition(p, size);
92
+ positions.set(positionKey(mapped.symbol, mapped.side), mapped);
93
+ }
94
+ return positions;
95
+ }
96
+ function mapPositionUpdates(raw) {
97
+ const positions = /* @__PURE__ */ new Map();
98
+ for (const p of raw) {
99
+ const size = p.size ?? "0";
100
+ const mapped = mapSinglePosition(p, size);
101
+ positions.set(positionKey(mapped.symbol, mapped.side), mapped);
102
+ }
103
+ return positions;
104
+ }
105
+
106
+ export { buildBalance, mapPositionUpdates, mapPositions, mapSinglePosition, mergeRawCoins };
@@ -0,0 +1,23 @@
1
+ import { SupportedCredentials } from '../../types.js';
2
+ import { BaseOrchestrator } from '../base.js';
3
+ import '@pear-protocol/types';
4
+
5
+ declare class BybitOrchestrator extends BaseOrchestrator {
6
+ private ws;
7
+ private rawCoins;
8
+ private lastWallet;
9
+ private trackedAssetInfos;
10
+ private marginMode;
11
+ private collateralRatios;
12
+ start(credentials: SupportedCredentials): Promise<void>;
13
+ stop(): void;
14
+ get isConnected(): boolean;
15
+ onAssetTracked(coin: string): void;
16
+ onAssetUntracked(coin: string): void;
17
+ setLeverage(symbol: string, leverage: string): Promise<void>;
18
+ private stopAll;
19
+ private handleMessage;
20
+ private handleWalletUpdate;
21
+ }
22
+
23
+ export { BybitOrchestrator };
@@ -0,0 +1,159 @@
1
+ import { BaseOrchestrator } from '../base';
2
+ import { mergeRawCoins, buildBalance, mapPositions, mapPositionUpdates } from './mapper';
3
+ import { fetchWalletBalance, fetchPositions, fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, setLeverage } from './rest';
4
+ import { BybitWs } from './ws';
5
+
6
+ class BybitOrchestrator extends BaseOrchestrator {
7
+ ws = null;
8
+ rawCoins = /* @__PURE__ */ new Map();
9
+ lastWallet = null;
10
+ trackedAssetInfos = /* @__PURE__ */ new Map();
11
+ marginMode = "cross";
12
+ collateralRatios = /* @__PURE__ */ new Map();
13
+ async start(credentials) {
14
+ const gen = ++this.startGeneration;
15
+ this.manualClose = false;
16
+ this.credentials = credentials;
17
+ const creds = credentials;
18
+ this.stopAll();
19
+ try {
20
+ const [wallet, positions, accountInfo, ratios] = await Promise.all([
21
+ fetchWalletBalance(creds, this.demo),
22
+ fetchPositions(creds, this.demo),
23
+ fetchAccountInfo(creds, this.demo),
24
+ fetchCollateralRatios(creds, this.demo)
25
+ ]);
26
+ if (gen !== this.startGeneration) return;
27
+ const MARGIN_MODE_MAP = {
28
+ ISOLATED_MARGIN: "isolated",
29
+ PORTFOLIO_MARGIN: "portfolio",
30
+ REGULAR_MARGIN: "cross"
31
+ };
32
+ const rawMode = accountInfo?.marginMode ?? "REGULAR_MARGIN";
33
+ this.marginMode = MARGIN_MODE_MAP[rawMode] ?? "cross";
34
+ this.collateralRatios = ratios;
35
+ if (wallet) {
36
+ this.lastWallet = wallet;
37
+ this.rawCoins = mergeRawCoins(/* @__PURE__ */ new Map(), wallet.coin ?? []);
38
+ this.onStateUpdate({
39
+ balance: buildBalance(wallet, this.rawCoins, this.marginMode, this.collateralRatios)
40
+ });
41
+ }
42
+ this.onStateUpdate({ positions: mapPositions(positions) });
43
+ this.ws = new BybitWs(
44
+ {
45
+ onMessage: (topic, data) => this.handleMessage(topic, data),
46
+ onClose: () => {
47
+ if (!this.manualClose) {
48
+ this.stopAll();
49
+ this.scheduleReconnect();
50
+ }
51
+ }
52
+ },
53
+ this.demo
54
+ );
55
+ await this.ws.connect(creds);
56
+ this.reconnectAttempts = 0;
57
+ } catch (err) {
58
+ if (gen !== this.startGeneration) return;
59
+ this.stopAll();
60
+ this.onError(err instanceof Error ? err : new Error(String(err)));
61
+ this.scheduleReconnect();
62
+ }
63
+ }
64
+ stop() {
65
+ this.manualClose = true;
66
+ this.stopAll();
67
+ this.rawCoins.clear();
68
+ this.lastWallet = null;
69
+ this.trackedAssetInfos.clear();
70
+ this.collateralRatios.clear();
71
+ this.marginMode = "cross";
72
+ this.credentials = null;
73
+ }
74
+ get isConnected() {
75
+ return this.ws?.isConnected ?? false;
76
+ }
77
+ onAssetTracked(coin) {
78
+ const creds = this.credentials;
79
+ if (!creds) return;
80
+ fetchPositionLeverage(creds, coin, this.demo).then((pos) => {
81
+ const leverage = pos?.leverage || "1";
82
+ const marginType = pos?.tradeMode === "1" ? "isolated" : "cross";
83
+ const info = {
84
+ coin,
85
+ leverage,
86
+ marginType,
87
+ availableToTrade: null,
88
+ maxTradeSzs: null,
89
+ collateralToken: null
90
+ };
91
+ this.trackedAssetInfos.set(coin, info);
92
+ const trackedAssets = /* @__PURE__ */ new Map();
93
+ trackedAssets.set(coin, info);
94
+ this.onStateUpdate({ trackedAssets });
95
+ }).catch((err) => {
96
+ this.onError(err instanceof Error ? err : new Error(String(err)));
97
+ });
98
+ }
99
+ onAssetUntracked(coin) {
100
+ this.trackedAssetInfos.delete(coin);
101
+ this.onStateUpdate({ trackedAssets: new Map(this.trackedAssetInfos) });
102
+ }
103
+ async setLeverage(symbol, leverage) {
104
+ const creds = this.credentials;
105
+ if (!creds) throw new Error("Not connected");
106
+ await setLeverage(creds, symbol, leverage, this.demo);
107
+ }
108
+ stopAll() {
109
+ this.clearReconnectTimer();
110
+ this.ws?.close();
111
+ this.ws = null;
112
+ }
113
+ handleMessage(topic, data) {
114
+ if (topic === "wallet") {
115
+ const wallet = data[0];
116
+ if (wallet) this.handleWalletUpdate(wallet);
117
+ } else if (topic === "position") {
118
+ const rawPositions = data;
119
+ const positions = mapPositionUpdates(rawPositions);
120
+ if (positions.size > 0) {
121
+ const trackedAssets = /* @__PURE__ */ new Map();
122
+ for (const pos of rawPositions) {
123
+ const existing = this.trackedAssetInfos.get(pos.symbol);
124
+ if (existing) {
125
+ const leverage = pos.leverage || existing.leverage;
126
+ const marginType = pos.tradeMode === "1" ? "isolated" : "cross";
127
+ const updated = { ...existing, leverage, marginType };
128
+ this.trackedAssetInfos.set(pos.symbol, updated);
129
+ trackedAssets.set(pos.symbol, updated);
130
+ }
131
+ }
132
+ this.onStateUpdate({
133
+ positions,
134
+ ...trackedAssets.size > 0 ? { trackedAssets } : {}
135
+ });
136
+ }
137
+ }
138
+ }
139
+ handleWalletUpdate(raw) {
140
+ this.rawCoins = mergeRawCoins(this.rawCoins, raw.coin ?? []);
141
+ const merged = this.lastWallet ? {
142
+ totalEquity: raw.totalEquity || this.lastWallet.totalEquity,
143
+ totalWalletBalance: raw.totalWalletBalance || this.lastWallet.totalWalletBalance,
144
+ totalMarginBalance: raw.totalMarginBalance || this.lastWallet.totalMarginBalance,
145
+ totalAvailableBalance: raw.totalAvailableBalance || this.lastWallet.totalAvailableBalance,
146
+ totalPerpUPL: raw.totalPerpUPL || this.lastWallet.totalPerpUPL,
147
+ totalInitialMargin: raw.totalInitialMargin || this.lastWallet.totalInitialMargin,
148
+ totalMaintenanceMargin: raw.totalMaintenanceMargin || this.lastWallet.totalMaintenanceMargin,
149
+ accountMMRate: raw.accountMMRate || this.lastWallet.accountMMRate,
150
+ coin: raw.coin ?? this.lastWallet.coin
151
+ } : raw;
152
+ this.lastWallet = merged;
153
+ this.onStateUpdate({
154
+ balance: buildBalance(merged, this.rawCoins, this.marginMode, this.collateralRatios)
155
+ });
156
+ }
157
+ }
158
+
159
+ export { BybitOrchestrator };
@@ -0,0 +1,11 @@
1
+ import { BybitCredentialsDTO } from '@pear-protocol/types';
2
+ import { BybitRawAccountInfo, BybitRawPosition, BybitRawWallet } from './types.js';
3
+
4
+ declare function fetchWalletBalance(creds: BybitCredentialsDTO, demo: boolean): Promise<BybitRawWallet | undefined>;
5
+ declare function fetchPositions(creds: BybitCredentialsDTO, demo: boolean): Promise<BybitRawPosition[]>;
6
+ declare function fetchPositionLeverage(creds: BybitCredentialsDTO, symbol: string, demo: boolean): Promise<BybitRawPosition | undefined>;
7
+ declare function setLeverage(creds: BybitCredentialsDTO, symbol: string, leverage: string, demo: boolean): Promise<void>;
8
+ declare function fetchAccountInfo(creds: BybitCredentialsDTO, demo: boolean): Promise<BybitRawAccountInfo>;
9
+ declare function fetchCollateralRatios(creds: BybitCredentialsDTO, demo: boolean): Promise<Map<string, string>>;
10
+
11
+ export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance, setLeverage };
@@ -0,0 +1,110 @@
1
+ import { hmacSha256Hex } from '../../../utils/hmac';
2
+ import { BASE_URL_DEMO, BASE_URL } from './const';
3
+
4
+ async function signedPost(creds, path, body, demo) {
5
+ const baseUrl = demo ? BASE_URL_DEMO : BASE_URL;
6
+ const timestamp = Date.now().toString();
7
+ const recvWindow = "5000";
8
+ const bodyStr = JSON.stringify(body);
9
+ const signPayload = `${timestamp}${creds.api_key}${recvWindow}${bodyStr}`;
10
+ const signature = await hmacSha256Hex(creds.api_secret, signPayload);
11
+ const response = await fetch(`${baseUrl}${path}`, {
12
+ method: "POST",
13
+ headers: {
14
+ "X-BAPI-API-KEY": creds.api_key,
15
+ "X-BAPI-SIGN": signature,
16
+ "X-BAPI-TIMESTAMP": timestamp,
17
+ "X-BAPI-RECV-WINDOW": recvWindow,
18
+ "Content-Type": "application/json"
19
+ },
20
+ body: bodyStr
21
+ });
22
+ if (!response.ok) {
23
+ throw new Error(`Bybit API error: ${response.status} ${response.statusText}`);
24
+ }
25
+ const result = await response.json();
26
+ if (result.retCode !== 0) {
27
+ throw new Error(`Bybit API error: ${result.retCode} ${result.retMsg}`);
28
+ }
29
+ return result.result;
30
+ }
31
+ async function signedGet(creds, path, params, demo) {
32
+ const baseUrl = demo ? BASE_URL_DEMO : BASE_URL;
33
+ const timestamp = Date.now().toString();
34
+ const recvWindow = "5000";
35
+ const queryString = new URLSearchParams(params).toString();
36
+ const signPayload = `${timestamp}${creds.api_key}${recvWindow}${queryString}`;
37
+ const signature = await hmacSha256Hex(creds.api_secret, signPayload);
38
+ const response = await fetch(`${baseUrl}${path}?${queryString}`, {
39
+ headers: {
40
+ "X-BAPI-API-KEY": creds.api_key,
41
+ "X-BAPI-SIGN": signature,
42
+ "X-BAPI-TIMESTAMP": timestamp,
43
+ "X-BAPI-RECV-WINDOW": recvWindow
44
+ }
45
+ });
46
+ if (!response.ok) {
47
+ throw new Error(`Bybit API error: ${response.status} ${response.statusText}`);
48
+ }
49
+ const body = await response.json();
50
+ if (body.retCode !== 0) {
51
+ throw new Error(`Bybit API error: ${body.retCode} ${body.retMsg}`);
52
+ }
53
+ return body.result;
54
+ }
55
+ async function fetchWalletBalance(creds, demo) {
56
+ const result = await signedGet(
57
+ creds,
58
+ "/v5/account/wallet-balance",
59
+ { accountType: "UNIFIED" },
60
+ demo
61
+ );
62
+ return result.list[0];
63
+ }
64
+ async function fetchPositions(creds, demo) {
65
+ const result = await signedGet(
66
+ creds,
67
+ "/v5/position/list",
68
+ { category: "linear", settleCoin: "USDT" },
69
+ demo
70
+ );
71
+ return result.list;
72
+ }
73
+ async function fetchPositionLeverage(creds, symbol, demo) {
74
+ const result = await signedGet(
75
+ creds,
76
+ "/v5/position/list",
77
+ { category: "linear", symbol },
78
+ demo
79
+ );
80
+ return result.list[0];
81
+ }
82
+ async function setLeverage(creds, symbol, leverage, demo) {
83
+ await signedPost(
84
+ creds,
85
+ "/v5/position/set-leverage",
86
+ { category: "linear", symbol, buyLeverage: leverage, sellLeverage: leverage },
87
+ demo
88
+ );
89
+ }
90
+ async function fetchAccountInfo(creds, demo) {
91
+ return signedGet(creds, "/v5/account/account-info", {}, demo);
92
+ }
93
+ async function fetchCollateralRatios(creds, demo) {
94
+ const result = await signedGet(
95
+ creds,
96
+ "/v5/spot-margin-uta/tier-collateral-ratio",
97
+ {},
98
+ demo
99
+ );
100
+ const ratios = /* @__PURE__ */ new Map();
101
+ for (const entry of result ?? []) {
102
+ const first = entry.tiers?.[0];
103
+ if (first) {
104
+ ratios.set(entry.currency, first.rate);
105
+ }
106
+ }
107
+ return ratios;
108
+ }
109
+
110
+ export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance, setLeverage };
@@ -0,0 +1,59 @@
1
+ interface BybitRawCoin {
2
+ coin: string;
3
+ equity: string;
4
+ usdValue: string;
5
+ walletBalance: string;
6
+ unrealisedPnl: string;
7
+ totalPositionIM: string;
8
+ totalPositionMM: string;
9
+ totalOrderIM: string;
10
+ locked: string;
11
+ borrowAmount: string;
12
+ marginCollateral: boolean;
13
+ collateralSwitch: boolean;
14
+ }
15
+ interface BybitRawWallet {
16
+ totalEquity: string;
17
+ totalWalletBalance: string;
18
+ totalMarginBalance: string;
19
+ totalAvailableBalance: string;
20
+ totalPerpUPL: string;
21
+ totalInitialMargin: string;
22
+ totalMaintenanceMargin: string;
23
+ accountMMRate: string;
24
+ coin: BybitRawCoin[];
25
+ }
26
+ interface BybitRawAccountInfo {
27
+ marginMode: string;
28
+ updatedTime: string;
29
+ }
30
+ interface BybitRawCollateralTier {
31
+ collateralCoin: string;
32
+ rate: string;
33
+ collateralAmount: string;
34
+ }
35
+ interface BybitRawCollateralRatioEntry {
36
+ currency: string;
37
+ tiers: BybitRawCollateralTier[];
38
+ }
39
+ interface BybitRawPosition {
40
+ symbol: string;
41
+ side: string;
42
+ size: string;
43
+ avgPrice: string;
44
+ unrealisedPnl: string;
45
+ leverage: string;
46
+ tradeMode: string;
47
+ liqPrice: string;
48
+ }
49
+ interface BybitResponse<T> {
50
+ retCode: number;
51
+ retMsg: string;
52
+ result: T;
53
+ }
54
+ interface BybitWsHandlers {
55
+ onMessage: (topic: string, data: unknown[]) => void;
56
+ onClose: () => void;
57
+ }
58
+
59
+ export type { BybitRawAccountInfo, BybitRawCoin, BybitRawCollateralRatioEntry, BybitRawCollateralTier, BybitRawPosition, BybitRawWallet, BybitResponse, BybitWsHandlers };
@@ -0,0 +1,18 @@
1
+ import { BybitCredentialsDTO } from '@pear-protocol/types';
2
+ import { BybitWsHandlers } from './types.js';
3
+
4
+ declare class BybitWs {
5
+ private ws;
6
+ private pingTimer;
7
+ private handlers;
8
+ private demo;
9
+ private readonly pingIntervalMs;
10
+ constructor(handlers: BybitWsHandlers, demo: boolean, pingIntervalMs?: number);
11
+ connect(creds: BybitCredentialsDTO): Promise<void>;
12
+ close(): void;
13
+ get isConnected(): boolean;
14
+ private clearPing;
15
+ private authenticate;
16
+ }
17
+
18
+ export { BybitWs };
@@ -0,0 +1,74 @@
1
+ import { hmacSha256Hex } from '../../../utils/hmac';
2
+ import { connectWs, sendWs, clearIntervalTimer } from '../../../utils/ws';
3
+ import { DEFAULT_PING_INTERVAL_MS, WS_URL_DEMO, WS_URL } from './const';
4
+
5
+ class BybitWs {
6
+ ws = null;
7
+ pingTimer = null;
8
+ handlers;
9
+ demo;
10
+ pingIntervalMs;
11
+ constructor(handlers, demo, pingIntervalMs = DEFAULT_PING_INTERVAL_MS) {
12
+ this.handlers = handlers;
13
+ this.demo = demo;
14
+ this.pingIntervalMs = pingIntervalMs;
15
+ }
16
+ async connect(creds) {
17
+ this.ws = await connectWs(this.demo ? WS_URL_DEMO : WS_URL);
18
+ this.ws.onclose = () => {
19
+ this.clearPing();
20
+ this.handlers.onClose();
21
+ };
22
+ this.ws.onerror = () => {
23
+ };
24
+ this.ws.addEventListener("message", (event) => {
25
+ try {
26
+ const msg = JSON.parse(event.data);
27
+ if (msg.topic && msg.data) {
28
+ this.handlers.onMessage(msg.topic, msg.data);
29
+ }
30
+ } catch {
31
+ }
32
+ });
33
+ await this.authenticate(creds);
34
+ sendWs(this.ws, { op: "subscribe", args: ["wallet", "position", "execution"] });
35
+ this.pingTimer = setInterval(() => sendWs(this.ws, { op: "ping" }), this.pingIntervalMs);
36
+ }
37
+ close() {
38
+ this.clearPing();
39
+ if (this.ws) {
40
+ this.ws.onclose = null;
41
+ this.ws.close();
42
+ this.ws = null;
43
+ }
44
+ }
45
+ get isConnected() {
46
+ return this.ws?.readyState === WebSocket.OPEN;
47
+ }
48
+ clearPing() {
49
+ this.pingTimer = clearIntervalTimer(this.pingTimer);
50
+ }
51
+ async authenticate(creds) {
52
+ const expires = Date.now() + 1e4;
53
+ const signature = await hmacSha256Hex(creds.api_secret, `GET/realtime${expires}`);
54
+ return new Promise((resolve, reject) => {
55
+ const timeout = setTimeout(() => reject(new Error("Bybit auth timeout")), 1e4);
56
+ const handler = (event) => {
57
+ try {
58
+ const msg = JSON.parse(event.data);
59
+ if (msg.op === "auth") {
60
+ clearTimeout(timeout);
61
+ this.ws?.removeEventListener("message", handler);
62
+ if (msg.success) resolve();
63
+ else reject(new Error(`Bybit auth failed: ${msg.ret_msg}`));
64
+ }
65
+ } catch {
66
+ }
67
+ };
68
+ this.ws?.addEventListener("message", handler);
69
+ sendWs(this.ws, { op: "auth", args: [creds.api_key, expires, signature] });
70
+ });
71
+ }
72
+ }
73
+
74
+ export { BybitWs };
@@ -0,0 +1,8 @@
1
+ import { Connector } from '@pear-protocol/types';
2
+ import { OrchestratorConfig, BaseOrchestrator } from './base.js';
3
+ export { Orchestrator } from './base.js';
4
+ import '../types.js';
5
+
6
+ declare function createOrchestrator(exchange: Connector, config: OrchestratorConfig): BaseOrchestrator;
7
+
8
+ export { OrchestratorConfig, createOrchestrator };
@@ -0,0 +1,16 @@
1
+ import './base';
2
+ import { BinanceOrchestrator } from './binance/orchestrator';
3
+ import { BybitOrchestrator } from './bybit/orchestrator';
4
+
5
+ function createOrchestrator(exchange, config) {
6
+ switch (exchange) {
7
+ case "binanceusdm":
8
+ return new BinanceOrchestrator(config);
9
+ case "bybit":
10
+ return new BybitOrchestrator(config);
11
+ default:
12
+ throw new Error(`Unsupported exchange: ${exchange}`);
13
+ }
14
+ }
15
+
16
+ export { createOrchestrator };
@@ -0,0 +1,8 @@
1
+ export { AccountManager } from './account-manager.js';
2
+ export { ExchangeAccount } from './exchange-account.js';
3
+ export { createOrchestrator } from './exchanges/index.js';
4
+ export { AccountBalance, AccountConfig, AccountPosition, AssetBalance, BalanceCallback, ExchangeState, MarginMode, PositionsCallback, StateUpdate, SupportedCredentials, TrackedAssetCallback, TrackedAssetInfo, Tracker } from './types.js';
5
+ export { Orchestrator, OrchestratorConfig } from './exchanges/base.js';
6
+ import '@pear-protocol/types';
7
+ import '../credentials.js';
8
+ import '@pear-protocol/core-sdk';
@@ -0,0 +1,4 @@
1
+ export * from './account-manager';
2
+ export * from './exchange-account';
3
+ export * from './exchanges';
4
+ export * from './types';