@lifi/perps-sdk-provider-lighter 22.1.1 → 24.0.0
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/cjs/LighterProvider.d.ts.map +1 -1
- package/dist/cjs/LighterProvider.js +57 -10
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +1 -0
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/pnl.d.ts +25 -0
- package/dist/cjs/types/pnl.d.ts.map +1 -0
- package/dist/cjs/types/pnl.js +3 -0
- package/dist/cjs/types/pnl.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +3 -1
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/mapMarketContext.d.ts.map +1 -1
- package/dist/cjs/utils/mapMarketContext.js +2 -1
- package/dist/cjs/utils/mapMarketContext.js.map +1 -1
- package/dist/cjs/utils/mapPortfolioHistory.d.ts +5 -0
- package/dist/cjs/utils/mapPortfolioHistory.d.ts.map +1 -0
- package/dist/cjs/utils/mapPortfolioHistory.js +40 -0
- package/dist/cjs/utils/mapPortfolioHistory.js.map +1 -0
- package/dist/cjs/utils/marketLimits.d.ts +2 -2
- package/dist/cjs/utils/marketLimits.d.ts.map +1 -1
- package/dist/cjs/utils/marketLimits.js +2 -1
- package/dist/cjs/utils/marketLimits.js.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.js +1 -5
- package/dist/cjs/websocket/LighterWsProvider.js.map +1 -1
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +68 -12
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/esm/types/index.js +1 -0
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/pnl.d.ts +33 -0
- package/dist/esm/types/pnl.d.ts.map +1 -0
- package/dist/esm/types/pnl.js +4 -0
- package/dist/esm/types/pnl.js.map +1 -0
- package/dist/esm/types/ws.d.ts +2 -2
- package/dist/esm/utils/index.d.ts +1 -0
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/mapMarketContext.d.ts.map +1 -1
- package/dist/esm/utils/mapMarketContext.js +2 -1
- package/dist/esm/utils/mapMarketContext.js.map +1 -1
- package/dist/esm/utils/mapPortfolioHistory.d.ts +12 -0
- package/dist/esm/utils/mapPortfolioHistory.d.ts.map +1 -0
- package/dist/esm/utils/mapPortfolioHistory.js +40 -0
- package/dist/esm/utils/mapPortfolioHistory.js.map +1 -0
- package/dist/esm/utils/marketLimits.d.ts +3 -3
- package/dist/esm/utils/marketLimits.d.ts.map +1 -1
- package/dist/esm/utils/marketLimits.js +3 -2
- package/dist/esm/utils/marketLimits.js.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.d.ts +1 -1
- package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.js +2 -12
- package/dist/esm/websocket/LighterWsProvider.js.map +1 -1
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/types/pnl.d.ts +33 -0
- package/dist/types/types/pnl.d.ts.map +1 -0
- package/dist/types/types/ws.d.ts +2 -2
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/mapMarketContext.d.ts.map +1 -1
- package/dist/types/utils/mapPortfolioHistory.d.ts +12 -0
- package/dist/types/utils/mapPortfolioHistory.d.ts.map +1 -0
- package/dist/types/utils/marketLimits.d.ts +3 -3
- package/dist/types/utils/marketLimits.d.ts.map +1 -1
- package/dist/types/websocket/LighterWsProvider.d.ts +1 -1
- package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/LighterProvider.ts +120 -25
- package/src/types/index.ts +1 -0
- package/src/types/pnl.ts +36 -0
- package/src/types/ws.ts +2 -2
- package/src/utils/index.ts +1 -0
- package/src/utils/mapMarketContext.ts +3 -1
- package/src/utils/mapPortfolioHistory.ts +52 -0
- package/src/utils/marketLimits.ts +5 -4
- package/src/websocket/LighterWsProvider.ts +10 -16
package/src/types/pnl.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Account PnL shapes returned by Lighter's `/api/v1/pnl` endpoint.
|
|
2
|
+
// Lighter serializes an empty list as JSON `null`, so list members are nullable.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* One PnL bucket. `timestamp` is Unix milliseconds. Every other field is a
|
|
6
|
+
* JSON number in USD for that bucket alone; the endpoint reports no account
|
|
7
|
+
* value.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export interface LtPnLEntry {
|
|
12
|
+
timestamp: number
|
|
13
|
+
trade_pnl: number
|
|
14
|
+
inflow: number
|
|
15
|
+
outflow: number
|
|
16
|
+
pool_pnl: number
|
|
17
|
+
pool_inflow: number
|
|
18
|
+
pool_outflow: number
|
|
19
|
+
pool_total_shares: number
|
|
20
|
+
spot_inflow: number
|
|
21
|
+
spot_outflow: number
|
|
22
|
+
staked_lit: number
|
|
23
|
+
staking_inflow: number
|
|
24
|
+
staking_outflow: number
|
|
25
|
+
staking_pnl: number
|
|
26
|
+
trade_spot_pnl: number
|
|
27
|
+
volume: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** `GET /api/v1/pnl` response envelope. @public */
|
|
31
|
+
export interface LtAccountPnL {
|
|
32
|
+
code: number
|
|
33
|
+
message?: string
|
|
34
|
+
resolution: string
|
|
35
|
+
pnl: LtPnLEntry[] | null
|
|
36
|
+
}
|
package/src/types/ws.ts
CHANGED
|
@@ -33,8 +33,8 @@ export type LtWsPingMessage = LtWsMessage & { type: 'ping' }
|
|
|
33
33
|
/**
|
|
34
34
|
* Perpetual-market statistics pushed by Lighter's `market_stats` channels.
|
|
35
35
|
* Price, interest, and funding values are decimal strings; the daily volume
|
|
36
|
-
* and change figures are JSON numbers
|
|
37
|
-
* timestamp in milliseconds.
|
|
36
|
+
* and change figures are JSON numbers. `funding_timestamp` is the last funding
|
|
37
|
+
* payment time as a Unix timestamp in milliseconds.
|
|
38
38
|
*
|
|
39
39
|
* @public
|
|
40
40
|
*/
|
package/src/utils/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ export {
|
|
|
25
25
|
mapStatusReason,
|
|
26
26
|
mapTriggerOrder,
|
|
27
27
|
} from './mapOrder.js'
|
|
28
|
+
export { mapPortfolioHistory } from './mapPortfolioHistory.js'
|
|
28
29
|
export { leverageFromImf, mapPosition } from './mapPosition.js'
|
|
29
30
|
export { mapMarketOrderLimits } from './marketLimits.js'
|
|
30
31
|
export { mapInterval } from './ohlcvInterval.js'
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { MarketContext } from '@lifi/perps-types'
|
|
2
2
|
import type { LtWsMarketStats, LtWsSpotMarketStats } from '../types/index.js'
|
|
3
3
|
|
|
4
|
+
const FUNDING_PERIOD_MS = 60 * 60 * 1000
|
|
5
|
+
|
|
4
6
|
const isPerpStats = (
|
|
5
7
|
stats: LtWsMarketStats | LtWsSpotMarketStats
|
|
6
8
|
): stats is LtWsMarketStats => 'mark_price' in stats
|
|
@@ -27,7 +29,7 @@ export const mapMarketContext = (
|
|
|
27
29
|
openInterest: stats.open_interest,
|
|
28
30
|
funding: {
|
|
29
31
|
rate: stats.current_funding_rate,
|
|
30
|
-
nextFundingTime: stats.funding_timestamp,
|
|
32
|
+
nextFundingTime: stats.funding_timestamp + FUNDING_PERIOD_MS,
|
|
31
33
|
},
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PortfolioHistoryRange,
|
|
3
|
+
PortfolioHistoryResponse,
|
|
4
|
+
} from '@lifi/perps-types'
|
|
5
|
+
import Big from 'big.js'
|
|
6
|
+
import type { LtPnLEntry } from '../types/pnl.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build the portfolio series from Lighter PnL buckets and the account's
|
|
10
|
+
* current value. Lighter reports no historical account value, so each
|
|
11
|
+
* point's `accountValue` is `currentValue` with every later bucket's
|
|
12
|
+
* `trade_pnl + trade_spot_pnl + inflow - outflow` removed. `pnl` is the
|
|
13
|
+
* running `trade_pnl + trade_spot_pnl`, so both series unwind the same PnL.
|
|
14
|
+
*/
|
|
15
|
+
export const mapPortfolioHistory = (
|
|
16
|
+
range: PortfolioHistoryRange,
|
|
17
|
+
entries: LtPnLEntry[],
|
|
18
|
+
currentValue: Big
|
|
19
|
+
): PortfolioHistoryResponse => {
|
|
20
|
+
const ordered = [...entries].sort((a, b) => a.timestamp - b.timestamp)
|
|
21
|
+
|
|
22
|
+
const accountValues: Big[] = []
|
|
23
|
+
let value = currentValue
|
|
24
|
+
for (let i = ordered.length - 1; i >= 0; i -= 1) {
|
|
25
|
+
accountValues[i] = value
|
|
26
|
+
const entry = ordered[i]
|
|
27
|
+
value = value
|
|
28
|
+
.minus(new Big(entry.trade_pnl))
|
|
29
|
+
.minus(new Big(entry.trade_spot_pnl))
|
|
30
|
+
.minus(new Big(entry.inflow))
|
|
31
|
+
.plus(new Big(entry.outflow))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let pnl = new Big(0)
|
|
35
|
+
let volume = new Big(0)
|
|
36
|
+
const points = ordered.map((entry, i) => {
|
|
37
|
+
pnl = pnl.plus(new Big(entry.trade_pnl)).plus(new Big(entry.trade_spot_pnl))
|
|
38
|
+
volume = volume.plus(new Big(entry.volume))
|
|
39
|
+
return {
|
|
40
|
+
timestamp: entry.timestamp,
|
|
41
|
+
accountValue: accountValues[i].toFixed(),
|
|
42
|
+
pnl: pnl.toFixed(),
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
range,
|
|
48
|
+
points,
|
|
49
|
+
volume: volume.toFixed(),
|
|
50
|
+
totalPnl: points.at(-1)?.pnl,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -3,25 +3,26 @@ import type { LtPerpsOrderBookDetail } from '../types/index.js'
|
|
|
3
3
|
|
|
4
4
|
type MarketOrderLimits = Pick<
|
|
5
5
|
PerpsMarket,
|
|
6
|
-
'maxMarketOrderUsd' | 'maxLimitOrderUsd'
|
|
6
|
+
'maxMarketOrderUsd' | 'maxLimitOrderUsd' | 'minOrderValueUsd'
|
|
7
7
|
>
|
|
8
8
|
|
|
9
9
|
type LighterMarketLimitSource = Pick<
|
|
10
10
|
LtPerpsOrderBookDetail,
|
|
11
|
-
'order_quote_limit'
|
|
11
|
+
'order_quote_limit' | 'min_quote_amount'
|
|
12
12
|
>
|
|
13
13
|
|
|
14
14
|
const NO_ORDER_QUOTE_LIMIT = '281474976.710655'
|
|
15
15
|
|
|
16
|
-
/** Map the order-value
|
|
16
|
+
/** Map the order-value limits published by Lighter's order-book metadata. */
|
|
17
17
|
export const mapMarketOrderLimits = (
|
|
18
18
|
market: LighterMarketLimitSource
|
|
19
19
|
): MarketOrderLimits => {
|
|
20
20
|
if (market.order_quote_limit === NO_ORDER_QUOTE_LIMIT) {
|
|
21
|
-
return {}
|
|
21
|
+
return { minOrderValueUsd: market.min_quote_amount }
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
return {
|
|
25
|
+
minOrderValueUsd: market.min_quote_amount,
|
|
25
26
|
maxMarketOrderUsd: market.order_quote_limit,
|
|
26
27
|
maxLimitOrderUsd: market.order_quote_limit,
|
|
27
28
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
toPerpsMarketDisplay,
|
|
14
14
|
WsProviderBase,
|
|
15
15
|
type WsProviderFactory,
|
|
16
|
+
type WsProviderFactoryParams,
|
|
16
17
|
wsLog,
|
|
17
18
|
} from '@lifi/perps-sdk'
|
|
18
19
|
import {
|
|
@@ -159,7 +160,7 @@ export interface LighterWsProviderOptions {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
/**
|
|
162
|
-
* Lighter
|
|
163
|
+
* Lighter WebSocket provider (extends {@link WsProviderBase}): subscribes to
|
|
163
164
|
* Lighter's WS channels (orderbook, marketsContext, orders, positions), attaching auth
|
|
164
165
|
* tokens to gated channels. Construct via {@link lighterWsProvider}.
|
|
165
166
|
*
|
|
@@ -257,14 +258,6 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
257
258
|
}
|
|
258
259
|
|
|
259
260
|
protected async openChannel(sub: Subscription): Promise<() => void> {
|
|
260
|
-
// Lighter has no live OHLC channel — there's nothing to subscribe to.
|
|
261
|
-
// Return a no-op unsubscribe so the caller's UX (chart still rendering
|
|
262
|
-
// from REST history + price-tick mid line) is unaffected, instead of
|
|
263
|
-
// throwing and surfacing a console error on every chart mount.
|
|
264
|
-
if (sub.channel === 'candle') {
|
|
265
|
-
return () => {}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
261
|
// Only the auth channels (orders/fills/positions) resolve markets.
|
|
269
262
|
// `marketsContext`/`orderbook` are keyed purely by `String(market_id)`, so
|
|
270
263
|
// gating them on the registry sync would let a failed `/markets` fetch kill
|
|
@@ -377,10 +370,7 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
377
370
|
return `positions:${sub.address.toLowerCase()}`
|
|
378
371
|
case 'accountSummary':
|
|
379
372
|
return `accountSummary:${sub.address.toLowerCase()}`
|
|
380
|
-
// No live wire sub (openChannel returns a no-op), but a stable key is
|
|
381
|
-
// still needed so the base's fan-out registry can track/release it.
|
|
382
373
|
case 'candle':
|
|
383
|
-
return `candle:${sub.marketId}:${sub.interval}`
|
|
384
374
|
case 'spotBalances':
|
|
385
375
|
throw new Error(`Lighter WS does not support channel: ${sub.channel}.`)
|
|
386
376
|
}
|
|
@@ -1006,7 +996,11 @@ function collectAuthChannelItems<T>(
|
|
|
1006
996
|
*
|
|
1007
997
|
* @public
|
|
1008
998
|
*/
|
|
1009
|
-
export const lighterWsProvider =
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
999
|
+
export const lighterWsProvider = (
|
|
1000
|
+
options?: LighterWsProviderOptions
|
|
1001
|
+
): WsProviderFactory =>
|
|
1002
|
+
Object.assign(
|
|
1003
|
+
({ provider, wsUrl, client }: WsProviderFactoryParams) =>
|
|
1004
|
+
new LighterWsProvider(wsUrl, provider, options, client),
|
|
1005
|
+
{ streamsCandles: false }
|
|
1006
|
+
)
|