2022-09-29 06:51:09 -07:00
import { BN } from '@project-serum/anchor' ;
import { OpenOrders } from '@project-serum/serum' ;
2022-08-17 23:48:45 -07:00
import { PublicKey } from '@solana/web3.js' ;
2022-08-11 08:44:12 -07:00
import _ from 'lodash' ;
import {
HUNDRED_I80F48 ,
I80F48 ,
I80F48Dto ,
MAX_I80F48 ,
2022-12-02 06:48:43 -08:00
ONE_I80F48 ,
2022-08-11 08:44:12 -07:00
ZERO_I80F48 ,
2022-09-30 06:07:43 -07:00
} from '../numbers/I80F48' ;
import { Bank , BankForHealth , TokenIndex } from './bank' ;
import { Group } from './group' ;
2022-09-29 06:51:09 -07:00
import { HealthType , MangoAccount , PerpPosition } from './mangoAccount' ;
2022-09-23 02:43:26 -07:00
import { PerpMarket , PerpOrderSide } from './perp' ;
2022-09-29 06:51:09 -07:00
import { MarketIndex , Serum3Market , Serum3Side } from './serum3' ;
2022-07-13 10:18:55 -07:00
// ░░░░
//
// ██
// ██░░██
// ░░ ░░ ██░░░░░░██ ░░░░
// ██░░░░░░░░░░██
// ██░░░░░░░░░░██
// ██░░░░░░░░░░░░░░██
// ██░░░░░░██████░░░░░░██
// ██░░░░░░██████░░░░░░██
// ██░░░░░░░░██████░░░░░░░░██
// ██░░░░░░░░██████░░░░░░░░██
// ██░░░░░░░░░░██████░░░░░░░░░░██
// ██░░░░░░░░░░░░██████░░░░░░░░░░░░██
// ██░░░░░░░░░░░░██████░░░░░░░░░░░░██
// ██░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░██
// ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██
// ██░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░░░██
// ██░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░░░██
// ██░░░░░░░░░░░░░░░░░░██████░░░░░░░░░░░░░░░░░░██
// ░░ ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░██
// ██████████████████████████████████████████
// warning: this code is copy pasta from rust, keep in sync with health.rs
export class HealthCache {
2022-08-31 02:36:44 -07:00
constructor (
public tokenInfos : TokenInfo [ ] ,
public serum3Infos : Serum3Info [ ] ,
public perpInfos : PerpInfo [ ] ,
) { }
2022-09-29 06:51:09 -07:00
static fromMangoAccount (
group : Group ,
mangoAccount : MangoAccount ,
) : HealthCache {
// token contribution from token accounts
const tokenInfos = mangoAccount . tokensActive ( ) . map ( ( tokenPosition ) = > {
const bank = group . getFirstBankByTokenIndex ( tokenPosition . tokenIndex ) ;
return TokenInfo . fromBank ( bank , tokenPosition . balance ( bank ) ) ;
} ) ;
// Fill the TokenInfo balance with free funds in serum3 oo accounts, and fill
// the serum3MaxReserved with their reserved funds. Also build Serum3Infos.
const serum3Infos = mangoAccount . serum3Active ( ) . map ( ( serum3 ) = > {
const oo = mangoAccount . getSerum3OoAccount ( serum3 . marketIndex ) ;
// find the TokenInfos for the market's base and quote tokens
const baseIndex = tokenInfos . findIndex (
( tokenInfo ) = > tokenInfo . tokenIndex === serum3 . baseTokenIndex ,
) ;
const baseInfo = tokenInfos [ baseIndex ] ;
if ( ! baseInfo ) {
throw new Error (
` BaseInfo not found for market with marketIndex ${ serum3 . marketIndex } ! ` ,
) ;
}
const quoteIndex = tokenInfos . findIndex (
( tokenInfo ) = > tokenInfo . tokenIndex === serum3 . quoteTokenIndex ,
) ;
const quoteInfo = tokenInfos [ quoteIndex ] ;
if ( ! quoteInfo ) {
throw new Error (
` QuoteInfo not found for market with marketIndex ${ serum3 . marketIndex } ! ` ,
) ;
}
return Serum3Info . fromOoModifyingTokenInfos (
baseIndex ,
baseInfo ,
quoteIndex ,
quoteInfo ,
serum3 . marketIndex ,
oo ,
) ;
} ) ;
// health contribution from perp accounts
const perpInfos = mangoAccount . perpActive ( ) . map ( ( perpPosition ) = > {
const perpMarket = group . getPerpMarketByMarketIndex (
perpPosition . marketIndex ,
) ;
return PerpInfo . fromPerpPosition ( perpMarket , perpPosition ) ;
} ) ;
return new HealthCache ( tokenInfos , serum3Infos , perpInfos ) ;
}
static fromDto ( dto ) : HealthCache {
2022-08-31 02:36:44 -07:00
return new HealthCache (
dto . tokenInfos . map ( ( dto ) = > TokenInfo . fromDto ( dto ) ) ,
dto . serum3Infos . map ( ( dto ) = > Serum3Info . fromDto ( dto ) ) ,
2022-09-23 02:43:26 -07:00
dto . perpInfos . map ( ( dto ) = > PerpInfo . fromDto ( dto ) ) ,
2022-08-31 02:36:44 -07:00
) ;
2022-07-13 10:18:55 -07:00
}
2022-12-02 06:48:43 -08:00
computeSerum3Reservations ( healthType : HealthType ) : {
tokenMaxReserved : I80F48 [ ] ;
serum3Reserved : Serum3Reserved [ ] ;
} {
// For each token, compute the sum of serum-reserved amounts over all markets.
const tokenMaxReserved = new Array ( this . tokenInfos . length )
. fill ( null )
. map ( ( ignored ) = > ZERO_I80F48 ( ) ) ;
// For each serum market, compute what happened if reserved_base was converted to quote
// or reserved_quote was converted to base.
const serum3Reserved : Serum3Reserved [ ] = [ ] ;
for ( const info of this . serum3Infos ) {
const quote = this . tokenInfos [ info . quoteIndex ] ;
const base = this . tokenInfos [ info . baseIndex ] ;
const reservedBase = info . reservedBase ;
const reservedQuote = info . reservedQuote ;
const quoteAsset = quote . prices . asset ( healthType ) ;
const baseLiab = base . prices . liab ( healthType ) ;
const allReservedAsBase = reservedBase . add (
reservedQuote . mul ( quoteAsset ) . div ( baseLiab ) ,
) ;
const baseAsset = base . prices . asset ( healthType ) ;
const quoteLiab = quote . prices . liab ( healthType ) ;
const allReservedAsQuote = reservedQuote . add (
reservedBase . mul ( baseAsset ) . div ( quoteLiab ) ,
) ;
const baseMaxReserved = tokenMaxReserved [ info . baseIndex ] ;
baseMaxReserved . iadd ( allReservedAsBase ) ;
const quoteMaxReserved = tokenMaxReserved [ info . quoteIndex ] ;
quoteMaxReserved . iadd ( allReservedAsQuote ) ;
serum3Reserved . push (
new Serum3Reserved ( allReservedAsBase , allReservedAsQuote ) ,
) ;
}
return {
tokenMaxReserved : tokenMaxReserved ,
serum3Reserved : serum3Reserved ,
} ;
}
2022-07-13 10:18:55 -07:00
public health ( healthType : HealthType ) : I80F48 {
2022-09-01 00:48:23 -07:00
const health = ZERO_I80F48 ( ) ;
2022-07-13 10:18:55 -07:00
for ( const tokenInfo of this . tokenInfos ) {
2022-08-04 10:42:41 -07:00
const contrib = tokenInfo . healthContribution ( healthType ) ;
2022-09-29 06:51:09 -07:00
// console.log(` - ti ${contrib}`);
2022-09-01 00:48:23 -07:00
health . iadd ( contrib ) ;
2022-07-13 10:18:55 -07:00
}
2022-12-02 06:48:43 -08:00
const res = this . computeSerum3Reservations ( healthType ) ;
for ( const [ index , serum3Info ] of this . serum3Infos . entries ( ) ) {
2022-08-04 10:42:41 -07:00
const contrib = serum3Info . healthContribution (
healthType ,
this . tokenInfos ,
2022-12-02 06:48:43 -08:00
res . tokenMaxReserved ,
res . serum3Reserved [ index ] ,
2022-08-04 10:42:41 -07:00
) ;
2022-09-29 06:51:09 -07:00
// console.log(` - si ${contrib}`);
2022-09-01 00:48:23 -07:00
health . iadd ( contrib ) ;
2022-07-13 10:18:55 -07:00
}
for ( const perpInfo of this . perpInfos ) {
2022-08-04 10:42:41 -07:00
const contrib = perpInfo . healthContribution ( healthType ) ;
2022-09-29 06:51:09 -07:00
// console.log(` - pi ${contrib}`);
2022-09-01 00:48:23 -07:00
health . iadd ( contrib ) ;
2022-07-13 10:18:55 -07:00
}
return health ;
}
2022-08-11 08:44:12 -07:00
2022-11-21 11:36:13 -08:00
// Note: only considers positive perp pnl contributions, see program code for more reasoning
public perpSettleHealth ( ) : I80F48 {
const health = ZERO_I80F48 ( ) ;
for ( const tokenInfo of this . tokenInfos ) {
const contrib = tokenInfo . healthContribution ( HealthType . maint ) ;
// console.log(` - ti ${contrib}`);
health . iadd ( contrib ) ;
}
2022-12-02 06:48:43 -08:00
const res = this . computeSerum3Reservations ( HealthType . maint ) ;
for ( const [ index , serum3Info ] of this . serum3Infos . entries ( ) ) {
2022-11-21 11:36:13 -08:00
const contrib = serum3Info . healthContribution (
HealthType . maint ,
this . tokenInfos ,
2022-12-02 06:48:43 -08:00
res . tokenMaxReserved ,
res . serum3Reserved [ index ] ,
2022-11-21 11:36:13 -08:00
) ;
// console.log(` - si ${contrib}`);
health . iadd ( contrib ) ;
}
for ( const perpInfo of this . perpInfos ) {
if ( perpInfo . trustedMarket ) {
const positiveContrib = perpInfo
2022-12-02 06:48:43 -08:00
. uncappedHealthContribution ( HealthType . maint )
2022-11-21 11:36:13 -08:00
. max ( ZERO_I80F48 ( ) ) ;
// console.log(` - pi ${positiveContrib}`);
health . iadd ( positiveContrib ) ;
}
}
return health ;
}
2022-08-11 08:44:12 -07:00
public assets ( healthType : HealthType ) : I80F48 {
2022-09-01 00:48:23 -07:00
const assets = ZERO_I80F48 ( ) ;
2022-08-11 08:44:12 -07:00
for ( const tokenInfo of this . tokenInfos ) {
const contrib = tokenInfo . healthContribution ( healthType ) ;
if ( contrib . isPos ( ) ) {
2022-09-01 00:48:23 -07:00
assets . iadd ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
2022-12-02 06:48:43 -08:00
const res = this . computeSerum3Reservations ( HealthType . maint ) ;
for ( const [ index , serum3Info ] of this . serum3Infos . entries ( ) ) {
2022-08-11 08:44:12 -07:00
const contrib = serum3Info . healthContribution (
healthType ,
this . tokenInfos ,
2022-12-02 06:48:43 -08:00
res . tokenMaxReserved ,
res . serum3Reserved [ index ] ,
2022-08-11 08:44:12 -07:00
) ;
if ( contrib . isPos ( ) ) {
2022-09-01 00:48:23 -07:00
assets . iadd ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
for ( const perpInfo of this . perpInfos ) {
const contrib = perpInfo . healthContribution ( healthType ) ;
if ( contrib . isPos ( ) ) {
2022-09-01 00:48:23 -07:00
assets . iadd ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
return assets ;
}
public liabs ( healthType : HealthType ) : I80F48 {
2022-09-01 00:48:23 -07:00
const liabs = ZERO_I80F48 ( ) ;
2022-08-11 08:44:12 -07:00
for ( const tokenInfo of this . tokenInfos ) {
const contrib = tokenInfo . healthContribution ( healthType ) ;
if ( contrib . isNeg ( ) ) {
2022-09-01 00:48:23 -07:00
liabs . isub ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
2022-12-02 06:48:43 -08:00
const res = this . computeSerum3Reservations ( HealthType . maint ) ;
for ( const [ index , serum3Info ] of this . serum3Infos . entries ( ) ) {
2022-08-11 08:44:12 -07:00
const contrib = serum3Info . healthContribution (
healthType ,
this . tokenInfos ,
2022-12-02 06:48:43 -08:00
res . tokenMaxReserved ,
res . serum3Reserved [ index ] ,
2022-08-11 08:44:12 -07:00
) ;
if ( contrib . isNeg ( ) ) {
2022-09-01 00:48:23 -07:00
liabs . isub ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
for ( const perpInfo of this . perpInfos ) {
const contrib = perpInfo . healthContribution ( healthType ) ;
if ( contrib . isNeg ( ) ) {
2022-09-01 00:48:23 -07:00
liabs . isub ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
return liabs ;
}
public healthRatio ( healthType : HealthType ) : I80F48 {
2022-09-01 00:48:23 -07:00
const assets = ZERO_I80F48 ( ) ;
const liabs = ZERO_I80F48 ( ) ;
2022-08-11 08:44:12 -07:00
for ( const tokenInfo of this . tokenInfos ) {
const contrib = tokenInfo . healthContribution ( healthType ) ;
if ( contrib . isPos ( ) ) {
2022-09-01 00:48:23 -07:00
assets . iadd ( contrib ) ;
2022-08-11 08:44:12 -07:00
} else {
2022-09-01 00:48:23 -07:00
liabs . isub ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
2022-12-02 06:48:43 -08:00
const res = this . computeSerum3Reservations ( HealthType . maint ) ;
for ( const [ index , serum3Info ] of this . serum3Infos . entries ( ) ) {
2022-08-11 08:44:12 -07:00
const contrib = serum3Info . healthContribution (
healthType ,
this . tokenInfos ,
2022-12-02 06:48:43 -08:00
res . tokenMaxReserved ,
res . serum3Reserved [ index ] ,
2022-08-11 08:44:12 -07:00
) ;
if ( contrib . isPos ( ) ) {
2022-09-01 00:48:23 -07:00
assets . iadd ( contrib ) ;
2022-08-11 08:44:12 -07:00
} else {
2022-09-01 00:48:23 -07:00
liabs . isub ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
for ( const perpInfo of this . perpInfos ) {
const contrib = perpInfo . healthContribution ( healthType ) ;
if ( contrib . isPos ( ) ) {
2022-09-01 00:48:23 -07:00
assets . iadd ( contrib ) ;
2022-08-11 08:44:12 -07:00
} else {
2022-09-01 00:48:23 -07:00
liabs . isub ( contrib ) ;
2022-08-11 08:44:12 -07:00
}
}
2022-12-02 06:48:43 -08:00
// console.log(` - assets ${assets}, liabs ${liabs}`);
2022-09-27 08:33:51 -07:00
if ( liabs . gt ( I80F48 . fromNumber ( 0.001 ) ) ) {
2022-09-01 00:48:23 -07:00
return HUNDRED_I80F48 ( ) . mul ( assets . sub ( liabs ) . div ( liabs ) ) ;
2022-08-11 08:44:12 -07:00
} else {
2022-09-01 00:48:23 -07:00
return MAX_I80F48 ( ) ;
2022-08-11 08:44:12 -07:00
}
}
2022-09-29 06:51:09 -07:00
findTokenInfoIndex ( tokenIndex : TokenIndex ) : number {
2022-08-11 08:44:12 -07:00
return this . tokenInfos . findIndex (
2022-09-29 06:51:09 -07:00
( tokenInfo ) = > tokenInfo . tokenIndex === tokenIndex ,
2022-08-11 08:44:12 -07:00
) ;
}
2022-09-23 02:43:26 -07:00
getOrCreateTokenInfoIndex ( bank : BankForHealth ) : number {
2022-08-12 02:05:39 -07:00
const index = this . findTokenInfoIndex ( bank . tokenIndex ) ;
if ( index == - 1 ) {
2022-09-29 06:51:09 -07:00
this . tokenInfos . push ( TokenInfo . fromBank ( bank ) ) ;
2022-08-12 02:05:39 -07:00
}
return this . findTokenInfoIndex ( bank . tokenIndex ) ;
}
2022-10-07 04:52:04 -07:00
simHealthRatioWithTokenPositionChanges (
group : Group ,
nativeTokenChanges : {
nativeTokenAmount : I80F48 ;
mintPk : PublicKey ;
} [ ] ,
healthType : HealthType = HealthType . init ,
) : I80F48 {
const adjustedCache : HealthCache = _ . cloneDeep ( this ) ;
// HealthCache.logHealthCache('beforeChange', adjustedCache);
for ( const change of nativeTokenChanges ) {
const bank : Bank = group . getFirstBankByMint ( change . mintPk ) ;
const changeIndex = adjustedCache . getOrCreateTokenInfoIndex ( bank ) ;
2022-12-02 06:48:43 -08:00
// TODO: this will no longer work as easily because of the health weight changes
adjustedCache . tokenInfos [ changeIndex ] . balanceNative . iadd (
change . nativeTokenAmount ,
2022-10-07 04:52:04 -07:00
) ;
}
// HealthCache.logHealthCache('afterChange', adjustedCache);
return adjustedCache . healthRatio ( healthType ) ;
}
2022-09-29 06:51:09 -07:00
findSerum3InfoIndex ( marketIndex : MarketIndex ) : number {
2022-09-23 00:34:08 -07:00
return this . serum3Infos . findIndex (
( serum3Info ) = > serum3Info . marketIndex === marketIndex ,
) ;
}
2022-09-29 06:51:09 -07:00
getOrCreateSerum3InfoIndex (
baseBank : BankForHealth ,
quoteBank : BankForHealth ,
serum3Market : Serum3Market ,
) : number {
2022-09-23 00:34:08 -07:00
const index = this . findSerum3InfoIndex ( serum3Market . marketIndex ) ;
const baseEntryIndex = this . getOrCreateTokenInfoIndex ( baseBank ) ;
const quoteEntryIndex = this . getOrCreateTokenInfoIndex ( quoteBank ) ;
if ( index == - 1 ) {
this . serum3Infos . push (
Serum3Info . emptyFromSerum3Market (
serum3Market ,
baseEntryIndex ,
quoteEntryIndex ,
) ,
) ;
}
return this . findSerum3InfoIndex ( serum3Market . marketIndex ) ;
}
2022-08-31 02:36:44 -07:00
adjustSerum3Reserved (
2022-09-29 06:51:09 -07:00
baseBank : BankForHealth ,
quoteBank : BankForHealth ,
2022-09-23 00:34:08 -07:00
serum3Market : Serum3Market ,
2022-08-31 02:36:44 -07:00
reservedBaseChange : I80F48 ,
freeBaseChange : I80F48 ,
reservedQuoteChange : I80F48 ,
freeQuoteChange : I80F48 ,
2022-09-29 06:51:09 -07:00
) : void {
2022-09-23 00:34:08 -07:00
const baseEntryIndex = this . getOrCreateTokenInfoIndex ( baseBank ) ;
const quoteEntryIndex = this . getOrCreateTokenInfoIndex ( quoteBank ) ;
2022-08-31 02:36:44 -07:00
const baseEntry = this . tokenInfos [ baseEntryIndex ] ;
const quoteEntry = this . tokenInfos [ quoteEntryIndex ] ;
// Apply it to the tokens
2022-12-02 06:48:43 -08:00
baseEntry . balanceNative . iadd ( freeBaseChange ) ;
quoteEntry . balanceNative . iadd ( freeQuoteChange ) ;
2022-08-31 02:36:44 -07:00
// Apply it to the serum3 info
2022-09-29 06:51:09 -07:00
const index = this . getOrCreateSerum3InfoIndex (
baseBank ,
quoteBank ,
serum3Market ,
) ;
2022-09-23 00:34:08 -07:00
const serum3Info = this . serum3Infos [ index ] ;
2022-12-02 06:48:43 -08:00
serum3Info . reservedBase . iadd ( reservedBaseChange ) ;
serum3Info . reservedQuote . iadd ( reservedQuoteChange ) ;
2022-08-31 02:36:44 -07:00
}
simHealthRatioWithSerum3BidChanges (
2022-09-29 06:51:09 -07:00
baseBank : BankForHealth ,
quoteBank : BankForHealth ,
2022-08-31 02:36:44 -07:00
bidNativeQuoteAmount : I80F48 ,
serum3Market : Serum3Market ,
healthType : HealthType = HealthType . init ,
) : I80F48 {
const adjustedCache : HealthCache = _ . cloneDeep ( this ) ;
2022-08-31 05:37:45 -07:00
const quoteIndex = adjustedCache . getOrCreateTokenInfoIndex ( quoteBank ) ;
2022-08-31 02:36:44 -07:00
// Move token balance to reserved funds in open orders,
// essentially simulating a place order
// Reduce token balance for quote
2022-12-02 06:48:43 -08:00
adjustedCache . tokenInfos [ quoteIndex ] . balanceNative . isub (
bidNativeQuoteAmount ,
2022-09-01 00:48:23 -07:00
) ;
2022-08-31 02:36:44 -07:00
// Increase reserved in Serum3Info for quote
adjustedCache . adjustSerum3Reserved (
2022-09-29 06:51:09 -07:00
baseBank ,
quoteBank ,
2022-09-23 00:34:08 -07:00
serum3Market ,
2022-09-01 00:48:23 -07:00
ZERO_I80F48 ( ) ,
ZERO_I80F48 ( ) ,
2022-08-31 02:36:44 -07:00
bidNativeQuoteAmount ,
2022-09-01 00:48:23 -07:00
ZERO_I80F48 ( ) ,
2022-08-31 02:36:44 -07:00
) ;
return adjustedCache . healthRatio ( healthType ) ;
}
simHealthRatioWithSerum3AskChanges (
2022-09-29 06:51:09 -07:00
baseBank : BankForHealth ,
quoteBank : BankForHealth ,
2022-08-31 02:36:44 -07:00
askNativeBaseAmount : I80F48 ,
serum3Market : Serum3Market ,
healthType : HealthType = HealthType . init ,
) : I80F48 {
const adjustedCache : HealthCache = _ . cloneDeep ( this ) ;
2022-08-31 05:37:45 -07:00
const baseIndex = adjustedCache . getOrCreateTokenInfoIndex ( baseBank ) ;
2022-08-31 02:36:44 -07:00
// Move token balance to reserved funds in open orders,
// essentially simulating a place order
// Reduce token balance for base
2022-12-02 06:48:43 -08:00
adjustedCache . tokenInfos [ baseIndex ] . balanceNative . isub ( askNativeBaseAmount ) ;
2022-08-31 02:36:44 -07:00
// Increase reserved in Serum3Info for base
adjustedCache . adjustSerum3Reserved (
2022-09-29 06:51:09 -07:00
baseBank ,
quoteBank ,
2022-09-23 00:34:08 -07:00
serum3Market ,
2022-08-31 02:36:44 -07:00
askNativeBaseAmount ,
2022-09-01 00:48:23 -07:00
ZERO_I80F48 ( ) ,
ZERO_I80F48 ( ) ,
ZERO_I80F48 ( ) ,
2022-08-31 02:36:44 -07:00
) ;
return adjustedCache . healthRatio ( healthType ) ;
}
2022-10-07 04:52:04 -07:00
findPerpInfoIndex ( perpMarketIndex : number ) : number {
return this . perpInfos . findIndex (
( perpInfo ) = > perpInfo . perpMarketIndex === perpMarketIndex ,
) ;
}
getOrCreatePerpInfoIndex ( perpMarket : PerpMarket ) : number {
const index = this . findPerpInfoIndex ( perpMarket . perpMarketIndex ) ;
if ( index == - 1 ) {
this . perpInfos . push ( PerpInfo . emptyFromPerpMarket ( perpMarket ) ) ;
}
return this . findPerpInfoIndex ( perpMarket . perpMarketIndex ) ;
}
2022-12-02 06:48:43 -08:00
adjustPerpInfo (
2022-10-07 04:52:04 -07:00
perpInfoIndex : number ,
2022-12-02 06:48:43 -08:00
price : I80F48 ,
2022-10-07 04:52:04 -07:00
side : PerpOrderSide ,
newOrderBaseLots : BN ,
) : void {
if ( side == PerpOrderSide . bid ) {
2022-12-02 06:48:43 -08:00
this . perpInfos [ perpInfoIndex ] . baseLots . iadd ( newOrderBaseLots ) ;
this . perpInfos [ perpInfoIndex ] . quote . isub (
I80F48 . fromI64 ( newOrderBaseLots )
. mul ( I80F48 . fromI64 ( this . perpInfos [ perpInfoIndex ] . baseLotSize ) )
. mul ( price ) ,
) ;
2022-10-07 04:52:04 -07:00
} else {
2022-12-02 06:48:43 -08:00
this . perpInfos [ perpInfoIndex ] . baseLots . isub ( newOrderBaseLots ) ;
this . perpInfos [ perpInfoIndex ] . quote . iadd (
I80F48 . fromI64 ( newOrderBaseLots )
. mul ( I80F48 . fromI64 ( this . perpInfos [ perpInfoIndex ] . baseLotSize ) )
. mul ( price ) ,
) ;
2022-10-07 04:52:04 -07:00
}
}
simHealthRatioWithPerpOrderChanges (
perpMarket : PerpMarket ,
existingPerpPosition : PerpPosition ,
side : PerpOrderSide ,
2022-12-02 06:48:43 -08:00
baseLots : BN ,
price : I80F48 ,
2022-10-07 04:52:04 -07:00
healthType : HealthType = HealthType . init ,
) : I80F48 {
const clonedHealthCache : HealthCache = _ . cloneDeep ( this ) ;
const perpInfoIndex =
clonedHealthCache . getOrCreatePerpInfoIndex ( perpMarket ) ;
2022-12-02 06:48:43 -08:00
clonedHealthCache . adjustPerpInfo ( perpInfoIndex , price , side , baseLots ) ;
2022-10-07 04:52:04 -07:00
return clonedHealthCache . healthRatio ( healthType ) ;
}
2022-12-02 06:48:43 -08:00
public logHealthCache ( debug : string ) : void {
2022-10-07 04:52:04 -07:00
if ( debug ) console . log ( debug ) ;
2022-12-02 06:48:43 -08:00
for ( const token of this . tokenInfos ) {
2022-10-07 04:52:04 -07:00
console . log ( ` ${ token . toString ( ) } ` ) ;
}
2022-12-02 06:48:43 -08:00
const res = this . computeSerum3Reservations ( HealthType . maint ) ;
for ( const [ index , serum3Info ] of this . serum3Infos . entries ( ) ) {
console . log (
` ${ serum3Info . toString (
this . tokenInfos ,
res . tokenMaxReserved ,
res . serum3Reserved [ index ] ,
) } ` ,
) ;
2022-10-07 04:52:04 -07:00
}
console . log (
2022-12-02 06:48:43 -08:00
` assets ${ this . assets ( HealthType . init ) } , liabs ${ this . liabs (
2022-10-07 04:52:04 -07:00
HealthType . init ,
2022-12-02 06:48:43 -08:00
) } , ` ,
2022-10-07 04:52:04 -07:00
) ;
2022-12-02 06:48:43 -08:00
console . log ( ` health(HealthType.init) ${ this . health ( HealthType . init ) } ` ) ;
2022-10-07 04:52:04 -07:00
console . log (
2022-12-02 06:48:43 -08:00
` healthRatio(HealthType.init) ${ this . healthRatio ( HealthType . init ) } ` ,
2022-10-07 04:52:04 -07:00
) ;
}
2022-08-31 02:36:44 -07:00
private static binaryApproximationSearch (
left : I80F48 ,
leftRatio : I80F48 ,
right : I80F48 ,
rightRatio : I80F48 ,
targetRatio : I80F48 ,
2022-12-02 06:48:43 -08:00
minStep : I80F48 ,
2022-08-31 02:36:44 -07:00
healthRatioAfterActionFn : ( I80F48 ) = > I80F48 ,
2022-09-29 06:51:09 -07:00
) : I80F48 {
2022-12-02 06:48:43 -08:00
const maxIterations = 20 ;
2022-09-30 05:59:12 -07:00
const targetError = I80F48 . fromNumber ( 0.1 ) ;
2022-08-31 02:36:44 -07:00
if (
( leftRatio . sub ( targetRatio ) . isPos ( ) &&
rightRatio . sub ( targetRatio ) . isPos ( ) ) ||
( leftRatio . sub ( targetRatio ) . isNeg ( ) &&
rightRatio . sub ( targetRatio ) . isNeg ( ) )
) {
throw new Error (
2022-09-29 06:51:09 -07:00
` Internal error: left ${ leftRatio . toNumber ( ) } and right ${ rightRatio . toNumber ( ) } don't contain the target value ${ targetRatio . toNumber ( ) } , likely reason is the zeroAmount not been tight enough! ` ,
2022-08-31 02:36:44 -07:00
) ;
}
let newAmount ;
2022-09-29 06:51:09 -07:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2022-08-31 02:36:44 -07:00
for ( const key of Array ( maxIterations ) . fill ( 0 ) . keys ( ) ) {
2022-12-02 06:48:43 -08:00
if ( right . sub ( left ) . abs ( ) . lt ( minStep ) ) {
return left ;
}
2022-08-31 02:36:44 -07:00
newAmount = left . add ( right ) . mul ( I80F48 . fromNumber ( 0.5 ) ) ;
const newAmountRatio = healthRatioAfterActionFn ( newAmount ) ;
const error = newAmountRatio . sub ( targetRatio ) ;
if ( error . isPos ( ) && error . lt ( targetError ) ) {
return newAmount ;
}
if ( newAmountRatio . gt ( targetRatio ) != rightRatio . gt ( targetRatio ) ) {
left = newAmount ;
2022-12-02 06:48:43 -08:00
leftRatio = newAmountRatio ;
2022-08-31 02:36:44 -07:00
} else {
right = newAmount ;
rightRatio = newAmountRatio ;
}
2022-12-02 06:48:43 -08:00
// console.log(
// ` -- ${left.toNumber().toFixed(3)} (${leftRatio
// .toNumber()
// .toFixed(3)}) ${right.toNumber().toFixed(3)} (${rightRatio
// .toNumber()
// .toFixed(3)})`,
// );
2022-08-31 02:36:44 -07:00
}
2022-12-02 06:48:43 -08:00
2022-08-31 02:36:44 -07:00
console . error (
` Unable to get targetRatio within ${ maxIterations } iterations ` ,
) ;
return newAmount ;
}
2022-08-11 08:44:12 -07:00
getMaxSourceForTokenSwap (
2022-09-23 02:43:26 -07:00
sourceBank : BankForHealth ,
targetBank : BankForHealth ,
2022-12-02 06:48:43 -08:00
price : I80F48 ,
2022-08-11 08:44:12 -07:00
minRatio : I80F48 ,
) : I80F48 {
2022-08-15 11:23:51 -07:00
if (
sourceBank . initLiabWeight
. sub ( targetBank . initAssetWeight )
. abs ( )
2022-09-01 00:48:23 -07:00
. lte ( ZERO_I80F48 ( ) )
2022-08-15 11:23:51 -07:00
) {
2022-09-01 00:48:23 -07:00
return ZERO_I80F48 ( ) ;
2022-08-15 11:23:51 -07:00
}
2022-08-11 08:44:12 -07:00
// The health_ratio is a nonlinear based on swap amount.
// For large swap amounts the slope is guaranteed to be negative, but small amounts
// can have positive slope (e.g. using source deposits to pay back target borrows).
//
// That means:
// - even if the initial ratio is < minRatio it can be useful to swap to *increase* health
// - be careful about finding the minRatio point: the function isn't convex
const initialRatio = this . healthRatio ( HealthType . init ) ;
2022-09-29 06:51:09 -07:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2022-09-23 02:43:26 -07:00
const initialHealth = this . health ( HealthType . init ) ;
2022-09-01 00:48:23 -07:00
if ( initialRatio . lte ( ZERO_I80F48 ( ) ) ) {
return ZERO_I80F48 ( ) ;
2022-08-11 08:44:12 -07:00
}
2022-12-02 06:48:43 -08:00
const healthCacheClone : HealthCache = _ . cloneDeep ( this ) ;
const sourceIndex = healthCacheClone . getOrCreateTokenInfoIndex ( sourceBank ) ;
const targetIndex = healthCacheClone . getOrCreateTokenInfoIndex ( targetBank ) ;
const source = healthCacheClone . tokenInfos [ sourceIndex ] ;
const target = healthCacheClone . tokenInfos [ targetIndex ] ;
2022-09-23 02:43:26 -07:00
// If the price is sufficiently good, then health will just increase from swapping:
// once we've swapped enough, swapping x reduces health by x * source_liab_weight and
// increases it by x * target_asset_weight * price_factor.
2022-12-02 06:48:43 -08:00
const finalHealthSlope = source . initLiabWeight
2022-09-23 02:43:26 -07:00
. neg ( )
2022-12-02 06:48:43 -08:00
. mul ( source . prices . liab ( HealthType . init ) )
. add (
target . initAssetWeight
. mul ( target . prices . asset ( HealthType . init ) )
. mul ( price ) ,
) ;
2022-09-23 02:43:26 -07:00
if ( finalHealthSlope . gte ( ZERO_I80F48 ( ) ) ) {
return MAX_I80F48 ( ) ;
}
2022-08-11 08:44:12 -07:00
// There are two key slope changes: Assume source.balance > 0 and target.balance < 0. Then
// initially health ratio goes up. When one of balances flips sign, the health ratio slope
// may be positive or negative for a bit, until both balances have flipped and the slope is
// negative.
// The maximum will be at one of these points (ignoring serum3 effects).
2022-08-12 02:05:39 -07:00
2022-09-29 06:51:09 -07:00
function cacheAfterSwap ( amount : I80F48 ) : HealthCache {
2022-08-12 02:05:39 -07:00
const adjustedCache : HealthCache = _ . cloneDeep ( healthCacheClone ) ;
2022-12-02 06:48:43 -08:00
// adjustedCache.logHealthCache('beforeSwap', adjustedCache);
adjustedCache . tokenInfos [ sourceIndex ] . balanceNative . isub ( amount ) ;
adjustedCache . tokenInfos [ targetIndex ] . balanceNative . iadd (
amount . mul ( price ) ,
2022-09-23 02:43:26 -07:00
) ;
2022-12-02 06:48:43 -08:00
// adjustedCache.logHealthCache('afterSwap', adjustedCache);
2022-08-11 08:44:12 -07:00
return adjustedCache ;
}
function healthRatioAfterSwap ( amount : I80F48 ) : I80F48 {
return cacheAfterSwap ( amount ) . healthRatio ( HealthType . init ) ;
}
2022-12-02 06:48:43 -08:00
function healthAfterSwap ( amount : I80F48 ) : I80F48 {
return cacheAfterSwap ( amount ) . health ( HealthType . init ) ;
}
2022-09-23 02:43:26 -07:00
// There are two key slope changes: Assume source.balance > 0 and target.balance < 0.
// When these values flip sign, the health slope decreases, but could still be positive.
// After point1 it's definitely negative (due to finalHealthSlope check above).
// The maximum health ratio will be at 0 or at one of these points (ignoring serum3 effects).
2022-12-02 06:48:43 -08:00
const sourceForZeroTargetBalance = target . balanceNative . neg ( ) . div ( price ) ;
const point0Amount = source . balanceNative
2022-09-23 02:43:26 -07:00
. min ( sourceForZeroTargetBalance )
2022-09-01 00:48:23 -07:00
. max ( ZERO_I80F48 ( ) ) ;
2022-12-02 06:48:43 -08:00
const point1Amount = source . balanceNative
2022-09-23 02:43:26 -07:00
. max ( sourceForZeroTargetBalance )
2022-09-01 00:48:23 -07:00
. max ( ZERO_I80F48 ( ) ) ;
2022-08-23 02:43:25 -07:00
const cache0 = cacheAfterSwap ( point0Amount ) ;
const point0Ratio = cache0 . healthRatio ( HealthType . init ) ;
2022-12-02 06:48:43 -08:00
const point0Health = cache0 . health ( HealthType . init ) ;
2022-08-23 02:43:25 -07:00
const cache1 = cacheAfterSwap ( point1Amount ) ;
const point1Ratio = cache1 . healthRatio ( HealthType . init ) ;
const point1Health = cache1 . health ( HealthType . init ) ;
2022-08-11 08:44:12 -07:00
let amount : I80F48 ;
if (
initialRatio . lte ( minRatio ) &&
point0Ratio . lt ( minRatio ) &&
point1Ratio . lt ( minRatio )
) {
// If we have to stay below the target ratio, pick the highest one
if ( point0Ratio . gt ( initialRatio ) ) {
if ( point1Ratio . gt ( point0Ratio ) ) {
amount = point1Amount ;
} else {
amount = point0Amount ;
}
} else if ( point1Ratio . gt ( initialRatio ) ) {
amount = point1Amount ;
} else {
2022-09-01 00:48:23 -07:00
amount = ZERO_I80F48 ( ) ;
2022-08-11 08:44:12 -07:00
}
} else if ( point1Ratio . gte ( minRatio ) ) {
// If point1Ratio is still bigger than minRatio, the target amount must be >point1Amount
// search to the right of point1Amount: but how far?
// At point1, source.balance < 0 and target.balance > 0, so use a simple estimation for
2022-09-23 02:43:26 -07:00
// zero health: health - source_liab_weight * a + target_asset_weight * a * priceFactor = 0.
2022-09-01 00:48:23 -07:00
if ( point1Health . lte ( ZERO_I80F48 ( ) ) ) {
return ZERO_I80F48 ( ) ;
2022-08-11 08:44:12 -07:00
}
2022-09-23 02:43:26 -07:00
const zeroHealthAmount = point1Amount . sub (
point1Health . div ( finalHealthSlope ) ,
2022-08-11 08:44:12 -07:00
) ;
const zeroHealthRatio = healthRatioAfterSwap ( zeroHealthAmount ) ;
2022-12-02 06:48:43 -08:00
const zeroHealth = healthAfterSwap ( zeroHealthAmount ) ;
2022-08-31 02:36:44 -07:00
amount = HealthCache . binaryApproximationSearch (
2022-08-11 08:44:12 -07:00
point1Amount ,
point1Ratio ,
zeroHealthAmount ,
zeroHealthRatio ,
minRatio ,
2022-12-02 06:48:43 -08:00
ZERO_I80F48 ( ) ,
2022-08-31 02:36:44 -07:00
healthRatioAfterSwap ,
2022-08-11 08:44:12 -07:00
) ;
} else if ( point0Ratio . gte ( minRatio ) ) {
// Must be between point0Amount and point1Amount.
2022-08-31 02:36:44 -07:00
amount = HealthCache . binaryApproximationSearch (
2022-08-11 08:44:12 -07:00
point0Amount ,
point0Ratio ,
point1Amount ,
point1Ratio ,
minRatio ,
2022-12-02 06:48:43 -08:00
ZERO_I80F48 ( ) ,
2022-08-31 02:36:44 -07:00
healthRatioAfterSwap ,
2022-08-11 08:44:12 -07:00
) ;
} else {
2022-09-23 02:43:26 -07:00
// Must be between 0 and point0_amount
amount = HealthCache . binaryApproximationSearch (
ZERO_I80F48 ( ) ,
initialRatio ,
point0Amount ,
point0Ratio ,
minRatio ,
2022-12-02 06:48:43 -08:00
ZERO_I80F48 ( ) ,
2022-09-23 02:43:26 -07:00
healthRatioAfterSwap ,
2022-08-11 08:44:12 -07:00
) ;
}
2022-12-02 06:48:43 -08:00
return amount ;
2022-08-11 08:44:12 -07:00
}
2022-08-31 02:36:44 -07:00
2022-09-23 02:43:26 -07:00
getMaxSerum3OrderForHealthRatio (
2022-09-29 06:51:09 -07:00
baseBank : BankForHealth ,
quoteBank : BankForHealth ,
2022-08-31 02:36:44 -07:00
serum3Market : Serum3Market ,
side : Serum3Side ,
minRatio : I80F48 ,
2022-09-29 06:51:09 -07:00
) : I80F48 {
2022-08-31 02:36:44 -07:00
const healthCacheClone : HealthCache = _ . cloneDeep ( this ) ;
2022-08-31 05:37:45 -07:00
const baseIndex = healthCacheClone . getOrCreateTokenInfoIndex ( baseBank ) ;
2022-09-01 00:48:23 -07:00
const quoteIndex = healthCacheClone . getOrCreateTokenInfoIndex ( quoteBank ) ;
2022-08-31 02:36:44 -07:00
const base = healthCacheClone . tokenInfos [ baseIndex ] ;
const quote = healthCacheClone . tokenInfos [ quoteIndex ] ;
// Binary search between current health (0 sized new order) and
// an amount to trade which will bring health to 0.
// Current health and amount i.e. 0
2022-09-01 00:48:23 -07:00
const initialAmount = ZERO_I80F48 ( ) ;
2022-08-31 02:36:44 -07:00
const initialHealth = this . health ( HealthType . init ) ;
const initialRatio = this . healthRatio ( HealthType . init ) ;
2022-09-01 00:48:23 -07:00
if ( initialRatio . lte ( ZERO_I80F48 ( ) ) ) {
return ZERO_I80F48 ( ) ;
2022-08-31 02:36:44 -07:00
}
// Amount which would bring health to 0
2022-09-23 00:34:08 -07:00
// where M = max(A_deposits, B_borrows)
// amount = M + (init_health + M * (B_init_liab - A_init_asset)) / (A_init_liab - B_init_asset);
2022-08-31 02:36:44 -07:00
// A is what we would be essentially swapping for B
// So when its an ask, then base->quote,
// and when its a bid, then quote->bid
let zeroAmount ;
if ( side == Serum3Side . ask ) {
2022-12-02 06:48:43 -08:00
const quoteBorrows = quote . balanceNative . lt ( ZERO_I80F48 ( ) )
? quote . balanceNative . abs ( ) . mul ( quote . prices . liab ( HealthType . init ) )
2022-09-01 00:48:23 -07:00
: ZERO_I80F48 ( ) ;
2022-12-02 06:48:43 -08:00
const max = base . balanceNative
. mul ( base . prices . asset ( HealthType . init ) )
. max ( quoteBorrows ) ;
2022-09-23 00:34:08 -07:00
zeroAmount = max . add (
initialHealth
. add ( max . mul ( quote . initLiabWeight . sub ( base . initAssetWeight ) ) )
. div (
2022-08-31 02:36:44 -07:00
base
. liabWeight ( HealthType . init )
. sub ( quote . assetWeight ( HealthType . init ) ) ,
) ,
2022-09-23 00:34:08 -07:00
) ;
2022-08-31 02:36:44 -07:00
} else {
2022-12-02 06:48:43 -08:00
const baseBorrows = base . balanceNative . lt ( ZERO_I80F48 ( ) )
? base . balanceNative . abs ( ) . mul ( base . prices . liab ( HealthType . init ) )
2022-09-01 00:48:23 -07:00
: ZERO_I80F48 ( ) ;
2022-12-02 06:48:43 -08:00
const max = quote . balanceNative
. mul ( quote . prices . asset ( HealthType . init ) )
. max ( baseBorrows ) ;
2022-09-23 00:34:08 -07:00
zeroAmount = max . add (
initialHealth
. add ( max . mul ( base . initLiabWeight . sub ( quote . initAssetWeight ) ) )
. div (
2022-08-31 02:36:44 -07:00
quote
. liabWeight ( HealthType . init )
. sub ( base . assetWeight ( HealthType . init ) ) ,
) ,
2022-09-23 00:34:08 -07:00
) ;
2022-08-31 02:36:44 -07:00
}
2022-09-23 00:34:08 -07:00
2022-08-31 02:36:44 -07:00
const cache = cacheAfterPlacingOrder ( zeroAmount ) ;
2022-09-29 06:51:09 -07:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2022-09-23 00:34:08 -07:00
const zeroAmountHealth = cache . health ( HealthType . init ) ;
2022-08-31 02:36:44 -07:00
const zeroAmountRatio = cache . healthRatio ( HealthType . init ) ;
2022-09-29 06:51:09 -07:00
function cacheAfterPlacingOrder ( amount : I80F48 ) : HealthCache {
2022-08-31 02:36:44 -07:00
const adjustedCache : HealthCache = _ . cloneDeep ( healthCacheClone ) ;
2022-12-02 06:48:43 -08:00
// adjustedCache.logHealthCache(` before placing order ${amount}`);
// TODO: there should also be some issue with oracle vs stable price here;
// probably better to pass in not the quote amount but the base or quote native amount
2022-08-31 02:36:44 -07:00
side === Serum3Side . ask
2022-12-02 06:48:43 -08:00
? adjustedCache . tokenInfos [ baseIndex ] . balanceNative . isub (
amount . div ( base . prices . oracle ) ,
)
: adjustedCache . tokenInfos [ quoteIndex ] . balanceNative . isub (
amount . div ( quote . prices . oracle ) ,
) ;
2022-08-31 02:36:44 -07:00
adjustedCache . adjustSerum3Reserved (
2022-09-29 06:51:09 -07:00
baseBank ,
quoteBank ,
2022-09-23 00:34:08 -07:00
serum3Market ,
2022-12-02 06:48:43 -08:00
side === Serum3Side . ask
? amount . div ( base . prices . oracle )
: ZERO_I80F48 ( ) ,
2022-09-01 00:48:23 -07:00
ZERO_I80F48 ( ) ,
2022-12-02 06:48:43 -08:00
side === Serum3Side . bid
? amount . div ( quote . prices . oracle )
: ZERO_I80F48 ( ) ,
2022-09-01 00:48:23 -07:00
ZERO_I80F48 ( ) ,
2022-08-31 02:36:44 -07:00
) ;
2022-12-02 06:48:43 -08:00
// adjustedCache.logHealthCache(' after placing order');
2022-08-31 02:36:44 -07:00
return adjustedCache ;
}
function healthRatioAfterPlacingOrder ( amount : I80F48 ) : I80F48 {
return cacheAfterPlacingOrder ( amount ) . healthRatio ( HealthType . init ) ;
}
const amount = HealthCache . binaryApproximationSearch (
initialAmount ,
initialRatio ,
zeroAmount ,
zeroAmountRatio ,
minRatio ,
2022-12-02 06:48:43 -08:00
ONE_I80F48 ( ) ,
2022-08-31 02:36:44 -07:00
healthRatioAfterPlacingOrder ,
) ;
2022-09-29 06:51:09 -07:00
return amount ;
2022-08-31 02:36:44 -07:00
}
2022-09-23 02:43:26 -07:00
getMaxPerpForHealthRatio (
perpMarket : PerpMarket ,
2022-12-02 06:48:43 -08:00
price ,
2022-09-23 02:43:26 -07:00
side : PerpOrderSide ,
minRatio : I80F48 ,
) : I80F48 {
const healthCacheClone : HealthCache = _ . cloneDeep ( this ) ;
const initialRatio = this . healthRatio ( HealthType . init ) ;
if ( initialRatio . lt ( ZERO_I80F48 ( ) ) ) {
return ZERO_I80F48 ( ) ;
}
const direction = side == PerpOrderSide . bid ? 1 : - 1 ;
2022-10-07 04:52:04 -07:00
const perpInfoIndex = healthCacheClone . getOrCreatePerpInfoIndex ( perpMarket ) ;
const perpInfo = healthCacheClone . perpInfos [ perpInfoIndex ] ;
2022-12-02 06:48:43 -08:00
const prices = perpInfo . prices ;
2022-09-30 06:07:43 -07:00
const baseLotSize = I80F48 . fromI64 ( perpMarket . baseLotSize ) ;
2022-09-23 02:43:26 -07:00
// If the price is sufficiently good then health will just increase from trading
const finalHealthSlope =
direction == 1
2022-12-02 06:48:43 -08:00
? perpInfo . initAssetWeight . mul ( prices . asset ( HealthType . init ) ) . sub ( price )
: price . sub ( perpInfo . initLiabWeight . mul ( prices . liab ( HealthType . init ) ) ) ;
2022-09-23 02:43:26 -07:00
if ( finalHealthSlope . gte ( ZERO_I80F48 ( ) ) ) {
return MAX_I80F48 ( ) ;
}
2022-12-02 06:48:43 -08:00
function cacheAfterTrade ( baseLots : BN ) : HealthCache {
2022-09-23 02:43:26 -07:00
const adjustedCache : HealthCache = _ . cloneDeep ( healthCacheClone ) ;
2022-12-02 06:48:43 -08:00
// adjustedCache.logHealthCache(' -- before trade');
adjustedCache . adjustPerpInfo ( perpInfoIndex , price , side , baseLots ) ;
// adjustedCache.logHealthCache(' -- after trade');
2022-09-23 02:43:26 -07:00
return adjustedCache ;
}
function healthAfterTrade ( baseLots : I80F48 ) : I80F48 {
2022-12-02 06:48:43 -08:00
return cacheAfterTrade ( new BN ( baseLots . toNumber ( ) ) ) . health (
2022-10-07 04:52:04 -07:00
HealthType . init ,
) ;
2022-09-23 02:43:26 -07:00
}
function healthRatioAfterTrade ( baseLots : I80F48 ) : I80F48 {
2022-12-02 06:48:43 -08:00
return cacheAfterTrade ( new BN ( baseLots . toNumber ( ) ) ) . healthRatio (
2022-10-07 04:52:04 -07:00
HealthType . init ,
) ;
2022-09-23 02:43:26 -07:00
}
2022-12-02 06:48:43 -08:00
function healthRatioAfterTradeTrunc ( baseLots : I80F48 ) : I80F48 {
return healthRatioAfterTrade ( baseLots . floor ( ) ) ;
}
2022-09-23 02:43:26 -07:00
2022-12-02 06:48:43 -08:00
const initialBaseLots = I80F48 . fromU64 ( perpInfo . baseLots ) ;
2022-09-23 02:43:26 -07:00
// There are two cases:
// 1. We are increasing abs(baseLots)
// 2. We are bringing the base position to 0, and then going to case 1.
const hasCase2 =
( initialBaseLots . gt ( ZERO_I80F48 ( ) ) && direction == - 1 ) ||
( initialBaseLots . lt ( ZERO_I80F48 ( ) ) && direction == 1 ) ;
let case1Start : I80F48 , case1StartRatio : I80F48 ;
if ( hasCase2 ) {
case1Start = initialBaseLots . abs ( ) ;
case1StartRatio = healthRatioAfterTrade ( case1Start ) ;
} else {
case1Start = ZERO_I80F48 ( ) ;
case1StartRatio = initialRatio ;
}
// If we start out below minRatio and can't go above, pick the best case
let baseLots : I80F48 ;
if ( initialRatio . lte ( minRatio ) && case1StartRatio . lt ( minRatio ) ) {
if ( case1StartRatio . gte ( initialRatio ) ) {
baseLots = case1Start ;
} else {
baseLots = ZERO_I80F48 ( ) ;
}
} else if ( case1StartRatio . gte ( minRatio ) ) {
// Must reach minRatio to the right of case1Start
2022-12-02 06:48:43 -08:00
// Need to figure out how many lots to trade to reach zero health (zero_health_amount).
// We do this by looking at the starting health and the health slope per
// traded base lot (final_health_slope).
const startCache = cacheAfterTrade ( new BN ( case1Start . toNumber ( ) ) ) ;
const startHealth = startCache . health ( HealthType . init ) ;
if ( startHealth . lte ( ZERO_I80F48 ( ) ) ) {
2022-09-23 02:43:26 -07:00
return ZERO_I80F48 ( ) ;
}
2022-12-02 06:48:43 -08:00
// The perp market's contribution to the health above may be capped. But we need to trade
// enough to fully reduce any positive-pnl buffer. Thus get the uncapped health:
const perpInfo = startCache . perpInfos [ perpInfoIndex ] ;
const startHealthUncapped = startHealth
. sub ( perpInfo . healthContribution ( HealthType . init ) )
. add ( perpInfo . uncappedHealthContribution ( HealthType . init ) ) ;
const zeroHealthAmount = case1Start
. sub ( startHealthUncapped . div ( finalHealthSlope ) . div ( baseLotSize ) )
. add ( ONE_I80F48 ( ) ) ;
const zeroHealthRatio = healthRatioAfterTradeTrunc ( zeroHealthAmount ) ;
2022-09-23 02:43:26 -07:00
baseLots = HealthCache . binaryApproximationSearch (
case1Start ,
case1StartRatio ,
zeroHealthAmount ,
zeroHealthRatio ,
minRatio ,
2022-12-02 06:48:43 -08:00
ONE_I80F48 ( ) ,
healthRatioAfterTradeTrunc ,
2022-09-23 02:43:26 -07:00
) ;
} else {
// Between 0 and case1Start
baseLots = HealthCache . binaryApproximationSearch (
ZERO_I80F48 ( ) ,
initialRatio ,
case1Start ,
case1StartRatio ,
minRatio ,
2022-12-02 06:48:43 -08:00
ONE_I80F48 ( ) ,
healthRatioAfterTradeTrunc ,
2022-09-23 02:43:26 -07:00
) ;
}
return baseLots . floor ( ) ;
}
2022-07-13 10:18:55 -07:00
}
2022-12-02 06:48:43 -08:00
export class Prices {
constructor ( public oracle : I80F48 , public stable : I80F48 ) { }
public liab ( healthType : HealthType ) : I80F48 {
if ( healthType == HealthType . maint ) {
return this . oracle ;
}
return this . oracle . max ( this . stable ) ;
}
public asset ( healthType : HealthType ) : I80F48 {
if ( healthType == HealthType . maint ) {
return this . oracle ;
}
return this . oracle . min ( this . stable ) ;
}
}
2022-07-13 10:18:55 -07:00
export class TokenInfo {
2022-08-12 02:05:39 -07:00
constructor (
2022-09-29 06:51:09 -07:00
public tokenIndex : TokenIndex ,
2022-08-12 02:05:39 -07:00
public maintAssetWeight : I80F48 ,
public initAssetWeight : I80F48 ,
public maintLiabWeight : I80F48 ,
public initLiabWeight : I80F48 ,
2022-12-02 06:48:43 -08:00
public prices : Prices ,
public balanceNative : I80F48 ,
2022-08-12 02:05:39 -07:00
) { }
static fromDto ( dto : TokenInfoDto ) : TokenInfo {
return new TokenInfo (
2022-09-29 06:51:09 -07:00
dto . tokenIndex as TokenIndex ,
2022-08-12 02:05:39 -07:00
I80F48 . from ( dto . maintAssetWeight ) ,
I80F48 . from ( dto . initAssetWeight ) ,
I80F48 . from ( dto . maintLiabWeight ) ,
I80F48 . from ( dto . initLiabWeight ) ,
2022-12-02 06:48:43 -08:00
new Prices (
I80F48 . from ( dto . prices . oracle ) ,
I80F48 . from ( dto . prices . stable ) ,
) ,
I80F48 . from ( dto . balanceNative ) ,
2022-08-12 02:05:39 -07:00
) ;
2022-07-13 10:18:55 -07:00
}
2022-12-02 06:48:43 -08:00
static fromBank ( bank : BankForHealth , nativeBalance? : I80F48 ) : TokenInfo {
2022-08-12 02:05:39 -07:00
return new TokenInfo (
bank . tokenIndex ,
bank . maintAssetWeight ,
bank . initAssetWeight ,
bank . maintLiabWeight ,
bank . initLiabWeight ,
2022-12-02 06:48:43 -08:00
new Prices (
bank . price ,
I80F48 . fromNumber ( bank . stablePriceModel . stablePrice ) ,
) ,
nativeBalance ? nativeBalance : ZERO_I80F48 ( ) ,
2022-08-12 02:05:39 -07:00
) ;
}
2022-07-13 10:18:55 -07:00
assetWeight ( healthType : HealthType ) : I80F48 {
return healthType == HealthType . init
? this . initAssetWeight
: this . maintAssetWeight ;
}
liabWeight ( healthType : HealthType ) : I80F48 {
return healthType == HealthType . init
? this . initLiabWeight
: this . maintLiabWeight ;
}
healthContribution ( healthType : HealthType ) : I80F48 {
2022-12-02 06:48:43 -08:00
let weight , price ;
if ( this . balanceNative . isNeg ( ) ) {
weight = this . liabWeight ( healthType ) ;
price = this . prices . liab ( healthType ) ;
} else {
weight = this . assetWeight ( healthType ) ;
price = this . prices . asset ( healthType ) ;
}
return this . balanceNative . mul ( weight ) . mul ( price ) ;
2022-07-13 10:18:55 -07:00
}
2022-08-23 00:42:00 -07:00
2022-09-29 06:51:09 -07:00
toString ( ) : string {
2022-12-02 06:48:43 -08:00
return ` tokenIndex: ${ this . tokenIndex } , balanceNative: ${
this . balanceNative
2022-08-31 02:36:44 -07:00
} , initHealth $ { this . healthContribution ( HealthType . init ) } ` ;
2022-08-23 00:42:00 -07:00
}
2022-07-13 10:18:55 -07:00
}
2022-12-02 06:48:43 -08:00
export class Serum3Reserved {
constructor (
public allReservedAsBase : I80F48 ,
public allReservedAsQuote : I80F48 ,
) { }
}
2022-07-13 10:18:55 -07:00
export class Serum3Info {
2022-08-31 02:36:44 -07:00
constructor (
2022-12-02 06:48:43 -08:00
public reservedBase : I80F48 ,
public reservedQuote : I80F48 ,
2022-08-31 02:36:44 -07:00
public baseIndex : number ,
public quoteIndex : number ,
2022-09-29 06:51:09 -07:00
public marketIndex : MarketIndex ,
2022-08-31 02:36:44 -07:00
) { }
2022-07-13 10:18:55 -07:00
2022-09-29 06:51:09 -07:00
static fromDto ( dto : Serum3InfoDto ) : Serum3Info {
2022-08-31 02:36:44 -07:00
return new Serum3Info (
2022-12-02 06:48:43 -08:00
I80F48 . from ( dto . reservedBase ) ,
I80F48 . from ( dto . reservedQuote ) ,
2022-08-31 02:36:44 -07:00
dto . baseIndex ,
dto . quoteIndex ,
2022-09-29 06:51:09 -07:00
dto . marketIndex as MarketIndex ,
2022-08-31 02:36:44 -07:00
) ;
}
2022-07-13 10:18:55 -07:00
2022-09-23 00:34:08 -07:00
static emptyFromSerum3Market (
serum3Market : Serum3Market ,
baseEntryIndex : number ,
quoteEntryIndex : number ,
2022-09-29 06:51:09 -07:00
) : Serum3Info {
2022-09-23 00:34:08 -07:00
return new Serum3Info (
2022-12-02 06:48:43 -08:00
ZERO_I80F48 ( ) ,
2022-09-23 00:34:08 -07:00
ZERO_I80F48 ( ) ,
baseEntryIndex ,
quoteEntryIndex ,
serum3Market . marketIndex ,
) ;
}
2022-09-29 06:51:09 -07:00
static fromOoModifyingTokenInfos (
baseIndex : number ,
baseInfo : TokenInfo ,
quoteIndex : number ,
quoteInfo : TokenInfo ,
marketIndex : MarketIndex ,
oo : OpenOrders ,
) : Serum3Info {
2022-12-02 06:48:43 -08:00
// add the amounts that are freely settleable immediately to token balances
2022-09-30 06:07:43 -07:00
const baseFree = I80F48 . fromI64 ( oo . baseTokenFree ) ;
2022-09-29 06:51:09 -07:00
// NOTE: referrerRebatesAccrued is not declared on oo class, but the layout
// is aware of it
2022-09-30 06:07:43 -07:00
const quoteFree = I80F48 . fromI64 (
oo . quoteTokenFree . add ( ( oo as any ) . referrerRebatesAccrued ) ,
2022-09-29 06:51:09 -07:00
) ;
2022-12-02 06:48:43 -08:00
baseInfo . balanceNative . iadd ( baseFree ) ;
quoteInfo . balanceNative . iadd ( quoteFree ) ;
2022-09-29 06:51:09 -07:00
2022-12-02 06:48:43 -08:00
// track the reserved amounts
2022-09-30 06:07:43 -07:00
const reservedBase = I80F48 . fromI64 (
oo . baseTokenTotal . sub ( oo . baseTokenFree ) ,
2022-09-29 06:51:09 -07:00
) ;
2022-09-30 06:07:43 -07:00
const reservedQuote = I80F48 . fromI64 (
oo . quoteTokenTotal . sub ( oo . quoteTokenFree ) ,
2022-09-29 06:51:09 -07:00
) ;
2022-12-02 06:48:43 -08:00
return new Serum3Info (
reservedBase ,
reservedQuote ,
baseIndex ,
quoteIndex ,
marketIndex ,
) ;
2022-09-29 06:51:09 -07:00
}
2022-12-02 06:48:43 -08:00
healthContribution (
healthType : HealthType ,
tokenInfos : TokenInfo [ ] ,
tokenMaxReserved : I80F48 [ ] ,
marketReserved : Serum3Reserved ,
) : I80F48 {
if (
marketReserved . allReservedAsBase . isZero ( ) ||
marketReserved . allReservedAsQuote . isZero ( )
) {
2022-09-01 00:48:23 -07:00
return ZERO_I80F48 ( ) ;
2022-07-13 10:18:55 -07:00
}
2022-12-02 06:48:43 -08:00
const baseInfo = tokenInfos [ this . baseIndex ] ;
const quoteInfo = tokenInfos [ this . quoteIndex ] ;
const baseMaxReserved = tokenMaxReserved [ this . baseIndex ] ;
const quoteMaxReserved = tokenMaxReserved [ this . quoteIndex ] ;
2022-07-13 10:18:55 -07:00
// How much the health would increase if the reserved balance were applied to the passed
// token info?
2022-12-02 06:48:43 -08:00
const computeHealthEffect = function (
tokenInfo : TokenInfo ,
tokenMaxReserved : I80F48 ,
marketReserved : I80F48 ,
) : I80F48 {
2022-07-13 10:18:55 -07:00
// This balance includes all possible reserved funds from markets that relate to the
2022-12-02 06:48:43 -08:00
// token, including this market itself: `tokenMaxReserved` is already included in `maxBalance`.
const maxBalance = tokenInfo . balanceNative . add ( tokenMaxReserved ) ;
2022-07-13 10:18:55 -07:00
// Assuming `reserved` was added to `max_balance` last (because that gives the smallest
// health effects): how much did health change because of it?
let assetPart , liabPart ;
2022-12-02 06:48:43 -08:00
if ( maxBalance . gte ( marketReserved ) ) {
assetPart = marketReserved ;
2022-09-01 00:48:23 -07:00
liabPart = ZERO_I80F48 ( ) ;
2022-07-13 10:18:55 -07:00
} else if ( maxBalance . isNeg ( ) ) {
2022-09-01 00:48:23 -07:00
assetPart = ZERO_I80F48 ( ) ;
2022-12-02 06:48:43 -08:00
liabPart = marketReserved ;
2022-07-13 10:18:55 -07:00
} else {
assetPart = maxBalance ;
2022-12-02 06:48:43 -08:00
liabPart = marketReserved . sub ( maxBalance ) ;
2022-07-13 10:18:55 -07:00
}
2022-08-04 10:42:41 -07:00
const assetWeight = tokenInfo . assetWeight ( healthType ) ;
const liabWeight = tokenInfo . liabWeight ( healthType ) ;
2022-12-02 06:48:43 -08:00
const assetPrice = tokenInfo . prices . asset ( healthType ) ;
const liabPrice = tokenInfo . prices . liab ( healthType ) ;
2022-09-29 06:51:09 -07:00
2022-12-02 06:48:43 -08:00
return assetWeight
. mul ( assetPart )
. mul ( assetPrice )
. add ( liabWeight . mul ( liabPart ) . mul ( liabPrice ) ) ;
2022-07-13 10:18:55 -07:00
} ;
2022-12-02 06:48:43 -08:00
const healthBase = computeHealthEffect (
baseInfo ,
baseMaxReserved ,
marketReserved . allReservedAsBase ,
) ;
const healthQuote = computeHealthEffect (
quoteInfo ,
quoteMaxReserved ,
marketReserved . allReservedAsQuote ,
) ;
return healthBase . min ( healthQuote ) ;
2022-07-13 10:18:55 -07:00
}
2022-08-31 02:36:44 -07:00
2022-12-02 06:48:43 -08:00
toString (
tokenInfos : TokenInfo [ ] ,
tokenMaxReserved : I80F48 [ ] ,
marketReserved : Serum3Reserved ,
) : string {
2022-08-31 02:36:44 -07:00
return ` marketIndex: ${ this . marketIndex } , baseIndex: ${
this . baseIndex
2022-12-02 06:48:43 -08:00
} , quoteIndex : $ { this . quoteIndex } , reservedBase : $ {
this . reservedBase
} , reservedQuote : $ {
this . reservedQuote
} , initHealth $ { this . healthContribution (
HealthType . init ,
tokenInfos ,
tokenMaxReserved ,
marketReserved ,
) } ` ;
2022-08-31 02:36:44 -07:00
}
2022-07-13 10:18:55 -07:00
}
export class PerpInfo {
2022-09-23 02:43:26 -07:00
constructor (
public perpMarketIndex : number ,
public maintAssetWeight : I80F48 ,
public initAssetWeight : I80F48 ,
public maintLiabWeight : I80F48 ,
public initLiabWeight : I80F48 ,
2022-12-02 06:48:43 -08:00
public baseLotSize : BN ,
public baseLots : BN ,
public bidsBaseLots : BN ,
public asksBaseLots : BN ,
2022-09-23 02:43:26 -07:00
public quote : I80F48 ,
2022-12-02 06:48:43 -08:00
public prices : Prices ,
2022-09-23 02:43:26 -07:00
public hasOpenOrders : boolean ,
2022-09-29 06:51:09 -07:00
public trustedMarket : boolean ,
2022-09-23 02:43:26 -07:00
) { }
2022-09-29 06:51:09 -07:00
static fromDto ( dto : PerpInfoDto ) : PerpInfo {
2022-09-23 02:43:26 -07:00
return new PerpInfo (
dto . perpMarketIndex ,
I80F48 . from ( dto . maintAssetWeight ) ,
I80F48 . from ( dto . initAssetWeight ) ,
I80F48 . from ( dto . maintLiabWeight ) ,
I80F48 . from ( dto . initLiabWeight ) ,
2022-12-02 06:48:43 -08:00
dto . baseLotSize ,
dto . baseLots ,
dto . bidsBaseLots ,
dto . asksBaseLots ,
2022-09-23 02:43:26 -07:00
I80F48 . from ( dto . quote ) ,
2022-12-02 06:48:43 -08:00
new Prices (
I80F48 . from ( dto . prices . oracle ) ,
I80F48 . from ( dto . prices . stable ) ,
) ,
2022-09-23 02:43:26 -07:00
dto . hasOpenOrders ,
2022-09-29 06:51:09 -07:00
dto . trustedMarket ,
) ;
}
static fromPerpPosition (
perpMarket : PerpMarket ,
perpPosition : PerpPosition ,
) : PerpInfo {
2022-12-02 06:48:43 -08:00
const baseLots = perpPosition . basePositionLots . add (
perpPosition . takerBaseLots ,
2022-09-29 06:51:09 -07:00
) ;
2022-10-07 04:52:04 -07:00
const unsettledFunding = perpPosition . getUnsettledFunding ( perpMarket ) ;
2022-09-29 06:51:09 -07:00
2022-09-30 06:07:43 -07:00
const takerQuote = I80F48 . fromI64 (
new BN ( perpPosition . takerQuoteLots ) . mul ( perpMarket . quoteLotSize ) ,
2022-09-29 06:51:09 -07:00
) ;
2022-09-30 06:07:43 -07:00
const quoteCurrent = perpPosition . quotePositionNative
2022-09-29 06:51:09 -07:00
. sub ( unsettledFunding )
. add ( takerQuote ) ;
return new PerpInfo (
perpMarket . perpMarketIndex ,
perpMarket . maintAssetWeight ,
perpMarket . initAssetWeight ,
perpMarket . maintLiabWeight ,
perpMarket . initLiabWeight ,
2022-12-02 06:48:43 -08:00
perpMarket . baseLotSize ,
baseLots ,
perpPosition . bidsBaseLots ,
perpPosition . asksBaseLots ,
quoteCurrent ,
new Prices (
perpMarket . price ,
I80F48 . fromNumber ( perpMarket . stablePriceModel . stablePrice ) ,
) ,
2022-09-29 06:51:09 -07:00
perpPosition . hasOpenOrders ( ) ,
perpMarket . trustedMarket ,
2022-09-23 02:43:26 -07:00
) ;
}
2022-07-13 10:18:55 -07:00
healthContribution ( healthType : HealthType ) : I80F48 {
2022-12-02 06:48:43 -08:00
return this . trustedMarket
? this . uncappedHealthContribution ( healthType )
: this . uncappedHealthContribution ( healthType ) . min ( ZERO_I80F48 ( ) ) ;
}
2022-07-13 10:18:55 -07:00
2022-12-02 06:48:43 -08:00
uncappedHealthContribution ( healthType : HealthType ) : I80F48 {
function orderExecutionCase (
pi : PerpInfo ,
ordersBaseLots : BN ,
orderPrice : I80F48 ,
) : I80F48 {
const netBaseNative = I80F48 . fromU64 (
pi . baseLots . add ( ordersBaseLots ) . mul ( pi . baseLotSize ) ,
) ;
2022-09-29 06:51:09 -07:00
2022-12-02 06:48:43 -08:00
let weight , basePrice ;
if ( healthType == HealthType . init ) {
if ( netBaseNative . isNeg ( ) ) {
weight = pi . initLiabWeight ;
basePrice = pi . prices . liab ( healthType ) ;
} else {
weight = pi . initAssetWeight ;
basePrice = pi . prices . asset ( healthType ) ;
}
} else {
if ( netBaseNative . isNeg ( ) ) {
weight = pi . maintLiabWeight ;
basePrice = pi . prices . liab ( healthType ) ;
} else {
weight = pi . maintAssetWeight ;
basePrice = pi . prices . asset ( healthType ) ;
}
}
// Total value of the order-execution adjusted base position
const baseHealth = netBaseNative . mul ( weight ) . mul ( basePrice ) ;
const ordersBaseNative = I80F48 . fromU64 (
ordersBaseLots . mul ( pi . baseLotSize ) ,
) ;
// The quote change from executing the bids/asks
const orderQuote = ordersBaseNative . neg ( ) . mul ( orderPrice ) ;
return baseHealth . add ( orderQuote ) ;
2022-09-29 06:51:09 -07:00
}
2022-12-02 06:48:43 -08:00
// What is worse: Executing all bids at oracle_price.liab, or executing all asks at oracle_price.asset?
const bidsCase = orderExecutionCase (
this ,
this . bidsBaseLots ,
this . prices . liab ( healthType ) ,
) ;
const asksCase = orderExecutionCase (
this ,
this . asksBaseLots . neg ( ) ,
this . prices . asset ( healthType ) ,
) ;
const worstCase = bidsCase . min ( asksCase ) ;
return this . quote . add ( worstCase ) ;
2022-07-13 10:18:55 -07:00
}
2022-09-23 02:43:26 -07:00
static emptyFromPerpMarket ( perpMarket : PerpMarket ) : PerpInfo {
return new PerpInfo (
perpMarket . perpMarketIndex ,
perpMarket . maintAssetWeight ,
perpMarket . initAssetWeight ,
perpMarket . maintLiabWeight ,
perpMarket . initLiabWeight ,
2022-12-02 06:48:43 -08:00
perpMarket . baseLotSize ,
new BN ( 0 ) ,
new BN ( 0 ) ,
new BN ( 0 ) ,
2022-09-23 02:43:26 -07:00
ZERO_I80F48 ( ) ,
2022-12-02 06:48:43 -08:00
new Prices (
perpMarket . price ,
I80F48 . fromNumber ( perpMarket . stablePriceModel . stablePrice ) ,
) ,
2022-09-23 02:43:26 -07:00
false ,
2022-09-29 06:51:09 -07:00
perpMarket . trustedMarket ,
2022-09-23 02:43:26 -07:00
) ;
}
2022-09-29 06:51:09 -07:00
toString ( ) : string {
return ` perpMarketIndex: ${ this . perpMarketIndex } , base: ${
2022-12-02 06:48:43 -08:00
this . baseLots
2022-09-29 06:51:09 -07:00
} , quote : $ { this . quote } , oraclePrice : $ {
2022-12-02 06:48:43 -08:00
this . prices . oracle
} , uncapped health contribution $ { this . uncappedHealthContribution (
HealthType . init ,
) } ` ;
2022-09-29 06:51:09 -07:00
}
2022-07-13 10:18:55 -07:00
}
export class HealthCacheDto {
tokenInfos : TokenInfoDto [ ] ;
serum3Infos : Serum3InfoDto [ ] ;
perpInfos : PerpInfoDto [ ] ;
}
export class TokenInfoDto {
tokenIndex : number ;
maintAssetWeight : I80F48Dto ;
initAssetWeight : I80F48Dto ;
maintLiabWeight : I80F48Dto ;
initLiabWeight : I80F48Dto ;
2022-12-02 06:48:43 -08:00
prices : { oracle : I80F48Dto ; stable : I80F48Dto } ;
balanceNative : I80F48Dto ;
2022-08-31 02:36:44 -07:00
constructor (
tokenIndex : number ,
maintAssetWeight : I80F48Dto ,
initAssetWeight : I80F48Dto ,
maintLiabWeight : I80F48Dto ,
initLiabWeight : I80F48Dto ,
2022-12-02 06:48:43 -08:00
prices : { oracle : I80F48Dto ; stable : I80F48Dto } ,
balanceNative : I80F48Dto ,
2022-08-31 02:36:44 -07:00
) {
this . tokenIndex = tokenIndex ;
this . maintAssetWeight = maintAssetWeight ;
this . initAssetWeight = initAssetWeight ;
this . maintLiabWeight = maintLiabWeight ;
this . initLiabWeight = initLiabWeight ;
2022-12-02 06:48:43 -08:00
this . prices = prices ;
this . balanceNative = balanceNative ;
2022-08-31 02:36:44 -07:00
}
2022-07-13 10:18:55 -07:00
}
export class Serum3InfoDto {
2022-12-02 06:48:43 -08:00
reservedBase : I80F48Dto ;
reservedQuote : I80F48Dto ;
2022-07-13 10:18:55 -07:00
baseIndex : number ;
quoteIndex : number ;
2022-08-31 02:36:44 -07:00
marketIndex : number ;
2022-12-02 06:48:43 -08:00
constructor (
reservedBase : I80F48Dto ,
reservedQuote : I80F48Dto ,
baseIndex : number ,
quoteIndex : number ,
) {
this . reservedBase = reservedBase ;
this . reservedQuote = reservedQuote ;
2022-08-31 02:36:44 -07:00
this . baseIndex = baseIndex ;
this . quoteIndex = quoteIndex ;
}
2022-07-13 10:18:55 -07:00
}
export class PerpInfoDto {
2022-09-23 02:43:26 -07:00
perpMarketIndex : number ;
2022-07-13 10:18:55 -07:00
maintAssetWeight : I80F48Dto ;
initAssetWeight : I80F48Dto ;
maintLiabWeight : I80F48Dto ;
initLiabWeight : I80F48Dto ;
2022-12-02 06:48:43 -08:00
public baseLotSize : BN ;
public baseLots : BN ;
public bidsBaseLots : BN ;
public asksBaseLots : BN ;
2022-07-13 10:18:55 -07:00
quote : I80F48Dto ;
2022-12-02 06:48:43 -08:00
prices : { oracle : I80F48Dto ; stable : I80F48Dto } ;
2022-09-23 02:43:26 -07:00
hasOpenOrders : boolean ;
2022-09-29 06:51:09 -07:00
trustedMarket : boolean ;
2022-07-13 10:18:55 -07:00
}