@quantara/sdk
Version:
JavaScript/TypeScript SDK for interacting with Quantara Protocol on Neura Testnet
134 lines (133 loc) • 6.03 kB
JavaScript
import { BASIS_POINTS_DIVISOR_BIGINT } from "../configs/factors";
import { bigMath } from "./bigmath";
import { getPositionFee, getPriceImpactForPosition } from "./fees";
import { getCappedPoolPnl, getMarketPnl, getPoolUsdWithoutPnl } from "./markets";
import { applyFactor, expandDecimals } from "./numbers";
import { convertToUsd, getIsEquivalentTokens } from "./tokens";
export function getPositionKey(account, marketAddress, collateralAddress, isLong) {
return `${account}:${marketAddress}:${collateralAddress}:${isLong}`;
}
export function parsePositionKey(positionKey) {
const [account, marketAddress, collateralAddress, isLong] = positionKey.split(":");
return { account, marketAddress, collateralAddress, isLong: isLong === "true" };
}
export function getEntryPrice(p) {
const { sizeInUsd, sizeInTokens, indexToken } = p;
if (sizeInTokens <= 0) {
return undefined;
}
return bigMath.mulDiv(sizeInUsd, expandDecimals(1, indexToken.decimals), sizeInTokens);
}
export function getPositionPnlUsd(p) {
const { marketInfo, sizeInUsd, sizeInTokens, markPrice, isLong } = p;
const positionValueUsd = getPositionValueUsd({ indexToken: marketInfo.indexToken, sizeInTokens, markPrice });
let totalPnl = isLong ? positionValueUsd - sizeInUsd : sizeInUsd - positionValueUsd;
if (totalPnl <= 0) {
return totalPnl;
}
const poolPnl = getMarketPnl(marketInfo, isLong, true);
const poolUsd = getPoolUsdWithoutPnl(marketInfo, isLong, "minPrice");
const cappedPnl = getCappedPoolPnl({
marketInfo,
poolUsd,
poolPnl,
isLong,
});
const WEI_PRECISION = expandDecimals(1, 18);
if (cappedPnl !== poolPnl && cappedPnl > 0 && poolPnl > 0) {
totalPnl = bigMath.mulDiv(totalPnl, cappedPnl / WEI_PRECISION, poolPnl / WEI_PRECISION);
}
return totalPnl;
}
export function getPositionValueUsd(p) {
const { indexToken, sizeInTokens, markPrice } = p;
return convertToUsd(sizeInTokens, indexToken.decimals, markPrice);
}
export function getPositionPendingFeesUsd(p) {
const { pendingFundingFeesUsd, pendingBorrowingFeesUsd } = p;
return pendingBorrowingFeesUsd + pendingFundingFeesUsd;
}
export function getPositionNetValue(p) {
const { pnl, closingFeeUsd, collateralUsd, uiFeeUsd } = p;
const pendingFeesUsd = getPositionPendingFeesUsd(p);
return collateralUsd - pendingFeesUsd - closingFeeUsd - uiFeeUsd + pnl;
}
export function getLeverage(p) {
const { pnl, sizeInUsd, collateralUsd, pendingBorrowingFeesUsd, pendingFundingFeesUsd } = p;
const totalPendingFeesUsd = getPositionPendingFeesUsd({ pendingFundingFeesUsd, pendingBorrowingFeesUsd });
const remainingCollateralUsd = collateralUsd + (pnl ?? 0n) - totalPendingFeesUsd;
if (remainingCollateralUsd <= 0) {
return undefined;
}
return bigMath.mulDiv(sizeInUsd, BASIS_POINTS_DIVISOR_BIGINT, remainingCollateralUsd);
}
export function getLiquidationPrice(p) {
const { sizeInUsd, sizeInTokens, collateralUsd, collateralAmount, marketInfo, collateralToken, pendingFundingFeesUsd, pendingBorrowingFeesUsd, minCollateralUsd, isLong, userReferralInfo, useMaxPriceImpact, } = p;
if (sizeInUsd <= 0 || sizeInTokens <= 0) {
return undefined;
}
const { indexToken } = marketInfo;
const closingFeeUsd = getPositionFee(marketInfo, sizeInUsd, false, userReferralInfo).positionFeeUsd;
const totalPendingFeesUsd = getPositionPendingFeesUsd({ pendingFundingFeesUsd, pendingBorrowingFeesUsd });
const totalFeesUsd = totalPendingFeesUsd + closingFeeUsd;
const maxNegativePriceImpactUsd = -1n * applyFactor(sizeInUsd, marketInfo.maxPositionImpactFactorForLiquidations);
let priceImpactDeltaUsd = 0n;
if (useMaxPriceImpact) {
priceImpactDeltaUsd = maxNegativePriceImpactUsd;
}
else {
priceImpactDeltaUsd = getPriceImpactForPosition(marketInfo, -sizeInUsd, isLong, { fallbackToZero: true });
if (priceImpactDeltaUsd < maxNegativePriceImpactUsd) {
priceImpactDeltaUsd = maxNegativePriceImpactUsd;
}
// Ignore positive price impact
if (priceImpactDeltaUsd > 0) {
priceImpactDeltaUsd = 0n;
}
}
let liquidationCollateralUsd = applyFactor(sizeInUsd, marketInfo.minCollateralFactor);
if (liquidationCollateralUsd < minCollateralUsd) {
liquidationCollateralUsd = minCollateralUsd;
}
let liquidationPrice;
if (getIsEquivalentTokens(collateralToken, indexToken)) {
if (isLong) {
const denominator = sizeInTokens + collateralAmount;
if (denominator == 0n) {
return undefined;
}
liquidationPrice =
((sizeInUsd + liquidationCollateralUsd - priceImpactDeltaUsd + totalFeesUsd) / denominator) *
expandDecimals(1, indexToken.decimals);
}
else {
const denominator = sizeInTokens - collateralAmount;
if (denominator == 0n) {
return undefined;
}
liquidationPrice =
((sizeInUsd - liquidationCollateralUsd + priceImpactDeltaUsd - totalFeesUsd) / denominator) *
expandDecimals(1, indexToken.decimals);
}
}
else {
if (sizeInTokens == 0n) {
return undefined;
}
const remainingCollateralUsd = collateralUsd + priceImpactDeltaUsd - totalPendingFeesUsd - closingFeeUsd;
if (isLong) {
liquidationPrice =
((liquidationCollateralUsd - remainingCollateralUsd + sizeInUsd) / sizeInTokens) *
expandDecimals(1, indexToken.decimals);
}
else {
liquidationPrice =
((liquidationCollateralUsd - remainingCollateralUsd - sizeInUsd) / -sizeInTokens) *
expandDecimals(1, indexToken.decimals);
}
}
if (liquidationPrice <= 0) {
return undefined;
}
return liquidationPrice;
}