diff --git a/Cargo.toml b/Cargo.toml index 2dd1c96..63da070 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,6 @@ debug = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fixed="1.27.0" +rand="0.8.5" +finum={ path="../finum" } + diff --git a/src/#main.rs# b/src/#main.rs# deleted file mode 100644 index dbc1438..0000000 --- a/src/#main.rs# +++ /dev/null @@ -1,259 +0,0 @@ -use std::collections::HashMap; -use std::cmp::Ordering; -use std::cmp::min; -use finum::FiNum; - -#[derive(Debug, Clone)] -struct Trader { - name: String, - id: i32, - balances: HashMap, // Maps Currency to Amount - } - -impl Trader { - fn new(name:&str,id:i32) -> Self { - Trader { - name: String::from(name), - id: id, - balances: HashMap::new() - } - } - fn add_balance(&mut self, cur:i32, delta:FiNum) { - self.balances.entry(cur).and_modify(|ent| *ent+=delta ).or_insert_with(|| delta); - } - fn sub_balance(&mut self, cur:i32, delta:FiNum) { - self.balances.entry(cur).and_modify(|ent| *ent-=delta ); - } - fn get_balance(&self, cur:i32) -> FiNum { - *self.balances.get(&cur).map(|bal| bal).unwrap_or(&FiNum::new(0u64)) - } - } - -#[derive(Debug, Clone)] -struct Offer { - sell_qty: FiNum, - sell_remain: FiNum, - buy_qty: FiNum, - owner: i32, - } - -trait Dumpable { - fn dump(&self); - } - -impl Dumpable for Offer { - fn dump(&self) { - println!("Giving {}/{} to get {}",self.sell_remain,self.sell_qty,self.buy_qty); - } - } - - -impl Offer { - fn dump(self) { - println!("Remaining: {} Price: {}",self.sell_remain,self.buy_qty/self.sell_qty); - } - } - -struct Market { - asset_name2num: HashMap, - asset_num2name: HashMap, - asset_count:i32, - money_supply: HashMap, - offers: HashMap<(i32,i32),PQueue>, - traders: Vec, - trader_name2num: HashMap, - } - -impl Market { - fn new() -> Self { - Market { - asset_name2num: HashMap::new(), - asset_num2name: HashMap::new(), - asset_count:0, - money_supply: HashMap::new(), - offers: HashMap::new(), - traders: Vec::new(), - trader_name2num: HashMap::new(), - } - } - fn sanity_check(&self) { - println!("Sanity Checking Market..."); - for (cur,amt) in self.money_supply.iter() { - println!("Money Supply {}: {}",self.number_to_name(*cur),*amt); - let mut acc_offers=FiNum::new(0); - for (ac,pq) in &self.offers { if ac.0==*cur { - for off in &*pq.v { acc_offers+=off.sell_remain; } - } } - let mut acc_traders=FiNum::new(0); - for t in &self.traders { acc_traders+=t.get_balance(*cur); } - let acc=acc_offers+acc_traders; - println!(" {}: Offers {} Traders {} Total {} Should Be {}",self.number_to_name(*cur),acc_offers,acc_traders,acc,*amt); - } - } - fn register_trader(&mut self, name:&str) -> i32 { // Add error checking for inserting a trader twice - let rval=self.traders.len() as i32; - self.trader_name2num.insert(String::from(name),self.traders.len() as i32); - self.traders.push(Trader::new(name,rval)); - rval - } - // These are the only ways to get money into or out of the market. - fn add_trader_balance(&mut self, who:i32, cur:i32, delta: FiNum) { - self.traders[who as usize].add_balance(cur,delta); - *self.money_supply.get_mut(&cur).unwrap()+=delta; - } - fn sub_trader_balance(&mut self, who:i32, cur:i32, delta: FiNum) { - self.traders[who as usize].sub_balance(cur,delta); - *self.money_supply.get_mut(&cur).unwrap()-=delta; - } - 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.money_supply.insert(self.asset_count,FiNum::new(0)); - 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:"); - for t in &self.traders { - println!(" Trader {}: {}",t.id,t.name); - for (cur,bal) in t.balances.iter() { - println!(" {}: {}",self.number_to_name(*cur),*bal) - } - } - 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(); - } - } - fn make_offer(&mut self, owner:i32, sell_type:i32, buy_type:i32, sell_qty_initial:FiNum, buy_qty_initial:FiNum) -> bool // Dollars, Bitcoin, 64000, 1 - { - if self.traders[owner as usize].get_balance(sell_type)FiNum::new(0) && self.offers.contains_key(&ap) && self.offers.get(&ap).unwrap().v.len()>0 { - let elt=&mut self.offers.get_mut(&ap).unwrap().v[0]; - if sell_qty_initial/buy_qty_initial>=elt.buy_qty/elt.sell_qty { // Transact at ask_rate - let qty=std::cmp::min(elt.sell_remain,buy_qty); - elt.sell_remain-=qty; - buy_qty-=qty; - let pay_qty=qty*elt.buy_qty/elt.sell_qty; - self.traders[owner as usize].sub_balance(sell_type,pay_qty); - self.traders[owner as usize].add_balance(buy_type ,qty); - self.traders[elt.owner as usize].add_balance(sell_type,pay_qty); - if elt.sell_remain==0.into() { self.offers.get_mut(&ap).unwrap().pop(); } - } - } - if buy_qty>0.into() { - let ap=(sell_type,buy_type); - if let None=self.offers.get_mut(&ap) { self.offers.insert(ap,PQueue::new()); } - let bids=self.offers.get_mut(&ap).unwrap(); - let sell_qty_remain=sell_qty_initial*buy_qty/buy_qty_initial; - bids.insert(Offer { owner:owner, sell_qty:sell_qty_remain, sell_remain:sell_qty_remain, buy_qty:buy_qty } ); - self.traders[owner as usize].sub_balance(sell_type,sell_qty_remain); - } - true - } - } - -impl PartialOrd for Offer { - fn partial_cmp(&self, other:&Self) -> Option { - let ord0=self .sell_qty/self .buy_qty; - let ord1=other.buy_qty /other.sell_qty; - if ord0ord1 { Some(Ordering::Greater) } - else { Some(Ordering::Equal) } - } - } - -impl PartialEq for Offer { - fn eq(&self, other:&Self) -> bool { - let ord0=self .sell_qty/self .buy_qty; - let ord1=other.sell_qty/other.buy_qty; - ord0==ord1 - } - } - - -struct PQueue { - v: Vec, - } - -impl PQueue { - 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] Option { - 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(); - } - } - } - - - -fn main() { - let a=FiNum::new_i32(2); - let b=FiNum::new_i32(3); - let c=b+a; - println!("C is {}, C.value() is {}",c,c.value()); - 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 - 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,5.into()); - m.add_trader_balance(luni ,usd,300000.into()); - m.make_offer(teppy,btc,usd,1.into(),60000.into()); - m.make_offer(luni ,usd,btc,128000.into(),2.into()); // Buy 1 BTC for 64000 USD - m.dump(); - m.sanity_check(); -} diff --git a/src/.#main.rs b/src/.#main.rs deleted file mode 120000 index a3babb2..0000000 --- a/src/.#main.rs +++ /dev/null @@ -1 +0,0 @@ -teppy@teppy-VirtualBox.1978:1716054237 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f7ca02a..22def8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use std::cmp::Ordering; use std::cmp::min; +use rand::prelude::*; +use rand::rngs::StdRng; use finum::FiNum; #[derive(Debug, Clone)] @@ -48,12 +50,6 @@ impl Dumpable for Offer { } -impl Offer { - fn dump(self) { - println!("Remaining: {} Price: {}",self.sell_remain,self.buy_qty/self.sell_qty); - } - } - struct Market { asset_name2num: HashMap, asset_num2name: HashMap, @@ -126,9 +122,17 @@ impl Market { println!(" {}: {}",self.number_to_name(*cur),*bal) } } - 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(); + for (ap,pq) in &self.offers { + println!("Offers selling {} to buy {}:",self.number_to_name(ap.0),self.number_to_name(ap.1)); + let mut sorted=pq.v.clone(); + sorted.sort_by(|a,b| (a.sell_qty/a.buy_qty).cmp(&(b.sell_qty/b.buy_qty))); + for off in sorted.iter() { + println!(" {} @ {} ({})", + off.buy_qty*(off.sell_remain/off.sell_qty), + off.sell_qty/off.buy_qty, + self.traders[off.owner as usize].name); + } + //value.dump(); } } fn make_offer(&mut self, owner:i32, sell_type:i32, buy_type:i32, sell_qty_initial:FiNum, buy_qty_initial:FiNum) -> bool // Dollars, Bitcoin, 64000, 1 @@ -241,20 +245,31 @@ impl PQueue { fn main() { - let a=FiNum::new_i32(2); - let b=FiNum::new_i32(3); - let c=b+a; - println!("C is {}, C.value() is {}",c,c.value()); + let mut rng: StdRng=StdRng::seed_from_u64(1u64); 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 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,5.into()); - m.add_trader_balance(luni ,usd,300000.into()); - m.make_offer(teppy,btc,usd,1.into(),60000.into()); - m.sanity_check(); - m.make_offer(luni ,usd,btc,128000.into(),2.into()); // Buy 1 BTC for 64000 USD + m.add_trader_balance(teppy,btc,100.into()); + m.add_trader_balance(luni ,usd,1000000.into()); + for i in 1..=5 { + let seller=if rng.gen_bool(0.5) { teppy } else { luni }; + let (buy_type,sell_type,buy_qty,sell_qty):(i32,i32,FiNum,FiNum); + if rng.gen_bool(0.5) { + sell_type=btc; + buy_type=usd; + sell_qty=rng.gen_range(2..=5).into(); + buy_qty=sell_qty*rng.gen_range(60000..=70000).into(); + } else { + sell_type=usd; + buy_type=btc; + buy_qty=rng.gen_range(2..=5).into(); + sell_qty=buy_qty*rng.gen_range(60000..=70000).into(); + } + println!("Sell {} {} to buy {} {} ({})",sell_qty,m.number_to_name(sell_type),buy_qty,m.number_to_name(buy_type),m.traders[seller as usize].name); + m.make_offer(seller,sell_type,buy_type,sell_qty,buy_qty); + } m.dump(); m.sanity_check(); }