@lifi/perps-sdk-provider-lighter 26.0.0 → 28.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 +26 -12
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/accountSummary.d.ts.map +1 -1
- package/dist/cjs/accountSummary.js +17 -20
- package/dist/cjs/accountSummary.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +1 -1
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +2 -2
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/mapOrder.d.ts.map +1 -1
- package/dist/cjs/utils/mapOrder.js +2 -1
- package/dist/cjs/utils/mapOrder.js.map +1 -1
- package/dist/cjs/utils/spotPrice.d.ts +4 -0
- package/dist/cjs/utils/spotPrice.d.ts.map +1 -0
- package/dist/cjs/utils/spotPrice.js +24 -0
- package/dist/cjs/utils/spotPrice.js.map +1 -0
- package/dist/cjs/utils/transferMargin.d.ts +2 -2
- package/dist/cjs/utils/transferMargin.d.ts.map +1 -1
- package/dist/cjs/utils/transferMargin.js +33 -8
- package/dist/cjs/utils/transferMargin.js.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.js +1 -0
- package/dist/cjs/websocket/LighterWsProvider.js.map +1 -1
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +28 -21
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/accountSummary.d.ts +6 -6
- package/dist/esm/accountSummary.d.ts.map +1 -1
- package/dist/esm/accountSummary.js +24 -27
- package/dist/esm/accountSummary.js.map +1 -1
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/mapOrder.d.ts.map +1 -1
- package/dist/esm/utils/mapOrder.js +2 -1
- package/dist/esm/utils/mapOrder.js.map +1 -1
- package/dist/esm/utils/spotPrice.d.ts +14 -0
- package/dist/esm/utils/spotPrice.d.ts.map +1 -0
- package/dist/esm/utils/spotPrice.js +30 -0
- package/dist/esm/utils/spotPrice.js.map +1 -0
- package/dist/esm/utils/transferMargin.d.ts +11 -5
- package/dist/esm/utils/transferMargin.d.ts.map +1 -1
- package/dist/esm/utils/transferMargin.js +39 -11
- package/dist/esm/utils/transferMargin.js.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.js +1 -0
- package/dist/esm/websocket/LighterWsProvider.js.map +1 -1
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/accountSummary.d.ts +6 -6
- package/dist/types/accountSummary.d.ts.map +1 -1
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/mapOrder.d.ts.map +1 -1
- package/dist/types/utils/spotPrice.d.ts +14 -0
- package/dist/types/utils/spotPrice.d.ts.map +1 -0
- package/dist/types/utils/transferMargin.d.ts +11 -5
- package/dist/types/utils/transferMargin.d.ts.map +1 -1
- package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/LighterProvider.ts +50 -36
- package/src/accountSummary.ts +35 -28
- package/src/utils/index.ts +1 -1
- package/src/utils/mapOrder.ts +2 -1
- package/src/utils/spotPrice.ts +39 -0
- package/src/utils/transferMargin.ts +52 -14
- package/src/websocket/LighterWsProvider.ts +1 -0
package/src/accountSummary.ts
CHANGED
|
@@ -1,54 +1,61 @@
|
|
|
1
|
+
import { PerpsError } from '@lifi/perps-sdk'
|
|
1
2
|
import type {
|
|
2
3
|
AccountResponse,
|
|
3
4
|
AccountSummary,
|
|
5
|
+
LighterAccountConfig,
|
|
4
6
|
Position,
|
|
5
7
|
} from '@lifi/perps-types'
|
|
6
|
-
import {
|
|
8
|
+
import { PerpsErrorCode } from '@lifi/perps-types'
|
|
7
9
|
import Big from 'big.js'
|
|
10
|
+
import { toRequiredBig } from './utils/decimal.js'
|
|
11
|
+
|
|
12
|
+
const lighterConfig = (account: AccountResponse): LighterAccountConfig => {
|
|
13
|
+
if (account.config.provider !== 'lighter') {
|
|
14
|
+
throw new PerpsError(
|
|
15
|
+
PerpsErrorCode.SDKError,
|
|
16
|
+
`Lighter account summary received a '${account.config.provider}' account config`
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
return account.config
|
|
20
|
+
}
|
|
8
21
|
|
|
9
22
|
/**
|
|
10
|
-
* Roll a Lighter
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* but adds unrealized PnL only for isolated positions.
|
|
23
|
+
* Roll a Lighter account up into an {@link AccountSummary}. `totalAssetValue`
|
|
24
|
+
* is perps-route equity and excludes the spot route, so `portfolioValue` adds
|
|
25
|
+
* every spot `balances` row, settlement included; the `collateralBalances` row
|
|
26
|
+
* is buying power already inside `totalAssetValue`. The positions supply only
|
|
27
|
+
* the margin and PnL breakdown.
|
|
16
28
|
*
|
|
29
|
+
* @throws {PerpsError} `SDKError` when the account is not a Lighter one.
|
|
17
30
|
* @public
|
|
18
31
|
*/
|
|
19
32
|
export function getAccountSummary(
|
|
20
33
|
account: AccountResponse,
|
|
21
34
|
positions: Position[]
|
|
22
35
|
): AccountSummary {
|
|
23
|
-
|
|
24
|
-
for (const balance of account.collateralBalances) {
|
|
25
|
-
availableMargin = availableMargin.plus(balance.valueUsd)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let nonCollateralValue = new Big(0)
|
|
29
|
-
for (const balance of account.balances) {
|
|
30
|
-
nonCollateralValue = nonCollateralValue.plus(balance.valueUsd)
|
|
31
|
-
}
|
|
36
|
+
const config = lighterConfig(account)
|
|
32
37
|
|
|
33
38
|
let marginUsed = new Big(0)
|
|
34
39
|
let unrealizedPnl = new Big(0)
|
|
35
|
-
let isolatedUnrealizedPnl = new Big(0)
|
|
36
40
|
for (const position of positions) {
|
|
37
41
|
marginUsed = marginUsed.plus(position.marginUsed)
|
|
38
42
|
unrealizedPnl = unrealizedPnl.plus(position.unrealizedPnl)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let portfolioValue = toRequiredBig(config.totalAssetValue, 'totalAssetValue')
|
|
46
|
+
for (const balance of account.balances) {
|
|
47
|
+
portfolioValue = portfolioValue.plus(
|
|
48
|
+
toRequiredBig(balance.valueUsd, 'valueUsd')
|
|
49
|
+
)
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
return {
|
|
45
|
-
portfolioValue:
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
unrealizedPnl: unrealizedPnl.toString(),
|
|
53
|
+
portfolioValue: portfolioValue.toFixed(),
|
|
54
|
+
availableMargin: toRequiredBig(
|
|
55
|
+
config.availableBalance,
|
|
56
|
+
'availableBalance'
|
|
57
|
+
).toFixed(),
|
|
58
|
+
marginUsed: marginUsed.toFixed(),
|
|
59
|
+
unrealizedPnl: unrealizedPnl.toFixed(),
|
|
53
60
|
}
|
|
54
61
|
}
|
package/src/utils/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ export { mapMarketOrderLimits } from './marketLimits.js'
|
|
|
23
23
|
export { mapInterval } from './ohlcvInterval.js'
|
|
24
24
|
export { formatOrderPrice, formatOrderSize } from './orderFormatting.js'
|
|
25
25
|
export { toIsoFromMs, toIsoFromSeconds } from './time.js'
|
|
26
|
-
export {
|
|
26
|
+
export { positionRemovableMargin } from './transferMargin.js'
|
|
27
27
|
export {
|
|
28
28
|
leverageToFraction,
|
|
29
29
|
mapOrderTypeToInt,
|
package/src/utils/mapOrder.ts
CHANGED
|
@@ -61,9 +61,10 @@ const mapOrderStatus = (
|
|
|
61
61
|
filledSize = new Big(order.filled_base_amount)
|
|
62
62
|
): OrderStatus => {
|
|
63
63
|
switch (order.status) {
|
|
64
|
-
case 'pending':
|
|
65
64
|
case 'in-progress':
|
|
66
65
|
return OrderStatus.ACCEPTED
|
|
66
|
+
case 'pending':
|
|
67
|
+
return OrderStatus.OPEN
|
|
67
68
|
case 'open':
|
|
68
69
|
if (order.trigger_status === 'parent-order') {
|
|
69
70
|
return OrderStatus.PENDING
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Market, MarketContext } from '@lifi/perps-types'
|
|
2
|
+
import type Big from 'big.js'
|
|
3
|
+
import { toRequiredBig } from './decimal.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Unit prices keyed by spot base `Asset.id` (the venue `asset_id`), read from
|
|
7
|
+
* each spot market's `markPrice` for the base assets in `assetIds` only. A
|
|
8
|
+
* market without a context row or with a non-positive mark adds no entry; when
|
|
9
|
+
* several spot markets share a base asset, the first priced one in `markets`
|
|
10
|
+
* order wins. The mark is read as a USD price; `quoteAsset` is not checked.
|
|
11
|
+
*
|
|
12
|
+
* @throws {PerpsError} `SDKError` when a context row for a requested asset
|
|
13
|
+
* carries a non-decimal mark.
|
|
14
|
+
*/
|
|
15
|
+
export const spotPriceByAssetId = (
|
|
16
|
+
markets: readonly Market[],
|
|
17
|
+
spotCategoryId: string,
|
|
18
|
+
contexts: readonly MarketContext[],
|
|
19
|
+
assetIds: ReadonlySet<string>
|
|
20
|
+
): Map<string, Big> => {
|
|
21
|
+
const markByMarketId = new Map(contexts.map((c) => [c.marketId, c.markPrice]))
|
|
22
|
+
const priceByAssetId = new Map<string, Big>()
|
|
23
|
+
for (const market of markets) {
|
|
24
|
+
const mark = markByMarketId.get(market.id)
|
|
25
|
+
if (
|
|
26
|
+
market.categoryId !== spotCategoryId ||
|
|
27
|
+
mark === undefined ||
|
|
28
|
+
!assetIds.has(market.baseAsset.id) ||
|
|
29
|
+
priceByAssetId.has(market.baseAsset.id)
|
|
30
|
+
) {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
const price = toRequiredBig(mark, 'markPrice')
|
|
34
|
+
if (price.gt(0)) {
|
|
35
|
+
priceByAssetId.set(market.baseAsset.id, price)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return priceByAssetId
|
|
39
|
+
}
|
|
@@ -1,31 +1,69 @@
|
|
|
1
|
+
import { PerpsError } from '@lifi/perps-sdk'
|
|
1
2
|
import {
|
|
3
|
+
PerpsErrorCode,
|
|
2
4
|
type Position,
|
|
3
|
-
type PositionMarginConstraints,
|
|
4
5
|
positionSupportsMarginAdjustment,
|
|
6
|
+
positionSupportsMarginRemoval,
|
|
5
7
|
} from '@lifi/perps-types'
|
|
6
|
-
import
|
|
8
|
+
import Big from 'big.js'
|
|
7
9
|
|
|
8
|
-
const
|
|
10
|
+
const AMOUNT_DECIMALS = 6
|
|
11
|
+
|
|
12
|
+
function positionAmount(value: string, field: string): Big {
|
|
13
|
+
try {
|
|
14
|
+
return new Big(value)
|
|
15
|
+
} catch {
|
|
16
|
+
throw new PerpsError(
|
|
17
|
+
PerpsErrorCode.ValidationError,
|
|
18
|
+
`Invalid decimal string on Position.${field}: '${value}'`
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function positivePositionAmount(value: string, field: string): Big {
|
|
24
|
+
const amount = positionAmount(value, field)
|
|
25
|
+
if (amount.lte(0)) {
|
|
26
|
+
throw new PerpsError(
|
|
27
|
+
PerpsErrorCode.ValidationError,
|
|
28
|
+
`Position.${field} must be greater than zero.`
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
return amount
|
|
32
|
+
}
|
|
9
33
|
|
|
10
34
|
/**
|
|
11
|
-
* Lighter
|
|
12
|
-
*
|
|
13
|
-
* the
|
|
35
|
+
* Lighter removable isolated margin:
|
|
36
|
+
* `allocated_margin + unrealized PnL − initial margin requirement`. Lighter
|
|
37
|
+
* `allocated_margin` (`Position.marginUsed`) excludes the unrealized PnL, and
|
|
38
|
+
* Lighter publishes no separate notional floor.
|
|
14
39
|
*
|
|
40
|
+
* @returns `undefined` for cross positions and markets without individual
|
|
41
|
+
* margin adjustment; `'0'` for add-only markets and when nothing is
|
|
42
|
+
* removable.
|
|
43
|
+
* @throws {PerpsError} `ValidationError` when a required `Position` decimal
|
|
44
|
+
* is malformed or not positive.
|
|
15
45
|
* @see https://docs.lighter.xyz/trading/liquidations-and-llp-insurance-fund
|
|
16
46
|
* @public
|
|
17
47
|
*/
|
|
18
|
-
export function
|
|
48
|
+
export function positionRemovableMargin(
|
|
19
49
|
position: Position
|
|
20
|
-
):
|
|
50
|
+
): string | undefined {
|
|
21
51
|
if (!positionSupportsMarginAdjustment(position)) {
|
|
22
52
|
return undefined
|
|
23
53
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
54
|
+
if (!positionSupportsMarginRemoval(position)) {
|
|
55
|
+
return '0'
|
|
56
|
+
}
|
|
57
|
+
const removable = positivePositionAmount(position.marginUsed, 'marginUsed')
|
|
58
|
+
.plus(positionAmount(position.unrealizedPnl, 'unrealizedPnl'))
|
|
59
|
+
.minus(
|
|
60
|
+
positivePositionAmount(
|
|
61
|
+
position.initialMarginRequirement,
|
|
62
|
+
'initialMarginRequirement'
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
if (removable.lte(0)) {
|
|
66
|
+
return '0'
|
|
30
67
|
}
|
|
68
|
+
return removable.round(AMOUNT_DECIMALS, Big.roundDown).toFixed()
|
|
31
69
|
}
|
|
@@ -374,6 +374,7 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
374
374
|
return `accountSummary:${sub.address.toLowerCase()}`
|
|
375
375
|
case 'candle':
|
|
376
376
|
case 'spotBalances':
|
|
377
|
+
case 'availableToTrade':
|
|
377
378
|
throw new Error(`Lighter WS does not support channel: ${sub.channel}.`)
|
|
378
379
|
}
|
|
379
380
|
}
|