2022-05-27 22:05:34 -07:00
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
sync::{Arc, RwLock},
|
|
|
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
|
|
|
};
|
|
|
|
|
|
|
|
use fixed::types::I80F48;
|
|
|
|
use futures::Future;
|
2023-02-14 23:42:07 -08:00
|
|
|
use mango_v4::accounts_ix::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
|
2022-05-27 22:05:34 -07:00
|
|
|
|
|
|
|
use tokio::time;
|
|
|
|
|
|
|
|
use crate::MangoClient;
|
|
|
|
|
|
|
|
pub async fn runner(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
_debugging_handle: impl Future,
|
|
|
|
) -> Result<(), anyhow::Error> {
|
2022-12-16 04:10:46 -08:00
|
|
|
ensure_deposit(&mango_client).await?;
|
|
|
|
ensure_oo(&mango_client).await?;
|
2022-05-27 22:05:34 -07:00
|
|
|
|
|
|
|
let mut price_arcs = HashMap::new();
|
2022-07-16 05:37:15 -07:00
|
|
|
for market_name in mango_client.context.serum3_market_indexes_by_name.keys() {
|
2022-05-27 22:05:34 -07:00
|
|
|
let price = mango_client
|
2023-07-03 05:09:11 -07:00
|
|
|
.get_oracle_price_deprecated(
|
2022-05-27 22:05:34 -07:00
|
|
|
market_name
|
|
|
|
.split('/')
|
|
|
|
.collect::<Vec<&str>>()
|
2022-08-30 04:46:39 -07:00
|
|
|
.first()
|
2022-05-27 22:05:34 -07:00
|
|
|
.unwrap(),
|
|
|
|
)
|
2022-12-16 04:10:46 -08:00
|
|
|
.await
|
2022-05-27 22:05:34 -07:00
|
|
|
.unwrap();
|
|
|
|
price_arcs.insert(
|
|
|
|
market_name.to_owned(),
|
|
|
|
Arc::new(RwLock::new(
|
|
|
|
I80F48::from_num(price.price) / I80F48::from_num(10u64.pow(-price.expo as u32)),
|
|
|
|
)),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let handles1 = mango_client
|
2022-07-16 05:37:15 -07:00
|
|
|
.context
|
|
|
|
.serum3_market_indexes_by_name
|
2022-05-27 22:05:34 -07:00
|
|
|
.keys()
|
|
|
|
.map(|market_name| {
|
|
|
|
loop_blocking_price_update(
|
|
|
|
mango_client.clone(),
|
|
|
|
market_name.to_owned(),
|
|
|
|
price_arcs.get(market_name).unwrap().clone(),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let handles2 = mango_client
|
2022-07-16 05:37:15 -07:00
|
|
|
.context
|
|
|
|
.serum3_market_indexes_by_name
|
2022-05-27 22:05:34 -07:00
|
|
|
.keys()
|
|
|
|
.map(|market_name| {
|
|
|
|
loop_blocking_orders(
|
|
|
|
mango_client.clone(),
|
|
|
|
market_name.to_owned(),
|
|
|
|
price_arcs.get(market_name).unwrap().clone(),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
futures::join!(
|
|
|
|
futures::future::join_all(handles1),
|
|
|
|
futures::future::join_all(handles2)
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
async fn ensure_oo(mango_client: &Arc<MangoClient>) -> Result<(), anyhow::Error> {
|
|
|
|
let account = mango_client.mango_account().await?;
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
for (market_index, serum3_market) in mango_client.context.serum3_markets.iter() {
|
2022-08-30 03:47:15 -07:00
|
|
|
if account.serum3_orders(*market_index).is_err() {
|
2022-12-16 04:10:46 -08:00
|
|
|
mango_client
|
|
|
|
.serum3_create_open_orders(serum3_market.market.name())
|
|
|
|
.await?;
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
async fn ensure_deposit(mango_client: &Arc<MangoClient>) -> Result<(), anyhow::Error> {
|
|
|
|
let mango_account = mango_client.mango_account().await?;
|
2022-06-27 02:27:17 -07:00
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
for &token_index in mango_client.context.tokens.keys() {
|
2022-12-16 04:10:46 -08:00
|
|
|
let bank = mango_client.first_bank(token_index).await?;
|
2022-07-16 05:37:15 -07:00
|
|
|
let desired_balance = I80F48::from_num(10_000 * 10u64.pow(bank.mint_decimals as u32));
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2022-08-18 04:45:31 -07:00
|
|
|
let token_account_opt = mango_account.token_position(token_index).ok();
|
2022-05-29 03:25:12 -07:00
|
|
|
|
|
|
|
let deposit_native = match token_account_opt {
|
|
|
|
Some(token_account) => {
|
2022-06-27 02:27:17 -07:00
|
|
|
let native = token_account.native(&bank);
|
2022-07-16 05:37:15 -07:00
|
|
|
let ui = token_account.ui(&bank);
|
2022-05-29 03:25:12 -07:00
|
|
|
log::info!("Current balance {} {}", ui, bank.name());
|
|
|
|
|
|
|
|
if native < I80F48::ZERO {
|
|
|
|
desired_balance - native
|
|
|
|
} else {
|
|
|
|
desired_balance - native.min(desired_balance)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => desired_balance,
|
2022-05-27 22:05:34 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
if deposit_native == I80F48::ZERO {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
log::info!("Depositing {} {}", deposit_native, bank.name());
|
2022-12-16 04:10:46 -08:00
|
|
|
mango_client
|
2023-01-04 00:24:40 -08:00
|
|
|
.token_deposit(bank.mint, desired_balance.to_num(), false)
|
2022-12-16 04:10:46 -08:00
|
|
|
.await?;
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn loop_blocking_price_update(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
market_name: String,
|
|
|
|
price: Arc<RwLock<I80F48>>,
|
|
|
|
) {
|
|
|
|
let mut interval = time::interval(Duration::from_secs(1));
|
2022-12-16 04:10:46 -08:00
|
|
|
let token_name = market_name.split('/').collect::<Vec<&str>>()[0];
|
2022-05-27 22:05:34 -07:00
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
|
|
|
|
2023-07-03 05:09:11 -07:00
|
|
|
let fresh_price = mango_client
|
|
|
|
.get_oracle_price_deprecated(token_name)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2022-12-16 04:10:46 -08:00
|
|
|
log::info!("{} Updated price is {:?}", token_name, fresh_price.price);
|
|
|
|
if let Ok(mut price) = price.write() {
|
|
|
|
*price = I80F48::from_num(fresh_price.price)
|
2023-06-15 08:20:31 -07:00
|
|
|
/ I80F48::from_num(10f64.powi(-fresh_price.expo));
|
2022-05-29 00:07:15 -07:00
|
|
|
}
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn loop_blocking_orders(
|
|
|
|
mango_client: Arc<MangoClient>,
|
|
|
|
market_name: String,
|
|
|
|
price: Arc<RwLock<I80F48>>,
|
|
|
|
) {
|
|
|
|
let mut interval = time::interval(Duration::from_secs(5));
|
|
|
|
|
|
|
|
// Cancel existing orders
|
2022-12-16 04:10:46 -08:00
|
|
|
let orders: Vec<u128> = mango_client
|
|
|
|
.serum3_cancel_all_orders(&market_name)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2022-05-27 22:05:34 -07:00
|
|
|
log::info!("Cancelled orders - {:?} for {}", orders, market_name);
|
|
|
|
|
2023-06-15 08:20:31 -07:00
|
|
|
let market_index = mango_client.context.serum3_market_index(&market_name);
|
|
|
|
let s3 = mango_client.context.serum3(market_index);
|
|
|
|
|
|
|
|
let base_decimals = mango_client
|
|
|
|
.context
|
|
|
|
.token(s3.market.base_token_index)
|
|
|
|
.decimals as i32;
|
|
|
|
let quote_decimals = mango_client
|
|
|
|
.context
|
|
|
|
.token(s3.market.quote_token_index)
|
|
|
|
.decimals as i32;
|
|
|
|
|
2022-05-27 22:05:34 -07:00
|
|
|
loop {
|
|
|
|
interval.tick().await;
|
|
|
|
|
|
|
|
let client = mango_client.clone();
|
|
|
|
let market_name = market_name.clone();
|
|
|
|
let price = price.clone();
|
|
|
|
|
2023-06-15 08:20:31 -07:00
|
|
|
let res: anyhow::Result<()> = (|| async move {
|
2022-12-16 04:10:46 -08:00
|
|
|
client.serum3_settle_funds(&market_name).await?;
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2023-06-15 08:20:31 -07:00
|
|
|
let fresh_price = price.read().unwrap().to_num::<f64>();
|
|
|
|
let bid_price = fresh_price * 1.1;
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2023-06-15 08:20:31 -07:00
|
|
|
let bid_price_lots =
|
|
|
|
bid_price * 10f64.powi(quote_decimals - base_decimals) * s3.coin_lot_size as f64
|
|
|
|
/ s3.pc_lot_size as f64;
|
2022-05-27 22:05:34 -07:00
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let res = client
|
|
|
|
.serum3_place_order(
|
|
|
|
&market_name,
|
|
|
|
Serum3Side::Bid,
|
2023-06-15 08:20:31 -07:00
|
|
|
bid_price_lots.round() as u64,
|
|
|
|
1,
|
|
|
|
u64::MAX,
|
2022-12-16 04:10:46 -08:00
|
|
|
Serum3SelfTradeBehavior::DecrementTake,
|
|
|
|
Serum3OrderType::ImmediateOrCancel,
|
|
|
|
SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() as u64,
|
|
|
|
10,
|
|
|
|
)
|
|
|
|
.await;
|
2022-05-27 22:05:34 -07:00
|
|
|
if let Err(e) = res {
|
|
|
|
log::error!("Error while placing taker bid {:#?}", e)
|
|
|
|
} else {
|
|
|
|
log::info!("Placed bid at {} for {}", bid_price, market_name)
|
|
|
|
}
|
|
|
|
|
2023-06-15 08:20:31 -07:00
|
|
|
let ask_price = fresh_price * 0.9;
|
|
|
|
let ask_price_lots =
|
|
|
|
ask_price * 10f64.powi(quote_decimals - base_decimals) * s3.coin_lot_size as f64
|
|
|
|
/ s3.pc_lot_size as f64;
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
let res = client
|
|
|
|
.serum3_place_order(
|
|
|
|
&market_name,
|
|
|
|
Serum3Side::Ask,
|
2023-06-15 08:20:31 -07:00
|
|
|
ask_price_lots.round() as u64,
|
|
|
|
1,
|
|
|
|
u64::MAX,
|
2022-12-16 04:10:46 -08:00
|
|
|
Serum3SelfTradeBehavior::DecrementTake,
|
|
|
|
Serum3OrderType::ImmediateOrCancel,
|
|
|
|
SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() as u64,
|
|
|
|
10,
|
|
|
|
)
|
|
|
|
.await;
|
2022-05-27 22:05:34 -07:00
|
|
|
if let Err(e) = res {
|
|
|
|
log::error!("Error while placing taker ask {:#?}", e)
|
|
|
|
} else {
|
|
|
|
log::info!("Placed ask at {} for {}", ask_price, market_name)
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2022-12-16 04:10:46 -08:00
|
|
|
})()
|
2022-05-29 00:07:15 -07:00
|
|
|
.await;
|
|
|
|
|
2022-12-16 04:10:46 -08:00
|
|
|
if let Err(err) = res {
|
|
|
|
log::error!("{:?}", err);
|
2022-05-29 00:07:15 -07:00
|
|
|
}
|
2022-05-27 22:05:34 -07:00
|
|
|
}
|
|
|
|
}
|