@pear-protocol/exchanges-sdk 0.1.1 → 0.1.4
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/hyperliquid/types.d.ts +76 -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 +2 -1
- package/dist/helper/lighter/helper.d.ts +2 -1
- package/dist/helper/lighter/index.d.ts +3 -2
- package/dist/helper/lighter/index.js +1 -1
- package/dist/helper/lighter/types.d.ts +1 -0
- package/dist/helper/types.d.ts +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/sdk.d.ts +3 -2
- package/dist/sdk.js +4 -3
- package/dist/syncer/base.d.ts +20 -6
- package/dist/syncer/base.js +47 -2
- package/dist/syncer/binance/syncer.d.ts +31 -0
- package/dist/syncer/binance/syncer.js +82 -0
- package/dist/syncer/bybit/syncer.d.ts +28 -0
- package/dist/syncer/bybit/syncer.js +36 -0
- package/dist/syncer/factory.d.ts +22 -0
- package/dist/syncer/factory.js +26 -0
- package/dist/syncer/hyperliquid/syncer.d.ts +28 -0
- package/dist/syncer/hyperliquid/syncer.js +53 -0
- package/dist/syncer/index.d.ts +12 -1
- package/dist/syncer/index.js +6 -0
- package/dist/syncer/lighter/syncer.d.ts +37 -0
- package/dist/syncer/lighter/syncer.js +94 -0
- package/dist/syncer/okx/syncer.d.ts +29 -0
- package/dist/syncer/okx/syncer.js +48 -0
- package/package.json +7 -9
package/dist/syncer/base.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import PearSDK from '@pear-protocol/core-sdk';
|
|
2
|
-
import { Connector } from '@pear-protocol/types';
|
|
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';
|
|
@@ -16,18 +16,32 @@ import '../client/lighter/ws.js';
|
|
|
16
16
|
import '../client/okx/exchange-ws.js';
|
|
17
17
|
import '../client/okx/ws.js';
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
interface AccountSyncerConfig {
|
|
20
20
|
sdk: PearSDK;
|
|
21
|
+
connector: Connector;
|
|
21
22
|
credentials: SupportedCredentials;
|
|
22
23
|
ws: ExchangeWsByConnector[Connector];
|
|
23
24
|
demo: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
25
|
+
}
|
|
26
|
+
interface AccountSyncer {
|
|
27
|
+
synchronize(): Promise<void>;
|
|
28
|
+
}
|
|
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 {
|
|
26
36
|
protected sdk: PearSDK;
|
|
37
|
+
protected connector: Connector;
|
|
27
38
|
protected credentials: SupportedCredentials;
|
|
28
39
|
protected ws: ExchangeWsByConnector[Connector];
|
|
29
40
|
protected demo: boolean;
|
|
30
|
-
constructor(config:
|
|
41
|
+
constructor(config: AccountSyncerConfig);
|
|
42
|
+
protected abstract listExternalFills(params: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
43
|
+
protected abstract isReduceOnly(fill: ExternalFill): boolean;
|
|
44
|
+
synchronize(): Promise<void>;
|
|
31
45
|
}
|
|
32
46
|
|
|
33
|
-
export {
|
|
47
|
+
export { type AccountSyncer, type AccountSyncerConfig, BaseAccountSyncer, type ListExternalFillsParams };
|
package/dist/syncer/base.js
CHANGED
|
@@ -1,14 +1,59 @@
|
|
|
1
|
-
|
|
1
|
+
import { computeSyncPayload } from '@pear-protocol/utils';
|
|
2
|
+
|
|
3
|
+
class BaseAccountSyncer {
|
|
2
4
|
sdk;
|
|
5
|
+
connector;
|
|
3
6
|
credentials;
|
|
4
7
|
ws;
|
|
5
8
|
demo;
|
|
6
9
|
constructor(config) {
|
|
7
10
|
this.sdk = config.sdk;
|
|
11
|
+
this.connector = config.connector;
|
|
8
12
|
this.credentials = config.credentials;
|
|
9
13
|
this.ws = config.ws;
|
|
10
14
|
this.demo = config.demo;
|
|
11
15
|
}
|
|
16
|
+
async synchronize() {
|
|
17
|
+
const [{ positions }, { fills: provisionalFills }] = await Promise.all([
|
|
18
|
+
this.sdk.core.positions.openLite(),
|
|
19
|
+
this.sdk.core.fills.provisionals()
|
|
20
|
+
]);
|
|
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 }),
|
|
26
|
+
this.sdk.core.sync.since({
|
|
27
|
+
overlapMs: 6e4,
|
|
28
|
+
nowMs: Date.now(),
|
|
29
|
+
bootstrapLookbackMs: 30 * 24 * 60 * 60 * 1e3
|
|
30
|
+
})
|
|
31
|
+
]);
|
|
32
|
+
const externalFills = await this.listExternalFills({
|
|
33
|
+
since: new Date(since.since),
|
|
34
|
+
instruments,
|
|
35
|
+
openPositions: positions,
|
|
36
|
+
provisionalFills
|
|
37
|
+
});
|
|
38
|
+
const payload = await computeSyncPayload({
|
|
39
|
+
openPositions: positions,
|
|
40
|
+
provisionalFills,
|
|
41
|
+
confirmedExchangeFillIds: since.confirmedExchangeFillIds ?? [],
|
|
42
|
+
externalFills
|
|
43
|
+
});
|
|
44
|
+
if (!payload) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (payload.reconcilables) {
|
|
48
|
+
await this.sdk.core.sync.reconcile(payload.reconcilables);
|
|
49
|
+
}
|
|
50
|
+
if (payload.confirmables) {
|
|
51
|
+
await this.sdk.core.sync.confirm(payload.confirmables);
|
|
52
|
+
}
|
|
53
|
+
if (payload.creatables) {
|
|
54
|
+
await this.sdk.core.sync.create(payload.creatables);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
12
57
|
}
|
|
13
58
|
|
|
14
|
-
export {
|
|
59
|
+
export { BaseAccountSyncer };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { BinanceRawUserTrade } from '../../client/binance/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
4
|
+
import '@pear-protocol/core-sdk';
|
|
5
|
+
import '../../client/index.js';
|
|
6
|
+
import '../../types.js';
|
|
7
|
+
import '../../client/binance/exchange-ws.js';
|
|
8
|
+
import '../../client/base-ws.js';
|
|
9
|
+
import 'partysocket';
|
|
10
|
+
import '../../client/exchange-ws.js';
|
|
11
|
+
import '../../client/binance/ws.js';
|
|
12
|
+
import '../../client/bybit/exchange-ws.js';
|
|
13
|
+
import '../../client/bybit/ws.js';
|
|
14
|
+
import '../../client/hyperliquid/exchange-ws.js';
|
|
15
|
+
import '../../client/hyperliquid/ws.js';
|
|
16
|
+
import '../../client/lighter/exchange-ws.js';
|
|
17
|
+
import '../../client/lighter/ws.js';
|
|
18
|
+
import '../../client/okx/exchange-ws.js';
|
|
19
|
+
import '../../client/okx/ws.js';
|
|
20
|
+
|
|
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;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { BinanceAccountSyncer };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchUserTradesSince, fetchAllOrdersSince } from '../../client/binance/rest';
|
|
3
|
+
import { BaseAccountSyncer } from '../base';
|
|
4
|
+
|
|
5
|
+
class BinanceAccountSyncer extends BaseAccountSyncer {
|
|
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}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { BinanceAccountSyncer };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { BybitRawExecution } from '../../client/bybit/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
4
|
+
import '@pear-protocol/core-sdk';
|
|
5
|
+
import '../../client/index.js';
|
|
6
|
+
import '../../types.js';
|
|
7
|
+
import '../../client/binance/exchange-ws.js';
|
|
8
|
+
import '../../client/base-ws.js';
|
|
9
|
+
import 'partysocket';
|
|
10
|
+
import '../../client/exchange-ws.js';
|
|
11
|
+
import '../../client/binance/ws.js';
|
|
12
|
+
import '../../client/bybit/exchange-ws.js';
|
|
13
|
+
import '../../client/bybit/ws.js';
|
|
14
|
+
import '../../client/hyperliquid/exchange-ws.js';
|
|
15
|
+
import '../../client/hyperliquid/ws.js';
|
|
16
|
+
import '../../client/lighter/exchange-ws.js';
|
|
17
|
+
import '../../client/lighter/ws.js';
|
|
18
|
+
import '../../client/okx/exchange-ws.js';
|
|
19
|
+
import '../../client/okx/ws.js';
|
|
20
|
+
|
|
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;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { BybitAccountSyncer };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchExecutionsSince } from '../../client/bybit/rest';
|
|
3
|
+
import { BaseAccountSyncer } from '../base';
|
|
4
|
+
|
|
5
|
+
class BybitAccountSyncer extends BaseAccountSyncer {
|
|
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;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { BybitAccountSyncer };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Connector } from '@pear-protocol/types';
|
|
2
|
+
import { AccountSyncerConfig, AccountSyncer } from './base.js';
|
|
3
|
+
import '@pear-protocol/core-sdk';
|
|
4
|
+
import '../client/index.js';
|
|
5
|
+
import '../types.js';
|
|
6
|
+
import '../client/binance/exchange-ws.js';
|
|
7
|
+
import '../client/base-ws.js';
|
|
8
|
+
import 'partysocket';
|
|
9
|
+
import '../client/exchange-ws.js';
|
|
10
|
+
import '../client/binance/ws.js';
|
|
11
|
+
import '../client/bybit/exchange-ws.js';
|
|
12
|
+
import '../client/bybit/ws.js';
|
|
13
|
+
import '../client/hyperliquid/exchange-ws.js';
|
|
14
|
+
import '../client/hyperliquid/ws.js';
|
|
15
|
+
import '../client/lighter/exchange-ws.js';
|
|
16
|
+
import '../client/lighter/ws.js';
|
|
17
|
+
import '../client/okx/exchange-ws.js';
|
|
18
|
+
import '../client/okx/ws.js';
|
|
19
|
+
|
|
20
|
+
declare function createAccountSyncer(exchange: Connector, config: AccountSyncerConfig): AccountSyncer;
|
|
21
|
+
|
|
22
|
+
export { createAccountSyncer };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BinanceAccountSyncer } from './binance/syncer';
|
|
2
|
+
import { BybitAccountSyncer } from './bybit/syncer';
|
|
3
|
+
import { HyperliquidAccountSyncer } from './hyperliquid/syncer';
|
|
4
|
+
import { LighterAccountSyncer } from './lighter/syncer';
|
|
5
|
+
import { OkxAccountSyncer } from './okx/syncer';
|
|
6
|
+
|
|
7
|
+
function createAccountSyncer(exchange, config) {
|
|
8
|
+
switch (exchange) {
|
|
9
|
+
case "binance":
|
|
10
|
+
return new BinanceAccountSyncer(config);
|
|
11
|
+
case "bybit":
|
|
12
|
+
return new BybitAccountSyncer(config);
|
|
13
|
+
case "okx":
|
|
14
|
+
return new OkxAccountSyncer(config);
|
|
15
|
+
case "hyperliquid":
|
|
16
|
+
return new HyperliquidAccountSyncer(config);
|
|
17
|
+
case "lighter":
|
|
18
|
+
return new LighterAccountSyncer(config);
|
|
19
|
+
default: {
|
|
20
|
+
const _exhaustive = exchange;
|
|
21
|
+
throw new Error(`Unsupported exchange: ${String(_exhaustive)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { createAccountSyncer };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { UserFillsByTimeResponse } from '../../client/hyperliquid/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
4
|
+
import '@pear-protocol/core-sdk';
|
|
5
|
+
import '../../client/index.js';
|
|
6
|
+
import '../../types.js';
|
|
7
|
+
import '../../client/binance/exchange-ws.js';
|
|
8
|
+
import '../../client/base-ws.js';
|
|
9
|
+
import 'partysocket';
|
|
10
|
+
import '../../client/exchange-ws.js';
|
|
11
|
+
import '../../client/binance/ws.js';
|
|
12
|
+
import '../../client/bybit/exchange-ws.js';
|
|
13
|
+
import '../../client/bybit/ws.js';
|
|
14
|
+
import '../../client/hyperliquid/exchange-ws.js';
|
|
15
|
+
import '../../client/hyperliquid/ws.js';
|
|
16
|
+
import '../../client/lighter/exchange-ws.js';
|
|
17
|
+
import '../../client/lighter/ws.js';
|
|
18
|
+
import '../../client/okx/exchange-ws.js';
|
|
19
|
+
import '../../client/okx/ws.js';
|
|
20
|
+
|
|
21
|
+
declare class HyperliquidAccountSyncer extends BaseAccountSyncer<UserFillsByTimeResponse> {
|
|
22
|
+
private readonly address;
|
|
23
|
+
constructor(config: AccountSyncerConfig);
|
|
24
|
+
protected listExternalFills({ since, instruments, }: ListExternalFillsParams): Promise<HttpTypes.UnknownExternalFill[]>;
|
|
25
|
+
protected isReduceOnly(fill: UserFillsByTimeResponse): boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { HyperliquidAccountSyncer };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { fetchInfo } from '../../client/hyperliquid/rest';
|
|
2
|
+
import { BaseAccountSyncer } from '../base';
|
|
3
|
+
|
|
4
|
+
class HyperliquidAccountSyncer extends BaseAccountSyncer {
|
|
5
|
+
address;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
super(config);
|
|
8
|
+
const creds = config.credentials;
|
|
9
|
+
this.address = creds.accountIdentifier;
|
|
10
|
+
}
|
|
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
|
+
}, {});
|
|
19
|
+
const result = await fetchInfo({
|
|
20
|
+
type: "userFillsByTime",
|
|
21
|
+
user: this.address,
|
|
22
|
+
startTime: since.getTime(),
|
|
23
|
+
aggregateByTime: true
|
|
24
|
+
});
|
|
25
|
+
return result.reduce((acc, fill) => {
|
|
26
|
+
const instrumentId = instrumentSymbolToIdMap[fill.coin];
|
|
27
|
+
if (!instrumentId) {
|
|
28
|
+
console.warn(`Instrument symbol ${fill.coin} not found in instrument symbol to id map.`);
|
|
29
|
+
return acc;
|
|
30
|
+
}
|
|
31
|
+
acc.push({
|
|
32
|
+
connector: "hyperliquid",
|
|
33
|
+
exchangeFillId: fill.tid.toFixed(),
|
|
34
|
+
cloid: fill.cloid ?? null,
|
|
35
|
+
symbol: instrumentId,
|
|
36
|
+
price: fill.px,
|
|
37
|
+
quantity: fill.sz,
|
|
38
|
+
side: fill.side === "B" ? "BUY" : "SELL",
|
|
39
|
+
reduceOnly: this.isReduceOnly(fill),
|
|
40
|
+
timestamp: fill.time,
|
|
41
|
+
tradeFee: fill.fee,
|
|
42
|
+
builderFee: fill.builderFee ?? "0",
|
|
43
|
+
liquidation: fill.liquidation ? { mark: fill.liquidation.markPx } : null
|
|
44
|
+
});
|
|
45
|
+
return acc;
|
|
46
|
+
}, []);
|
|
47
|
+
}
|
|
48
|
+
isReduceOnly(fill) {
|
|
49
|
+
return fill.dir.includes("Close");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { HyperliquidAccountSyncer };
|
package/dist/syncer/index.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { AccountSyncer, AccountSyncerConfig, BaseAccountSyncer, ListExternalFillsParams } from './base.js';
|
|
2
|
+
export { BinanceAccountSyncer } from './binance/syncer.js';
|
|
3
|
+
export { BybitAccountSyncer } from './bybit/syncer.js';
|
|
4
|
+
export { createAccountSyncer } from './factory.js';
|
|
5
|
+
export { HyperliquidAccountSyncer } from './hyperliquid/syncer.js';
|
|
6
|
+
export { LighterAccountSyncer } from './lighter/syncer.js';
|
|
7
|
+
export { OkxAccountSyncer } from './okx/syncer.js';
|
|
2
8
|
import '@pear-protocol/core-sdk';
|
|
3
9
|
import '@pear-protocol/types';
|
|
4
10
|
import '../client/index.js';
|
|
@@ -16,3 +22,8 @@ import '../client/lighter/exchange-ws.js';
|
|
|
16
22
|
import '../client/lighter/ws.js';
|
|
17
23
|
import '../client/okx/exchange-ws.js';
|
|
18
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';
|
package/dist/syncer/index.js
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { LighterRawTrade } from '../../client/lighter/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
4
|
+
import '@pear-protocol/core-sdk';
|
|
5
|
+
import '../../client/index.js';
|
|
6
|
+
import '../../types.js';
|
|
7
|
+
import '../../client/binance/exchange-ws.js';
|
|
8
|
+
import '../../client/base-ws.js';
|
|
9
|
+
import 'partysocket';
|
|
10
|
+
import '../../client/exchange-ws.js';
|
|
11
|
+
import '../../client/binance/ws.js';
|
|
12
|
+
import '../../client/bybit/exchange-ws.js';
|
|
13
|
+
import '../../client/bybit/ws.js';
|
|
14
|
+
import '../../client/hyperliquid/exchange-ws.js';
|
|
15
|
+
import '../../client/hyperliquid/ws.js';
|
|
16
|
+
import '../../client/lighter/exchange-ws.js';
|
|
17
|
+
import '../../client/lighter/ws.js';
|
|
18
|
+
import '../../client/okx/exchange-ws.js';
|
|
19
|
+
import '../../client/okx/ws.js';
|
|
20
|
+
|
|
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;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { LighterAccountSyncer };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchTradesSince } from '../../client/lighter/rest';
|
|
3
|
+
import { BaseAccountSyncer } from '../base';
|
|
4
|
+
|
|
5
|
+
const LIGHTER_FEE_TICK_DENOMINATOR = new BigNumber(1e6);
|
|
6
|
+
class LighterAccountSyncer extends BaseAccountSyncer {
|
|
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();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { LighterAccountSyncer };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HttpTypes } from '@pear-protocol/types';
|
|
2
|
+
import { OkxRawFill } from '../../client/okx/types.js';
|
|
3
|
+
import { BaseAccountSyncer, AccountSyncerConfig, ListExternalFillsParams } from '../base.js';
|
|
4
|
+
import '@pear-protocol/core-sdk';
|
|
5
|
+
import '../../client/index.js';
|
|
6
|
+
import '../../types.js';
|
|
7
|
+
import '../../client/binance/exchange-ws.js';
|
|
8
|
+
import '../../client/base-ws.js';
|
|
9
|
+
import 'partysocket';
|
|
10
|
+
import '../../client/exchange-ws.js';
|
|
11
|
+
import '../../client/binance/ws.js';
|
|
12
|
+
import '../../client/bybit/exchange-ws.js';
|
|
13
|
+
import '../../client/bybit/ws.js';
|
|
14
|
+
import '../../client/hyperliquid/exchange-ws.js';
|
|
15
|
+
import '../../client/hyperliquid/ws.js';
|
|
16
|
+
import '../../client/lighter/exchange-ws.js';
|
|
17
|
+
import '../../client/lighter/ws.js';
|
|
18
|
+
import '../../client/okx/exchange-ws.js';
|
|
19
|
+
import '../../client/okx/ws.js';
|
|
20
|
+
|
|
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;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { OkxAccountSyncer };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { fetchFillsHistorySince } from '../../client/okx/rest';
|
|
3
|
+
import { BaseAccountSyncer } from '../base';
|
|
4
|
+
|
|
5
|
+
class OkxAccountSyncer extends BaseAccountSyncer {
|
|
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();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { OkxAccountSyncer };
|