@@ -108,7 +108,11 @@ import { StrictOraclePrice } from './oracles/strictOraclePrice';
108108
109109import { calculateSpotFuelBonus , calculatePerpFuelBonus } from './math/fuel' ;
110110import { grpcUserAccountSubscriber } from './accounts/grpcUserAccountSubscriber' ;
111- import { MarginCalculation , MarginContext } from './marginCalculation' ;
111+ import {
112+ IsolatedMarginCalculation ,
113+ MarginCalculation ,
114+ MarginContext ,
115+ } from './marginCalculation' ;
112116
113117export type MarginType = 'Cross' | 'Isolated' ;
114118
@@ -350,6 +354,7 @@ export class User {
350354
351355 public getIsolatePerpPositionTokenAmount ( perpMarketIndex : number ) : BN {
352356 const perpPosition = this . getPerpPosition ( perpMarketIndex ) ;
357+ if ( ! perpPosition ) return ZERO ;
353358 const perpMarket = this . driftClient . getPerpMarketAccount ( perpMarketIndex ) ;
354359 const spotMarket = this . driftClient . getSpotMarketAccount (
355360 perpMarket . quoteSpotMarketIndex
@@ -609,6 +614,7 @@ export class User {
609614 if ( perpMarketIndex !== undefined ) {
610615 const isolatedMarginCalculation =
611616 marginCalc . isolatedMarginCalculations . get ( perpMarketIndex ) ;
617+ if ( ! isolatedMarginCalculation ) return ZERO ;
612618 const { marginRequirement, marginRequirementPlusBuffer } =
613619 isolatedMarginCalculation ;
614620
@@ -1281,11 +1287,15 @@ export class User {
12811287 * @returns : number (value from [0, 100])
12821288 */
12831289 public getHealth ( perpMarketIndex ?: number ) : number {
1284- const marginCalc = this . getMarginCalculation ( 'Maintenance' ) ;
1285- if ( this . isCrossMarginBeingLiquidated ( marginCalc ) && ! perpMarketIndex ) {
1290+ if ( this . isCrossMarginBeingLiquidated ( ) && ! perpMarketIndex ) {
1291+ return 0 ;
1292+ }
1293+ if ( this . hasIsolatedPositionBeingLiquidated ( ) && perpMarketIndex ) {
12861294 return 0 ;
12871295 }
12881296
1297+ const marginCalc = this . getMarginCalculation ( 'Maintenance' ) ;
1298+
12891299 let totalCollateral : BN ;
12901300 let maintenanceMarginReq : BN ;
12911301
@@ -1671,8 +1681,14 @@ export class User {
16711681 oraclePriceData
16721682 ) ;
16731683
1684+ const tokenAmount = getTokenAmount (
1685+ perpPosition . isolatedPositionScaledBalance ?? ZERO ,
1686+ quoteSpotMarket ,
1687+ SpotBalanceType . DEPOSIT
1688+ ) ;
1689+
16741690 const spotAssetValue = getStrictTokenValue (
1675- perpPosition . isolatedPositionScaledBalance ?? ZERO , //TODO remove ? later
1691+ tokenAmount ,
16761692 quoteSpotMarket . decimals ,
16771693 strictOracle
16781694 ) ;
@@ -2051,23 +2067,49 @@ export class User {
20512067 ) ;
20522068 }
20532069
2070+ public isCrossMarginBeingLiquidated ( ) : boolean {
2071+ return (
2072+ ( this . getUserAccount ( ) . status &
2073+ ( UserStatus . BEING_LIQUIDATED | UserStatus . BANKRUPT ) ) >
2074+ 0
2075+ ) ;
2076+ }
2077+
20542078 /** Returns true if cross margin is currently below maintenance requirement (no buffer). */
2055- public isCrossMarginBeingLiquidated ( marginCalc ?: MarginCalculation ) : boolean {
2079+ public canCrossMarginBeLiquidated ( marginCalc ?: MarginCalculation ) : boolean {
20562080 const calc = marginCalc ?? this . getMarginCalculation ( 'Maintenance' ) ;
20572081 return calc . totalCollateral . lt ( calc . marginRequirement ) ;
20582082 }
20592083
2084+ public hasIsolatedPositionBeingLiquidated ( ) : boolean {
2085+ return this . getActivePerpPositions ( ) . some (
2086+ ( position ) =>
2087+ ( position . positionFlag &
2088+ ( PositionFlag . BeingLiquidated | PositionFlag . Bankruptcy ) ) >
2089+ 0
2090+ ) ;
2091+ }
2092+
20602093 /** Returns true if any isolated perp position is currently below its maintenance requirement (no buffer). */
2061- public isIsolatedMarginBeingLiquidated (
2094+ public getLiquidatableIsolatedPositions (
20622095 marginCalc ?: MarginCalculation
2063- ) : boolean {
2096+ ) : number [ ] {
2097+ const liquidatableIsolatedPositions = [ ] ;
20642098 const calc = marginCalc ?? this . getMarginCalculation ( 'Maintenance' ) ;
2065- for ( const [ , isoCalc ] of calc . isolatedMarginCalculations ) {
2066- if ( isoCalc . totalCollateral . lt ( isoCalc . marginRequirement ) ) {
2067- return true ;
2099+ for ( const [ marketIndex , isoCalc ] of calc . isolatedMarginCalculations ) {
2100+ if ( this . canIsolatedPositionMarginBeLiquidated ( isoCalc ) ) {
2101+ liquidatableIsolatedPositions . push ( marketIndex ) ;
20682102 }
20692103 }
2070- return false ;
2104+ return liquidatableIsolatedPositions ;
2105+ }
2106+
2107+ public canIsolatedPositionMarginBeLiquidated (
2108+ isolatedMarginCalculation : IsolatedMarginCalculation
2109+ ) : boolean {
2110+ return isolatedMarginCalculation . totalCollateral . lt (
2111+ isolatedMarginCalculation . marginRequirement
2112+ ) ;
20712113 }
20722114
20732115 public hasStatus ( status : UserStatus ) : boolean {
@@ -2247,6 +2289,7 @@ export class User {
22472289 } ) ;
22482290 const isolatedMarginCalculation =
22492291 marginCalculation . isolatedMarginCalculations . get ( marketIndex ) ;
2292+ if ( ! isolatedMarginCalculation ) return ZERO ;
22502293 const { totalCollateral, marginRequirement } = isolatedMarginCalculation ;
22512294
22522295 const freeCollateral = BN . max (
@@ -2287,10 +2330,6 @@ export class User {
22872330 includeOpenOrders
22882331 ) ;
22892332
2290- // console.log(
2291- // 'new user liq price totalCollateral',
2292- // totalCollateral.toString()
2293- // );
22942333 const marginRequirement = this . getMarginRequirement (
22952334 marginCategory ,
22962335 undefined ,
@@ -2299,10 +2338,6 @@ export class User {
22992338 enteringHighLeverage
23002339 ) ;
23012340
2302- // console.log(
2303- // 'new user liq price marginRequirement',
2304- // marginRequirement.toString()
2305- // );
23062341 let freeCollateral = BN . max (
23072342 ZERO ,
23082343 totalCollateral . sub ( marginRequirement )
@@ -2514,7 +2549,6 @@ export class User {
25142549 this . getUserAccount ( ) . maxMarginRatio
25152550 ) ;
25162551
2517- // TODO: does this work in an isolated position context, cc perp
25182552 const marginRatio = calculateMarketMarginRatio (
25192553 market ,
25202554 proposedBaseAssetAmount . abs ( ) ,
@@ -4467,6 +4501,15 @@ export class User {
44674501 }
44684502 }
44694503
4504+ // perp position liability
4505+ const hasPerpLiability =
4506+ ! marketPosition . baseAssetAmount . eq ( ZERO ) ||
4507+ marketPosition . quoteAssetAmount . lt ( ZERO ) ||
4508+ marketPosition . openOrders !== 0 ;
4509+ if ( hasPerpLiability ) {
4510+ calc . addPerpLiability ( ) ;
4511+ }
4512+
44704513 // Add perp contribution: isolated vs cross
44714514 const isIsolated = this . isPerpPositionIsolated ( marketPosition ) ;
44724515 if ( isIsolated ) {
@@ -4513,13 +4556,6 @@ export class User {
45134556 worstCaseLiabilityValue
45144557 ) ;
45154558 calc . addCrossMarginTotalCollateral ( positionUnrealizedPnl ) ;
4516- const hasPerpLiability =
4517- ! marketPosition . baseAssetAmount . eq ( ZERO ) ||
4518- marketPosition . quoteAssetAmount . lt ( ZERO ) ||
4519- marketPosition . openOrders !== 0 ;
4520- if ( hasPerpLiability ) {
4521- calc . addPerpLiability ( ) ;
4522- }
45234559 }
45244560 }
45254561 return calc ;
0 commit comments