2022-09-23 02:59:18 -07:00
|
|
|
use anchor_lang::prelude::*;
|
|
|
|
use anchor_lang::Discriminator;
|
|
|
|
use fixed::types::I80F48;
|
|
|
|
use solana_sdk::account::ReadableAccount;
|
|
|
|
|
|
|
|
use crate::*;
|
|
|
|
use mango_v4::accounts_zerocopy::KeyedAccountSharedData;
|
|
|
|
use mango_v4::state::*;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum Direction {
|
|
|
|
MaxPositive,
|
|
|
|
MaxNegative,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns up to `count` accounts with highest abs pnl (by `direction`) in descending order.
|
|
|
|
pub fn fetch_top(
|
|
|
|
context: &crate::context::MangoGroupContext,
|
|
|
|
account_fetcher: &impl AccountFetcher,
|
|
|
|
perp_market_index: PerpMarketIndex,
|
|
|
|
direction: Direction,
|
|
|
|
count: usize,
|
|
|
|
) -> anyhow::Result<Vec<(Pubkey, MangoAccountValue, I80F48)>> {
|
2022-09-26 04:27:31 -07:00
|
|
|
let perp = context.perp(perp_market_index);
|
2022-09-23 02:59:18 -07:00
|
|
|
let perp_market =
|
2022-09-26 04:27:31 -07:00
|
|
|
account_fetcher_fetch_anchor_account::<PerpMarket>(account_fetcher, &perp.address)?;
|
2022-09-23 02:59:18 -07:00
|
|
|
let oracle_acc = account_fetcher.fetch_raw_account(&perp_market.oracle)?;
|
2022-11-10 06:47:11 -08:00
|
|
|
let oracle_price = perp_market.oracle_price(
|
|
|
|
&KeyedAccountSharedData::new(perp_market.oracle, oracle_acc),
|
|
|
|
None,
|
|
|
|
)?;
|
2022-09-23 02:59:18 -07:00
|
|
|
|
|
|
|
let accounts =
|
|
|
|
account_fetcher.fetch_program_accounts(&mango_v4::id(), MangoAccount::discriminator())?;
|
|
|
|
|
|
|
|
let mut accounts_pnl = accounts
|
|
|
|
.iter()
|
|
|
|
.filter_map(|(pk, acc)| {
|
|
|
|
let data = acc.data();
|
|
|
|
let mango_acc = MangoAccountValue::from_bytes(&data[8..]);
|
|
|
|
if mango_acc.is_err() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let mango_acc = mango_acc.unwrap();
|
|
|
|
let perp_pos = mango_acc.perp_position(perp_market_index);
|
|
|
|
if perp_pos.is_err() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let perp_pos = perp_pos.unwrap();
|
|
|
|
let pnl = perp_pos.base_position_native(&perp_market) * oracle_price
|
|
|
|
+ perp_pos.quote_position_native();
|
|
|
|
if pnl >= 0 && direction == Direction::MaxNegative
|
|
|
|
|| pnl <= 0 && direction == Direction::MaxPositive
|
|
|
|
{
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Some((*pk, mango_acc, pnl))
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
// Sort the top accounts to the front
|
|
|
|
match direction {
|
|
|
|
Direction::MaxPositive => {
|
|
|
|
accounts_pnl.sort_by(|a, b| b.2.cmp(&a.2));
|
|
|
|
}
|
|
|
|
Direction::MaxNegative => {
|
|
|
|
accounts_pnl.sort_by(|a, b| a.2.cmp(&b.2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 05:35:01 -07:00
|
|
|
// Negative pnl needs to be limited by perp_settle_health.
|
2022-09-23 02:59:18 -07:00
|
|
|
// We're doing it in a second step, because it's pretty expensive and we don't
|
|
|
|
// want to run this for all accounts.
|
|
|
|
if direction == Direction::MaxNegative {
|
|
|
|
let mut stable = 0;
|
|
|
|
for i in 0..accounts_pnl.len() {
|
|
|
|
let (_, acc, pnl) = &accounts_pnl[i];
|
|
|
|
let next_pnl = if i + 1 < accounts_pnl.len() {
|
|
|
|
accounts_pnl[i + 1].2
|
|
|
|
} else {
|
|
|
|
I80F48::ZERO
|
|
|
|
};
|
2022-09-29 05:35:01 -07:00
|
|
|
let perp_settle_health =
|
|
|
|
crate::health_cache::new(context, account_fetcher, &acc)?.perp_settle_health();
|
|
|
|
let settleable_pnl = if perp_settle_health > 0 && !acc.being_liquidated() {
|
|
|
|
(*pnl).max(-perp_settle_health)
|
2022-09-23 02:59:18 -07:00
|
|
|
} else {
|
|
|
|
I80F48::ZERO
|
|
|
|
};
|
|
|
|
accounts_pnl[i].2 = settleable_pnl;
|
|
|
|
|
|
|
|
// if the ordering was unchanged `count` times we know we have the top `count` accounts
|
|
|
|
if settleable_pnl <= next_pnl {
|
|
|
|
stable += 1;
|
|
|
|
if stable >= count {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
accounts_pnl.sort_by(|a, b| a.2.cmp(&b.2));
|
|
|
|
}
|
|
|
|
|
|
|
|
// return highest abs pnl accounts
|
2022-09-26 04:27:31 -07:00
|
|
|
Ok(accounts_pnl.into_iter().take(count).collect::<Vec<_>>())
|
2022-09-23 02:59:18 -07:00
|
|
|
}
|