diff --git a/src/#main.rs# b/src/#main.rs# new file mode 100644 index 0000000..156391e --- /dev/null +++ b/src/#main.rs# @@ -0,0 +1,453 @@ +// +// When inserting a new Order into the royalties tree, we must make sure the existing +// royalties don't get shared with that new node. To do this, we must "shave" down +// the tree, pushing royalties from the root down to all nodes to the left of the +// new node. +// +// Case to think about: +// Selling 140000 USD to buy 2 BTC. Weight is ===140k USD +// Selling 50000 GBP to buy 1 BTC. Weight is === 50k GBP +// +#![allow(unsafe_code)] +#![allow(unused_variables)] +#![allow(dead_code)] +use std::env; +use std::collections::HashMap; +use std::cmp::Ordering; +use std::rc::Rc; +use std::cell::RefCell; +use rand::prelude::*; +use rand::rngs::StdRng; +use finum::FiNum; + +#[derive(Debug, Clone)] +struct Trader { + name: String, + id: usize, + balances: HashMap, // Maps Currency to Amount + } + +impl Trader { + fn new(name:&str,id:usize) -> Self { + Trader { + name: String::from(name), + id: id, + balances: HashMap::new() + } + } + fn add_balance(&mut self, cur:usize, delta:FiNum) { + self.balances.entry(cur).and_modify(|ent| *ent+=delta ).or_insert_with(|| delta); + } + fn sub_balance(&mut self, cur:usize, delta:FiNum) { + self.balances.entry(cur).and_modify(|ent| *ent-=delta ); + } + fn get_balance(&self, cur:usize) -> FiNum { + *self.balances.get(&cur).map(|bal| bal).unwrap_or(&FiNum::new(0u64)) + } + } + +#[derive(Debug, Clone)] +struct Order { + sell_qty: FiNum, + sell_remain: FiNum, + buy_qty: FiNum, + owner: usize, + rt_loc: usize, // Location in the Royalty Tree + } + +struct RoyaltyTree { + tree: Vec, + } + +impl RoyaltyTree { + fn new() -> Self { + RoyaltyTree { tree:Vec::new() } + } + fn get_weight(&self, index:usize) -> FiNum { + println!("Get_weight {} self.tree.len() {} ",index,self.tree.len()); + if index=self.tree.len() { index=wt_left(index).unwrap() } + self.tree[index].weight + } + } + } + +struct Royalty { + weight: FiNum, // Here and below + acc: FiNum, // Here and Below + } + +impl Royalty { + fn new(weight: FiNum) -> Self { + Royalty { weight:weight, acc:FiNum::new(0u64) } + } + } + +impl RoyaltyTree { + fn insert(&mut self, weight: FiNum) -> usize { + let last=self.tree.len(); + let mut nweight=weight; + if let Some(left )=wt_left (last) { if left > { + fn dump(&self) { + } + } + + +impl Dumpable for Order { + fn dump(&self) { + println!("Giving {}/{} to get {}",self.sell_remain,self.sell_qty,self.buy_qty); + } + } + + + +struct Market { + asset_name2num: HashMap, + asset_num2name: HashMap, + asset_count:usize, + money_supply: HashMap, + traders: Vec, + trader_name2num: HashMap, + orders: HashMap<(usize,usize),PQueue>>>, + royalties: HashMap, // Active orders that are accepting asset X. They receive royalties when someone makes an order to sell X + } + +impl Market { + fn new() -> Self { + let mut rval=Market { + asset_name2num: HashMap::new(), + asset_num2name: HashMap::new(), + asset_count:0, + money_supply: HashMap::new(), + orders: HashMap::new(), + royalties: HashMap::new(), + traders: Vec::new(), + trader_name2num: HashMap::new(), + }; + rval.register_trader("*NONE*"); + rval + } + fn distribute_royalty(&self, amount:FiNum) { + + } + 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_orders=FiNum::new(0); + for (ac,pq) in &self.orders { if ac.0==*cur { + for off in &*pq.v { acc_orders+=off.borrow().sell_remain; } + } } + let mut acc_traders=FiNum::new(0); + for t in &self.traders { acc_traders+=t.get_balance(*cur); } + let acc=acc_orders+acc_traders; + println!(" {}: Orders {} Traders {} Total {} Should Be {}",self.number_to_name(*cur),acc_orders,acc_traders,acc,*amt); + } + } + fn register_trader(&mut self, name:&str) -> usize { // Add error checking for inserting a trader twice + let rval=self.traders.len(); + self.trader_name2num.insert(String::from(name),self.traders.len()); + 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:usize, cur:usize, 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:usize, cur:usize, 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) -> usize { + 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) -> usize { + *self.asset_name2num.get(name).unwrap() + } + fn number_to_name(&self, num:usize) -> &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 (ap,pq) in &self.orders { + println!("Orders 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| { let a=a.borrow(); let b=b.borrow(); (a.sell_qty/a.buy_qty).cmp(&(b.sell_qty/b.buy_qty)) }); + for off in sorted.iter() { + let off=off.borrow(); + println!(" {} @ {} ({})", + off.sell_remain, // off.buy_qty*off.sell_remain/off.sell_qty, + off.buy_qty/off.sell_qty, + self.traders[off.owner as usize].name); + } + pq.dump(); + } + } + fn make_order(&mut self, owner:usize, sell_type:usize, buy_type:usize, sell_qty_initial:FiNum, buy_qty_initial:FiNum) -> bool // Dollars, Bitcoin, 64000, 1 + { + let initial_balance=self.traders[owner as usize].get_balance(sell_type); + if initial_balanceFiNum::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].borrow())).clone(); + 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; // 1.8499*194623/2.9744 + 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.orders.get_mut(&ap).unwrap().pop(); } + else { self.orders.get(&ap).unwrap().v[0].borrow_mut().sell_remain-=qty; } + } else { break; } + } + if buy_qty>0.into() { + let ap=(sell_type,buy_type); + if let None=self.orders.get_mut(&ap) { self.orders.insert(ap,PQueue::new()); } + let bids=self.orders.get_mut(&ap).unwrap(); + let sell_qty_remain=sell_qty_initial*buy_qty/buy_qty_initial; + if sell_qty_remain>0.into() { + let rt_loc=self.royalties.entry(sell_type).or_insert(RoyaltyTree::new()).insert(sell_qty_remain); + let neworder=Rc::new(RefCell::new( + Order { owner:owner, sell_qty:sell_qty_remain, sell_remain:sell_qty_remain, buy_qty:buy_qty, rt_loc: rt_loc } )); + bids.insert(neworder); + self.traders[owner as usize].sub_balance(sell_type,sell_qty_remain); + } + } + true + } + } + +impl PartialOrd for Order { + fn partial_cmp(&self, other:&Self) -> Option { + let ord0=self .sell_qty/self .buy_qty; + let ord1=other.sell_qty/other.buy_qty; + if ord0ord1 { Some(Ordering::Greater) } + else { Some(Ordering::Equal) } + } + } + +impl PartialEq for Order { + 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(); + } + } + } + +impl Market { + fn exercise(&mut self) { + let mut rng: StdRng=StdRng::seed_from_u64(13u64); + let teppy=self.register_trader("Teppy"); + let luni =self.register_trader("Luni"); + let usd =self.register_asset("USD"); + let btc =self.register_asset("BTC"); + self.add_trader_balance(teppy,btc,100000.into()); + self.add_trader_balance(luni ,usd,650000000.into()); + let mut count=0; + let mut tries=0; + for _i in 1..=1000000 { + let seller=if rng.gen_bool(0.5) { teppy } else { luni }; + let (buy_type,sell_type,buy_qty,sell_qty):(usize,usize,FiNum,FiNum); + if rng.gen_bool(0.5) { + sell_type=btc; + buy_type=usd; + sell_qty=rng.gen_range(1..=5).into(); + buy_qty=sell_qty*rng.gen_range(60..=70).into(); + } else { + sell_type=usd; + buy_type=btc; + buy_qty=rng.gen_range(1..=5).into(); + sell_qty=buy_qty*rng.gen_range(60..=70).into(); + } + let mut _success=false; + if self.make_order(seller,sell_type,buy_type,sell_qty,buy_qty) { count+=1; _success=true; }; + if count>=1000000 { break; } + tries+=1; + } + println!("Tries: {} Trades: {}",tries,count); + self.sanity_check(); + } + } + +fn wt_level(index:usize) -> usize { + (index^(index+1)).trailing_ones() as usize-1 + } + +fn wt_leaf(index:usize) -> bool { + index&1==0 + } + +fn wt_left(index:usize) -> Option { + let level=wt_level(index); + if level>0 { Some(index-(1<<(wt_level(index)-1))) } else { None } + } + +fn wt_right(index:usize) -> Option { + let level=wt_level(index); + if level>0 { Some(index+(1<<(wt_level(index)-1))) } else { None } + } + +fn wt_parent(index:usize) -> usize { + let lev=wt_level(index); + let first_in_row=index%(1<>1; + first_in_parent_row+nth_in_parent_row*skip_in_parent_row + } + +fn wt_forefather(max_index:usize) -> usize { + let mut rval=max_index; + rval=rval|(rval>>1); + rval=rval|(rval>>2); + rval=rval|(rval>>4); + rval=rval|(rval>>8); + rval=rval|(rval>>16); + rval=rval|(rval>>32); + if rval>max_index { wt_left(rval).unwrap() } else { rval } + } + +fn tree_stuff() { + for i in (0..=60).step_by(1) { + println!("Index {} Forefather {} Parent {}",i,wt_forefather(i),wt_parent(i)); + } + } + +fn royalty_stuff() { + let mut rt=RoyaltyTree::new(); + for _ in 0..20 { + rt.insert(FiNum::new_i32(10)); + } + for index in 0..rt.tree.len() { + println!("Index: {} Royalty: {}",index,rt.tree[index].weight); + } + } + + +fn main() { + let args: Vec = env::args().collect(); + let mode=if args.len()<=1 { "--exercise" } else { args[1].as_str() }; + let mut m=Market::new(); // USD type is 1, EUR type is 2, BTC type is 10, ETH type is 11, zKN6FBdD SOL type is 12 + match mode { + "--exercise" => m.exercise(), + "--treestuff" => tree_stuff(), + "--royaltystuff" => royalty_stuff(), + _ => println!("Unknown mode: {}",mode), + } + } diff --git a/src/.#main.rs b/src/.#main.rs new file mode 100644 index 0000000..07129e7 --- /dev/null +++ b/src/.#main.rs @@ -0,0 +1 @@ +teppy@HAWAII.19788:1716742567 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6131124..5ca9494 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,7 +52,7 @@ struct Order { sell_remain: FiNum, buy_qty: FiNum, owner: usize, - rt_loc: usize, // Location in the Royalty Tree + rt_loc: usize, // Location in the Royalty Tree } struct RoyaltyTree { @@ -61,8 +61,22 @@ struct RoyaltyTree { impl RoyaltyTree { fn new() -> Self { - RoyaltyTree { tree:Vec::new() } - } + RoyaltyTree { tree:Vec::new() } + } + fn get_weight(&self, index:usize) -> FiNum { + println!("Get_weight {} self.tree.len() {} ",index,self.tree.len()); + if index=self.tree.len() { index=wt_left(index).unwrap() } + self.tree[index].weight + } + } } struct Royalty { @@ -78,21 +92,24 @@ impl Royalty { impl RoyaltyTree { fn insert(&mut self, weight: FiNum) -> usize { - self.tree.push(Royalty::new(weight)); - let last=self.tree.len()-1; + let last=self.tree.len(); + let mut nweight=weight; + if let Some(left )=wt_left (last) { if left usize { (index^(index+1)).trailing_ones() as usize-1 } +fn wt_leaf(index:usize) -> bool { + index&1==0 + } + fn wt_left(index:usize) -> Option { let level=wt_level(index); if level>0 { Some(index-(1<<(wt_level(index)-1))) } else { None } @@ -405,6 +426,16 @@ fn tree_stuff() { } } +fn royalty_stuff() { + let mut rt=RoyaltyTree::new(); + for _ in 0..20 { + rt.insert(FiNum::new_i32(10)); + } + for index in 0..rt.tree.len() { + println!("Index: {} Royalty: {}",index,rt.tree[index].weight); + } + } + fn main() { let args: Vec = env::args().collect(); @@ -413,6 +444,7 @@ fn main() { match mode { "--exercise" => m.exercise(), "--treestuff" => tree_stuff(), + "--royaltystuff" => royalty_stuff(), _ => println!("Unknown mode: {}",mode), } }