Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Risk Engine Reliability and Performance Improvements #143

Merged
merged 11 commits into from
Dec 15, 2023
25 changes: 19 additions & 6 deletions clients/rust/marginfi-cli/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,8 @@ pub fn bank_get_all(config: Config, marginfi_group: Option<Pubkey>) -> Result<()

#[cfg(feature = "dev")]
pub fn bank_inspect_price_oracle(config: Config, bank_pk: Pubkey) -> Result<()> {
use marginfi::state::price::{OraclePriceType, PriceBias};

let bank: Bank = config.mfi_program.account(bank_pk)?;
let mut price_oracle_account = config
.mfi_program
Expand All @@ -810,7 +812,14 @@ pub fn bank_inspect_price_oracle(config: Config, bank_pk: Pubkey) -> Result<()>
OraclePriceFeedAdapter::try_from_bank_config(&bank.config, &[price_oracle_ai], 0, u64::MAX)
.unwrap();

let (worst, best) = opfa.get_price_range().unwrap();
let (real_price, maint_asset_price, maint_liab_price, init_asset_price, init_liab_price) = (
opfa.get_price_of_type(OraclePriceType::RealTime, None)?,
opfa.get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::Low))?,
opfa.get_price_of_type(OraclePriceType::RealTime, Some(PriceBias::High))?,
opfa.get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::Low))?,
opfa.get_price_of_type(OraclePriceType::TimeWeighted, Some(PriceBias::High))?,
);

let keys = bank
.config
.oracle_keys
Expand All @@ -822,14 +831,18 @@ pub fn bank_inspect_price_oracle(config: Config, bank_pk: Pubkey) -> Result<()>
r##"
Oracle Setup: {setup:?}
Oracle Keys: {keys:#?}
Price: ${price} (worst: ${worst}, best: ${best}, std_dev: ${std})
Prince:
Realtime: {real_price}
Maint: {maint_asset_price} (asset) {maint_liab_price} (liab)
Init: {init_asset_price} (asset) {init_liab_price} (liab)
"##,
setup = bank.config.oracle_setup,
keys = keys,
price = opfa.get_price().unwrap(),
worst = worst,
best = best,
std = opfa.get_confidence_interval().unwrap(),
real_price = real_price,
maint_asset_price = maint_asset_price,
maint_liab_price = maint_liab_price,
init_asset_price = init_asset_price,
init_liab_price = init_liab_price,
);

Ok(())
Expand Down
134 changes: 83 additions & 51 deletions observability/indexer/src/utils/marginfi_account_dup.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
use fixed::types::I80F48;
use marginfi::{
constants::TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE,
state::{
marginfi_account::{
calc_asset_value, Balance, BalanceSide, MarginfiAccount, RiskRequirementType,
WeightType,
},
marginfi_group::Bank,
price::{OraclePriceFeedAdapter, PriceAdapter},
use marginfi::state::{
marginfi_account::{
calc_value, Balance, BalanceSide, MarginfiAccount, RequirementType, RiskRequirementType,
},
marginfi_group::{Bank, RiskTier},
price::{OraclePriceFeedAdapter, PriceAdapter, PriceBias},
};
use solana_sdk::pubkey::Pubkey;

Expand Down Expand Up @@ -48,52 +44,88 @@ impl BankAccountWithPriceFeed2 {
#[inline(always)]
pub fn calc_weighted_assets_and_liabilities_values(
&self,
weight_type: WeightType,
requirement_type: RequirementType,
) -> anyhow::Result<(I80F48, I80F48)> {
let (worst_price, best_price) = self.price_feed.get_price_range()?;
let (mut asset_weight, liability_weight) = self.bank.config.get_weights(weight_type);
let mint_decimals = self.bank.mint_decimals;

let asset_amount = self
.bank
.get_asset_amount(self.balance.asset_shares.into())?;
let liability_amount = self
.bank
.get_liability_amount(self.balance.liability_shares.into())?;

if matches!(weight_type, WeightType::Initial)
&& self.bank.config.total_asset_value_init_limit
!= TOTAL_ASSET_VALUE_INIT_LIMIT_INACTIVE
{
let bank_total_assets_value = calc_asset_value(
self.bank
.get_asset_amount(self.bank.total_asset_shares.into())?,
worst_price,
mint_decimals,
None,
)?;

let total_asset_value_init_limit =
I80F48::from_num(self.bank.config.total_asset_value_init_limit);

if bank_total_assets_value > total_asset_value_init_limit {
let discount = total_asset_value_init_limit
.checked_div(bank_total_assets_value)
.unwrap();
match self.balance.get_side() {
Some(side) => {
let bank = &self.bank;
match side {
BalanceSide::Assets => Ok((
self.calc_weighted_assets(requirement_type, &bank)?,
I80F48::ZERO,
)),
BalanceSide::Liabilities => Ok((
I80F48::ZERO,
self.calc_weighted_liabs(requirement_type, &bank)?,
)),
}
}
None => Ok((I80F48::ZERO, I80F48::ZERO)),
}
}

asset_weight = asset_weight.checked_mul(discount).unwrap();
#[inline(always)]
fn calc_weighted_assets(
&self,
requirement_type: RequirementType,
bank: &Bank,
) -> anyhow::Result<I80F48> {
match bank.config.risk_tier {
RiskTier::Collateral => {
let price_feed = &self.price_feed;
let mut asset_weight = bank
.config
.get_weight(requirement_type, BalanceSide::Assets);

let lower_price = price_feed.get_price_of_type(
requirement_type.get_oracle_price_type(),
Some(PriceBias::Low),
)?;

if matches!(requirement_type, RequirementType::Initial) {
if let Some(discount) =
bank.maybe_get_asset_weight_init_discount(lower_price)?
{
asset_weight = asset_weight
.checked_mul(discount)
.ok_or_else(|| anyhow::anyhow!("Math error"))?;
}
}

Ok(calc_value(
bank.get_asset_amount(self.balance.asset_shares.into())?,
lower_price,
bank.mint_decimals,
Some(asset_weight),
)
.unwrap())
}
RiskTier::Isolated => Ok(I80F48::ZERO),
}
}

Ok((
calc_asset_value(asset_amount, worst_price, mint_decimals, Some(asset_weight))?,
calc_asset_value(
liability_amount,
best_price,
mint_decimals,
Some(liability_weight),
)?,
))
#[inline(always)]
fn calc_weighted_liabs(
&self,
requirement_type: RequirementType,
bank: &Bank,
) -> anyhow::Result<I80F48> {
let price_feed = &self.price_feed;
let liability_weight = bank
.config
.get_weight(requirement_type, BalanceSide::Liabilities);

let higher_price = price_feed.get_price_of_type(
requirement_type.get_oracle_price_type(),
Some(PriceBias::High),
)?;

Ok(calc_value(
bank.get_liability_amount(self.balance.liability_shares.into())?,
higher_price,
bank.mint_decimals,
Some(liability_weight),
)?)
}

#[inline]
Expand Down Expand Up @@ -144,7 +176,7 @@ impl RiskEngine2 {
self.bank_accounts_with_price
.iter()
.map(|a: &BankAccountWithPriceFeed2| {
a.calc_weighted_assets_and_liabilities_values(WeightType::Equity)
a.calc_weighted_assets_and_liabilities_values(RequirementType::Equity)
})
.try_fold(
(I80F48::ZERO, I80F48::ZERO),
Expand Down
Loading
Loading