liquidator: make alternate routes configurable (#916)

This commit is contained in:
Christian Kamm 2024-03-13 09:13:07 +01:00 committed by GitHub
parent c2398d1abe
commit 4ea7c531ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 50 additions and 36 deletions

View File

@ -113,6 +113,14 @@ pub struct Cli {
#[clap(long, env, value_parser, value_delimiter = ',')]
pub(crate) rebalance_skip_tokens: Option<Vec<u16>>,
/// query jupiter for direct routes to and from these tokens
///
/// These alternate routes will only be used when the main USDC-based one does not
/// work or does not fit in a transaction. A direct route to/from USDC is always also an alternative.
/// The alternate route with the lowest price impact will be used.
#[clap(long, env, value_parser, value_delimiter = ',')]
pub(crate) rebalance_alternate_jupiter_route_tokens: Option<Vec<u16>>,
/// When closing borrows, the rebalancer can't close token positions exactly.
/// Instead it purchases too much and then gets rid of the excess in a second step.
/// If this is 0.05, then it'll swap borrow_value * (1 + 0.05) quote token into borrow token.

View File

@ -242,9 +242,13 @@ async fn main() -> anyhow::Result<()> {
borrow_settle_excess: (1f64 + cli.rebalance_borrow_settle_excess).max(1f64),
refresh_timeout: Duration::from_secs(cli.rebalance_refresh_timeout_secs),
jupiter_version: cli.jupiter_version.into(),
skip_tokens: cli.rebalance_skip_tokens.unwrap_or(Vec::new()),
skip_tokens: cli.rebalance_skip_tokens.unwrap_or_default(),
alternate_jupiter_route_tokens: cli
.rebalance_alternate_jupiter_route_tokens
.unwrap_or_default(),
allow_withdraws: signer_is_owner,
};
rebalance_config.validate(&mango_client.context);
let rebalancer = Arc::new(rebalance::Rebalancer {
mango_client: mango_client.clone(),

View File

@ -5,7 +5,7 @@ use mango_v4::state::{
PlaceOrderType, Side, TokenIndex, QUOTE_TOKEN_INDEX,
};
use mango_v4_client::{
chain_data, jupiter, perp_pnl, MangoClient, PerpMarketContext, TokenContext,
chain_data, jupiter, perp_pnl, MangoClient, MangoGroupContext, PerpMarketContext, TokenContext,
TransactionBuilder, TransactionSize,
};
@ -28,9 +28,22 @@ pub struct Config {
pub refresh_timeout: Duration,
pub jupiter_version: jupiter::Version,
pub skip_tokens: Vec<TokenIndex>,
pub alternate_jupiter_route_tokens: Vec<TokenIndex>,
pub allow_withdraws: bool,
}
impl Config {
// panics on failure
pub fn validate(&self, context: &MangoGroupContext) {
self.skip_tokens.iter().for_each(|&ti| {
context.token(ti);
});
self.alternate_jupiter_route_tokens.iter().for_each(|&ti| {
context.token(ti);
});
}
}
fn token_bank(
token: &TokenContext,
account_fetcher: &chain_data::AccountFetcher,
@ -106,7 +119,7 @@ impl Rebalancer {
/// Grab three possible routes:
/// 1. USDC -> output (complex routes)
/// 2. USDC -> output (direct route only)
/// 3. SOL -> output (direct route only)
/// 3. alternate_jupiter_route_tokens -> output (direct route only)
/// Use 1. if it fits into a tx. Otherwise use the better of 2./3.
async fn token_swap_buy(
&self,
@ -114,16 +127,7 @@ impl Rebalancer {
in_amount_quote: u64,
) -> anyhow::Result<(Signature, jupiter::Quote)> {
let quote_token = self.mango_client.context.token(QUOTE_TOKEN_INDEX);
let sol_token = self.mango_client.context.token(
*self
.mango_client
.context
.token_indexes_by_name
.get("SOL") // TODO: better use mint
.unwrap(),
);
let quote_mint = quote_token.mint;
let sol_mint = sol_token.mint;
let jupiter_version = self.config.jupiter_version;
let full_route_job = self.jupiter_quote(
@ -140,18 +144,21 @@ impl Rebalancer {
true,
jupiter_version,
);
let mut jobs = vec![full_route_job, direct_quote_route_job];
// For the SOL -> output route we need to adjust the in amount by the SOL price
let sol_price = self
.account_fetcher
.fetch_bank_price(&sol_token.first_bank())?;
let in_amount_sol = (I80F48::from(in_amount_quote) / sol_price)
.ceil()
.to_num::<u64>();
let direct_sol_route_job =
self.jupiter_quote(sol_mint, output_mint, in_amount_sol, true, jupiter_version);
let jobs = vec![full_route_job, direct_quote_route_job, direct_sol_route_job];
for in_token_index in &self.config.alternate_jupiter_route_tokens {
let in_token = self.mango_client.context.token(*in_token_index);
// For the alternate output routes we need to adjust the in amount by the token price
let in_price = self
.account_fetcher
.fetch_bank_price(&in_token.first_bank())?;
let in_amount = (I80F48::from(in_amount_quote) / in_price)
.ceil()
.to_num::<u64>();
let direct_route_job =
self.jupiter_quote(in_token.mint, output_mint, in_amount, true, jupiter_version);
jobs.push(direct_route_job);
}
let mut results = futures::future::join_all(jobs).await;
let full_route = results.remove(0)?;
@ -173,7 +180,7 @@ impl Rebalancer {
/// Grab three possible routes:
/// 1. input -> USDC (complex routes)
/// 2. input -> USDC (direct route only)
/// 3. input -> SOL (direct route only)
/// 3. input -> alternate_jupiter_route_tokens (direct route only)
/// Use 1. if it fits into a tx. Otherwise use the better of 2./3.
async fn token_swap_sell(
&self,
@ -181,26 +188,21 @@ impl Rebalancer {
in_amount: u64,
) -> anyhow::Result<(Signature, jupiter::Quote)> {
let quote_token = self.mango_client.context.token(QUOTE_TOKEN_INDEX);
let sol_token = self.mango_client.context.token(
*self
.mango_client
.context
.token_indexes_by_name
.get("SOL") // TODO: better use mint
.unwrap(),
);
let quote_mint = quote_token.mint;
let sol_mint = sol_token.mint;
let jupiter_version = self.config.jupiter_version;
let full_route_job =
self.jupiter_quote(input_mint, quote_mint, in_amount, false, jupiter_version);
let direct_quote_route_job =
self.jupiter_quote(input_mint, quote_mint, in_amount, true, jupiter_version);
let direct_sol_route_job =
self.jupiter_quote(input_mint, sol_mint, in_amount, true, jupiter_version);
let mut jobs = vec![full_route_job, direct_quote_route_job];
let jobs = vec![full_route_job, direct_quote_route_job, direct_sol_route_job];
for out_token_index in &self.config.alternate_jupiter_route_tokens {
let out_token = self.mango_client.context.token(*out_token_index);
let direct_route_job =
self.jupiter_quote(input_mint, out_token.mint, in_amount, true, jupiter_version);
jobs.push(direct_route_job);
}
let mut results = futures::future::join_all(jobs).await;
let full_route = results.remove(0)?;