Health: cleanup and new functions for ratio and assets/liabs

This commit is contained in:
Christian Kamm 2022-07-11 22:31:36 +02:00
parent 6f758ff25b
commit 86a84396f5
3 changed files with 71 additions and 37 deletions

View File

@ -17,8 +17,9 @@ pub fn compute_account_data(ctx: Context<ComputeAccountData>) -> Result<()> {
let account_retriever = ScanningAccountRetriever::new(ctx.remaining_accounts, &group_pk)?;
let init_health = compute_health(&account, HealthType::Init, &account_retriever)?;
let maint_health = compute_health(&account, HealthType::Maint, &account_retriever)?;
let health_cache = new_health_cache(&account, &account_retriever)?;
let init_health = health_cache.health(HealthType::Init);
let maint_health = health_cache.health(HealthType::Maint);
let equity = compute_equity(&account, &account_retriever)?;

View File

@ -46,7 +46,7 @@ pub fn liq_token_with_token(
// Initial liqee health check
let mut liqee_health_cache = new_health_cache(&liqee, &account_retriever)?;
let init_health = liqee_health_cache.health(HealthType::Init)?;
let init_health = liqee_health_cache.health(HealthType::Init);
if liqee.being_liquidated() {
if init_health > I80F48::ZERO {
liqee.set_being_liquidated(false);
@ -54,7 +54,7 @@ pub fn liq_token_with_token(
return Ok(());
}
} else {
let maint_health = liqee_health_cache.health(HealthType::Maint)?;
let maint_health = liqee_health_cache.health(HealthType::Maint);
require!(maint_health < I80F48::ZERO, MangoError::SomeError);
liqee.set_being_liquidated(true);
}
@ -211,11 +211,11 @@ pub fn liq_token_with_token(
}
// Check liqee health again
let maint_health = liqee_health_cache.health(HealthType::Maint)?;
let maint_health = liqee_health_cache.health(HealthType::Maint);
if maint_health < I80F48::ZERO {
liqee.set_bankrupt(!liqee_health_cache.has_liquidatable_assets());
} else {
let init_health = liqee_health_cache.health(HealthType::Init)?;
let init_health = liqee_health_cache.health(HealthType::Init);
// this is equivalent to one native USDC or 1e-6 USDC
// This is used as threshold to flip flag instead of 0 because of dust issues

View File

@ -398,7 +398,7 @@ pub fn compute_health_from_fixed_accounts(
begin_perp: cm!(active_token_len * 2),
begin_serum3: cm!(active_token_len * 2 + active_perp_len),
};
new_health_cache(account, &retriever)?.health(health_type)
Ok(new_health_cache(account, &retriever)?.health(health_type))
}
/// Compute health with an arbitrary AccountRetriever
@ -407,7 +407,7 @@ pub fn compute_health(
health_type: HealthType,
retriever: &impl AccountRetriever,
) -> Result<I80F48> {
new_health_cache(account, retriever)?.health(health_type)
Ok(new_health_cache(account, retriever)?.health(health_type))
}
struct TokenInfo {
@ -439,6 +439,16 @@ impl TokenInfo {
HealthType::Maint => self.maint_liab_weight,
}
}
#[inline(always)]
fn health_contribution(&self, health_type: HealthType) -> I80F48 {
let weight = if self.balance.is_negative() {
self.liab_weight(health_type)
} else {
self.asset_weight(health_type)
};
cm!(self.balance * weight)
}
}
struct Serum3Info {
@ -532,21 +542,13 @@ pub struct HealthCache {
}
impl HealthCache {
pub fn health(&self, health_type: HealthType) -> Result<I80F48> {
pub fn health(&self, health_type: HealthType) -> I80F48 {
let mut health = I80F48::ZERO;
for token_info in self.token_infos.iter() {
let contrib = health_contribution(health_type, token_info, token_info.balance)?;
let sum = |contrib| {
health = cm!(health + contrib);
}
for serum3_info in self.serum3_infos.iter() {
let contrib = serum3_info.health_contribution(health_type, &self.token_infos);
health = cm!(health + contrib);
}
for perp_info in self.perp_infos.iter() {
let contrib = perp_info.health_contribution(health_type);
health = cm!(health + contrib);
}
Ok(health)
};
self.health_sum(health_type, sum);
health
}
pub fn adjust_token_balance(&mut self, token_index: TokenIndex, change: I80F48) -> Result<()> {
@ -580,23 +582,54 @@ impl HealthCache {
.any(|p| p.quote.is_negative() || p.base != 0);
spot_borrows || perp_borrows
}
}
/// Compute health contribution for a given balance
/// wart: independent of the balance stored in TokenInfo
/// balance is in health-reference-token native units
#[inline(always)]
fn health_contribution(
health_type: HealthType,
info: &TokenInfo,
balance: I80F48,
) -> Result<I80F48> {
let weight = if balance.is_negative() {
info.liab_weight(health_type)
} else {
info.asset_weight(health_type)
};
Ok(cm!(balance * weight))
fn health_sum(&self, health_type: HealthType, mut action: impl FnMut(I80F48)) {
for token_info in self.token_infos.iter() {
let contrib = token_info.health_contribution(health_type);
action(contrib);
}
for serum3_info in self.serum3_infos.iter() {
let contrib = serum3_info.health_contribution(health_type, &self.token_infos);
action(contrib);
}
for perp_info in self.perp_infos.iter() {
let contrib = perp_info.health_contribution(health_type);
action(contrib);
}
}
/// Sum of only the positive health components (assets) and
/// sum of absolute values of all negative health components (liabs, always >= 0)
pub fn health_assets_and_liabs(&self, health_type: HealthType) -> (I80F48, I80F48) {
let mut assets = I80F48::ZERO;
let mut liabs = I80F48::ZERO;
let sum = |contrib| {
if contrib > 0 {
assets = cm!(assets + contrib);
} else {
liabs = cm!(liabs - contrib);
}
};
self.health_sum(health_type, sum);
(assets, liabs)
}
/// The health ratio is
/// - 0 if health is 0 - meaning assets = liabs
/// - 100 if there's 2x as many assets as liabs
/// - 200 if there's 3x as many assets as liabs
/// - MAX if liabs = 0
///
/// Maybe talking about the collateralization ratio assets/liabs is more intuitive?
pub fn health_ratio(&self, health_type: HealthType) -> I80F48 {
let (assets, liabs) = self.health_assets_and_liabs(health_type);
let hundred = I80F48::from(100);
if liabs > 0 {
cm!(hundred * (assets - liabs) / liabs)
} else {
I80F48::MAX
}
}
}
/// Generate a HealthCache for an account and its health accounts.