@dainprotocol/drift-sdk
Version:
SDK for Drift Protocol
175 lines (174 loc) • 9.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculatePerpMarketBaseLiquidatorFee = exports.calculateAvailablePerpLiquidity = exports.calculateNetUserPnlImbalance = exports.calculateNetUserPnl = exports.calculateMarketMaxAvailableInsurance = exports.calculateMarketAvailablePNL = exports.calculateUnrealizedAssetWeight = exports.calculateMarketMarginRatio = exports.calculateOracleSpread = exports.calculateOracleReserveSpread = exports.calculateNewMarketAfterTrade = exports.calculateAskPrice = exports.calculateBidPrice = exports.calculateReservePrice = void 0;
const anchor_1 = require("@coral-xyz/anchor");
const types_1 = require("../types");
const amm_1 = require("./amm");
const margin_1 = require("./margin");
const numericConstants_1 = require("../constants/numericConstants");
const spotBalance_1 = require("./spotBalance");
const assert_1 = require("../assert/assert");
/**
* Calculates market mark price
*
* @param market
* @return markPrice : Precision PRICE_PRECISION
*/
function calculateReservePrice(market, oraclePriceData) {
const newAmm = (0, amm_1.calculateUpdatedAMM)(market.amm, oraclePriceData);
return (0, amm_1.calculatePrice)(newAmm.baseAssetReserve, newAmm.quoteAssetReserve, newAmm.pegMultiplier);
}
exports.calculateReservePrice = calculateReservePrice;
/**
* Calculates market bid price
*
* @param market
* @return bidPrice : Precision PRICE_PRECISION
*/
function calculateBidPrice(market, oraclePriceData) {
const { baseAssetReserve, quoteAssetReserve, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, types_1.PositionDirection.SHORT, oraclePriceData);
return (0, amm_1.calculatePrice)(baseAssetReserve, quoteAssetReserve, newPeg);
}
exports.calculateBidPrice = calculateBidPrice;
/**
* Calculates market ask price
*
* @param market
* @return askPrice : Precision PRICE_PRECISION
*/
function calculateAskPrice(market, oraclePriceData) {
const { baseAssetReserve, quoteAssetReserve, newPeg } = (0, amm_1.calculateUpdatedAMMSpreadReserves)(market.amm, types_1.PositionDirection.LONG, oraclePriceData);
return (0, amm_1.calculatePrice)(baseAssetReserve, quoteAssetReserve, newPeg);
}
exports.calculateAskPrice = calculateAskPrice;
function calculateNewMarketAfterTrade(baseAssetAmount, direction, market) {
const [newQuoteAssetReserve, newBaseAssetReserve] = (0, amm_1.calculateAmmReservesAfterSwap)(market.amm, 'base', baseAssetAmount.abs(), (0, amm_1.getSwapDirection)('base', direction));
const newAmm = Object.assign({}, market.amm);
const newMarket = Object.assign({}, market);
newMarket.amm = newAmm;
newMarket.amm.quoteAssetReserve = newQuoteAssetReserve;
newMarket.amm.baseAssetReserve = newBaseAssetReserve;
return newMarket;
}
exports.calculateNewMarketAfterTrade = calculateNewMarketAfterTrade;
function calculateOracleReserveSpread(market, oraclePriceData) {
const reservePrice = calculateReservePrice(market, oraclePriceData);
return calculateOracleSpread(reservePrice, oraclePriceData);
}
exports.calculateOracleReserveSpread = calculateOracleReserveSpread;
function calculateOracleSpread(price, oraclePriceData) {
return price.sub(oraclePriceData.price);
}
exports.calculateOracleSpread = calculateOracleSpread;
function calculateMarketMarginRatio(market, size, marginCategory, customMarginRatio = 0, userHighLeverageMode = false) {
let marginRationInitial;
let marginRatioMaintenance;
if (userHighLeverageMode &&
market.highLeverageMarginRatioInitial > 0 &&
market.highLeverageMarginRatioMaintenance) {
marginRationInitial = market.highLeverageMarginRatioInitial;
marginRatioMaintenance = market.highLeverageMarginRatioMaintenance;
}
else {
marginRationInitial = market.marginRatioInitial;
marginRatioMaintenance = market.marginRatioMaintenance;
}
let marginRatio;
switch (marginCategory) {
case 'Initial': {
// use lowest leverage between max allowed and optional user custom max
marginRatio = Math.max((0, margin_1.calculateSizePremiumLiabilityWeight)(size, new anchor_1.BN(market.imfFactor), new anchor_1.BN(marginRationInitial), numericConstants_1.MARGIN_PRECISION).toNumber(), customMarginRatio);
break;
}
case 'Maintenance': {
marginRatio = (0, margin_1.calculateSizePremiumLiabilityWeight)(size, new anchor_1.BN(market.imfFactor), new anchor_1.BN(marginRatioMaintenance), numericConstants_1.MARGIN_PRECISION).toNumber();
break;
}
}
return marginRatio;
}
exports.calculateMarketMarginRatio = calculateMarketMarginRatio;
function calculateUnrealizedAssetWeight(market, quoteSpotMarket, unrealizedPnl, marginCategory, oraclePriceData) {
let assetWeight;
switch (marginCategory) {
case 'Initial':
assetWeight = new anchor_1.BN(market.unrealizedPnlInitialAssetWeight);
if (market.unrealizedPnlMaxImbalance.gt(numericConstants_1.ZERO)) {
const netUnsettledPnl = calculateNetUserPnlImbalance(market, quoteSpotMarket, oraclePriceData);
if (netUnsettledPnl.gt(market.unrealizedPnlMaxImbalance)) {
assetWeight = assetWeight
.mul(market.unrealizedPnlMaxImbalance)
.div(netUnsettledPnl);
}
}
assetWeight = (0, margin_1.calculateSizeDiscountAssetWeight)(unrealizedPnl, new anchor_1.BN(market.unrealizedPnlImfFactor), assetWeight);
break;
case 'Maintenance':
assetWeight = new anchor_1.BN(market.unrealizedPnlMaintenanceAssetWeight);
break;
}
return assetWeight;
}
exports.calculateUnrealizedAssetWeight = calculateUnrealizedAssetWeight;
function calculateMarketAvailablePNL(perpMarket, spotMarket) {
return (0, spotBalance_1.getTokenAmount)(perpMarket.pnlPool.scaledBalance, spotMarket, types_1.SpotBalanceType.DEPOSIT);
}
exports.calculateMarketAvailablePNL = calculateMarketAvailablePNL;
function calculateMarketMaxAvailableInsurance(perpMarket, spotMarket) {
(0, assert_1.assert)(spotMarket.marketIndex == numericConstants_1.QUOTE_SPOT_MARKET_INDEX);
// todo: insuranceFundAllocation technically not guaranteed to be in Insurance Fund
const insuranceFundAllocation = perpMarket.insuranceClaim.quoteMaxInsurance.sub(perpMarket.insuranceClaim.quoteSettledInsurance);
const ammFeePool = (0, spotBalance_1.getTokenAmount)(perpMarket.amm.feePool.scaledBalance, spotMarket, types_1.SpotBalanceType.DEPOSIT);
return insuranceFundAllocation.add(ammFeePool);
}
exports.calculateMarketMaxAvailableInsurance = calculateMarketMaxAvailableInsurance;
function calculateNetUserPnl(perpMarket, oraclePriceData) {
const netUserPositionValue = perpMarket.amm.baseAssetAmountWithAmm
.add(perpMarket.amm.baseAssetAmountWithUnsettledLp)
.mul(oraclePriceData.price)
.div(numericConstants_1.BASE_PRECISION)
.div(numericConstants_1.PRICE_TO_QUOTE_PRECISION);
const netUserCostBasis = perpMarket.amm.quoteAssetAmount
.add(perpMarket.amm.quoteAssetAmountWithUnsettledLp)
.add(perpMarket.amm.netUnsettledFundingPnl);
const netUserPnl = netUserPositionValue.add(netUserCostBasis);
return netUserPnl;
}
exports.calculateNetUserPnl = calculateNetUserPnl;
function calculateNetUserPnlImbalance(perpMarket, spotMarket, oraclePriceData, applyFeePoolDiscount = true) {
const netUserPnl = calculateNetUserPnl(perpMarket, oraclePriceData);
const pnlPool = (0, spotBalance_1.getTokenAmount)(perpMarket.pnlPool.scaledBalance, spotMarket, types_1.SpotBalanceType.DEPOSIT);
let feePool = (0, spotBalance_1.getTokenAmount)(perpMarket.amm.feePool.scaledBalance, spotMarket, types_1.SpotBalanceType.DEPOSIT);
if (applyFeePoolDiscount) {
feePool = feePool.div(new anchor_1.BN(5));
}
const imbalance = netUserPnl.sub(pnlPool.add(feePool));
return imbalance;
}
exports.calculateNetUserPnlImbalance = calculateNetUserPnlImbalance;
function calculateAvailablePerpLiquidity(market, oraclePriceData, dlob, slot) {
let [bids, asks] = (0, amm_1.calculateMarketOpenBidAsk)(market.amm.baseAssetReserve, market.amm.minBaseAssetReserve, market.amm.maxBaseAssetReserve, market.amm.orderStepSize);
asks = asks.abs();
for (const bid of dlob.getRestingLimitBids(market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData)) {
bids = bids.add(bid.order.baseAssetAmount.sub(bid.order.baseAssetAmountFilled));
}
for (const ask of dlob.getRestingLimitAsks(market.marketIndex, slot, types_1.MarketType.PERP, oraclePriceData)) {
asks = asks.add(ask.order.baseAssetAmount.sub(ask.order.baseAssetAmountFilled));
}
return {
bids: bids,
asks: asks,
};
}
exports.calculateAvailablePerpLiquidity = calculateAvailablePerpLiquidity;
function calculatePerpMarketBaseLiquidatorFee(market, userHighLeverageMode) {
if (userHighLeverageMode && market.highLeverageMarginRatioMaintenance > 0) {
const marginRatio = market.highLeverageMarginRatioMaintenance * 100;
// min(liquidator_fee, .8 * high_leverage_margin_ratio_maintenance)
return Math.min(market.liquidatorFee, marginRatio - Math.floor(marginRatio / 5));
}
else {
return market.liquidatorFee;
}
}
exports.calculatePerpMarketBaseLiquidatorFee = calculatePerpMarketBaseLiquidatorFee;