diff --git a/sdk/src/clearingHouseUser.ts b/sdk/src/clearingHouseUser.ts index 4586bd17..779a0cb0 100644 --- a/sdk/src/clearingHouseUser.ts +++ b/sdk/src/clearingHouseUser.ts @@ -11,7 +11,10 @@ import { UserPosition, UserPositionsAccount, } from './types'; -import { calculateEntryPrice } from './math/position'; +import { + calculateEntryPrice, + calculatePositionPNLWithExitPrice, +} from './math/position'; import { MARK_PRICE_PRECISION, AMM_TO_QUOTE_PRECISION_RATIO, @@ -254,7 +257,11 @@ export class ClearingHouseUser { * calculates unrealized position price pnl * @returns : Precision QUOTE_PRECISION */ - public getUnrealizedPNL(withFunding?: boolean, marketIndex?: BN): BN { + public getUnrealizedPNL( + withFunding?: boolean, + marketIndex?: BN, + withoutSlippage?: boolean + ): BN { return this.getUserPositionsAccount() .positions.filter((pos) => marketIndex ? pos.marketIndex === marketIndex : true @@ -262,7 +269,14 @@ export class ClearingHouseUser { .reduce((pnl, marketPosition) => { const market = this.clearingHouse.getMarket(marketPosition.marketIndex); return pnl.add( - calculatePositionPNL(market, marketPosition, withFunding) + withoutSlippage + ? calculatePositionPNLWithExitPrice( + market, + marketPosition, + calculateMarkPrice(market), + withFunding + ) + : calculatePositionPNL(market, marketPosition, withFunding) ); }, ZERO); } @@ -286,10 +300,11 @@ export class ClearingHouseUser { * calculates TotalCollateral: collateral + unrealized pnl * @returns : Precision QUOTE_PRECISION */ - public getTotalCollateral(): BN { + public getTotalCollateral(withoutSlippage = false): BN { return ( - this.getUserAccount().collateral.add(this.getUnrealizedPNL(true)) ?? - new BN(0) + this.getUserAccount().collateral.add( + this.getUnrealizedPNL(true, null, withoutSlippage) + ) ?? new BN(0) ); } diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index e58ea4d8..2ed634c3 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -90,6 +90,49 @@ export function calculatePositionPNL( return pnl; } +/** + * calculatePositionPNLWithoutSlippage + * = BaseAssetAmount * (Avg Exit Price - Avg Entry Price) + * @param market + * @param marketPosition + * @param withFunding (adds unrealized funding payment pnl to result) + * @returns BaseAssetAmount : Precision QUOTE_PRECISION + */ +export function calculatePositionPNLWithExitPrice( + market: Market, + marketPosition: UserPosition, + exitPrice: BN, + withFunding = false +): BN { + if (marketPosition.baseAssetAmount.eq(ZERO)) { + return ZERO; + } + + const baseAssetValue = marketPosition.baseAssetAmount + .abs() + .mul(exitPrice) + .div(MARK_PRICE_PRECISION) + .div(AMM_TO_QUOTE_PRECISION_RATIO); + + let pnl; + if (marketPosition.baseAssetAmount.gt(ZERO)) { + pnl = baseAssetValue.sub(marketPosition.quoteAssetAmount); + } else { + pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue); + } + + if (withFunding) { + const fundingRatePnL = calculatePositionFundingPNL( + market, + marketPosition + ).div(PRICE_TO_QUOTE_PRECISION); + + pnl = pnl.add(fundingRatePnL); + } + + return pnl; +} + /** * * @param market