@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.
Files changed (85) hide show
  1. package/dist/cjs/LighterProvider.d.ts.map +1 -1
  2. package/dist/cjs/LighterProvider.js +57 -10
  3. package/dist/cjs/LighterProvider.js.map +1 -1
  4. package/dist/cjs/types/index.d.ts +1 -0
  5. package/dist/cjs/types/index.d.ts.map +1 -1
  6. package/dist/cjs/types/index.js +1 -0
  7. package/dist/cjs/types/index.js.map +1 -1
  8. package/dist/cjs/types/pnl.d.ts +25 -0
  9. package/dist/cjs/types/pnl.d.ts.map +1 -0
  10. package/dist/cjs/types/pnl.js +3 -0
  11. package/dist/cjs/types/pnl.js.map +1 -0
  12. package/dist/cjs/utils/index.d.ts +1 -0
  13. package/dist/cjs/utils/index.d.ts.map +1 -1
  14. package/dist/cjs/utils/index.js +3 -1
  15. package/dist/cjs/utils/index.js.map +1 -1
  16. package/dist/cjs/utils/mapMarketContext.d.ts.map +1 -1
  17. package/dist/cjs/utils/mapMarketContext.js +2 -1
  18. package/dist/cjs/utils/mapMarketContext.js.map +1 -1
  19. package/dist/cjs/utils/mapPortfolioHistory.d.ts +5 -0
  20. package/dist/cjs/utils/mapPortfolioHistory.d.ts.map +1 -0
  21. package/dist/cjs/utils/mapPortfolioHistory.js +40 -0
  22. package/dist/cjs/utils/mapPortfolioHistory.js.map +1 -0
  23. package/dist/cjs/utils/marketLimits.d.ts +2 -2
  24. package/dist/cjs/utils/marketLimits.d.ts.map +1 -1
  25. package/dist/cjs/utils/marketLimits.js +2 -1
  26. package/dist/cjs/utils/marketLimits.js.map +1 -1
  27. package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -1
  28. package/dist/cjs/websocket/LighterWsProvider.js +1 -5
  29. package/dist/cjs/websocket/LighterWsProvider.js.map +1 -1
  30. package/dist/esm/LighterProvider.d.ts.map +1 -1
  31. package/dist/esm/LighterProvider.js +68 -12
  32. package/dist/esm/LighterProvider.js.map +1 -1
  33. package/dist/esm/types/index.d.ts +1 -0
  34. package/dist/esm/types/index.d.ts.map +1 -1
  35. package/dist/esm/types/index.js +1 -0
  36. package/dist/esm/types/index.js.map +1 -1
  37. package/dist/esm/types/pnl.d.ts +33 -0
  38. package/dist/esm/types/pnl.d.ts.map +1 -0
  39. package/dist/esm/types/pnl.js +4 -0
  40. package/dist/esm/types/pnl.js.map +1 -0
  41. package/dist/esm/types/ws.d.ts +2 -2
  42. package/dist/esm/utils/index.d.ts +1 -0
  43. package/dist/esm/utils/index.d.ts.map +1 -1
  44. package/dist/esm/utils/index.js +1 -0
  45. package/dist/esm/utils/index.js.map +1 -1
  46. package/dist/esm/utils/mapMarketContext.d.ts.map +1 -1
  47. package/dist/esm/utils/mapMarketContext.js +2 -1
  48. package/dist/esm/utils/mapMarketContext.js.map +1 -1
  49. package/dist/esm/utils/mapPortfolioHistory.d.ts +12 -0
  50. package/dist/esm/utils/mapPortfolioHistory.d.ts.map +1 -0
  51. package/dist/esm/utils/mapPortfolioHistory.js +40 -0
  52. package/dist/esm/utils/mapPortfolioHistory.js.map +1 -0
  53. package/dist/esm/utils/marketLimits.d.ts +3 -3
  54. package/dist/esm/utils/marketLimits.d.ts.map +1 -1
  55. package/dist/esm/utils/marketLimits.js +3 -2
  56. package/dist/esm/utils/marketLimits.js.map +1 -1
  57. package/dist/esm/websocket/LighterWsProvider.d.ts +1 -1
  58. package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -1
  59. package/dist/esm/websocket/LighterWsProvider.js +2 -12
  60. package/dist/esm/websocket/LighterWsProvider.js.map +1 -1
  61. package/dist/types/LighterProvider.d.ts.map +1 -1
  62. package/dist/types/types/index.d.ts +1 -0
  63. package/dist/types/types/index.d.ts.map +1 -1
  64. package/dist/types/types/pnl.d.ts +33 -0
  65. package/dist/types/types/pnl.d.ts.map +1 -0
  66. package/dist/types/types/ws.d.ts +2 -2
  67. package/dist/types/utils/index.d.ts +1 -0
  68. package/dist/types/utils/index.d.ts.map +1 -1
  69. package/dist/types/utils/mapMarketContext.d.ts.map +1 -1
  70. package/dist/types/utils/mapPortfolioHistory.d.ts +12 -0
  71. package/dist/types/utils/mapPortfolioHistory.d.ts.map +1 -0
  72. package/dist/types/utils/marketLimits.d.ts +3 -3
  73. package/dist/types/utils/marketLimits.d.ts.map +1 -1
  74. package/dist/types/websocket/LighterWsProvider.d.ts +1 -1
  75. package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -1
  76. package/package.json +3 -3
  77. package/src/LighterProvider.ts +120 -25
  78. package/src/types/index.ts +1 -0
  79. package/src/types/pnl.ts +36 -0
  80. package/src/types/ws.ts +2 -2
  81. package/src/utils/index.ts +1 -0
  82. package/src/utils/mapMarketContext.ts +3 -1
  83. package/src/utils/mapPortfolioHistory.ts +52 -0
  84. package/src/utils/marketLimits.ts +5 -4
  85. package/src/websocket/LighterWsProvider.ts +10 -16
@@ -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 and the funding timestamp is a Unix
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
  */
@@ -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 cap published by Lighter's order-book metadata. */
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 realtime WS provider (extends {@link WsProviderBase}): subscribes to
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
- (options?: LighterWsProviderOptions): WsProviderFactory =>
1011
- ({ provider, wsUrl, client }) =>
1012
- new LighterWsProvider(wsUrl, provider, options, client)
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
+ )