@pear-protocol/exchanges-sdk 0.1.3 → 0.1.5
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.
- package/dist/client/binance/rest.d.ts +4 -2
- package/dist/client/binance/rest.js +75 -1
- package/dist/client/binance/types.d.ts +36 -1
- package/dist/client/bybit/rest.d.ts +3 -2
- package/dist/client/bybit/rest.js +34 -1
- package/dist/client/bybit/types.d.ts +14 -1
- package/dist/client/lighter/rest.d.ts +4 -1
- package/dist/client/lighter/rest.js +36 -1
- package/dist/client/lighter/types.d.ts +22 -1
- package/dist/client/okx/rest.d.ts +3 -2
- package/dist/client/okx/rest.js +28 -1
- package/dist/client/okx/types.d.ts +18 -1
- package/dist/helper/factory.d.ts +1 -0
- package/dist/helper/index.d.ts +1 -0
- package/dist/helper/lighter/helper.d.ts +1 -0
- package/dist/helper/lighter/helper.js +1 -1
- package/dist/helper/lighter/index.d.ts +1 -0
- package/dist/helper/lighter/types.d.ts +1 -0
- package/dist/helper/types.d.ts +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/sdk.d.ts +1 -0
- package/dist/syncer/base.d.ts +11 -4
- package/dist/syncer/base.js +14 -5
- package/dist/syncer/binance/syncer.d.ts +11 -4
- package/dist/syncer/binance/syncer.js +75 -2
- package/dist/syncer/bybit/syncer.d.ts +8 -4
- package/dist/syncer/bybit/syncer.js +29 -2
- package/dist/syncer/hyperliquid/syncer.d.ts +6 -4
- package/dist/syncer/hyperliquid/syncer.js +12 -2
- package/dist/syncer/index.d.ts +6 -1
- package/dist/syncer/lighter/syncer.d.ts +17 -4
- package/dist/syncer/lighter/syncer.js +87 -2
- package/dist/syncer/okx/syncer.d.ts +9 -4
- package/dist/syncer/okx/syncer.js +41 -2
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BinanceCredentialsDTO } from '@pear-protocol/types';
|
|
2
|
-
import { BinanceRawAccountStatus, BinanceRawPositionRisk } from './types.js';
|
|
2
|
+
import { BinanceRawAccountStatus, BinanceRawOrder, BinanceRawPositionRisk, BinanceRawUserTrade } from './types.js';
|
|
3
3
|
|
|
4
4
|
declare function createListenKey(apiKey: string, demo?: boolean): Promise<string>;
|
|
5
5
|
declare function keepAliveListenKey(apiKey: string, demo?: boolean): Promise<void>;
|
|
@@ -7,5 +7,7 @@ declare function fetchAccountStatus(creds: BinanceCredentialsDTO, demo: boolean)
|
|
|
7
7
|
declare function fetchPositionRisk(creds: BinanceCredentialsDTO, symbol: string, demo: boolean): Promise<BinanceRawPositionRisk | undefined>;
|
|
8
8
|
declare function fetchAllPositionRisk(creds: BinanceCredentialsDTO, demo: boolean): Promise<BinanceRawPositionRisk[]>;
|
|
9
9
|
declare function fetchMultiAssetsMargin(creds: BinanceCredentialsDTO, demo: boolean): Promise<boolean>;
|
|
10
|
+
declare function fetchUserTradesSince(creds: BinanceCredentialsDTO, symbols: string[], since: Date, demo: boolean): Promise<BinanceRawUserTrade[]>;
|
|
11
|
+
declare function fetchAllOrdersSince(creds: BinanceCredentialsDTO, symbols: string[], since: Date, demo: boolean): Promise<BinanceRawOrder[]>;
|
|
10
12
|
|
|
11
|
-
export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, keepAliveListenKey };
|
|
13
|
+
export { createListenKey, fetchAccountStatus, fetchAllOrdersSince, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, fetchUserTradesSince, keepAliveListenKey };
|
|
@@ -54,5 +54,79 @@ async function fetchMultiAssetsMargin(creds, demo) {
|
|
|
54
54
|
const res = await signedGet(creds, "/fapi/v1/multiAssetsMargin", {}, demo);
|
|
55
55
|
return res.multiAssetsMargin;
|
|
56
56
|
}
|
|
57
|
+
const USER_TRADES_LIMIT = 1e3;
|
|
58
|
+
const ALL_ORDERS_LIMIT = 1e3;
|
|
59
|
+
const BINANCE_MAX_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
60
|
+
async function fetchUserTradesSince(creds, symbols, since, demo) {
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
const results = [];
|
|
63
|
+
for (const symbol of symbols) {
|
|
64
|
+
let windowStart = since.getTime();
|
|
65
|
+
while (windowStart <= now) {
|
|
66
|
+
const windowEnd = Math.min(windowStart + BINANCE_MAX_WINDOW_MS - 1, now);
|
|
67
|
+
let pageStart = windowStart;
|
|
68
|
+
while (pageStart <= windowEnd) {
|
|
69
|
+
const page = await signedGet(
|
|
70
|
+
creds,
|
|
71
|
+
"/fapi/v1/userTrades",
|
|
72
|
+
{
|
|
73
|
+
symbol,
|
|
74
|
+
startTime: pageStart.toString(),
|
|
75
|
+
endTime: windowEnd.toString(),
|
|
76
|
+
limit: USER_TRADES_LIMIT.toString()
|
|
77
|
+
},
|
|
78
|
+
demo
|
|
79
|
+
);
|
|
80
|
+
results.push(...page);
|
|
81
|
+
if (page.length < USER_TRADES_LIMIT) break;
|
|
82
|
+
const last = page[page.length - 1];
|
|
83
|
+
if (!last || last.time <= pageStart) break;
|
|
84
|
+
pageStart = last.time + 1;
|
|
85
|
+
}
|
|
86
|
+
windowStart = windowEnd + 1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const byId = /* @__PURE__ */ new Map();
|
|
90
|
+
for (const trade of results) {
|
|
91
|
+
byId.set(`${trade.symbol}:${trade.id}`, trade);
|
|
92
|
+
}
|
|
93
|
+
return Array.from(byId.values());
|
|
94
|
+
}
|
|
95
|
+
async function fetchAllOrdersSince(creds, symbols, since, demo) {
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
const results = [];
|
|
98
|
+
for (const symbol of symbols) {
|
|
99
|
+
let windowStart = since.getTime();
|
|
100
|
+
while (windowStart <= now) {
|
|
101
|
+
const windowEnd = Math.min(windowStart + BINANCE_MAX_WINDOW_MS - 1, now);
|
|
102
|
+
let pageStart = windowStart;
|
|
103
|
+
while (pageStart <= windowEnd) {
|
|
104
|
+
const page = await signedGet(
|
|
105
|
+
creds,
|
|
106
|
+
"/fapi/v1/allOrders",
|
|
107
|
+
{
|
|
108
|
+
symbol,
|
|
109
|
+
startTime: pageStart.toString(),
|
|
110
|
+
endTime: windowEnd.toString(),
|
|
111
|
+
limit: ALL_ORDERS_LIMIT.toString()
|
|
112
|
+
},
|
|
113
|
+
demo
|
|
114
|
+
);
|
|
115
|
+
results.push(...page);
|
|
116
|
+
if (page.length < ALL_ORDERS_LIMIT) break;
|
|
117
|
+
const last = page[page.length - 1];
|
|
118
|
+
const lastTs = last ? Math.max(last.updateTime, last.time) : pageStart;
|
|
119
|
+
if (lastTs <= pageStart) break;
|
|
120
|
+
pageStart = lastTs + 1;
|
|
121
|
+
}
|
|
122
|
+
windowStart = windowEnd + 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const byId = /* @__PURE__ */ new Map();
|
|
126
|
+
for (const order of results) {
|
|
127
|
+
byId.set(`${order.symbol}:${order.orderId}`, order);
|
|
128
|
+
}
|
|
129
|
+
return Array.from(byId.values());
|
|
130
|
+
}
|
|
57
131
|
|
|
58
|
-
export { createListenKey, fetchAccountStatus, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, keepAliveListenKey };
|
|
132
|
+
export { createListenKey, fetchAccountStatus, fetchAllOrdersSince, fetchAllPositionRisk, fetchMultiAssetsMargin, fetchPositionRisk, fetchUserTradesSince, keepAliveListenKey };
|
|
@@ -40,6 +40,41 @@ interface BinanceRawAccountStatus {
|
|
|
40
40
|
interface BinanceMultiAssetsMarginResponse {
|
|
41
41
|
multiAssetsMargin: boolean;
|
|
42
42
|
}
|
|
43
|
+
interface BinanceRawUserTrade {
|
|
44
|
+
buyer: boolean;
|
|
45
|
+
commission: string;
|
|
46
|
+
commissionAsset: string;
|
|
47
|
+
id: number;
|
|
48
|
+
maker: boolean;
|
|
49
|
+
orderId: number;
|
|
50
|
+
price: string;
|
|
51
|
+
qty: string;
|
|
52
|
+
quoteQty: string;
|
|
53
|
+
realizedPnl: string;
|
|
54
|
+
side: 'BUY' | 'SELL';
|
|
55
|
+
positionSide: string;
|
|
56
|
+
symbol: string;
|
|
57
|
+
time: number;
|
|
58
|
+
}
|
|
59
|
+
interface BinanceRawOrder {
|
|
60
|
+
avgPrice: string;
|
|
61
|
+
clientOrderId: string;
|
|
62
|
+
cumQuote: string;
|
|
63
|
+
executedQty: string;
|
|
64
|
+
orderId: number;
|
|
65
|
+
origQty: string;
|
|
66
|
+
origType: string;
|
|
67
|
+
price: string;
|
|
68
|
+
reduceOnly: boolean;
|
|
69
|
+
side: 'BUY' | 'SELL';
|
|
70
|
+
positionSide: string;
|
|
71
|
+
status: string;
|
|
72
|
+
symbol: string;
|
|
73
|
+
time: number;
|
|
74
|
+
timeInForce: string;
|
|
75
|
+
type: string;
|
|
76
|
+
updateTime: number;
|
|
77
|
+
}
|
|
43
78
|
interface BinanceAssetIndex {
|
|
44
79
|
symbol: string;
|
|
45
80
|
time: number;
|
|
@@ -65,4 +100,4 @@ interface BinanceRawUserDataEvent {
|
|
|
65
100
|
};
|
|
66
101
|
}
|
|
67
102
|
|
|
68
|
-
export type { BinanceAssetIndex, BinanceMultiAssetsMarginResponse, BinanceRawAccountStatus, BinanceRawAsset, BinanceRawPositionRisk, BinanceRawUserDataEvent };
|
|
103
|
+
export type { BinanceAssetIndex, BinanceMultiAssetsMarginResponse, BinanceRawAccountStatus, BinanceRawAsset, BinanceRawOrder, BinanceRawPositionRisk, BinanceRawUserDataEvent, BinanceRawUserTrade };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { BybitCredentialsDTO } from '@pear-protocol/types';
|
|
2
|
-
import { BybitRawAccountInfo, BybitRawPosition, BybitRawWallet } from './types.js';
|
|
2
|
+
import { BybitRawAccountInfo, BybitRawExecution, BybitRawPosition, BybitRawWallet } from './types.js';
|
|
3
3
|
|
|
4
4
|
declare function fetchWalletBalance(creds: BybitCredentialsDTO, demo: boolean): Promise<BybitRawWallet | undefined>;
|
|
5
5
|
declare function fetchPositions(creds: BybitCredentialsDTO, settleCoin: string, demo: boolean): Promise<BybitRawPosition[]>;
|
|
6
6
|
declare function fetchPositionLeverage(creds: BybitCredentialsDTO, symbol: string, demo: boolean): Promise<BybitRawPosition | undefined>;
|
|
7
7
|
declare function fetchAccountInfo(creds: BybitCredentialsDTO, demo: boolean): Promise<BybitRawAccountInfo>;
|
|
8
8
|
declare function fetchCollateralRatios(creds: BybitCredentialsDTO, demo: boolean): Promise<Map<string, string>>;
|
|
9
|
+
declare function fetchExecutionsSince(creds: BybitCredentialsDTO, since: Date, demo: boolean): Promise<BybitRawExecution[]>;
|
|
9
10
|
|
|
10
|
-
export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance };
|
|
11
|
+
export { fetchAccountInfo, fetchCollateralRatios, fetchExecutionsSince, fetchPositionLeverage, fetchPositions, fetchWalletBalance };
|
|
@@ -72,5 +72,38 @@ async function fetchCollateralRatios(creds, demo) {
|
|
|
72
72
|
}
|
|
73
73
|
return ratios;
|
|
74
74
|
}
|
|
75
|
+
const EXECUTIONS_LIMIT = 100;
|
|
76
|
+
const BYBIT_MAX_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
77
|
+
async function fetchExecutionsSince(creds, since, demo) {
|
|
78
|
+
const results = [];
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
let windowStart = since.getTime();
|
|
81
|
+
while (windowStart <= now) {
|
|
82
|
+
const windowEnd = Math.min(windowStart + BYBIT_MAX_WINDOW_MS - 1, now);
|
|
83
|
+
let cursor;
|
|
84
|
+
do {
|
|
85
|
+
const result = await signedGet(
|
|
86
|
+
creds,
|
|
87
|
+
"/v5/execution/list",
|
|
88
|
+
{
|
|
89
|
+
category: "linear",
|
|
90
|
+
startTime: windowStart.toString(),
|
|
91
|
+
endTime: windowEnd.toString(),
|
|
92
|
+
limit: EXECUTIONS_LIMIT.toString(),
|
|
93
|
+
...cursor ? { cursor } : {}
|
|
94
|
+
},
|
|
95
|
+
demo
|
|
96
|
+
);
|
|
97
|
+
results.push(...result.list ?? []);
|
|
98
|
+
cursor = result.nextPageCursor || void 0;
|
|
99
|
+
} while (cursor);
|
|
100
|
+
windowStart = windowEnd + 1;
|
|
101
|
+
}
|
|
102
|
+
const byId = /* @__PURE__ */ new Map();
|
|
103
|
+
for (const execution of results) {
|
|
104
|
+
byId.set(execution.execId, execution);
|
|
105
|
+
}
|
|
106
|
+
return Array.from(byId.values());
|
|
107
|
+
}
|
|
75
108
|
|
|
76
|
-
export { fetchAccountInfo, fetchCollateralRatios, fetchPositionLeverage, fetchPositions, fetchWalletBalance };
|
|
109
|
+
export { fetchAccountInfo, fetchCollateralRatios, fetchExecutionsSince, fetchPositionLeverage, fetchPositions, fetchWalletBalance };
|
|
@@ -46,6 +46,19 @@ interface BybitRawPosition {
|
|
|
46
46
|
tradeMode: string;
|
|
47
47
|
liqPrice: string;
|
|
48
48
|
}
|
|
49
|
+
interface BybitRawExecution {
|
|
50
|
+
symbol: string;
|
|
51
|
+
orderId: string;
|
|
52
|
+
orderLinkId: string;
|
|
53
|
+
side: 'Buy' | 'Sell';
|
|
54
|
+
execFee: string;
|
|
55
|
+
execId: string;
|
|
56
|
+
execPrice: string;
|
|
57
|
+
execQty: string;
|
|
58
|
+
execTime: string;
|
|
59
|
+
execType: string;
|
|
60
|
+
closedSize: string;
|
|
61
|
+
}
|
|
49
62
|
interface BybitResponse<T> {
|
|
50
63
|
retCode: number;
|
|
51
64
|
retMsg: string;
|
|
@@ -59,4 +72,4 @@ type BybitWsMessage = {
|
|
|
59
72
|
data: BybitRawPosition[];
|
|
60
73
|
};
|
|
61
74
|
|
|
62
|
-
export type { BybitRawAccountInfo, BybitRawCoin, BybitRawCollateralRatioEntry, BybitRawCollateralTier, BybitRawPosition, BybitRawWallet, BybitResponse, BybitWsMessage };
|
|
75
|
+
export type { BybitRawAccountInfo, BybitRawCoin, BybitRawCollateralRatioEntry, BybitRawCollateralTier, BybitRawExecution, BybitRawPosition, BybitRawWallet, BybitResponse, BybitWsMessage };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LighterRawTrade } from './types.js';
|
|
2
|
+
|
|
1
3
|
type ApiKey = {
|
|
2
4
|
account_index: number;
|
|
3
5
|
api_key_index: number;
|
|
@@ -52,5 +54,6 @@ declare function fetchApiKeys(accountIndex: number): Promise<ApiKey[]>;
|
|
|
52
54
|
declare function fetchAccountsByL1Address(l1Address: string): Promise<LighterAccountResponse>;
|
|
53
55
|
declare function fetchAccount(accountIndex: number): Promise<DetailedAccountResponse>;
|
|
54
56
|
declare function fetchOrderBookDetails(): Promise<OrderBookDetail[]>;
|
|
57
|
+
declare function fetchTradesSince(accountIndex: number, authToken: string, since: Date): Promise<LighterRawTrade[]>;
|
|
55
58
|
|
|
56
|
-
export { type ApiKey, type ApprovedIntegrator, type DetailedAccountResponse, type LighterAccountResponse, type LighterSubAccount, type OrderBookDetail, type RestPosition, fetchAccount, fetchAccountsByL1Address, fetchApiKeys, fetchOrderBookDetails };
|
|
59
|
+
export { type ApiKey, type ApprovedIntegrator, type DetailedAccountResponse, type LighterAccountResponse, type LighterSubAccount, type OrderBookDetail, type RestPosition, fetchAccount, fetchAccountsByL1Address, fetchApiKeys, fetchOrderBookDetails, fetchTradesSince };
|
|
@@ -23,5 +23,40 @@ async function fetchOrderBookDetails() {
|
|
|
23
23
|
const data = await res.json();
|
|
24
24
|
return data.order_book_details;
|
|
25
25
|
}
|
|
26
|
+
async function fetchTradesSince(accountIndex, authToken, since) {
|
|
27
|
+
const results = [];
|
|
28
|
+
let cursor;
|
|
29
|
+
do {
|
|
30
|
+
const params = new URLSearchParams({
|
|
31
|
+
market_type: "all",
|
|
32
|
+
account_index: accountIndex.toString(),
|
|
33
|
+
sort_by: "timestamp",
|
|
34
|
+
sort_dir: "desc",
|
|
35
|
+
ask_filter: "-1",
|
|
36
|
+
role: "all",
|
|
37
|
+
type: "all",
|
|
38
|
+
limit: "100",
|
|
39
|
+
aggregate: "false",
|
|
40
|
+
...cursor ? { cursor } : {}
|
|
41
|
+
});
|
|
42
|
+
const res = await fetch(`${LIGHTER_BASE_URL}/api/v1/trades?${params.toString()}`, {
|
|
43
|
+
headers: { authorization: authToken }
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) throw new Error(`Lighter /trades returned ${res.status}`);
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
if (data.code !== 200) {
|
|
48
|
+
throw new Error(`Lighter /trades returned ${data.code}: ${data.message ?? "unknown error"}`);
|
|
49
|
+
}
|
|
50
|
+
const page = data.trades ?? [];
|
|
51
|
+
results.push(...page.filter((trade) => trade.timestamp >= since.getTime()));
|
|
52
|
+
const reachedSince = page.some((trade) => trade.timestamp < since.getTime());
|
|
53
|
+
cursor = reachedSince ? void 0 : data.next_cursor || void 0;
|
|
54
|
+
} while (cursor);
|
|
55
|
+
const byId = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const trade of results) {
|
|
57
|
+
byId.set(trade.trade_id_str ?? trade.trade_id.toString(), trade);
|
|
58
|
+
}
|
|
59
|
+
return Array.from(byId.values());
|
|
60
|
+
}
|
|
26
61
|
|
|
27
|
-
export { fetchAccount, fetchAccountsByL1Address, fetchApiKeys, fetchOrderBookDetails };
|
|
62
|
+
export { fetchAccount, fetchAccountsByL1Address, fetchApiKeys, fetchOrderBookDetails, fetchTradesSince };
|
|
@@ -28,5 +28,26 @@ type UserStatsEvent = {
|
|
|
28
28
|
margin_usage: string;
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
+
type LighterRawTrade = {
|
|
32
|
+
trade_id: number;
|
|
33
|
+
trade_id_str?: string;
|
|
34
|
+
type: 'trade' | 'liquidation' | 'deleverage' | 'market-settlement' | string;
|
|
35
|
+
market_id: number;
|
|
36
|
+
size: string;
|
|
37
|
+
price: string;
|
|
38
|
+
usd_amount: string;
|
|
39
|
+
ask_client_id_str?: string;
|
|
40
|
+
bid_client_id_str?: string;
|
|
41
|
+
ask_account_id: number;
|
|
42
|
+
bid_account_id: number;
|
|
43
|
+
is_maker_ask: boolean;
|
|
44
|
+
timestamp: number;
|
|
45
|
+
taker_fee?: number;
|
|
46
|
+
taker_position_size_before?: string;
|
|
47
|
+
maker_fee?: number;
|
|
48
|
+
maker_position_size_before?: string;
|
|
49
|
+
integrator_taker_fee?: number;
|
|
50
|
+
integrator_maker_fee?: number;
|
|
51
|
+
};
|
|
31
52
|
|
|
32
|
-
export type { AccountAllEvent, RawAsset, RawPosition, UserStatsEvent };
|
|
53
|
+
export type { AccountAllEvent, LighterRawTrade, RawAsset, RawPosition, UserStatsEvent };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { OkxCredentialsDTO } from '@pear-protocol/types';
|
|
2
|
-
import { OkxRawAccountConfig, OkxRawBalance, OkxRawLeverageInfo, OkxRawPosition } from './types.js';
|
|
2
|
+
import { OkxRawAccountConfig, OkxRawBalance, OkxRawFill, OkxRawLeverageInfo, OkxRawPosition } from './types.js';
|
|
3
3
|
|
|
4
4
|
declare function fetchAccountConfig(creds: OkxCredentialsDTO, demo: boolean): Promise<OkxRawAccountConfig>;
|
|
5
5
|
declare function fetchBalance(creds: OkxCredentialsDTO, demo: boolean): Promise<OkxRawBalance>;
|
|
6
6
|
declare function fetchPositions(creds: OkxCredentialsDTO, demo: boolean): Promise<OkxRawPosition[]>;
|
|
7
7
|
declare function fetchLeverageInfo(creds: OkxCredentialsDTO, instId: string, demo: boolean): Promise<OkxRawLeverageInfo | undefined>;
|
|
8
|
+
declare function fetchFillsHistorySince(creds: OkxCredentialsDTO, since: Date, demo: boolean): Promise<OkxRawFill[]>;
|
|
8
9
|
|
|
9
|
-
export { fetchAccountConfig, fetchBalance, fetchLeverageInfo, fetchPositions };
|
|
10
|
+
export { fetchAccountConfig, fetchBalance, fetchFillsHistorySince, fetchLeverageInfo, fetchPositions };
|
package/dist/client/okx/rest.js
CHANGED
|
@@ -53,5 +53,32 @@ async function fetchLeverageInfo(creds, instId, demo) {
|
|
|
53
53
|
).catch(() => []);
|
|
54
54
|
return result[0];
|
|
55
55
|
}
|
|
56
|
+
const FILLS_HISTORY_LIMIT = 100;
|
|
57
|
+
async function fetchFillsHistorySince(creds, since, demo) {
|
|
58
|
+
const results = [];
|
|
59
|
+
let after;
|
|
60
|
+
do {
|
|
61
|
+
const page = await signedGet(
|
|
62
|
+
creds,
|
|
63
|
+
"/api/v5/trade/fills-history",
|
|
64
|
+
{
|
|
65
|
+
instType: "SWAP",
|
|
66
|
+
begin: since.getTime().toString(),
|
|
67
|
+
end: Date.now().toString(),
|
|
68
|
+
limit: FILLS_HISTORY_LIMIT.toString(),
|
|
69
|
+
...after ? { after } : {}
|
|
70
|
+
},
|
|
71
|
+
demo
|
|
72
|
+
);
|
|
73
|
+
results.push(...page);
|
|
74
|
+
const last = page[page.length - 1];
|
|
75
|
+
after = page.length === FILLS_HISTORY_LIMIT && last?.billId ? last.billId : void 0;
|
|
76
|
+
} while (after);
|
|
77
|
+
const byId = /* @__PURE__ */ new Map();
|
|
78
|
+
for (const fill of results) {
|
|
79
|
+
byId.set(fill.billId || fill.tradeId, fill);
|
|
80
|
+
}
|
|
81
|
+
return Array.from(byId.values());
|
|
82
|
+
}
|
|
56
83
|
|
|
57
|
-
export { fetchAccountConfig, fetchBalance, fetchLeverageInfo, fetchPositions };
|
|
84
|
+
export { fetchAccountConfig, fetchBalance, fetchFillsHistorySince, fetchLeverageInfo, fetchPositions };
|
|
@@ -42,6 +42,23 @@ interface OkxRawLeverageInfo {
|
|
|
42
42
|
lever: string;
|
|
43
43
|
mgnMode: string;
|
|
44
44
|
}
|
|
45
|
+
interface OkxRawFill {
|
|
46
|
+
instType: string;
|
|
47
|
+
instId: string;
|
|
48
|
+
tradeId: string;
|
|
49
|
+
ordId: string;
|
|
50
|
+
clOrdId: string;
|
|
51
|
+
billId: string;
|
|
52
|
+
fillPx: string;
|
|
53
|
+
fillSz: string;
|
|
54
|
+
side: 'buy' | 'sell';
|
|
55
|
+
posSide: string;
|
|
56
|
+
execType: string;
|
|
57
|
+
fee: string;
|
|
58
|
+
feeCcy: string;
|
|
59
|
+
fillTime: string;
|
|
60
|
+
ts: string;
|
|
61
|
+
}
|
|
45
62
|
interface OkxWsAccountMessage {
|
|
46
63
|
arg: {
|
|
47
64
|
channel: 'account';
|
|
@@ -57,4 +74,4 @@ interface OkxWsPositionsMessage {
|
|
|
57
74
|
}
|
|
58
75
|
type OkxWsMessage = OkxWsAccountMessage | OkxWsPositionsMessage;
|
|
59
76
|
|
|
60
|
-
export type { OkxRawAccountConfig, OkxRawBalance, OkxRawBalanceDetail, OkxRawLeverageInfo, OkxRawPosition, OkxWsAccountMessage, OkxWsMessage, OkxWsPositionsMessage };
|
|
77
|
+
export type { OkxRawAccountConfig, OkxRawBalance, OkxRawBalanceDetail, OkxRawFill, OkxRawLeverageInfo, OkxRawPosition, OkxWsAccountMessage, OkxWsMessage, OkxWsPositionsMessage };
|
package/dist/helper/factory.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import './lighter/types.js';
|
|
|
4
4
|
import './lighter/ethereum/types.js';
|
|
5
5
|
import 'viem';
|
|
6
6
|
import '../client/lighter/rest.js';
|
|
7
|
+
import '../client/lighter/types.js';
|
|
7
8
|
|
|
8
9
|
declare function createExchangeHelper<E extends ExchangeHelperExchange>(exchange: E, sdk: PearSDK): ExchangeHelperMap[E];
|
|
9
10
|
|
package/dist/helper/index.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ export { ExchangeHelperExchange, ExchangeHelperMap } from './types.js';
|
|
|
4
4
|
export { ApiKey } from '../client/lighter/rest.js';
|
|
5
5
|
export { WalletClientWithAccount } from './lighter/ethereum/types.js';
|
|
6
6
|
import '@pear-protocol/core-sdk';
|
|
7
|
+
import '../client/lighter/types.js';
|
|
7
8
|
import 'viem';
|
|
@@ -26,7 +26,7 @@ function createLighterHelper(sdk) {
|
|
|
26
26
|
},
|
|
27
27
|
async connectWallet(walletClient, params) {
|
|
28
28
|
const apiKeys = await fetchApiKeys(params.accountIndex);
|
|
29
|
-
const apiKeyIndex =
|
|
29
|
+
const apiKeyIndex = Math.max(4, ...apiKeys.map((k) => k.api_key_index + 1));
|
|
30
30
|
const { privateKey, messageToSign, txInfo } = await sdk.core.misc.lighter.createApiKey({
|
|
31
31
|
accountIndex: params.accountIndex,
|
|
32
32
|
apiKeyIndex
|
package/dist/helper/types.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { createLighterHelper } from './helper/lighter/helper.js';
|
|
|
3
3
|
export { ASSET_DECIMALS, ApproveIntegratorParams, ApproveIntegratorResult, AssetId, ConnectWalletParams, ConnectWalletResult, DepositParams, LighterHelper, LighterLookupResult, RouteType, WithdrawParams, getAssetDecimals } from './helper/lighter/types.js';
|
|
4
4
|
export { ExchangeHelperExchange, ExchangeHelperMap } from './helper/types.js';
|
|
5
5
|
export { ExchangesSDK } from './sdk.js';
|
|
6
|
-
export { AccountSyncer, AccountSyncerConfig, BaseAccountSyncer } from './syncer/base.js';
|
|
6
|
+
export { AccountSyncer, AccountSyncerConfig, BaseAccountSyncer, ListExternalFillsParams } from './syncer/base.js';
|
|
7
7
|
export { BinanceAccountSyncer } from './syncer/binance/syncer.js';
|
|
8
8
|
export { BybitAccountSyncer } from './syncer/bybit/syncer.js';
|
|
9
9
|
export { createAccountSyncer } from './syncer/factory.js';
|
|
@@ -40,5 +40,10 @@ export { OkxWs, OkxWsConfig } from './client/okx/ws.js';
|
|
|
40
40
|
export { WalletClientWithAccount } from './helper/lighter/ethereum/types.js';
|
|
41
41
|
import '@pear-protocol/types';
|
|
42
42
|
import '@pear-protocol/core-sdk';
|
|
43
|
+
import './client/binance/types.js';
|
|
44
|
+
import './client/bybit/types.js';
|
|
45
|
+
import './client/hyperliquid/types.js';
|
|
46
|
+
import './client/lighter/types.js';
|
|
47
|
+
import './client/okx/types.js';
|
|
43
48
|
import 'partysocket';
|
|
44
49
|
import 'viem';
|
package/dist/sdk.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import './helper/lighter/types.js';
|
|
|
7
7
|
import './helper/lighter/ethereum/types.js';
|
|
8
8
|
import 'viem';
|
|
9
9
|
import './client/lighter/rest.js';
|
|
10
|
+
import './client/lighter/types.js';
|
|
10
11
|
import '@pear-protocol/core-sdk';
|
|
11
12
|
import './client/index.js';
|
|
12
13
|
import './client/binance/exchange-ws.js';
|
package/dist/syncer/base.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import PearSDK from '@pear-protocol/core-sdk';
|
|
2
|
-
import { Connector,
|
|
2
|
+
import { Connector, HttpTypes } from '@pear-protocol/types';
|
|
3
3
|
import { ExchangeWsByConnector } from '../client/index.js';
|
|
4
4
|
import { SupportedCredentials } from '../types.js';
|
|
5
5
|
import '../client/binance/exchange-ws.js';
|
|
@@ -26,15 +26,22 @@ interface AccountSyncerConfig {
|
|
|
26
26
|
interface AccountSyncer {
|
|
27
27
|
synchronize(): Promise<void>;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
interface ListExternalFillsParams {
|
|
30
|
+
since: Date;
|
|
31
|
+
instruments: HttpTypes.Instrument[];
|
|
32
|
+
openPositions: HttpTypes.OpenPositionLite[];
|
|
33
|
+
provisionalFills: HttpTypes.ProvisionalFill[];
|
|
34
|
+
}
|
|
35
|
+
declare abstract class BaseAccountSyncer<ExternalFill> implements AccountSyncer {
|
|
30
36
|
protected sdk: PearSDK;
|
|
31
37
|
protected connector: Connector;
|
|
32
38
|
protected credentials: SupportedCredentials;
|
|
33
39
|
protected ws: ExchangeWsByConnector[Connector];
|
|
34
40
|
protected demo: boolean;
|
|
35
41
|
constructor(config: AccountSyncerConfig);
|
|
36
|
-
protected abstract listExternalFills(
|
|
42
|
+
protected abstract listExternalFills(params: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
43
|
+
protected abstract isReduceOnly(fill: ExternalFill): boolean;
|
|
37
44
|
synchronize(): Promise<void>;
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
export { type AccountSyncer, type AccountSyncerConfig, BaseAccountSyncer };
|
|
47
|
+
export { type AccountSyncer, type AccountSyncerConfig, BaseAccountSyncer, type ListExternalFillsParams };
|
package/dist/syncer/base.js
CHANGED
|
@@ -18,23 +18,32 @@ class BaseAccountSyncer {
|
|
|
18
18
|
this.sdk.core.positions.openLite(),
|
|
19
19
|
this.sdk.core.fills.provisionals()
|
|
20
20
|
]);
|
|
21
|
-
if (!positions.length && !provisionalFills.length)
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (!positions.length && !provisionalFills.length) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const [{ instruments }, since] = await Promise.all([
|
|
25
|
+
this.sdk.core.instruments.list({ connector: this.connector }),
|
|
24
26
|
this.sdk.core.sync.since({
|
|
25
27
|
overlapMs: 6e4,
|
|
26
28
|
nowMs: Date.now(),
|
|
27
29
|
bootstrapLookbackMs: 30 * 24 * 60 * 60 * 1e3
|
|
28
30
|
})
|
|
29
31
|
]);
|
|
30
|
-
const externalFills = await this.listExternalFills(
|
|
32
|
+
const externalFills = await this.listExternalFills({
|
|
33
|
+
since: new Date(since.since),
|
|
34
|
+
instruments,
|
|
35
|
+
openPositions: positions,
|
|
36
|
+
provisionalFills
|
|
37
|
+
});
|
|
31
38
|
const payload = await computeSyncPayload({
|
|
32
39
|
openPositions: positions,
|
|
33
40
|
provisionalFills,
|
|
34
41
|
confirmedExchangeFillIds: since.confirmedExchangeFillIds ?? [],
|
|
35
42
|
externalFills
|
|
36
43
|
});
|
|
37
|
-
if (!payload)
|
|
44
|
+
if (!payload) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
38
47
|
if (payload.reconcilables) {
|
|
39
48
|
await this.sdk.core.sync.reconcile(payload.reconcilables);
|
|
40
49
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { BinanceRawUserTrade } from '../../client/binance/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
3
4
|
import '@pear-protocol/core-sdk';
|
|
4
5
|
import '../../client/index.js';
|
|
5
6
|
import '../../types.js';
|
|
@@ -17,8 +18,14 @@ import '../../client/lighter/ws.js';
|
|
|
17
18
|
import '../../client/okx/exchange-ws.js';
|
|
18
19
|
import '../../client/okx/ws.js';
|
|
19
20
|
|
|
20
|
-
declare class BinanceAccountSyncer extends BaseAccountSyncer {
|
|
21
|
-
|
|
21
|
+
declare class BinanceAccountSyncer extends BaseAccountSyncer<BinanceRawUserTrade> {
|
|
22
|
+
private readonly creds;
|
|
23
|
+
constructor(config: AccountSyncerConfig);
|
|
24
|
+
protected listExternalFills({ since, instruments, openPositions, provisionalFills, }: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
25
|
+
protected isReduceOnly(trade: BinanceRawUserTrade): boolean;
|
|
26
|
+
private collectRequestedInstrumentIds;
|
|
27
|
+
private mapOrdersBySymbolOrderId;
|
|
28
|
+
private orderKey;
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
export { BinanceAccountSyncer };
|
|
@@ -1,8 +1,81 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchUserTradesSince, fetchAllOrdersSince } from '../../client/binance/rest';
|
|
1
3
|
import { BaseAccountSyncer } from '../base';
|
|
2
4
|
|
|
3
5
|
class BinanceAccountSyncer extends BaseAccountSyncer {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
creds;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.creds = config.credentials;
|
|
10
|
+
}
|
|
11
|
+
async listExternalFills({
|
|
12
|
+
since,
|
|
13
|
+
instruments,
|
|
14
|
+
openPositions,
|
|
15
|
+
provisionalFills
|
|
16
|
+
}) {
|
|
17
|
+
const knownInstrumentIds = new Set(instruments.map((instrument) => instrument.id));
|
|
18
|
+
const requestedInstrumentIds = this.collectRequestedInstrumentIds(openPositions, provisionalFills);
|
|
19
|
+
const instrumentIds = [...requestedInstrumentIds].filter((instrumentId) => knownInstrumentIds.has(instrumentId));
|
|
20
|
+
if (!instrumentIds.length) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const [trades, orders] = await Promise.all([
|
|
24
|
+
fetchUserTradesSince(this.creds, instrumentIds, since, this.demo),
|
|
25
|
+
fetchAllOrdersSince(this.creds, instrumentIds, since, this.demo)
|
|
26
|
+
]);
|
|
27
|
+
const ordersBySymbolOrderId = this.mapOrdersBySymbolOrderId(orders);
|
|
28
|
+
return trades.reduce((acc, trade) => {
|
|
29
|
+
if (!requestedInstrumentIds.has(trade.symbol)) {
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
const order = ordersBySymbolOrderId.get(this.orderKey(trade.symbol, trade.orderId));
|
|
33
|
+
acc.push({
|
|
34
|
+
connector: "binance",
|
|
35
|
+
exchangeFillId: trade.id.toString(),
|
|
36
|
+
cloid: order?.clientOrderId || null,
|
|
37
|
+
symbol: trade.symbol,
|
|
38
|
+
price: trade.price,
|
|
39
|
+
quantity: trade.qty,
|
|
40
|
+
side: trade.side,
|
|
41
|
+
reduceOnly: this.isReduceOnly(trade),
|
|
42
|
+
timestamp: trade.time,
|
|
43
|
+
tradeFee: new BigNumber(trade.commission || 0).abs().toFixed(),
|
|
44
|
+
builderFee: "0",
|
|
45
|
+
liquidation: null
|
|
46
|
+
});
|
|
47
|
+
return acc;
|
|
48
|
+
}, []);
|
|
49
|
+
}
|
|
50
|
+
isReduceOnly(trade) {
|
|
51
|
+
if (trade.positionSide === "LONG") return trade.side === "SELL";
|
|
52
|
+
if (trade.positionSide === "SHORT") return trade.side === "BUY";
|
|
53
|
+
if (trade.positionSide === "BOTH") {
|
|
54
|
+
return !new BigNumber(trade.realizedPnl || 0).isZero();
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
collectRequestedInstrumentIds(openPositions, provisionalFills) {
|
|
59
|
+
const instrumentIds = /* @__PURE__ */ new Set();
|
|
60
|
+
for (const position of openPositions) {
|
|
61
|
+
for (const instrumentId of Object.keys(position.exposure)) {
|
|
62
|
+
instrumentIds.add(instrumentId);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const fill of provisionalFills) {
|
|
66
|
+
instrumentIds.add(fill.symbol);
|
|
67
|
+
}
|
|
68
|
+
return instrumentIds;
|
|
69
|
+
}
|
|
70
|
+
mapOrdersBySymbolOrderId(orders) {
|
|
71
|
+
const bySymbolOrderId = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const order of orders) {
|
|
73
|
+
bySymbolOrderId.set(this.orderKey(order.symbol, order.orderId), order);
|
|
74
|
+
}
|
|
75
|
+
return bySymbolOrderId;
|
|
76
|
+
}
|
|
77
|
+
orderKey(symbol, orderId) {
|
|
78
|
+
return `${symbol}:${orderId}`;
|
|
6
79
|
}
|
|
7
80
|
}
|
|
8
81
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { BybitRawExecution } from '../../client/bybit/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
3
4
|
import '@pear-protocol/core-sdk';
|
|
4
5
|
import '../../client/index.js';
|
|
5
6
|
import '../../types.js';
|
|
@@ -17,8 +18,11 @@ import '../../client/lighter/ws.js';
|
|
|
17
18
|
import '../../client/okx/exchange-ws.js';
|
|
18
19
|
import '../../client/okx/ws.js';
|
|
19
20
|
|
|
20
|
-
declare class BybitAccountSyncer extends BaseAccountSyncer {
|
|
21
|
-
|
|
21
|
+
declare class BybitAccountSyncer extends BaseAccountSyncer<BybitRawExecution> {
|
|
22
|
+
private readonly creds;
|
|
23
|
+
constructor(config: AccountSyncerConfig);
|
|
24
|
+
protected listExternalFills({ since }: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
25
|
+
protected isReduceOnly(execution: BybitRawExecution): boolean;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export { BybitAccountSyncer };
|
|
@@ -1,8 +1,35 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchExecutionsSince } from '../../client/bybit/rest';
|
|
1
3
|
import { BaseAccountSyncer } from '../base';
|
|
2
4
|
|
|
3
5
|
class BybitAccountSyncer extends BaseAccountSyncer {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
creds;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.creds = config.credentials;
|
|
10
|
+
}
|
|
11
|
+
async listExternalFills({ since }) {
|
|
12
|
+
const executions = await fetchExecutionsSince(this.creds, since, this.demo);
|
|
13
|
+
return executions.reduce((acc, execution) => {
|
|
14
|
+
acc.push({
|
|
15
|
+
connector: "bybit",
|
|
16
|
+
exchangeFillId: execution.execId,
|
|
17
|
+
cloid: execution.orderLinkId || null,
|
|
18
|
+
symbol: execution.symbol,
|
|
19
|
+
price: execution.execPrice,
|
|
20
|
+
quantity: execution.execQty,
|
|
21
|
+
side: execution.side === "Buy" ? "BUY" : "SELL",
|
|
22
|
+
reduceOnly: this.isReduceOnly(execution),
|
|
23
|
+
timestamp: Number(execution.execTime),
|
|
24
|
+
tradeFee: new BigNumber(execution.execFee || 0).abs().toFixed(),
|
|
25
|
+
builderFee: "0",
|
|
26
|
+
liquidation: null
|
|
27
|
+
});
|
|
28
|
+
return acc;
|
|
29
|
+
}, []);
|
|
30
|
+
}
|
|
31
|
+
isReduceOnly(execution) {
|
|
32
|
+
return Number(execution.closedSize || "0") > 0;
|
|
6
33
|
}
|
|
7
34
|
}
|
|
8
35
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { UserFillsByTimeResponse } from '../../client/hyperliquid/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
3
4
|
import '@pear-protocol/core-sdk';
|
|
4
5
|
import '../../client/index.js';
|
|
5
6
|
import '../../types.js';
|
|
@@ -17,10 +18,11 @@ import '../../client/lighter/ws.js';
|
|
|
17
18
|
import '../../client/okx/exchange-ws.js';
|
|
18
19
|
import '../../client/okx/ws.js';
|
|
19
20
|
|
|
20
|
-
declare class HyperliquidAccountSyncer extends BaseAccountSyncer {
|
|
21
|
+
declare class HyperliquidAccountSyncer extends BaseAccountSyncer<UserFillsByTimeResponse> {
|
|
21
22
|
private readonly address;
|
|
22
23
|
constructor(config: AccountSyncerConfig);
|
|
23
|
-
protected listExternalFills(since
|
|
24
|
+
protected listExternalFills({ since, instruments, }: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
25
|
+
protected isReduceOnly(fill: UserFillsByTimeResponse): boolean;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export { HyperliquidAccountSyncer };
|
|
@@ -8,7 +8,14 @@ class HyperliquidAccountSyncer extends BaseAccountSyncer {
|
|
|
8
8
|
const creds = config.credentials;
|
|
9
9
|
this.address = creds.accountIdentifier;
|
|
10
10
|
}
|
|
11
|
-
async listExternalFills(
|
|
11
|
+
async listExternalFills({
|
|
12
|
+
since,
|
|
13
|
+
instruments
|
|
14
|
+
}) {
|
|
15
|
+
const instrumentSymbolToIdMap = instruments.reduce((acc, instrument) => {
|
|
16
|
+
acc[instrument.base] = instrument.id;
|
|
17
|
+
return acc;
|
|
18
|
+
}, {});
|
|
12
19
|
const result = await fetchInfo({
|
|
13
20
|
type: "userFillsByTime",
|
|
14
21
|
user: this.address,
|
|
@@ -29,7 +36,7 @@ class HyperliquidAccountSyncer extends BaseAccountSyncer {
|
|
|
29
36
|
price: fill.px,
|
|
30
37
|
quantity: fill.sz,
|
|
31
38
|
side: fill.side === "B" ? "BUY" : "SELL",
|
|
32
|
-
reduceOnly:
|
|
39
|
+
reduceOnly: this.isReduceOnly(fill),
|
|
33
40
|
timestamp: fill.time,
|
|
34
41
|
tradeFee: fill.fee,
|
|
35
42
|
builderFee: fill.builderFee ?? "0",
|
|
@@ -38,6 +45,9 @@ class HyperliquidAccountSyncer extends BaseAccountSyncer {
|
|
|
38
45
|
return acc;
|
|
39
46
|
}, []);
|
|
40
47
|
}
|
|
48
|
+
isReduceOnly(fill) {
|
|
49
|
+
return fill.dir.includes("Close");
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
export { HyperliquidAccountSyncer };
|
package/dist/syncer/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AccountSyncer, AccountSyncerConfig, BaseAccountSyncer } from './base.js';
|
|
1
|
+
export { AccountSyncer, AccountSyncerConfig, BaseAccountSyncer, ListExternalFillsParams } from './base.js';
|
|
2
2
|
export { BinanceAccountSyncer } from './binance/syncer.js';
|
|
3
3
|
export { BybitAccountSyncer } from './bybit/syncer.js';
|
|
4
4
|
export { createAccountSyncer } from './factory.js';
|
|
@@ -22,3 +22,8 @@ import '../client/lighter/exchange-ws.js';
|
|
|
22
22
|
import '../client/lighter/ws.js';
|
|
23
23
|
import '../client/okx/exchange-ws.js';
|
|
24
24
|
import '../client/okx/ws.js';
|
|
25
|
+
import '../client/binance/types.js';
|
|
26
|
+
import '../client/bybit/types.js';
|
|
27
|
+
import '../client/hyperliquid/types.js';
|
|
28
|
+
import '../client/lighter/types.js';
|
|
29
|
+
import '../client/okx/types.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { LighterRawTrade } from '../../client/lighter/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
3
4
|
import '@pear-protocol/core-sdk';
|
|
4
5
|
import '../../client/index.js';
|
|
5
6
|
import '../../types.js';
|
|
@@ -17,8 +18,20 @@ import '../../client/lighter/ws.js';
|
|
|
17
18
|
import '../../client/okx/exchange-ws.js';
|
|
18
19
|
import '../../client/okx/ws.js';
|
|
19
20
|
|
|
20
|
-
declare class LighterAccountSyncer extends BaseAccountSyncer {
|
|
21
|
-
|
|
21
|
+
declare class LighterAccountSyncer extends BaseAccountSyncer<LighterRawTrade> {
|
|
22
|
+
private readonly accountIndex;
|
|
23
|
+
private readonly apiKeyIndex;
|
|
24
|
+
private readonly apiPrivateKey;
|
|
25
|
+
constructor(config: AccountSyncerConfig);
|
|
26
|
+
protected listExternalFills({ since, instruments, }: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
27
|
+
protected isReduceOnly(trade: LighterRawTrade): boolean;
|
|
28
|
+
private getAccountSide;
|
|
29
|
+
private getClientOrderId;
|
|
30
|
+
private isMaker;
|
|
31
|
+
private getPositionSizeBefore;
|
|
32
|
+
private getTradeFee;
|
|
33
|
+
private getIntegratorFee;
|
|
34
|
+
private feeTickToDecimal;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
export { LighterAccountSyncer };
|
|
@@ -1,8 +1,93 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchTradesSince } from '../../client/lighter/rest';
|
|
1
3
|
import { BaseAccountSyncer } from '../base';
|
|
2
4
|
|
|
5
|
+
const LIGHTER_FEE_TICK_DENOMINATOR = new BigNumber(1e6);
|
|
3
6
|
class LighterAccountSyncer extends BaseAccountSyncer {
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
accountIndex;
|
|
8
|
+
apiKeyIndex;
|
|
9
|
+
apiPrivateKey;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super(config);
|
|
12
|
+
const creds = config.credentials;
|
|
13
|
+
this.accountIndex = Number(creds.accountIdentifier);
|
|
14
|
+
this.apiKeyIndex = creds.api_key_index;
|
|
15
|
+
this.apiPrivateKey = creds.api_private_key;
|
|
16
|
+
}
|
|
17
|
+
async listExternalFills({
|
|
18
|
+
since,
|
|
19
|
+
instruments
|
|
20
|
+
}) {
|
|
21
|
+
const { token } = await this.sdk.core.misc.lighter.createAuthToken({
|
|
22
|
+
accountIndex: this.accountIndex,
|
|
23
|
+
apiKeyIndex: this.apiKeyIndex,
|
|
24
|
+
apiPrivateKey: this.apiPrivateKey
|
|
25
|
+
});
|
|
26
|
+
const trades = await fetchTradesSince(this.accountIndex, token, since);
|
|
27
|
+
const instrumentIds = new Set(instruments.map((instrument) => instrument.id));
|
|
28
|
+
return trades.toSorted((a, b) => a.timestamp - b.timestamp).reduce((acc, trade) => {
|
|
29
|
+
const instrumentId = trade.market_id.toString();
|
|
30
|
+
if (!instrumentIds.has(instrumentId)) {
|
|
31
|
+
console.warn(`Lighter market id ${trade.market_id} not found in instrument id map.`);
|
|
32
|
+
return acc;
|
|
33
|
+
}
|
|
34
|
+
const accountSide = this.getAccountSide(trade);
|
|
35
|
+
if (!accountSide) return acc;
|
|
36
|
+
const side = accountSide === "ask" ? "SELL" : "BUY";
|
|
37
|
+
acc.push({
|
|
38
|
+
connector: "lighter",
|
|
39
|
+
exchangeFillId: trade.trade_id_str ?? trade.trade_id.toString(),
|
|
40
|
+
cloid: this.getClientOrderId(trade, accountSide),
|
|
41
|
+
symbol: instrumentId,
|
|
42
|
+
price: trade.price,
|
|
43
|
+
quantity: trade.size,
|
|
44
|
+
side,
|
|
45
|
+
reduceOnly: this.isReduceOnly(trade),
|
|
46
|
+
timestamp: trade.timestamp,
|
|
47
|
+
tradeFee: this.feeTickToDecimal(this.getTradeFee(trade, accountSide)),
|
|
48
|
+
builderFee: this.feeTickToDecimal(this.getIntegratorFee(trade, accountSide)),
|
|
49
|
+
liquidation: trade.type === "liquidation" ? { mark: trade.price } : null
|
|
50
|
+
});
|
|
51
|
+
return acc;
|
|
52
|
+
}, []);
|
|
53
|
+
}
|
|
54
|
+
isReduceOnly(trade) {
|
|
55
|
+
if (trade.type === "liquidation" || trade.type === "deleverage" || trade.type === "market-settlement") {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
const accountSide = this.getAccountSide(trade);
|
|
59
|
+
if (!accountSide) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const positionSizeBefore = this.getPositionSizeBefore(trade, accountSide);
|
|
63
|
+
if (positionSizeBefore.isZero()) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return positionSizeBefore.isPositive() && accountSide === "ask" || positionSizeBefore.isNegative() && accountSide === "bid";
|
|
67
|
+
}
|
|
68
|
+
getAccountSide(trade) {
|
|
69
|
+
if (trade.ask_account_id === this.accountIndex) return "ask";
|
|
70
|
+
if (trade.bid_account_id === this.accountIndex) return "bid";
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
getClientOrderId(trade, side) {
|
|
74
|
+
return (side === "ask" ? trade.ask_client_id_str : trade.bid_client_id_str) || null;
|
|
75
|
+
}
|
|
76
|
+
isMaker(trade, side) {
|
|
77
|
+
return side === "ask" ? trade.is_maker_ask : !trade.is_maker_ask;
|
|
78
|
+
}
|
|
79
|
+
getPositionSizeBefore(trade, side) {
|
|
80
|
+
const value = this.isMaker(trade, side) ? trade.maker_position_size_before : trade.taker_position_size_before;
|
|
81
|
+
return new BigNumber(value ?? 0);
|
|
82
|
+
}
|
|
83
|
+
getTradeFee(trade, side) {
|
|
84
|
+
return this.isMaker(trade, side) ? trade.maker_fee ?? 0 : trade.taker_fee ?? 0;
|
|
85
|
+
}
|
|
86
|
+
getIntegratorFee(trade, side) {
|
|
87
|
+
return this.isMaker(trade, side) ? trade.integrator_maker_fee ?? 0 : trade.integrator_taker_fee ?? 0;
|
|
88
|
+
}
|
|
89
|
+
feeTickToDecimal(value) {
|
|
90
|
+
return new BigNumber(value).abs().div(LIGHTER_FEE_TICK_DENOMINATOR).toFixed();
|
|
6
91
|
}
|
|
7
92
|
}
|
|
8
93
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { OkxRawFill } from '../../client/okx/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
3
4
|
import '@pear-protocol/core-sdk';
|
|
4
5
|
import '../../client/index.js';
|
|
5
6
|
import '../../types.js';
|
|
@@ -17,8 +18,12 @@ import '../../client/lighter/ws.js';
|
|
|
17
18
|
import '../../client/okx/exchange-ws.js';
|
|
18
19
|
import '../../client/okx/ws.js';
|
|
19
20
|
|
|
20
|
-
declare class OkxAccountSyncer extends BaseAccountSyncer {
|
|
21
|
-
|
|
21
|
+
declare class OkxAccountSyncer extends BaseAccountSyncer<OkxRawFill> {
|
|
22
|
+
private readonly creds;
|
|
23
|
+
constructor(config: AccountSyncerConfig);
|
|
24
|
+
protected listExternalFills({ since, instruments, }: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
25
|
+
protected isReduceOnly(fill: OkxRawFill): boolean;
|
|
26
|
+
private contractsToBase;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
export { OkxAccountSyncer };
|
|
@@ -1,8 +1,47 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchFillsHistorySince } from '../../client/okx/rest';
|
|
1
3
|
import { BaseAccountSyncer } from '../base';
|
|
2
4
|
|
|
3
5
|
class OkxAccountSyncer extends BaseAccountSyncer {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
creds;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.creds = config.credentials;
|
|
10
|
+
}
|
|
11
|
+
async listExternalFills({
|
|
12
|
+
since,
|
|
13
|
+
instruments
|
|
14
|
+
}) {
|
|
15
|
+
const fills = await fetchFillsHistorySince(this.creds, since, this.demo);
|
|
16
|
+
const contractSizeByInstrumentId = new Map(
|
|
17
|
+
instruments.map((instrument) => [instrument.id, instrument.precision.quantity.contractSize])
|
|
18
|
+
);
|
|
19
|
+
return fills.reduce((acc, fill) => {
|
|
20
|
+
const contractSize = contractSizeByInstrumentId.get(fill.instId) ?? 1;
|
|
21
|
+
acc.push({
|
|
22
|
+
connector: "okx",
|
|
23
|
+
exchangeFillId: fill.billId || fill.tradeId,
|
|
24
|
+
cloid: fill.clOrdId || null,
|
|
25
|
+
symbol: fill.instId,
|
|
26
|
+
price: fill.fillPx,
|
|
27
|
+
quantity: this.contractsToBase(fill.fillSz, contractSize),
|
|
28
|
+
side: fill.side === "buy" ? "BUY" : "SELL",
|
|
29
|
+
reduceOnly: this.isReduceOnly(fill),
|
|
30
|
+
timestamp: Number(fill.fillTime || fill.ts),
|
|
31
|
+
tradeFee: new BigNumber(fill.fee || 0).abs().toFixed(),
|
|
32
|
+
builderFee: "0",
|
|
33
|
+
liquidation: null
|
|
34
|
+
});
|
|
35
|
+
return acc;
|
|
36
|
+
}, []);
|
|
37
|
+
}
|
|
38
|
+
isReduceOnly(fill) {
|
|
39
|
+
if (fill.posSide === "long") return fill.side === "sell";
|
|
40
|
+
if (fill.posSide === "short") return fill.side === "buy";
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
contractsToBase(quantity, contractSize) {
|
|
44
|
+
return new BigNumber(quantity).times(contractSize).toFixed();
|
|
6
45
|
}
|
|
7
46
|
}
|
|
8
47
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/exchanges-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Pear Protocol Exchanges SDK",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"typecheck": "tsc --noEmit"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@pear-protocol/core-sdk": "^0.0.
|
|
29
|
-
"@pear-protocol/types": "^0.0.
|
|
30
|
-
"@pear-protocol/utils": "^0.0.
|
|
28
|
+
"@pear-protocol/core-sdk": "^0.0.21",
|
|
29
|
+
"@pear-protocol/types": "^0.0.18",
|
|
30
|
+
"@pear-protocol/utils": "^0.0.18",
|
|
31
31
|
"bignumber.js": "^11.1.1",
|
|
32
32
|
"partysocket": "^1.0.3",
|
|
33
33
|
"viem": "^2.48.8"
|