Skip to main content
The SDK exports utility functions for position math, TP/SL calculations, fee estimation, order classification, and formatting. These are stateless pure functions — they do not call the API.

Position Calculations

import {
  calculatePositionSize,
  calculateNotionalValue,
  calculateUnrealizedPnl,
  calculateRoe,
  calculateRequiredMargin,
  calculateRealizedPnlPercent,
} from '@lifi/perps-sdk';

calculatePositionSize

Calculate position size from margin, leverage, and price.
const size = calculatePositionSize(marginUsd, leverage, price);
// e.g. calculatePositionSize(1000, 10, 95000) → position size for $10k notional
ParameterTypeDescription
marginUsdnumberMargin amount in USD
leveragenumberLeverage multiplier
pricenumberEntry price

calculateNotionalValue

const notional = calculateNotionalValue(size, price);

calculateUnrealizedPnl

const pnl = calculateUnrealizedPnl(entryPrice, currentPrice, size);

calculateRoe

Return on equity (PnL / margin).
const roe = calculateRoe(pnl, margin);

calculateRequiredMargin

const margin = calculateRequiredMargin(notionalValue, leverage);

calculateRealizedPnlPercent

const pnlPercent = calculateRealizedPnlPercent(realizedPnl, size, price);

Position Math (Predictive)

Pure-function helpers for predicting how a perp position changes after a fill — used by add-to-position and partial-close preview blocks. These read SDK shapes as plain numbers (callers parse Position.size, entryPrice, etc. from string), and use the convention long = +1, short = -1. Sizes passed in are non-negative magnitudes; direction is carried by isLong.
import {
  directionSign,
  predictAverageEntryPrice,
  predictNewLeverage,
  predictUnrealizedPnl,
  realizedPnlOnClose,
} from '@lifi/perps-sdk';

directionSign

Direction sign for a position.
directionSign(true);  // 1   (long)
directionSign(false); // -1  (short)
ParameterTypeDescription
isLongbooleanTrue for long positions, false for short
Returns: 1 | -1

predictAverageEntryPrice

Predicted average entry price after adding to an existing position. Weighted average of current entry and the new fill price, weighted by leg size in coin units. Both legs must be in the same direction (this helper is for adding to, not flipping, a position).
const newEntry = predictAverageEntryPrice({
  currentSize: 1,
  currentEntry: 95_000,
  addSize: 0.5,
  fillPrice: 96_500,
});
// 95_500
ParameterTypeDescription
currentSizenumberExisting position size in coin units (>= 0)
currentEntrynumberExisting position’s average entry price
addSizenumberSize being added in coin units (>= 0)
fillPricenumberPrice the new size is expected to fill at (mid for market, limit price for limit orders)
Returns: number | undefined — the new weighted-average entry price, or undefined if the inputs cannot produce a valid average (zero combined size, non-finite values).

predictNewLeverage

Predicted effective leverage (totalNotional / totalMargin) after adding margin and notional. The caller computes notional from size and price (e.g. with calculateNotionalValue) and supplies the additional margin the user is about to put up.
const newLeverage = predictNewLeverage({
  currentNotional: 10_000,
  currentMargin: 1_000,
  addNotional: 5_000,
  addMargin: 500,
});
// 10
ParameterTypeDescription
currentNotionalnumberCurrent position notional
currentMarginnumberCurrent position margin
addNotionalnumberNotional being added
addMarginnumberMargin being added
Returns: number | undefined — the new effective leverage, or undefined if total margin is non-positive.

predictUnrealizedPnl

Predicted unrealised PnL at the current mark price: (markPrice - entryPrice) * size * directionSign(isLong).
const uPnl = predictUnrealizedPnl({
  entryPrice: 95_000,
  markPrice: 96_000,
  size: 1,
  isLong: true,
});
// 1_000
ParameterTypeDescription
entryPricenumberPosition entry price
markPricenumberCurrent mark price
sizenumberPosition size as a non-negative magnitude
isLongbooleanDirection of the position
Returns: number

realizedPnlOnClose

Realised PnL on the portion of a position being closed: (closePrice - entryPrice) * closeSize * directionSign(isLong).
const rPnl = realizedPnlOnClose({
  entryPrice: 95_000,
  closePrice: 96_000,
  closeSize: 0.5,
  isLong: true,
});
// 500
ParameterTypeDescription
entryPricenumberPosition entry price
closePricenumberPrice the close fills at
closeSizenumberSize being closed as a non-negative magnitude
isLongbooleanDirection of the position being closed
Returns: number

Order Math (Expected rPnL Previews)

Helpers for previewing the realised PnL the user would lock in if a resting order filled against a matching position. Used by Orders-tab rows on the trading UI. These compose directionSign and realizedPnlOnClose from Position Math, so the two modules stay in lockstep. Orders that open or add to a position have no defined rPnL and return null (typically rendered as an em-dash in the UI).
import {
  expectedRealizedPnlForOpenOrder,
  expectedRealizedPnlForTriggerOrder,
  findMatchingPosition,
  resolveCloseSize,
} from '@lifi/perps-sdk';
import type { OpenOrder, Position, TriggerOrder } from '@lifi/perps-sdk';

findMatchingPosition

Pick the matching open position for an order’s asset, if any.
const matching = findMatchingPosition(order.market.id, positions);
ParameterTypeDescription
marketIdstringThe order’s market.id
positionsreadonly Position[]Open positions list (any symbol)
Returns: Position | undefined

resolveCloseSize

Resolve the close-size against a position, applying the spec’s cap rules:
  • orderSize === 0 is the Hyperliquid convention for “close entire position” used by trigger orders → close the full position size.
  • Otherwise cap at the absolute position size; an order larger than the position can only close what’s open.
Inputs are non-negative magnitudes.
resolveCloseSize(0, 1.5);   // 1.5  (zero == close-all)
resolveCloseSize(0.5, 1.5); // 0.5  (partial close)
resolveCloseSize(2, 1.5);   // 1.5  (capped at position)
ParameterTypeDescription
orderSizenumberOrder size, non-negative magnitude
positionSizenumberOpen position size, non-negative magnitude
Returns: number

expectedRealizedPnlForOpenOrder

Expected rPnL for a resting limit order against a matching position. Reducing requires opposite sides (long position + SELL, short position + BUY). Same-side orders add to the position and return null. Returns null if there is no matching position or the inputs are non-finite.
import { expectedRealizedPnlForOpenOrder, findMatchingPosition } from '@lifi/perps-sdk';
import type { OpenOrder, Position } from '@lifi/perps-sdk';

function previewOrderRPnl(order: OpenOrder, positions: readonly Position[]) {
  const position = findMatchingPosition(order.market.id, positions);
  return expectedRealizedPnlForOpenOrder(order, position);
  // number when the order would reduce the position; null otherwise
}
ParameterTypeDescription
orderOpenOrderThe resting limit order
positionPosition | undefinedMatching open position, if any
Returns: number | null

expectedRealizedPnlForTriggerOrder

Expected rPnL for a TP/SL trigger order against a matching position. A trigger order is by construction a closing leg — its direction is the opposite of the position’s. With no matching position there is nothing to close, so rPnL is null. The trigger price (always present on the SDK TriggerOrder shape) is used as the rPnL price; the optional limitPrice for STOP_LIMIT / TAKE_PROFIT_LIMIT is the post-trigger limit, not the rPnL price.
import {
  expectedRealizedPnlForTriggerOrder,
  findMatchingPosition,
} from '@lifi/perps-sdk';
import type { Position, TriggerOrder } from '@lifi/perps-sdk';

function previewTriggerRPnl(order: TriggerOrder, positions: readonly Position[]) {
  const position = findMatchingPosition(order.market.id, positions);
  return expectedRealizedPnlForTriggerOrder(order, position);
}
ParameterTypeDescription
orderTriggerOrderThe TP/SL trigger order
positionPosition | undefinedMatching open position, if any
Returns: number | null

TP/SL Calculations

import {
  calculateExpectedPnl,
  priceFromPercent,
  percentFromPrice,
} from '@lifi/perps-sdk';

calculateExpectedPnl

Calculate expected PnL if a trigger price is hit.
const { amount, percent } = calculateExpectedPnl(
  triggerPrice,
  entryPrice,
  leverage,
  isLong,
  margin
);
ParameterTypeDescription
triggerPricenumberTP/SL trigger price
entryPricenumberPosition entry price
leveragenumberLeverage
isLongbooleanWhether position is long
marginnumberPosition margin
Returns: { amount: number, percent: number }

priceFromPercent

Convert a target PnL percentage to a trigger price.
const price = priceFromPercent(percent, entryPrice, leverage, isLong);
// e.g. priceFromPercent(50, 95000, 10, true) → price for +50% ROE on a 10x long

percentFromPrice

Convert a trigger price to a PnL percentage.
const percent = percentFromPrice(price, entryPrice, leverage, isLong);

Fee & Slippage

import { estimateFees, applySlippage } from '@lifi/perps-sdk';

estimateFees

const fees = estimateFees(sizeUsd, feeRate);
// e.g. estimateFees(10000, 0.0005) → 5.0

applySlippage

Adjust a price by a slippage percentage.
const adjusted = applySlippage(price, slippagePercent, isBuy);
// Buy: price goes up. Sell: price goes down.

Account Summary

There is no standalone calculateAccountSummary util. Derive portfolio metrics with the PerpsClient.getAccountSummary() method:
const summary = perps.getAccountSummary(account, positions);
// { portfolioValue, availableMargin, marginUsed, unrealizedPnl } — all strings
ParameterTypeDescription
accountAccountResponseAccount from getAccount()
positionsPosition[]Positions from getPositions()
Returns: AccountSummary{ portfolioValue, availableMargin, marginUsed, unrealizedPnl }, all string fields. The underlying pure function is summarizeAccount(account, positions), exported from @lifi/perps-sdk.

Order Classification

import {
  isTakeProfitOrder,
  isStopLossOrder,
  isTpSlOrder,
  classifyFill,
} from '@lifi/perps-sdk';

isTakeProfitOrder / isStopLossOrder / isTpSlOrder

Check whether an order is a TP, SL, or either.
if (isTpSlOrder(order)) {
  console.log('This is a trigger order');
}

classifyFill

classifyFill is deprecated — read Fill.classification (already populated on every fill) instead.
Classify a fill based on side and realized PnL. Returns a FillClassification enum value (Title-Case strings):
const type = classifyFill(side, realizedPnl);
// FillClassification — e.g. 'Opened Long' | 'Opened Short' | 'Closed Long' | 'Closed Short'

Validation

import { validateMargin } from '@lifi/perps-sdk';

validateMargin

Check whether the user has sufficient margin for an order.
const result = validateMargin(margin, leverage, availableBalance, feeRate, minMarginUsd);
// '' (valid) | 'insufficient' | 'below-minimum'
ParameterTypeDescription
marginnumberMargin to allocate
leveragenumberLeverage multiplier
availableBalancenumberUser’s available balance
feeRatenumberExpected fee rate
minMarginUsdnumberMinimum margin in USD

Parsing & Conversion

import { stringToFloat, fromBaseUnits, fromBaseUnitsNumber } from '@lifi/perps-sdk';

stringToFloat

Parse formatted currency/percentage strings to a number.
stringToFloat('$1,234.56'); // 1234.56
stringToFloat('50%');        // 50

fromBaseUnits / fromBaseUnitsNumber

Convert a base-unit amount (passed as a string) to a decimal string or number.
fromBaseUnits('1000000', 6);       // '1'
fromBaseUnitsNumber('1000000', 6); // 1

Signing

import { signTypedData } from '@lifi/perps-sdk';

signTypedData

Sign EIP-712 typed data with a private key. Useful for server-side signing without a wallet. Async — returns Promise<Hex>.
const signature = await signTypedData(privateKey, typedData);
ParameterTypeDescription
privateKeyHexPrivate key (0x-prefixed)
typedDataPerpsTypedDataEIP-712 typed data from a create endpoint

Hyperliquid-specific

These utilities are specific to Hyperliquid’s order formatting requirements and are exported from @lifi/perps-sdk-provider-hyperliquid (not the core SDK):
import {
  calculateLiquidationPrice,
  calculateMaintenanceMarginRate,
  formatOrderPrice,
  formatOrderSize,
  getMaxPriceDecimals,
} from '@lifi/perps-sdk-provider-hyperliquid';
calculateLiquidationPrice and calculateMaintenanceMarginRate return number | undefined (undefined when inputs can’t yield a valid result).

calculateLiquidationPrice

const liqPrice = calculateLiquidationPrice(entryPrice, leverage, isLong, maxLeverage);

calculateMaintenanceMarginRate

const mmr = calculateMaintenanceMarginRate(maxLeverage);

formatOrderPrice

Format a price for Hyperliquid submission (5 significant figures, correct decimal places).
formatOrderPrice(95123.456, 2); // '95123'

formatOrderSize

Format a size for Hyperliquid submission (no trailing zeros).
formatOrderSize(0.100, 3); // '0.1'

getMaxPriceDecimals

Get the maximum number of price decimal places for an asset.
const decimals = getMaxPriceDecimals(szDecimals);