2024-05-09 09:52:52 -10:00
use std ::collections ::HashMap ;
use std ::cmp ::Ordering ;
use std ::cmp ::min ;
use fixed ::types ::U32F32 ;
2024-05-15 06:59:09 -04:00
#[ derive(Debug, Clone) ]
struct Trader {
name : String ,
id : i32 ,
balances : HashMap < i32 , u64 > , // Maps Currency to Amount
}
impl Trader {
fn new ( name :& str , id :i32 ) -> Self {
Trader {
name : String ::from ( name ) ,
id : id ,
balances : HashMap ::new ( )
}
}
2024-05-15 17:45:14 -04:00
fn add_balance ( & mut self , cur :i32 , delta :u64 ) {
self . balances . entry ( cur ) . and_modify ( | ent | * ent + = delta ) . or_insert_with ( | | delta ) ;
}
fn sub_balance ( & mut self , cur :i32 , delta :u64 ) {
self . balances . entry ( cur ) . and_modify ( | ent | * ent - = delta ) ;
}
fn get_balance ( & self , cur :i32 ) -> u64 {
* self . balances . get ( & cur ) . map ( | bal | bal ) . unwrap_or ( & 0 u64 )
}
2024-05-15 06:59:09 -04:00
}
2024-05-09 09:52:52 -10:00
#[ derive(Debug, Clone) ]
struct Offer {
2024-05-15 06:59:09 -04:00
sell_qty : u64 ,
sell_remain : u64 ,
buy_qty : u64 ,
owner : i32 ,
}
2024-05-09 09:52:52 -10:00
trait Dumpable {
fn dump ( & self ) ;
}
impl Dumpable for Offer {
fn dump ( & self ) {
2024-05-13 12:03:03 -04:00
println! ( " Giving {} / {} to get {} " , fxp_to_fp ( self . sell_remain ) , fxp_to_fp ( self . sell_qty ) , fxp_to_fp ( self . buy_qty ) ) ;
2024-05-09 09:52:52 -10:00
}
}
impl Offer {
fn dump ( self ) {
println! ( " Remaining: {} Price: {} " , self . sell_remain , fxp_to_fp ( fxp_div ( self . buy_qty , self . sell_qty ) ) ) ;
}
}
struct Market {
2024-05-15 06:59:09 -04:00
asset_name2num : HashMap < String , i32 > ,
asset_num2name : HashMap < i32 , String > ,
asset_count :i32 ,
offers : HashMap < ( i32 , i32 ) , PQueue < Offer > > ,
traders : Vec < Trader > ,
2024-05-16 15:50:37 -04:00
trader_name2num : HashMap < String , i32 > ,
2024-05-15 06:59:09 -04:00
}
2024-05-09 09:52:52 -10:00
impl Market {
fn new ( ) -> Self {
Market {
asset_name2num : HashMap ::new ( ) ,
asset_num2name : HashMap ::new ( ) ,
asset_count :0 ,
2024-05-15 06:59:09 -04:00
offers : HashMap ::new ( ) ,
2024-05-16 15:50:37 -04:00
traders : Vec ::new ( ) ,
trader_name2num : HashMap ::new ( ) ,
2024-05-09 09:52:52 -10:00
}
}
2024-05-16 15:50:37 -04:00
fn register_trader ( & mut self , name :& str ) -> i32 { // Add error checking for inserting a trader twice
2024-05-16 10:30:09 -04:00
let rval = self . traders . len ( ) as i32 ;
2024-05-16 15:50:37 -04:00
self . trader_name2num . insert ( String ::from ( name ) , self . traders . len ( ) as i32 ) ;
2024-05-16 10:30:09 -04:00
self . traders . push ( Trader ::new ( name , rval ) ) ;
rval
}
2024-05-16 15:50:37 -04:00
fn add_trader_balance ( & mut self , who :i32 , cur :i32 , delta : u64 ) {
self . traders [ who as usize ] . add_balance ( cur , delta ) ;
}
fn sub_trader_balance ( & mut self , who :i32 , cur :i32 , delta : u64 ) {
self . traders [ who as usize ] . sub_balance ( cur , delta ) ;
}
2024-05-09 09:52:52 -10:00
fn register_asset ( & mut self , name :& str ) -> i32 {
self . asset_count + = 1 ;
self . asset_name2num . insert ( String ::from ( name ) , self . asset_count ) ;
self . asset_num2name . insert ( self . asset_count , String ::from ( name ) ) ;
self . asset_count
}
fn name_to_number ( & self , name :& str ) -> i32 {
* self . asset_name2num . get ( name ) . unwrap ( )
}
fn number_to_name ( & self , num :i32 ) -> & str {
& * self . asset_num2name . get ( & num ) . unwrap ( )
}
fn dump ( & self ) {
println! ( " Dumping Market: " ) ;
2024-05-16 15:50:37 -04:00
for t in & self . traders {
println! ( " Trader {} : {} " , t . id , t . name ) ;
for ( cur , bal ) in t . balances . iter ( ) {
println! ( " {} : {} " , self . number_to_name ( * cur ) , fxp_to_fp ( * bal ) )
}
}
2024-05-09 09:52:52 -10:00
for ( key , value ) in & self . offers {
println! ( " Offers selling {} to buy {} : " , self . number_to_name ( key . 0 ) , self . number_to_name ( key . 1 ) ) ;
value . dump ( ) ;
}
}
2024-05-15 06:59:09 -04:00
fn make_offer ( & mut self , owner :i32 , sell_type :i32 , buy_type :i32 , sell_qty_initial :u64 , buy_qty_initial :u64 ) // Dollars, Bitcoin, 64000, 1
2024-05-09 12:08:26 -10:00
{ // Comments assume someone is already selling 1 Bitcoin for 48000 USD (Bitcoin, Dollars, 1, 48000). Then someone comes along and bids $64000 for 1 Bitcoin (sells 64000 USD to buy 1 Bitcoin.
2024-05-16 15:50:37 -04:00
let mut t0 = & mut self . traders [ owner as usize ] ;
t0 . sub_balance ( sell_type , sell_qty_initial ) ;
2024-05-09 09:52:52 -10:00
println! ( " Making offer to sell {} {} and get {} {} " , fxp_to_fp ( sell_qty_initial ) , self . number_to_name ( sell_type ) , fxp_to_fp ( buy_qty_initial ) , self . number_to_name ( buy_type ) ) ;
let mut sell_qty = sell_qty_initial ; // 64000 Dollars
let mut buy_qty = buy_qty_initial ; // 1 Bitcoin
2024-05-13 12:03:03 -04:00
let sell_rate = fxp_div ( sell_qty , buy_qty ) ;
2024-05-09 09:52:52 -10:00
let ap = ( buy_type , sell_type ) ;
2024-05-13 12:03:03 -04:00
let mut lc = 3 ;
while sell_qty > 0 & & lc > 0 {
2024-05-09 09:52:52 -10:00
if let Some ( asks ) = self . offers . get_mut ( & ap ) {
if asks . v . len ( ) > 0 {
let elt = & mut asks . v [ 0 ] ;
2024-05-16 15:50:37 -04:00
let t1 = & mut self . traders [ elt . owner as usize ] ;
2024-05-09 09:52:52 -10:00
let ask_rate = fxp_div ( elt . buy_qty , elt . sell_qty ) ;
2024-05-15 06:59:09 -04:00
let elt_buy_remain = fxp_mult ( elt . buy_qty , fxp_div ( elt . sell_remain , elt . sell_qty ) ) ;
2024-05-09 09:52:52 -10:00
if sell_rate > = ask_rate { // Transact at ask_rate
2024-05-15 06:59:09 -04:00
// Can we eat the entire ask?
if buy_qty > = elt . sell_remain {
sell_qty - = fxp_mult ( sell_qty , fxp_div ( elt . sell_remain , buy_qty_initial ) ) ;
buy_qty - = elt . sell_remain ;
2024-05-16 15:50:37 -04:00
t1 . add_balance ( sell_type , elt_buy_remain ) ;
2024-05-15 06:59:09 -04:00
asks . pop ( ) ;
} else if elt_buy_remain > = sell_qty { // Can we eat the rest of the offer?
elt . sell_remain - = buy_qty ;
2024-05-16 15:50:37 -04:00
t1 . add_balance ( sell_type , sell_qty ) ;
2024-05-15 06:59:09 -04:00
sell_qty = 0 ;
buy_qty = 0 ;
} else {
let transact = std ::cmp ::min ( sell_qty , fxp_mult ( elt . buy_qty , fxp_div ( elt . sell_remain , elt . sell_qty ) ) ) ;
elt . sell_remain - = fxp_div ( transact , ask_rate ) ;
buy_qty - = fxp_div ( transact , ask_rate ) ;
2024-05-16 15:50:37 -04:00
let mut sell_delta = fxp_mult ( transact , fxp_div ( sell_rate , ask_rate ) ) ;
t1 . add_balance ( sell_type , sell_delta ) ;
sell_qty - = sell_delta ;
2024-05-15 06:59:09 -04:00
if elt . sell_remain = = 0 { println! ( " Popping asks " ) ; asks . pop ( ) ; }
}
} else { break ; }
2024-05-13 12:03:03 -04:00
} else { break ; }
} else { break ; }
lc - = 1 ;
}
2024-05-09 09:52:52 -10:00
if sell_qty > 0 {
let ap = ( sell_type , buy_type ) ;
if let None = self . offers . get_mut ( & ap ) { self . offers . insert ( ap , PQueue ::new ( ) ) ; }
let mut bids = self . offers . get_mut ( & ap ) . unwrap ( ) ;
2024-05-13 12:03:03 -04:00
println! ( " Inserting new offer with sell_qty {} , buy_qty {} " , fxp_to_fp ( sell_qty ) , fxp_to_fp ( buy_qty ) ) ;
2024-05-15 06:59:09 -04:00
bids . insert ( Offer { owner :owner , sell_qty :sell_qty , sell_remain :sell_qty , buy_qty :buy_qty } ) ;
2024-05-13 12:03:03 -04:00
}
}
}
2024-05-09 09:52:52 -10:00
impl PartialOrd for Offer {
fn partial_cmp ( & self , other :& Self ) -> Option < Ordering > {
2024-05-09 12:08:26 -10:00
let ord0 = self . sell_qty / self . buy_qty ;
let ord1 = other . buy_qty / other . sell_qty ;
2024-05-09 09:52:52 -10:00
if ord0 < ord1 { Some ( Ordering ::Less ) }
else if ord0 > ord1 { Some ( Ordering ::Greater ) }
else { Some ( Ordering ::Equal ) }
}
}
impl PartialEq for Offer {
fn eq ( & self , other :& Self ) -> bool {
2024-05-13 12:03:03 -04:00
let ord0 = self . sell_qty / self . buy_qty ;
let ord1 = other . sell_qty / other . buy_qty ;
2024-05-09 09:52:52 -10:00
ord0 = = ord1
}
}
2024-05-09 12:08:26 -10:00
2024-05-09 09:52:52 -10:00
struct PQueue < T : Dumpable > {
v : Vec < T > ,
}
impl < T : Dumpable + std ::cmp ::PartialOrd + std ::fmt ::Debug > PQueue < T > {
fn new ( ) ->Self {
PQueue {
v : Vec ::new ( )
}
}
fn peek ( & self ) -> & T {
& self . v [ 0 ]
}
fn bubble_up ( & mut self , pos : usize ) {
if pos > 0 {
let parent = ( pos - 1 ) / 2 ;
if self . v [ parent ] < self . v [ pos ] {
self . v . swap ( parent , pos ) ;
self . bubble_up ( parent ) ;
}
}
}
fn trickle_down ( & mut self , pos : usize ) {
let mut pivot = pos ;
let child0 = pos * 2 + 1 ;
let child1 = pos * 2 + 2 ;
if child0 < self . v . len ( ) {
if self . v [ pos ] < self . v [ child0 ] { pivot = child0 ; }
if child1 < self . v . len ( ) {
if self . v [ pivot ] < self . v [ child1 ] { pivot = child1 ; }
}
if pivot ! = pos {
self . v . swap ( pivot , pos ) ;
self . trickle_down ( pivot ) ;
}
}
}
fn insert ( & mut self , item : T ) {
self . v . push ( item ) ;
self . bubble_up ( self . v . len ( ) - 1 ) ;
}
fn pop ( & mut self ) -> Option < T > {
if self . v . len ( ) = = 0 { None }
else {
let end = self . v . len ( ) - 1 ;
self . v . swap ( 0 , end ) ;
let rval = self . v . pop ( ) ;
self . trickle_down ( 0 ) ;
rval
}
}
fn dump ( & self ) {
for index in 0 .. self . v . len ( ) {
self . v [ index ] . dump ( ) ;
}
}
}
2024-05-13 12:03:03 -04:00
//fn fxp_mult(n0:u64, n1:u64) ->u64 { (((n0>>32)*(n1>>32))<<64)+(((n0>>32)*(n1&0xFFFFFFFF))<<32)+(((n0&0xFFFFFFFF)*(n1>>32))<<32)+(n0&0xFFFFFFFF)*(n1&0xFFFFFFFF) }
2024-05-09 09:52:52 -10:00
fn fxp_div ( n0 :u64 , n1 :u64 ) -> u64 { fxp_mult ( n0 , fxp_recip ( n1 ) ) }
fn fxp_from_int ( n :u32 ) -> u64 { ( n as u64 ) < < 32 }
fn fxp_recip ( n :u64 ) -> u64 { 0xFFFFFFFFFFFFFFFF u64 / n }
fn fxp_to_fp ( n :u64 ) -> f64 { ( n as f64 ) / ( 1 u64 < < 32 ) as f64 }
2024-05-13 12:03:03 -04:00
fn fxp_mult ( a : u64 , b : u64 ) -> u64 {
let a_high = a > > 32 ;
let a_low = a & 0xFFFFFFFF ;
let b_high = b > > 32 ;
let b_low = b & 0xFFFFFFFF ;
2024-05-09 09:52:52 -10:00
2024-05-13 12:03:03 -04:00
let high_high = a_high * b_high ;
let high_low = a_high * b_low ;
let low_high = a_low * b_high ;
let low_low = a_low * b_low ;
let mid_term = high_low + low_high ;
let carry = mid_term > > 32 ;
let result_high = high_high + carry ;
let result_low = ( mid_term < < 32 ) + low_low ;
( result_high < < 32 ) | ( result_low > > 32 )
}
2024-05-09 09:52:52 -10:00
fn main ( ) {
let mut m = Market ::new ( ) ; // USD type is 1, EUR type is 2, BTC type is 10, ETH type is 11, SOL type is 12
2024-05-16 15:50:37 -04:00
let teppy = m . register_trader ( " Teppy " ) ;
let luni = m . register_trader ( " Luni " ) ;
let usd = m . register_asset ( " USD " ) ;
let btc = m . register_asset ( " BTC " ) ;
m . add_trader_balance ( teppy , btc , fxp_from_int ( 5 ) ) ;
m . add_trader_balance ( luni , usd , fxp_from_int ( 300000 ) ) ;
m . dump ( ) ;
m . make_offer ( teppy , btc , usd , fxp_from_int ( 1 ) , fxp_from_int ( 60000 ) ) ;
2024-05-09 09:52:52 -10:00
m . dump ( ) ;
2024-05-16 15:50:37 -04:00
m . make_offer ( luni , usd , btc , fxp_from_int ( 128000 ) , fxp_from_int ( 2 ) ) ; // Buy 1 BTC for 64000 USD
2024-05-09 09:52:52 -10:00
m . dump ( ) ;
}