Fix computation of utilization time-weighted average

This commit is contained in:
Christian Kamm 2022-08-17 11:14:37 +02:00
parent 8a1c58e723
commit a23afed7de
3 changed files with 31 additions and 24 deletions

View File

@ -93,8 +93,7 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
{
let mut some_bank = ctx.remaining_accounts[0].load_mut::<Bank>()?;
let now_ts_i80f48 = I80F48::from_num(now_ts);
let diff_ts = I80F48::from_num(now_ts - some_bank.index_last_updated);
let diff_ts = I80F48::from_num(cm!(now_ts - some_bank.index_last_updated));
let (deposit_index, borrow_index, borrow_fees) =
some_bank.compute_index(indexed_total_deposits, indexed_total_borrows, diff_ts)?;
@ -104,7 +103,7 @@ pub fn token_update_index_and_rate(ctx: Context<TokenUpdateIndexAndRate>) -> Res
let new_avg_utilization = some_bank.compute_new_avg_utilization(
indexed_total_deposits,
indexed_total_borrows,
now_ts_i80f48,
now_ts,
);
let price = oracle_price(

View File

@ -425,7 +425,7 @@ impl Bank {
// The loan fee rate is not distributed to depositors.
let borrow_rate_with_fees = cm!(borrow_rate + self.loan_fee_rate);
let borrow_fees = native_total_borrows * self.loan_fee_rate * diff_ts / YEAR_I80F48;
let borrow_fees = cm!(native_total_borrows * self.loan_fee_rate * diff_ts / YEAR_I80F48);
let borrow_index = cm!(
(self.borrow_index * borrow_rate_with_fees * diff_ts) / YEAR_I80F48 + self.borrow_index
@ -479,9 +479,9 @@ impl Bank {
&self,
indexed_total_deposits: I80F48,
indexed_total_borrows: I80F48,
now_ts: I80F48,
now_ts: i64,
) -> I80F48 {
if now_ts == I80F48::ZERO {
if now_ts <= 0 {
return I80F48::ZERO;
}
@ -493,13 +493,18 @@ impl Bank {
cm!(native_total_borrows / native_total_deposits)
};
// combine old and new with relevant factors to form new avg_utilization
// scaling factor for previous avg_utilization is old_ts/new_ts
// scaling factor for instantaneous utilization is (new_ts - old_ts) / new_ts
let bank_rate_last_updated_i80f48 = I80F48::from_num(self.bank_rate_last_updated);
cm!((self.avg_utilization * bank_rate_last_updated_i80f48
+ instantaneous_utilization * (now_ts - bank_rate_last_updated_i80f48))
/ now_ts)
// Compute a time-weighted average since bank_rate_last_updated.
let previous_avg_time =
I80F48::from_num(cm!(self.index_last_updated - self.bank_rate_last_updated));
let diff_ts = I80F48::from_num(cm!(now_ts - self.index_last_updated));
let new_avg_time = I80F48::from_num(cm!(now_ts - self.bank_rate_last_updated));
if new_avg_time <= 0 {
return instantaneous_utilization;
}
cm!(
(self.avg_utilization * previous_avg_time + instantaneous_utilization * diff_ts)
/ new_avg_time
)
}
// computes new optimal rates and max rate
@ -667,28 +672,30 @@ mod tests {
let mut bank = Bank::zeroed();
bank.deposit_index = I80F48::from_num(1.0);
bank.borrow_index = I80F48::from_num(1.0);
bank.bank_rate_last_updated = 0;
bank.bank_rate_last_updated = 1000;
bank.index_last_updated = 1000;
let compute_new_avg_utilization_runner =
|bank: &mut Bank, utilization: I80F48, now_ts: i64| {
bank.avg_utilization = bank.compute_new_avg_utilization(
I80F48::ONE,
utilization,
I80F48::from_num(now_ts),
);
bank.bank_rate_last_updated = now_ts;
bank.avg_utilization =
bank.compute_new_avg_utilization(I80F48::ONE, utilization, now_ts);
bank.index_last_updated = now_ts;
};
compute_new_avg_utilization_runner(&mut bank, I80F48::ZERO, 0);
compute_new_avg_utilization_runner(&mut bank, I80F48::ZERO, 1000);
assert_eq!(bank.avg_utilization, I80F48::ZERO);
compute_new_avg_utilization_runner(&mut bank, I80F48::from_num(0.5), 10);
compute_new_avg_utilization_runner(&mut bank, I80F48::from_num(0.5), 1010);
assert!((bank.avg_utilization - I80F48::from_num(0.5)).abs() < 0.0001);
compute_new_avg_utilization_runner(&mut bank, I80F48::from_num(0.8), 15);
compute_new_avg_utilization_runner(&mut bank, I80F48::from_num(0.8), 1015);
assert!((bank.avg_utilization - I80F48::from_num(0.6)).abs() < 0.0001);
compute_new_avg_utilization_runner(&mut bank, I80F48::ONE, 20);
compute_new_avg_utilization_runner(&mut bank, I80F48::ONE, 1020);
assert!((bank.avg_utilization - I80F48::from_num(0.7)).abs() < 0.0001);
bank.bank_rate_last_updated = 1020;
compute_new_avg_utilization_runner(&mut bank, I80F48::ONE, 1040);
assert_eq!(bank.avg_utilization, I80F48::ONE);
}
}

View File

@ -155,6 +155,7 @@ async fn test_token_update_index_and_rate() -> Result<(), TransportError> {
.abs()
< 0.1
);
assert!((bank_after.avg_utilization.to_num::<f64>() - utilization).abs() < 0.01);
Ok(())
}