diff --git a/13.log b/13.log new file mode 100644 index 0000000..febf665 --- /dev/null +++ b/13.log @@ -0,0 +1,13 @@ +AF 2 1 14F0117759 +AF 1 1 1D1E0D15FF +AF 2 1 70D4B3855 +OR 2 0 22E869AB9A 1 2327CD75D7 +AF 2 1 12F54589D1 +AF 1 0 16EC0573FE +AF 3 1 1DBA3B2F64 +AF 1 0 22A45121F2 +AF 3 0 BDB8CCB60 +OR 2 0 21CDCD2B81 1 2407EB5978 +AF 3 1 24E1249869 +OR 3 0 231B497C77 1 7C6A3D844 +OR 1 1 161753A88E 0 21069FA53A diff --git a/demo1.log b/demo1.log new file mode 100644 index 0000000..900f1de --- /dev/null +++ b/demo1.log @@ -0,0 +1,11 @@ +AT Teppy +AT Luni +AT Hamza +AA USD +AA BTC +SR 0 51EB851 0 0 0 +SR 1 51EB851 0 0 0 +AF 1 1 500000000 +AF 2 0 3D09000000000 +AF 3 0 3D09000000000 +OR 1 1 100000000 0 C35000000000 diff --git a/demo13.log b/demo13.log new file mode 100644 index 0000000..1d8431f --- /dev/null +++ b/demo13.log @@ -0,0 +1,11 @@ +AT Teppy +AT Hamza +AA USD +AA BTC +SR 0 0 0 0 33333333 +SR 1 0 0 0 33333333 +AF 1 1 500000000 +AF 2 0 3D09000000000 +AF 1 1 1D1E0D15FF +OR 2 0 231B497C77 1 7C6A3D844 +OR 1 1 161753A88E 0 21069FA53A diff --git a/demo13x.log b/demo13x.log new file mode 100644 index 0000000..4d3954c --- /dev/null +++ b/demo13x.log @@ -0,0 +1,12 @@ +AT Teppy +AT Hamza +AA USD +AA BTC +NOP SR 0 0 0 33333333 33333333 +NOP SR 1 0 0 33333333 33333333 +SR 0 11111111 22222222 33333333 44444444 +SR 1 18888888 28888888 38888888 48888888 +AF 1 1 400000000 +AF 2 0 3D09000000000 +OR 2 0 2800000000 1 100000000 +OR 1 1 100000000 0 1900000000 diff --git a/demo2.log b/demo2.log new file mode 100644 index 0000000..61510dd --- /dev/null +++ b/demo2.log @@ -0,0 +1,11 @@ +AT Teppy +AT Luni +AT Hamza +AA USD +AA BTC +SR 0 0 28F5C28 0 0 +SR 1 0 28F5C28 0 0 +AF 1 1 500000000 +AF 2 0 3D09000000000 +AF 3 0 3D09000000000 +OR 1 1 100000000 0 C35000000000 diff --git a/demo3.log b/demo3.log new file mode 100644 index 0000000..7fdf7a5 --- /dev/null +++ b/demo3.log @@ -0,0 +1,11 @@ +AT Teppy +AT Luni +AT Hamza +AA USD +AA BTC +SR 0 0 0 33333333 0 +SR 1 0 0 33333333 0 +AF 1 1 500000000 +AF 2 0 3D09000000000 +AF 3 0 3D09000000000 +OR 1 1 100000000 0 C35000000000 diff --git a/demo4.log b/demo4.log new file mode 100644 index 0000000..dc754b9 --- /dev/null +++ b/demo4.log @@ -0,0 +1,11 @@ +AT Teppy +AT Luni +AT Hamza +AA USD +AA BTC +SR 0 0 0 0 19999999 +SR 1 0 0 0 19999999 +AF 1 1 500000000 +AF 2 0 3D09000000000 +AF 3 0 3D09000000000 +OR 1 1 100000000 0 C35000000000 diff --git a/demox.log b/demox.log index 3d979bf..0e76014 100644 --- a/demox.log +++ b/demox.log @@ -3,9 +3,9 @@ AT Luni AT Hamza AA USD AA BTC +SR 0 51EB851 28F5C28 33333333 19999999 SR 1 51EB851 28F5C28 33333333 19999999 AF 1 1 500000000 AF 2 0 3D09000000000 AF 3 0 3D09000000000 -SR 0 51EB851 28F5C28 33333333 19999999 OR 1 1 100000000 0 C35000000000 diff --git a/demoy.log b/demoy.log new file mode 100644 index 0000000..0e76014 --- /dev/null +++ b/demoy.log @@ -0,0 +1,11 @@ +AT Teppy +AT Luni +AT Hamza +AA USD +AA BTC +SR 0 51EB851 28F5C28 33333333 19999999 +SR 1 51EB851 28F5C28 33333333 19999999 +AF 1 1 500000000 +AF 2 0 3D09000000000 +AF 3 0 3D09000000000 +OR 1 1 100000000 0 C35000000000 diff --git a/demoz.log b/demoz.log new file mode 100644 index 0000000..014aa9b --- /dev/null +++ b/demoz.log @@ -0,0 +1,10 @@ +AT Teppy +AT Luni +AT Hamza +AA USD +AA BTC +SR 0 51EB851 28F5C28 33333333 19999999 +SR 1 51EB851 28F5C28 33333333 19999999 +AF 1 1 500000000 +AF 2 0 3D09000000000 +AF 3 0 3D09000000000 diff --git a/src/finum.rs b/src/finum.rs index e630dae..9ad0545 100644 --- a/src/finum.rs +++ b/src/finum.rs @@ -36,6 +36,9 @@ impl FiNum { pub fn recip(&self) -> Self { FiNum((0x8000000000000000u64/self.0)<<1) } + pub fn fmt_pct2(&self) -> String { + format!("{:>5.2}%", (100.0*(self.0 as f64))/((1u64<<32) as f64)) + } pub fn fmt_recip(&self) -> String { if *self std::fmt::Result { +// write!(f, "{:08X}.{:08X}", self.0>>32,self.0&0xFFFFFFFF) write!(f, "{:.8}", (self.0 as f64)/((1u64<<32) as f64)) } } diff --git a/src/main.rs b/src/main.rs index 2bbd0c0..49c7713 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,6 +50,8 @@ use std::collections::HashMap; use std::cmp::Ordering; use rand::prelude::*; use rand::rngs::StdRng; +use std::time::Instant; + //use hashbrown::HashMap; mod finum; use finum::FiNum; @@ -110,8 +112,8 @@ struct Order { sell_qty: FiNum, sell_remain: FiNum, buy_qty: FiNum, - royalty_remain: FiNum, // Based on royalty1 - commission_remain: FiNum, // Based on commission1 + royalty_remain: FiNum, // Based on royalty1. The thing that is being sold + commission_remain: FiNum, // Based on commission1. The thing that is being sold owner: usize, order_id: usize, } @@ -200,17 +202,14 @@ impl RoyaltyTree { self.tree[index].lazy=FiNum::zero(); } else { let lazy=self.tree[index].lazy; -// println!("Index is {}, lazy.0 {}",index,lazy.serialize()); -// self.raw_dump(); let d1=if lazy>0.into() { lazy*self.weight_here(index)/self.weight_here_below(index) } else { FiNum::zero() }; let d02=lazy-d1; let index_left =wt_left (index).unwrap(); let index_right=wt_right(index).unwrap(); -// println!("D02 is {}, self.weight_here_below({}) is {}, self.weight_below({}) is {}",d02.serialize(),index_left,self.weight_here_below(index_left),index,self.weight_below(index)); if self.weight_below(index)>0.into() { let d0=if d02>0.into() { d02*self.weight_here_below(index_left)/self.weight_below(index) } else { FiNum::zero() }; let d2=d02-d0; - self.sanity(); +// self.sanity(); if !self.weight_here_below(index_left ).is_zero() { self.tree[index_left ].lazy+=d0; } else { self.spare_change+=d0; } if !self.weight_here_below(index_right).is_zero() { self.tree[index_right].lazy+=d2; } else { self.spare_change+=d2; } } @@ -220,7 +219,6 @@ impl RoyaltyTree { self.tree[index].acc+=d1; self.tree[index].lazy=FiNum::zero(); } -// println!("Royalty at {} is {}",index,self.tree[index].acc); self } fn sanity(&self) { @@ -243,7 +241,10 @@ impl RoyaltyTree { if self.next_entry==0 || FiNum::is_zero(self.tree[self.forefather()].weight) { self.spare_change+=amount } else { let ff=self.forefather(); - if self.weight_here_below(ff).is_zero() { self.spare_change+=amount } else { self.tree[ff].lazy+=amount }; + if self.weight_here_below(ff).is_zero() { self.spare_change+=amount } else { + self.tree[ff].lazy+=amount+self.spare_change; + self.spare_change=FiNum::zero() + }; } } // We really need two methods: remove an order and redistribute, remove some and return captured amount @@ -302,7 +303,6 @@ impl RoyaltyTree { self.expand_to(ff1*2); while ff0 Option { @@ -468,12 +488,12 @@ impl Market { let rt=self.royalties.get_mut(&ap.1).unwrap(); let indexr=rt.order_finder.get(&off.order_id).unwrap(); let royalty_amt=rt.get_royalty(*indexr); - println!(" {} @ {} ({}) OrderID: {} Royalties: {}", + println!(" {} @ {} ({}) OrderID: {} Royalties Accumulated: {} Royalties Offered {} Commissions Offered: {}", off.sell_remain, // off.buy_qty*off.sell_remain/off.sell_qty, (off.buy_qty/off.sell_qty).fmt_recip(), self.traders[off.owner as usize].name, off.order_id, - royalty_amt); + royalty_amt,off.royalty_remain,off.commission_remain); } } println!("Royalty Accum SpareChange"); @@ -515,6 +535,7 @@ impl Market { let cmd=Command::deserialize(line); if let Command::NOP(comment)=cmd { println!("{}",comment); } else { + println!("{}",cmd.explain(self)); let res=self.execute(&cmd); println!("{}",res.describe()); } @@ -549,11 +570,14 @@ impl Market { Result::ExecutedBatch(log) } */ - fn retract_order(&mut self, order_id:usize) -> Result { // Still need to credit back funds! But first fix the bug where we are creating commission0 from thin air in make_order (not charging seller) + fn retract_order(&mut self, order_id:usize) -> Result { // Still need to credit back funds! let pair=self.order_finder.get(&order_id).unwrap(); + let sell_type=pair.0; let queue=self.orders.get_mut(pair).unwrap(); let queue_index=*queue.order_finder.get(&order_id).unwrap(); let order=&queue.v[queue_index]; + let credit=order.sell_remain+order.royalty_remain+order.commission_remain; + self.traders[order.owner].add_balance(sell_type,credit); println!("Order owner is {}",order.owner); // Remove from royalties let rt=self.royalties.get_mut(&pair.0).unwrap(); @@ -582,10 +606,9 @@ impl Market { let mut commission_acc=FiNum::zero(); let new_order_id:usize; while execute_if_possible && buy_qty>FiNum::new(0) && self.orders.contains_key(&ap) && self.orders.get(&ap).unwrap().v.len()>0 { - let mut elt=self.orders.get(&ap).unwrap().v[0].clone(); + let elt=self.orders.get(&ap).unwrap().v[0].clone(); if sell_qty/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 ].sub_balance(sell_type,pay_qty); @@ -597,9 +620,10 @@ impl Market { let cq0=commission0_qty*pay_qty/sell_qty_initial; let rq1=royalty1_qty *pay_qty/sell_qty_initial; let cq1=commission1_qty*pay_qty/sell_qty_initial; -// println!("Transact at ask rate. Qty={} elt.sell_remain={} elt.royalty_remain={} elt.commission_remain={}",qty,elt.sell_remain,elt.royalty_remain,elt.commission_remain); + //println!("Transact at ask rate. Qty={} elt.sell_remain={} elt.royalty_remain={} elt.commission_remain={}",qty,elt.sell_remain,elt.royalty_remain,elt.commission_remain); let rq2=if !elt.sell_remain.is_zero() { elt.royalty_remain *qty/elt.sell_remain } else { FiNum::zero() }; let cq2=if !elt.sell_remain.is_zero() { elt.commission_remain*qty/elt.sell_remain } else { FiNum::zero() }; + //elt.sell_remain-=qty; royalty_acc+=rq0+rq1; commission_acc+=cq0+cq1; royalty0_qty-=rq0; @@ -609,17 +633,23 @@ impl Market { self.royalties.entry(sell_type).or_insert(RoyaltyTree::new()).add_royalty(rq0+rq1); self.royalties.entry(buy_type) .or_insert(RoyaltyTree::new()).add_royalty(rq2); let top_order=self.orders.get_mut(&ap).unwrap().peek(); + //println!("top_order.commission_remain={} cq2={} elt.commission_remain={} qty={} elt.sell_remain={}",top_order.commission_remain,cq2,elt.commission_remain,qty,elt.sell_remain); top_order.royalty_remain-=rq2; top_order.commission_remain-=cq2; + //println!("Crediting {} commissions to house {}+{}+{}",sell_type,cq0,cq1,cq2); + self.traders[0].add_balance(sell_type,cq0+cq1); self.traders[0].add_balance(buy_type,cq2); - if elt.sell_remain==0.into() { // deal with pennies stored in royalty_remain and commission_remain - self.traders[0].add_balance(buy_type,top_order.commission_remain); - self.royalties.entry(buy_type).or_insert(RoyaltyTree::new()).add_royalty(top_order.royalty_remain); - self.royalties.get_mut(&sell_type).unwrap().remove_order_id(top_order.order_id); + if elt.sell_remain==qty { // deal with pennies stored in royalty_remain and commission_remain + //if !top_order.commission_remain.is_zero() { println!("Little commission? {} ",top_order.commission_remain); } + let rq3=top_order.royalty_remain; + let cq3=top_order.commission_remain; + self.royalties.get_mut(&buy_type).unwrap().remove_order_id(top_order.order_id); self.order_finder.remove(&top_order.order_id); self.traders[top_order.owner].order_finder.remove(&top_order.order_id); self.orders.get_mut(&ap).unwrap().pop(); // This removes id (which is stored as part of the Order) from self.order's finder self.order_count-=1; + self.traders[0].add_balance(buy_type,cq3); + self.royalties.entry(buy_type).or_insert(RoyaltyTree::new()).add_royalty(rq3); } else { top_order.sell_remain-=qty; } @@ -637,8 +667,8 @@ impl Market { self.next_order_id+=1; // Pay royalties to existing orders on the sell size - self.royalties.entry(sell_type).or_insert(RoyaltyTree::new()).add_royalty(royalty0_qty+royalty1_qty); - royalty_acc+=royalty0_qty+royalty1_qty; + self.royalties.entry(sell_type).or_insert(RoyaltyTree::new()).add_royalty(royalty0_qty); + royalty_acc+=royalty0_qty; // Create a new entry in the RoyaltyTree to accumulate for this Order let rt=self.royalties.entry(buy_type).or_insert(RoyaltyTree::new()); @@ -649,15 +679,18 @@ impl Market { // Insert the new order in the priority queue (OrderQueue) let neworder=Order { owner:owner, sell_qty:sell_qty_remain, sell_remain:sell_qty_remain, buy_qty:buy_qty, royalty_remain:royalty1_qty, commission_remain:commission1_qty, order_id:id }; bids.insert(neworder); + royalty_acc+=royalty1_qty; + commission_acc+=commission1_qty; self.order_count+=1; self.order_finder.insert(id,(sell_type,buy_type)); self.traders[owner].sub_balance(sell_type,sell_qty_remain); - commission_acc+=commission0_qty+commission1_qty; + self.traders[owner].sub_balance(sell_type,commission0_qty); + self.traders[0].add_balance(sell_type,commission0_qty); self.traders[owner].order_finder.insert(id,(sell_type,buy_type)); log.push(format!("Moved {} {} ({}) to market",sell_qty_remain,self.number_to_name(sell_type),self.traders[owner].name)); } } - self.traders[0].add_balance(sell_type,commission_acc); +// self.traders[0].add_balance(sell_type,commission_acc); self.traders[owner].sub_balance(sell_type,royalty_acc+commission_acc); log.push(format!("Paid {} {} in royalties and {} {} ({}) in commissions",royalty_acc ,self.number_to_name(sell_type), commission_acc,self.number_to_name(sell_type),self.traders[owner].name)); @@ -947,6 +980,28 @@ impl Command { _ => format!("NOP (This should never happen)"), } } + fn explain(&self, m: &Market) -> String { + match self { + Self::AddTrader { user: name } => format!("addtrader {}",name), + Self::AddAsset { asset: name } => format!("addasset {}",name), + Self::SetRoyalty { asset_id, roy0, com0 , roy1 , com1 } + => format!("setroyalty {} {} {} {} {}",m.number_to_name(*asset_id),roy0,com0,roy1,com1), + Self::AddFunds { user_id, asset_id, amt } + => format!("addfunds {} {} {}",m.traders[*user_id].name,m.number_to_name(*asset_id),amt), + Self::SubFunds { user_id, asset_id , amt } + => format!("subfunds {} {} {}",m.traders[*user_id].name,m.number_to_name(*asset_id),amt), + Self::Order { user_id, sell_type, sell_qty, buy_type, buy_qty } + => format!("order {} {} {} {} (as {})",sell_qty,m.number_to_name(*sell_type),buy_qty,m.number_to_name(*buy_type),m.traders[*user_id].name), + Self::OrderBatch { user_id, sell_type, sell_qty, buy_type, buy_qty } + => format!("orderbatch {} {} {} {} (as {})",sell_qty,m.number_to_name(*sell_type),buy_qty,m.number_to_name(*buy_type),m.traders[*user_id].name), + Self::ExecuteBatch { asset_type0, strike0, asset_type1, strike1 } + => format!("executebatch {} {} {} {}",strike0,m.number_to_name(*asset_type0),strike1,m.number_to_name(*asset_type1)), + Self::RetractOrder { order_id } => format!("retract {}",order_id), + Self::Error(str) => format!("NOP Error: {}",str), + Self::NOP(str) => format!("NOP {}",clean(str)), + _ => format!("NOP (This should never happen)"), + } + } fn deserialize(line: String) -> Self { let tokens: Vec<&str> = line.split_whitespace().collect(); match tokens.as_slice() { @@ -1010,21 +1065,18 @@ impl Market { Command::SetRoyalty { asset_id,roy0,com0,roy1,com1 } => { if *roy0+*com0+*roy1+*com1 { self.add_trader_balance(*user_id,*asset_id,*amt); -// println!("Added {} {} to {}",*amt,self.number_to_asset(*asset_id).name,self.traders[*user_id].name); Result::FundsRemaining(self.get_trader_balance(*user_id,*asset_id)) } Command::SubFunds { user_id,asset_id,amt } => { if *amt>self.get_trader_balance(*user_id,*asset_id) { Result::Error(format!("Not enough {} in {}",asset_id,user_id)) } else { self.sub_trader_balance(*user_id,*asset_id,*amt); - println!("Subtracted {} {} from {}",*amt,self.number_to_asset(*asset_id).name,self.traders[*user_id].name); Result::FundsRemaining(self.get_trader_balance(*user_id,*asset_id)) } } @@ -1161,7 +1213,8 @@ fn interactive(m: &mut Market, mut out: Option) { Ok(input) => { let tokens: Vec<&str> = input.split_whitespace().collect(); let cmd:Command=match tokens.as_slice() { - ["dump"] => { m.dump(); m.sanity_check(); Command::None }, + ["dump"] => { m.dump(); Command::None }, + ["sanity"] => { m.sanity_check(); Command::None }, ["randomorder"] => { println!("Random OrderID: {:?}",m.random_order_id()); Command::None } ["login", username] => { if let Some(t)=m.trader_name2num.get_mut(*username) { trader=*t; println!("Logged in as {}",m.traders[trader].name) } else { println!("Trader {} not found.",username) } @@ -1198,6 +1251,7 @@ fn interactive(m: &mut Market, mut out: Option) { } ["randomcommands", qty] => { let qty:u32=qty.parse().unwrap(); + let start = Instant::now(); for i in 0..qty { let cmd=m.random_command(); // println!("RandomCommand #{}: {}",1+i,cmd.serialize()); @@ -1210,6 +1264,8 @@ fn interactive(m: &mut Market, mut out: Option) { let res=m.execute(&cmd); // println!("Result: {}",res.describe()); } + let duration = start.elapsed(); + println!("Ran {} commands in {:?}",qty,duration); Command::None }, ["quit"] => { return },