2023-02-15 08:08:55 -08:00
use std ::{
str ::FromStr ,
sync ::{
atomic ::{ AtomicBool , AtomicU64 , Ordering } ,
2023-03-14 05:39:19 -07:00
Arc ,
2023-02-15 08:08:55 -08:00
} ,
time ::{ Duration , Instant } ,
} ;
use chrono ::Utc ;
2023-02-21 02:04:02 -08:00
use iter_tools ::Itertools ;
2023-03-14 05:39:19 -07:00
use log ::{ debug , info , warn } ;
2023-02-15 08:08:55 -08:00
use mango ::{
instruction ::{ cancel_all_perp_orders , place_perp_order2 } ,
matching ::Side ,
} ;
2023-02-24 06:48:08 -08:00
use rand ::{ distributions ::Uniform , prelude ::Distribution , seq ::SliceRandom } ;
2023-04-15 03:17:15 -07:00
use solana_client ::nonblocking ::rpc_client ::RpcClient ;
2023-02-15 08:08:55 -08:00
use solana_program ::pubkey ::Pubkey ;
use solana_sdk ::{
2023-02-21 02:04:02 -08:00
compute_budget , hash ::Hash , instruction ::Instruction , message ::Message , signature ::Keypair ,
signer ::Signer , transaction ::Transaction ,
2023-02-15 08:08:55 -08:00
} ;
2023-04-19 04:29:26 -07:00
use tokio ::{
sync ::RwLock ,
task ::{ self , JoinHandle } ,
} ;
2023-02-15 08:08:55 -08:00
use crate ::{
helpers ::{ to_sdk_instruction , to_sp_pk } ,
mango ::AccountKeys ,
states ::{ PerpMarketCache , TransactionSendRecord } ,
2023-04-19 04:29:26 -07:00
tpu_manager ::TpuManager ,
2023-02-15 08:08:55 -08:00
} ;
pub fn create_ask_bid_transaction (
c : & PerpMarketCache ,
mango_account_pk : Pubkey ,
mango_account_signer : & Keypair ,
2023-02-22 04:29:10 -08:00
prioritization_fee : u64 ,
2023-02-15 08:08:55 -08:00
) -> Transaction {
let mango_account_signer_pk = to_sp_pk ( & mango_account_signer . pubkey ( ) ) ;
let offset = rand ::random ::< i8 > ( ) as i64 ;
let spread = rand ::random ::< u8 > ( ) as i64 ;
debug! (
" price:{:?} price_quote_lots:{:?} order_base_lots:{:?} offset:{:?} spread:{:?} " ,
c . price , c . price_quote_lots , c . order_base_lots , offset , spread
) ;
2023-02-21 02:04:02 -08:00
let mut instructions = vec! [ ] ;
2023-02-22 04:29:10 -08:00
if prioritization_fee > 0 {
2023-02-23 02:02:57 -08:00
let pfees =
compute_budget ::ComputeBudgetInstruction ::set_compute_unit_price ( prioritization_fee ) ;
2023-02-21 02:04:02 -08:00
instructions . push ( pfees ) ;
}
2023-02-15 08:08:55 -08:00
let cancel_ix : Instruction = to_sdk_instruction (
cancel_all_perp_orders (
& c . mango_program_pk ,
& c . mango_group_pk ,
& mango_account_pk ,
& mango_account_signer_pk ,
& c . perp_market_pk ,
& c . perp_market . bids ,
& c . perp_market . asks ,
10 ,
)
. unwrap ( ) ,
) ;
2023-02-21 02:04:02 -08:00
instructions . push ( cancel_ix ) ;
2023-02-15 08:08:55 -08:00
let place_bid_ix : Instruction = to_sdk_instruction (
place_perp_order2 (
& c . mango_program_pk ,
& c . mango_group_pk ,
& mango_account_pk ,
& mango_account_signer_pk ,
& c . mango_cache_pk ,
& c . perp_market_pk ,
& c . perp_market . bids ,
& c . perp_market . asks ,
& c . perp_market . event_queue ,
None ,
& [ ] ,
Side ::Bid ,
c . price_quote_lots + offset - spread ,
c . order_base_lots ,
i64 ::MAX ,
2023-04-14 04:45:22 -07:00
Utc ::now ( ) . timestamp_micros ( ) as u64 ,
2023-02-15 08:08:55 -08:00
mango ::matching ::OrderType ::Limit ,
false ,
None ,
64 ,
mango ::matching ::ExpiryType ::Absolute ,
)
. unwrap ( ) ,
) ;
2023-02-21 02:04:02 -08:00
instructions . push ( place_bid_ix ) ;
2023-02-15 08:08:55 -08:00
let place_ask_ix : Instruction = to_sdk_instruction (
place_perp_order2 (
& c . mango_program_pk ,
& c . mango_group_pk ,
& mango_account_pk ,
& mango_account_signer_pk ,
& c . mango_cache_pk ,
& c . perp_market_pk ,
& c . perp_market . bids ,
& c . perp_market . asks ,
& c . perp_market . event_queue ,
None ,
& [ ] ,
Side ::Ask ,
c . price_quote_lots + offset + spread ,
c . order_base_lots ,
i64 ::MAX ,
2023-04-14 04:45:22 -07:00
Utc ::now ( ) . timestamp_micros ( ) as u64 ,
2023-02-15 08:08:55 -08:00
mango ::matching ::OrderType ::Limit ,
false ,
None ,
64 ,
mango ::matching ::ExpiryType ::Absolute ,
)
. unwrap ( ) ,
) ;
2023-02-21 02:04:02 -08:00
instructions . push ( place_ask_ix ) ;
2023-02-15 08:08:55 -08:00
Transaction ::new_unsigned ( Message ::new (
2023-02-21 02:04:02 -08:00
instructions . as_slice ( ) ,
2023-02-15 08:08:55 -08:00
Some ( & mango_account_signer . pubkey ( ) ) ,
) )
}
2023-02-23 02:02:57 -08:00
fn generate_random_fees (
prioritization_fee_proba : u8 ,
n : usize ,
min_fee : u64 ,
max_fee : u64 ,
) -> Vec < u64 > {
2023-02-22 04:29:10 -08:00
let mut rng = rand ::thread_rng ( ) ;
let range = Uniform ::from ( min_fee .. max_fee ) ;
let range_probability = Uniform ::from ( 1 .. 100 ) ;
( 0 .. n )
. map ( | _ | {
if prioritization_fee_proba = = 0 {
0
} else {
if range_probability . sample ( & mut rng ) < = prioritization_fee_proba {
range . sample ( & mut rng ) as u64
} else {
0
}
}
} )
. collect ( )
2023-02-23 02:02:57 -08:00
}
2023-02-22 04:29:10 -08:00
2023-03-14 05:39:19 -07:00
pub async fn send_mm_transactions (
2023-02-15 08:08:55 -08:00
quotes_per_second : u64 ,
perp_market_caches : & Vec < PerpMarketCache > ,
2023-03-15 08:13:25 -07:00
tpu_manager : TpuManager ,
2023-02-15 08:08:55 -08:00
mango_account_pk : Pubkey ,
mango_account_signer : & Keypair ,
blockhash : Arc < RwLock < Hash > > ,
slot : & AtomicU64 ,
2023-02-22 04:29:10 -08:00
prioritization_fee_proba : u8 ,
2023-02-15 08:08:55 -08:00
) {
let mango_account_signer_pk = to_sp_pk ( & mango_account_signer . pubkey ( ) ) ;
// update quotes 2x per second
for _ in 0 .. quotes_per_second {
2023-02-23 02:02:57 -08:00
let prioritization_fee_by_market = generate_random_fees (
prioritization_fee_proba ,
perp_market_caches . len ( ) ,
100 ,
1000 ,
) ;
2023-04-18 08:00:57 -07:00
let mut batch_to_send = Vec ::with_capacity ( perp_market_caches . len ( ) ) ;
2023-02-23 02:02:57 -08:00
for ( i , c ) in perp_market_caches . iter ( ) . enumerate ( ) {
2023-02-22 04:29:10 -08:00
let prioritization_fee = prioritization_fee_by_market [ i ] ;
2023-02-21 02:04:02 -08:00
let mut tx = create_ask_bid_transaction (
c ,
mango_account_pk ,
& mango_account_signer ,
prioritization_fee ,
) ;
2023-02-15 08:08:55 -08:00
2023-03-14 05:39:19 -07:00
let recent_blockhash = * blockhash . read ( ) . await ;
tx . sign ( & [ mango_account_signer ] , recent_blockhash ) ;
2023-02-23 02:02:57 -08:00
2023-04-18 08:00:57 -07:00
let record = TransactionSendRecord {
2023-02-15 08:08:55 -08:00
signature : tx . signatures [ 0 ] ,
sent_at : Utc ::now ( ) ,
sent_slot : slot . load ( Ordering ::Acquire ) ,
2023-03-15 08:13:25 -07:00
market_maker : Some ( mango_account_signer_pk ) ,
market : Some ( c . perp_market_pk ) ,
2023-02-22 04:29:10 -08:00
priority_fees : prioritization_fee ,
2023-03-15 08:13:25 -07:00
keeper_instruction : None ,
} ;
2023-04-18 08:00:57 -07:00
batch_to_send . push ( ( tx , record ) ) ;
}
2023-04-18 16:20:13 -07:00
let tpu_manager = tpu_manager . clone ( ) ;
2023-04-19 04:29:26 -07:00
task ::spawn ( async move {
2023-04-18 16:20:13 -07:00
if ! tpu_manager . send_transaction_batch ( & batch_to_send ) . await {
println! ( " sending failed on tpu client " ) ;
}
} ) ;
2023-02-15 08:08:55 -08:00
}
}
pub fn start_market_making_threads (
account_keys_parsed : Vec < AccountKeys > ,
perp_market_caches : Vec < PerpMarketCache > ,
exit_signal : Arc < AtomicBool > ,
blockhash : Arc < RwLock < Hash > > ,
current_slot : Arc < AtomicU64 > ,
2023-03-15 08:13:25 -07:00
tpu_manager : TpuManager ,
2023-02-15 08:08:55 -08:00
duration : & Duration ,
quotes_per_second : u64 ,
2023-02-22 04:29:10 -08:00
prioritization_fee_proba : u8 ,
2023-02-24 06:48:08 -08:00
number_of_markers_per_mm : u8 ,
2023-02-15 08:08:55 -08:00
) -> Vec < JoinHandle < ( ) > > {
2023-02-24 06:48:08 -08:00
let mut rng = rand ::thread_rng ( ) ;
2023-02-15 08:08:55 -08:00
account_keys_parsed
. iter ( )
. map ( | account_keys | {
2023-03-14 05:39:19 -07:00
let exit_signal = exit_signal . clone ( ) ;
2023-02-15 08:08:55 -08:00
let blockhash = blockhash . clone ( ) ;
let current_slot = current_slot . clone ( ) ;
let duration = duration . clone ( ) ;
let perp_market_caches = perp_market_caches . clone ( ) ;
let mango_account_pk =
Pubkey ::from_str ( account_keys . mango_account_pks [ 0 ] . as_str ( ) ) . unwrap ( ) ;
let mango_account_signer =
Keypair ::from_bytes ( account_keys . secret_key . as_slice ( ) ) . unwrap ( ) ;
2023-03-15 08:13:25 -07:00
let tpu_manager = tpu_manager . clone ( ) ;
2023-02-15 08:08:55 -08:00
info! (
2023-03-06 02:05:25 -08:00
" wallet: {:?} mango account: {:?} " ,
2023-02-15 08:08:55 -08:00
mango_account_signer . pubkey ( ) ,
mango_account_pk
) ;
2023-02-24 06:48:08 -08:00
let perp_market_caches = perp_market_caches
. choose_multiple ( & mut rng , number_of_markers_per_mm as usize )
. map ( | x | x . clone ( ) )
. collect_vec ( ) ;
2023-02-15 08:08:55 -08:00
2023-03-14 05:39:19 -07:00
tokio ::spawn ( async move {
for _i in 0 .. duration . as_secs ( ) {
if exit_signal . load ( Ordering ::Relaxed ) {
break ;
}
let start = Instant ::now ( ) ;
2023-02-21 02:04:02 -08:00
2023-03-14 05:39:19 -07:00
// send market maker transactions
send_mm_transactions (
quotes_per_second ,
& perp_market_caches ,
2023-03-15 08:13:25 -07:00
tpu_manager . clone ( ) ,
2023-03-14 05:39:19 -07:00
mango_account_pk ,
& mango_account_signer ,
blockhash . clone ( ) ,
current_slot . as_ref ( ) ,
prioritization_fee_proba ,
)
. await ;
2023-02-15 08:08:55 -08:00
2023-03-14 05:39:19 -07:00
let elapsed_millis : u64 = start . elapsed ( ) . as_millis ( ) as u64 ;
if elapsed_millis < 1000 {
tokio ::time ::sleep ( Duration ::from_millis ( 1000 - elapsed_millis ) ) . await ;
} else {
warn! (
" time taken to send transactions is greater than 1000ms {} " ,
elapsed_millis
) ;
2023-02-15 08:08:55 -08:00
}
2023-03-14 05:39:19 -07:00
}
} )
2023-02-15 08:08:55 -08:00
} )
. collect ( )
}
2023-04-15 03:17:15 -07:00
fn create_cancel_all_orders (
perp_market : & PerpMarketCache ,
mango_account_pk : Pubkey ,
mango_account_signer : & Keypair ,
) -> Transaction {
let mango_account_signer_pk = to_sp_pk ( & mango_account_signer . pubkey ( ) ) ;
let cb_instruction = compute_budget ::ComputeBudgetInstruction ::set_compute_unit_limit ( 1000000 ) ;
let pf_instruction = compute_budget ::ComputeBudgetInstruction ::set_compute_unit_price ( 1000 ) ;
let instruction : Instruction = to_sdk_instruction (
cancel_all_perp_orders (
& perp_market . mango_program_pk ,
& perp_market . mango_group_pk ,
& mango_account_pk ,
& mango_account_signer_pk ,
& perp_market . perp_market_pk ,
& perp_market . bids ,
& perp_market . asks ,
255 ,
)
. unwrap ( ) ,
) ;
Transaction ::new_unsigned ( Message ::new (
& [ cb_instruction , pf_instruction , instruction ] ,
Some ( & mango_account_signer . pubkey ( ) ) ,
) )
}
pub async fn clean_market_makers (
rpc_client : Arc < RpcClient > ,
account_keys_parsed : & Vec < AccountKeys > ,
perp_market_caches : & Vec < PerpMarketCache > ,
blockhash : Arc < RwLock < Hash > > ,
) {
info! ( " Cleaning previous transactions by market makers " ) ;
2023-06-15 01:41:58 -07:00
for account_keys_parsed in account_keys_parsed . chunks ( 10 ) {
let mut tasks = vec! [ ] ;
for market_maker in account_keys_parsed {
let mango_account_pk =
Pubkey ::from_str ( market_maker . mango_account_pks [ 0 ] . as_str ( ) ) . unwrap ( ) ;
for perp_market in perp_market_caches {
let market_maker = market_maker . clone ( ) ;
let perp_market = perp_market . clone ( ) ;
let rpc_client = rpc_client . clone ( ) ;
let mango_account_pk = mango_account_pk . clone ( ) ;
let blockhash = blockhash . clone ( ) ;
2023-04-15 03:17:15 -07:00
2023-06-15 01:41:58 -07:00
let task = tokio ::spawn ( async move {
let mango_account_signer =
Keypair ::from_bytes ( market_maker . secret_key . as_slice ( ) ) . unwrap ( ) ;
2023-04-15 03:17:15 -07:00
2023-06-15 01:41:58 -07:00
for _ in 0 .. 10 {
let mut tx = create_cancel_all_orders (
& perp_market ,
mango_account_pk ,
& mango_account_signer ,
) ;
2023-04-15 03:17:15 -07:00
2023-06-15 01:41:58 -07:00
let recent_blockhash = * blockhash . read ( ) . await ;
tx . sign ( & [ & mango_account_signer ] , recent_blockhash ) ;
let sig = tx . signatures [ 0 ] ;
// send and confirm the transaction with an RPC
if let Ok ( res ) = tokio ::time ::timeout (
Duration ::from_secs ( 10 ) ,
rpc_client . send_and_confirm_transaction ( & tx ) ,
)
. await
{
match res {
Ok ( _ ) = > break ,
Err ( e ) = > info! ( " Error occured while doing cancel all for ma : {}, sig : {} perp market : {} error : {} " , mango_account_pk , sig , perp_market . perp_market_pk , e ) ,
}
2023-04-15 03:17:15 -07:00
}
}
2023-06-15 01:41:58 -07:00
} ) ;
tasks . push ( task ) ;
}
2023-04-15 03:17:15 -07:00
}
2023-06-15 01:41:58 -07:00
futures ::future ::join_all ( tasks ) . await ;
}
2023-04-15 03:17:15 -07:00
info! ( " finished cleaning market makers " ) ;
}