2024-10-10 21:53:48 -04:00
//
2024-05-30 22:22:02 -04:00
// 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:
2024-07-29 16:45:47 -04:00
// Selling 140000 USD to buy 2 BTC. Weight is ===140k USD
2024-05-30 22:22:02 -04:00
// Selling 50000 GBP to buy 1 BTC. Weight is === 50k GBP
//
2024-07-26 16:54:03 -04:00
// Quick & Dirty Documentation
//
// Running lzf1 from a command line:
// lzf1 # --Interactive is the default mode, no need to
// lzf1 --log tuesday.log # Same as above, but log all commands to file tuesday.log
// lzf1 --replay tuesday.log # Replay tuesday.log, then go into interactive mode
// lzf1 --replay tuesday.log --log wednesday.log # Replay tuesday.log, then go interactive logging additional commands to wednesday.log
// lzf1 --replay tuesday.log --log tuesday.log # Replay tuesday.log, then go interactive, then append additional commands to tuesday.log
// Commands in Interactive Mode:
// addasset USD # Case sensitive, no spaces
// setroyalty USD 0.01 0.00 0.03 0.02 # Sets 1% royalty when order selling USD is placed, but no commission.
// # Then additional 3% royalty when order executes, plus 2% commission
// addtrader Teppy # Case sensitive, no spaces
// addfunds Teppy 20000 USD # Create funds from thin air. Do this when we receive a wire transfer, for instance.
// subfunds Teppy 0.5 BTC # Remove funds. Do this when we process a withdrawal.
// balances Teppy # Show all of Teppy's funds (but not what has been moved to the market)
// login Teppy # Some commands take an implicit Trader parameter. This sets that parameter
// whoami # Shows logged in name for this Interactive session
2024-08-07 18:50:46 -04:00
// showorders # Shows orders for the logged in Trader
2024-07-26 16:54:03 -04:00
// wallet # Show all of the logged-in trader's funds (but not what is on the market)
// order 0.5 BTC 30000 USD # Create an order selling 0.5 BTC to buy 30000 USD. Uses logged in Trader's balance.
2024-07-30 19:11:14 -04:00
// orderbatch 0.5 BTC 30000 USD # Enter an order but don't execute it (allow it to contribute to a crossed market)
2024-07-26 16:54:03 -04:00
// quit # Clean exit
//
//
2025-02-04 10:59:39 -05:00
//
// Thoughts on cheapest-path algorithm:
// Create a square matrix of costs from each asset to each asset. Some of the entries will be infinity. The goal is to lower the entries.
// In each cell, store not just the cost, but the path and path capacity.
// Starting with the source asset, see if visiting each other asset lowers the cost to get there. If it does, replace that asset's cost with the new cost and path.
//
2024-09-18 20:11:38 -04:00
2024-05-21 17:55:05 -04:00
#![ allow(unsafe_code) ]
2024-05-30 22:22:02 -04:00
#![ allow(unused_variables) ]
#![ allow(dead_code) ]
2024-07-26 17:06:50 -04:00
2024-07-26 16:54:03 -04:00
use std ::fs ::File ;
use std ::fs ::OpenOptions ;
use std ::path ::Path ;
use std ::io ::{ self , BufRead , Write } ;
2024-05-26 08:19:55 -04:00
use std ::env ;
2025-01-05 22:34:32 -05:00
use std ::collections ::HashMap ;
2025-04-21 23:37:02 -04:00
use std ::collections ::HashSet ;
2024-05-09 09:52:52 -10:00
use std ::cmp ::Ordering ;
2025-02-04 10:59:39 -05:00
use std ::cmp ::min ;
2024-05-19 09:47:40 -04:00
use rand ::prelude ::* ;
use rand ::rngs ::StdRng ;
2025-01-13 13:34:26 -05:00
use std ::time ::Instant ;
2025-01-05 22:34:32 -05:00
//use hashbrown::HashMap;
2024-07-26 16:54:03 -04:00
mod finum ;
2024-05-18 14:28:12 -04:00
use finum ::FiNum ;
2024-05-09 09:52:52 -10:00
2024-05-15 06:59:09 -04:00
#[ derive(Debug, Clone) ]
struct Trader {
name : String ,
2024-07-26 16:54:03 -04:00
password : String , // hash(name..salt..password)
2024-05-30 22:22:02 -04:00
id : usize ,
balances : HashMap < usize , FiNum > , // Maps Currency to Amount
2024-10-13 16:55:06 -04:00
order_finder : HashMap < usize , ( usize , usize ) > , // Maps OrderIDs to asset pairs (Order trees)
2024-05-15 06:59:09 -04:00
}
2024-07-26 16:54:03 -04:00
#[ derive(Clone) ]
struct Asset {
name : String ,
royalty0_rate : FiNum , // Traders get this when entered
royalty1_rate : FiNum , // Traders get this when executed
commission0_rate : FiNum , // House gets this when entered
commission1_rate : FiNum , // House gets this when executed
}
impl Asset {
fn new ( name : & str ) -> Self {
Asset {
name : String ::from ( name ) ,
royalty0_rate :FiNum ::zero ( ) ,
royalty1_rate :FiNum ::zero ( ) ,
commission0_rate :FiNum ::zero ( ) ,
commission1_rate :FiNum ::zero ( ) ,
}
}
}
2024-05-15 06:59:09 -04:00
impl Trader {
2024-05-30 22:22:02 -04:00
fn new ( name :& str , id :usize ) -> Self {
2024-05-15 06:59:09 -04:00
Trader {
2024-05-18 23:59:58 -04:00
name : String ::from ( name ) ,
2024-07-26 16:54:03 -04:00
password : String ::from ( " " ) ,
2024-05-18 23:59:58 -04:00
id : id ,
2024-08-06 20:08:00 -04:00
balances : HashMap ::new ( ) ,
2024-08-07 18:20:42 -04:00
order_finder : HashMap ::new ( ) ,
2024-05-18 23:59:58 -04:00
}
2024-05-15 06:59:09 -04:00
}
2024-05-30 22:22:02 -04:00
fn add_balance ( & mut self , cur :usize , delta :FiNum ) {
2024-05-18 23:59:58 -04:00
self . balances . entry ( cur ) . and_modify ( | ent | * ent + = delta ) . or_insert_with ( | | delta ) ;
}
2024-05-30 22:22:02 -04:00
fn sub_balance ( & mut self , cur :usize , delta :FiNum ) {
2024-05-18 23:59:58 -04:00
self . balances . entry ( cur ) . and_modify ( | ent | * ent - = delta ) ;
}
2024-05-30 22:22:02 -04:00
fn get_balance ( & self , cur :usize ) -> FiNum {
2024-05-18 14:28:12 -04:00
* self . balances . get ( & cur ) . map ( | bal | bal ) . unwrap_or ( & FiNum ::new ( 0 u64 ) )
2024-05-18 23:59:58 -04:00
}
2024-05-15 06:59:09 -04:00
}
2024-05-09 09:52:52 -10:00
#[ derive(Debug, Clone) ]
2024-05-26 08:19:55 -04:00
struct Order {
2024-05-18 14:28:12 -04:00
sell_qty : FiNum ,
sell_remain : FiNum ,
buy_qty : FiNum ,
2025-01-13 13:34:26 -05:00
royalty_remain : FiNum , // Based on royalty1. The thing that is being sold
commission_remain : FiNum , // Based on commission1. The thing that is being sold
2024-05-30 22:22:02 -04:00
owner : usize ,
2024-08-07 18:20:42 -04:00
order_id : usize ,
2024-05-15 06:59:09 -04:00
}
2024-05-09 09:52:52 -10:00
2024-05-30 22:22:02 -04:00
struct RoyaltyTree {
tree : Vec < Royalty > ,
2024-07-26 16:54:03 -04:00
next_entry : usize ,
spare_change : FiNum , // Waiting to be distributed
2024-10-13 16:55:06 -04:00
order_finder : HashMap < usize , usize > , // Maps OrderID to location (index) in the tree
2024-05-30 22:22:02 -04:00
}
struct Royalty {
2024-09-18 15:54:59 -04:00
count : usize , // Here and below
2024-05-30 22:22:02 -04:00
weight : FiNum , // Here and below
2024-07-26 16:54:03 -04:00
lazy : FiNum , // To be distributed to here and to below based on weights
acc : FiNum , // Accumulated here
2024-08-07 18:20:42 -04:00
order_id : usize ,
2024-05-30 22:22:02 -04:00
}
impl Royalty {
2024-07-26 16:54:03 -04:00
fn new ( ) -> Self {
2024-09-18 15:54:59 -04:00
Royalty { weight :FiNum ::zero ( ) , lazy :FiNum ::zero ( ) , acc :FiNum ::zero ( ) , order_id :0 , count :0 }
2024-05-30 22:22:02 -04:00
}
}
impl RoyaltyTree {
2024-07-26 16:54:03 -04:00
fn new ( ) -> Self {
2024-08-07 18:20:42 -04:00
RoyaltyTree { tree :Vec ::new ( ) , next_entry :0 , spare_change :FiNum ::zero ( ) , order_finder :HashMap ::new ( ) }
2024-07-26 16:54:03 -04:00
}
2024-07-29 16:45:47 -04:00
fn acc_total ( & self ) -> FiNum {
let mut rval = FiNum ::zero ( ) ;
2024-07-29 21:02:12 -04:00
for r in & self . tree { rval + = r . acc + r . lazy ; }
2024-07-29 16:45:47 -04:00
rval
}
2024-07-26 16:54:03 -04:00
fn weight_here_below ( & self , index :usize ) -> FiNum {
self . tree [ index ] . weight
}
2024-09-18 15:54:59 -04:00
fn count_here_below ( & self , index :usize ) -> usize {
self . tree [ index ] . count
}
2024-07-26 16:54:03 -04:00
fn weight_below ( & self , index : usize ) -> FiNum {
if index & 1 = = 0 {
FiNum ::zero ( )
} else {
let w0 = self . weight_here_below ( wt_left ( index ) . unwrap ( ) ) ;
let w1 = self . weight_here_below ( wt_right ( index ) . unwrap ( ) ) ;
w0 + w1
2024-05-30 22:22:02 -04:00
}
2024-07-26 16:54:03 -04:00
}
2024-09-18 15:54:59 -04:00
fn count_below ( & self , index : usize ) -> usize {
if index & 1 = = 0 {
0
} else {
let c0 = self . count_here_below ( wt_left ( index ) . unwrap ( ) ) ;
2025-01-05 22:34:32 -05:00
let c1 = self . count_here_below ( wt_right ( index ) . unwrap ( ) ) ;
2024-09-18 15:54:59 -04:00
c0 + c1
}
}
2024-07-26 16:54:03 -04:00
fn weight_here ( & self , index :usize ) -> FiNum {
if index & 1 = = 0 {
self . weight_here_below ( index )
} else {
let w0 = self . weight_here_below ( index ) ;
let w1 = self . weight_below ( index ) ;
w0 - w1
}
}
2024-09-18 15:54:59 -04:00
fn count_here ( & self , index :usize ) -> usize {
if index & 1 = = 0 {
self . count_here_below ( index )
} else {
let c0 = self . count_here_below ( index ) ;
let c1 = self . count_below ( index ) ;
if c1 > c0 { println! ( " Count_here( {} ) ... c0= {} c1= {} " , index , c0 , c1 ) } ;
c0 - c1
}
}
2024-07-26 16:54:03 -04:00
fn expand_to ( & mut self , index : usize ) -> & mut Self {
for _ in self . tree . len ( ) ..= index { self . tree . push ( Royalty ::new ( ) ) }
self
}
fn capture0 ( & mut self , index : usize ) -> & mut Self {
if index & 1 = = 0 {
let lazy = self . tree [ index ] . lazy ;
self . tree [ index ] . acc + = lazy ;
self . tree [ index ] . lazy = FiNum ::zero ( ) ;
} else {
let lazy = self . tree [ index ] . lazy ;
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 ( ) ;
2025-01-05 22:34:32 -05:00
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 ;
2025-01-13 13:34:26 -05:00
// self.sanity();
2025-01-05 22:34:32 -05:00
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 ; }
}
else {
self . spare_change + = d02 ;
}
2024-07-26 16:54:03 -04:00
self . tree [ index ] . acc + = d1 ;
self . tree [ index ] . lazy = FiNum ::zero ( ) ;
}
self
}
2024-08-14 18:53:55 -04:00
fn sanity ( & self ) {
for i in 0 .. self . tree . len ( ) {
assert! ( ! self . weight_here_below ( i ) . is_tiny ( ) , " Weight at {} is tiny " , i ) ;
assert! ( ! self . tree [ i ] . acc . is_tiny ( ) , " Acc at {} is tiny " , i ) ;
assert! ( ! self . tree [ i ] . lazy . is_tiny ( ) , " Lazy at {} is tiny " , i ) ;
}
}
2024-07-26 16:54:03 -04:00
fn get_royalty ( & mut self , index : usize ) -> FiNum {
let mut f = self . forefather ( ) ;
while f ! = index {
self . capture0 ( f ) ;
f = if f > index { wt_left ( f ) . unwrap ( ) } else { wt_right ( f ) . unwrap ( ) }
}
self . capture0 ( f ) ;
self . tree [ index ] . acc
}
fn add_royalty ( & mut self , amount : FiNum ) {
2024-08-10 16:52:07 -04:00
if self . next_entry = = 0 | | FiNum ::is_zero ( self . tree [ self . forefather ( ) ] . weight ) { self . spare_change + = amount }
2024-07-26 16:54:03 -04:00
else {
let ff = self . forefather ( ) ;
2025-01-13 13:34:26 -05:00
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 ( )
} ;
2024-07-26 16:54:03 -04:00
}
}
2024-08-14 18:53:55 -04:00
// We really need two methods: remove an order and redistribute, remove some and return captured amount
2025-01-20 16:16:43 -05:00
fn remove_order_id ( & mut self , id : usize ) -> FiNum { // And zero out in the tree
2025-01-05 22:34:32 -05:00
if let Some ( pos ) = self . order_finder . remove ( & id ) {
2025-01-24 20:20:37 -05:00
self . capture0 ( pos ) ;
2025-01-05 22:34:32 -05:00
let weight = self . weight_here ( pos ) ;
2025-01-24 20:20:37 -05:00
self . sub_weight ( pos , weight ) ;
let rval = self . tree [ pos ] . acc ;
self . tree [ pos ] . acc = FiNum ::zero ( ) ;
rval
2025-01-05 22:34:32 -05:00
}
2025-01-20 16:16:43 -05:00
else { FiNum ::zero ( ) }
2024-08-10 16:52:07 -04:00
}
2024-09-18 15:54:59 -04:00
fn random_order_id ( & self , rng : & mut rand ::rngs ::StdRng ) -> Option < usize > {
if self . tree . len ( ) = = 0 { return None }
let mut f = self . forefather ( ) ;
while self . count_here_below ( f ) > 0 {
2024-09-30 15:06:22 -04:00
if f & 1 = = 0 { return Some ( f ) ; }
2024-09-18 15:54:59 -04:00
let c0 = self . count_here ( f ) ;
2024-09-30 15:06:22 -04:00
let c1 = if f & 1 = = 0 { self . count_here ( f ) } else { self . count_here_below ( wt_left ( f ) . unwrap ( ) ) } ;
let c2 = if f & 1 = = 0 { self . count_here ( f ) } else { self . count_here_below ( wt_right ( f ) . unwrap ( ) ) } ;
2024-09-18 15:54:59 -04:00
let c = c0 + c1 + c2 ;
let r = rng . gen_range ( 0 .. c ) ;
2024-09-30 15:06:22 -04:00
if r < c0 { println! ( " returning {} " , f ) ; return Some ( f ) }
else if r < c0 + c1 { f = wt_left ( f ) . unwrap ( ) }
else { f = wt_right ( f ) . unwrap ( ) }
2024-09-18 15:54:59 -04:00
}
None
}
2025-01-24 20:20:37 -05:00
fn capture_by_order_id ( & mut self , order_id : usize ) -> FiNum {
if let Some ( & index ) = self . order_finder . get ( & order_id ) {
self . capture0 ( index ) ;
let rval = self . tree [ index ] . acc ;
self . tree [ index ] . acc = FiNum ::zero ( ) ;
rval
} else { FiNum ::zero ( ) }
2024-09-18 15:54:59 -04:00
}
2025-01-24 20:20:37 -05:00
fn sub_weight ( & mut self , index : usize , mut weight : FiNum ) {
2024-08-10 16:52:07 -04:00
self . get_royalty ( index ) ;
2024-09-30 15:06:22 -04:00
let delta_count ;
let wh = self . weight_here ( index ) ;
2025-01-20 16:16:43 -05:00
if weight > = wh {
delta_count = if wh > FiNum ::zero ( ) { 1 } else { 0 } ;
weight = wh ;
}
else {
delta_count = 0 ;
}
2024-08-10 16:52:07 -04:00
let mut index = index ;
let ff = self . forefather ( ) ;
2024-10-06 22:02:25 -04:00
loop {
2024-09-30 15:06:22 -04:00
self . tree [ index ] . weight - = weight ;
self . tree [ index ] . count - = delta_count ;
if index = = ff { break ; }
index = wt_parent ( index ) ;
}
2024-08-07 18:20:42 -04:00
}
fn set_location ( & mut self , id : usize , index : usize ) {
2025-01-10 21:13:04 -05:00
self . tree [ index ] . order_id = id ;
2024-08-07 18:20:42 -04:00
self . order_finder . insert ( id , index ) ;
}
2024-07-26 16:54:03 -04:00
fn add_weight ( & mut self , index : usize , weight : FiNum ) {
if self . tree . len ( ) > 0 {
let mut ff0 = self . forefather ( ) ;
let ff1 = wt_forefather ( index ) ;
let w = self . tree [ ff0 ] . weight ;
2024-09-18 15:54:59 -04:00
let c = self . tree [ ff0 ] . count ;
2024-07-26 16:54:03 -04:00
self . expand_to ( ff1 * 2 ) ;
while ff0 < ff1 {
ff0 = wt_parent ( ff0 ) ;
self . tree [ ff0 ] . weight = w ;
2024-09-18 15:54:59 -04:00
self . tree [ ff0 ] . count = c ;
2024-07-26 16:54:03 -04:00
}
}
else { self . expand_to ( wt_forefather ( index ) * 2 ) ; }
self . get_royalty ( index ) ; // Just for the side effect of capturing everything to this point
let mut index = index ;
2024-09-30 15:06:22 -04:00
let delta_count = if self . weight_here ( index ) = = FiNum ::zero ( ) { 1 } else { 0 } ;
2024-07-26 16:54:03 -04:00
let ff = self . forefather ( ) ;
2024-09-30 15:06:22 -04:00
loop {
2024-07-26 16:54:03 -04:00
self . tree [ index ] . weight + = weight ;
2024-09-30 15:06:22 -04:00
self . tree [ index ] . count + = delta_count ;
if index = = ff { break ; }
2024-07-26 16:54:03 -04:00
index = wt_parent ( index ) ;
2024-09-30 15:06:22 -04:00
}
2024-07-26 16:54:03 -04:00
}
fn forefather ( & self ) -> usize {
wt_forefather ( self . tree . len ( ) - 1 )
}
fn dump ( & mut self ) {
2024-06-03 19:36:51 -04:00
for index in 0 .. self . tree . len ( ) {
2024-07-26 16:54:03 -04:00
let roy = self . get_royalty ( index ) ;
2024-09-18 15:54:59 -04:00
println! ( " Index {} Count {} Weight {} Lazy {} Acc {} Royalty {} " , index , self . tree [ index ] . count , self . tree [ index ] . weight , self . tree [ index ] . lazy , self . tree [ index ] . acc , roy ) ;
2024-07-26 16:54:03 -04:00
}
}
2024-08-14 16:56:14 -04:00
fn raw_dump ( & mut self ) {
for index in 0 .. self . tree . len ( ) {
2024-08-14 18:53:55 -04:00
println! ( " Index {} : Weight {} Lazy {} Acc {} Order_ID {} " , index , self . tree [ index ] . weight , self . tree [ index ] . lazy . value ( ) , self . tree [ index ] . acc , self . tree [ index ] . order_id ) ;
2024-08-14 16:56:14 -04:00
}
}
2024-05-30 22:22:02 -04:00
}
2024-07-26 16:54:03 -04:00
2024-05-30 22:22:02 -04:00
2024-05-09 09:52:52 -10:00
trait Dumpable {
fn dump ( & self ) ;
}
2024-05-30 22:22:02 -04:00
impl Dumpable for usize {
2024-05-20 11:00:45 -04:00
fn dump ( & self ) {
println! ( " Dump Integer: {} " , self ) ;
}
}
2024-05-26 08:19:55 -04:00
impl Dumpable for Order {
2024-05-09 09:52:52 -10:00
fn dump ( & self ) {
2025-04-21 23:37:02 -04:00
println! ( " Giving {} / {} ( {} each) to get {} " , self . sell_remain , self . sell_qty , self . buy_qty / self . sell_qty , self . buy_qty ) ;
2024-05-09 09:52:52 -10:00
}
}
2024-05-26 08:19:55 -04:00
2024-05-09 09:52:52 -10:00
struct Market {
2024-05-30 22:22:02 -04:00
asset_name2num : HashMap < String , usize > ,
2024-07-26 16:54:03 -04:00
assets : Vec < Asset > ,
2024-05-30 22:22:02 -04:00
money_supply : HashMap < usize , FiNum > ,
2024-05-15 06:59:09 -04:00
traders : Vec < Trader > ,
2024-05-30 22:22:02 -04:00
trader_name2num : HashMap < String , usize > ,
2024-08-02 12:50:25 -04:00
orders : HashMap < ( usize , usize ) , OrderQueue > ,
2025-04-21 23:37:02 -04:00
shadows : HashSet < ( usize , usize ) > ,
2024-05-30 22:22:02 -04:00
royalties : HashMap < usize , RoyaltyTree > , // Active orders that are accepting asset X. They receive royalties when someone makes an order to sell X
2024-07-26 16:54:03 -04:00
royalty_rate : HashMap < usize , FiNum > ,
2024-10-13 16:55:06 -04:00
order_finder : HashMap < usize , ( usize , usize ) > , // Maps Order ID to an asset pair (in other systems, a "market.") From there you can look in self.orders.get((usize,usize)) which is an OrderQueue, and OrderQueues have a mapping from ID to position in the OrderQueue
order_count : usize ,
2024-07-29 19:30:36 -04:00
next_order_id : usize ,
2024-08-14 16:56:14 -04:00
rng : StdRng ,
2024-05-15 06:59:09 -04:00
}
2024-05-09 09:52:52 -10:00
impl Market {
fn new ( ) -> Self {
2024-05-30 22:22:02 -04:00
let mut rval = Market {
2024-05-09 09:52:52 -10:00
asset_name2num : HashMap ::new ( ) ,
2024-07-26 16:54:03 -04:00
assets : Vec ::new ( ) ,
2024-05-16 17:11:09 -04:00
money_supply : HashMap ::new ( ) ,
2024-05-18 23:59:58 -04:00
traders : Vec ::new ( ) ,
2024-05-16 15:50:37 -04:00
trader_name2num : HashMap ::new ( ) ,
2024-07-26 16:54:03 -04:00
orders : HashMap ::new ( ) ,
2025-04-21 23:37:02 -04:00
shadows : HashSet ::new ( ) ,
2024-07-26 16:54:03 -04:00
royalties : HashMap ::new ( ) ,
royalty_rate : HashMap ::new ( ) ,
2024-10-13 16:55:06 -04:00
order_finder : HashMap ::new ( ) ,
order_count : 0 ,
2024-07-29 19:30:36 -04:00
next_order_id : 1 ,
2024-08-14 16:56:14 -04:00
rng : StdRng ::seed_from_u64 ( 1 u64 ) ,
2024-05-30 22:22:02 -04:00
} ;
2024-07-26 16:54:03 -04:00
rval . register_trader ( " *HOUSE* " ) ;
rval
2024-05-26 20:37:08 -04:00
}
2024-05-16 17:11:09 -04:00
fn sanity_check ( & self ) {
println! ( " Sanity Checking Market... " ) ;
for ( cur , amt ) in self . money_supply . iter ( ) {
2024-05-18 14:28:12 -04:00
println! ( " Money Supply {} : {} " , self . number_to_name ( * cur ) , * amt ) ;
2025-01-13 13:34:26 -05:00
let mut off_orders = FiNum ::new ( 0 ) ;
let mut off_roy = FiNum ::new ( 0 ) ;
let mut off_com = FiNum ::new ( 0 ) ;
2024-05-26 08:19:55 -04:00
for ( ac , pq ) in & self . orders { if ac . 0 = = * cur {
2025-01-13 13:34:26 -05:00
for off in & * pq . v {
off_orders + = off . sell_remain ;
off_roy + = off . royalty_remain ;
off_com + = off . commission_remain ;
}
2024-05-16 17:11:09 -04:00
} }
2024-05-18 14:28:12 -04:00
let mut acc_traders = FiNum ::new ( 0 ) ;
2025-01-13 13:34:26 -05:00
let mut ntraders = 0 ;
for t in & self . traders {
let b = t . get_balance ( * cur ) ;
if ! b . is_zero ( ) { ntraders + = 1 }
acc_traders + = b ;
}
let ar = self . royalties . get ( cur ) . map ( | rt | rt . acc_total ( ) ) . unwrap_or ( FiNum ::zero ( ) ) ;
let sc = self . royalties . get ( cur ) . map ( | rt | rt . spare_change ) . unwrap_or ( FiNum ::zero ( ) ) ;
let acc = acc_traders + off_orders + off_roy + off_com + ar + sc ;
println! ( " Traders {} " , acc_traders ) ;
println! ( " Orders {} " , off_orders ) ;
println! ( " Royalties in Orders {} " , off_roy ) ;
println! ( " Commissions in Orders {} " , off_com ) ;
println! ( " Accumulated Royalties {} " , ar ) ;
println! ( " Spare Change Royalties {} " , sc ) ;
println! ( " Total from Above {} " , acc ) ;
println! ( " Total from Money Supply {} {} " , * amt , if * amt ! = acc { " Mismatch! " } else { " " } ) ;
2024-05-16 17:11:09 -04:00
}
}
2024-10-13 16:55:06 -04:00
fn random_order_id ( & mut self ) -> Option < usize > {
let x = self . rng . gen_range ( 0 .. self . order_count ) ;
let mut bottom = 0 ;
for ( pair , bucket ) in & self . orders {
if x < bottom + bucket . v . len ( ) { return Some ( bucket . v [ x - bottom ] . order_id ) }
bottom + = bucket . v . len ( ) ;
}
None
}
2024-05-30 22:22:02 -04:00
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 ( ) ) ;
2024-05-18 23:59:58 -04:00
self . traders . push ( Trader ::new ( name , rval ) ) ;
2024-05-16 17:11:09 -04:00
rval
2024-05-18 23:59:58 -04:00
}
2024-05-16 17:11:09 -04:00
// These are the only ways to get money into or out of the market.
2024-07-26 16:54:03 -04:00
fn get_trader_balance ( & self , who :usize , cur :usize ) -> FiNum {
self . traders [ who ] . get_balance ( cur )
}
2024-05-30 22:22:02 -04:00
fn add_trader_balance ( & mut self , who :usize , cur :usize , delta : FiNum ) {
2024-07-26 16:54:03 -04:00
self . traders [ who ] . add_balance ( cur , delta ) ;
2024-05-16 17:11:09 -04:00
* self . money_supply . get_mut ( & cur ) . unwrap ( ) + = delta ;
2024-05-16 15:50:37 -04:00
}
2024-05-30 22:22:02 -04:00
fn sub_trader_balance ( & mut self , who :usize , cur :usize , delta : FiNum ) {
2024-07-26 16:54:03 -04:00
self . traders [ who ] . sub_balance ( cur , delta ) ;
2024-05-16 17:11:09 -04:00
* self . money_supply . get_mut ( & cur ) . unwrap ( ) - = delta ;
2024-05-16 15:50:37 -04:00
}
2024-07-26 16:54:03 -04:00
fn register_asset ( & mut self , name :& str ) -> Option < usize > {
if self . asset_name2num . contains_key ( name ) { return None }
self . assets . push ( Asset ::new ( name ) ) ;
self . asset_name2num . insert ( String ::from ( name ) , self . assets . len ( ) - 1 ) ;
self . money_supply . insert ( self . assets . len ( ) - 1 , FiNum ::new ( 0 ) ) ;
let rval = self . assets . len ( ) - 1 ;
Some ( rval )
}
fn set_royalty ( & mut self , n : usize , roy0 : FiNum , com0 : FiNum , roy1 : FiNum , com1 : FiNum ) {
self . assets [ n ] . royalty0_rate = roy0 ;
self . assets [ n ] . commission0_rate = com0 ;
self . assets [ n ] . royalty1_rate = roy1 ;
self . assets [ n ] . commission1_rate = com1 ;
}
fn number_to_asset ( & self , n : usize ) -> Asset {
self . assets [ n ] . clone ( )
2024-05-09 09:52:52 -10:00
}
2024-07-26 16:54:03 -04:00
fn number_to_name ( & self , n : usize ) -> String {
self . assets [ n ] . name . clone ( )
}
fn name_to_number ( & self , name :& str ) -> Option < & usize > {
self . asset_name2num . get ( name )
2024-05-09 09:52:52 -10:00
}
2024-07-29 21:02:12 -04:00
fn dump ( & mut self ) {
2024-05-09 09:52:52 -10:00
println! ( " Dumping Market: " ) ;
2025-01-11 01:02:43 -05:00
println! ( " Asset MoneySupply Royalty% Commission% " ) ;
2024-08-02 12:50:25 -04:00
for index in 0 .. self . assets . len ( ) {
2025-01-11 01:02:43 -05:00
let ass = self . number_to_asset ( index ) ;
let aname = self . money_supply [ & index ] . to_string ( ) ;
println! ( " {:<3} {:>16} {} / {} {} / {} " , ass . name , aname , ass . royalty0_rate . fmt_pct2 ( ) , ass . royalty1_rate . fmt_pct2 ( ) , ass . commission0_rate . fmt_pct2 ( ) , ass . commission1_rate . fmt_pct2 ( ) ) ;
2024-08-02 12:50:25 -04:00
}
2024-05-26 08:19:55 -04:00
for ( ap , pq ) in & self . orders {
println! ( " Orders selling {} to buy {} : " , self . number_to_name ( ap . 0 ) , self . number_to_name ( ap . 1 ) ) ;
2024-05-19 09:47:40 -04:00
let mut sorted = pq . v . clone ( ) ;
2025-01-13 21:52:33 -05:00
sorted . sort_by ( | a , b | { ( b . sell_qty / b . buy_qty ) . cmp ( & ( a . sell_qty / a . buy_qty ) ) } ) ;
2024-05-19 09:47:40 -04:00
for off in sorted . iter ( ) {
2025-01-10 21:13:04 -05:00
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 ) ;
2025-01-13 21:52:33 -05:00
println! ( " {} @ {} ( {} ) OrderID: {} Accumulated: {} Royalties {} Commissions: {} " ,
2024-05-20 11:00:45 -04:00
off . sell_remain , // off.buy_qty*off.sell_remain/off.sell_qty,
2024-08-06 18:10:48 -04:00
( off . buy_qty / off . sell_qty ) . fmt_recip ( ) ,
2024-07-29 16:45:47 -04:00
self . traders [ off . owner as usize ] . name ,
2024-08-07 18:20:42 -04:00
off . order_id ,
2025-01-13 13:34:26 -05:00
royalty_amt , off . royalty_remain , off . commission_remain ) ;
2024-05-19 09:47:40 -04:00
}
2024-05-09 09:52:52 -10:00
}
2025-01-11 01:02:43 -05:00
println! ( " Royalty Accum SpareChange " ) ;
2024-07-29 16:45:47 -04:00
for index in 0 .. self . assets . len ( ) {
2025-01-10 21:13:04 -05:00
self . royalties . entry ( index ) . or_insert ( RoyaltyTree ::new ( ) ) ;
2025-01-11 01:02:43 -05:00
let name = self . number_to_asset ( index ) . name ;
2025-01-10 21:13:04 -05:00
let rt = self . royalties . get_mut ( & index ) . unwrap ( ) ;
2025-01-11 01:02:43 -05:00
let sc = rt . spare_change ;
let tot = rt . acc_total ( ) ;
println! ( " {:<3} {} {} " , name , tot , sc ) ;
2025-01-10 21:13:04 -05:00
for indexr in 0 .. rt . tree . len ( ) {
let royalty_amt = rt . get_royalty ( indexr ) ;
let r = & rt . tree [ indexr ] ;
let count = r . count ;
let weight = r . weight ;
let lazy = r . lazy ;
let acc = r . acc ;
if ! royalty_amt . is_zero ( ) | | count ! = 0 | | ! weight . is_zero ( ) | | ! lazy . is_zero ( ) | | ! acc . is_zero ( ) {
// Heavy Debug println!(" Index {} Count {} Weight {} Lazy {} Acc {} Royalty {} OrderID {}",indexr,r.count,r.weight,r.lazy,r.acc,royalty_amt,r.order_id);
println! ( " Index {} Weight {} Royalty {} OrderID {} " , indexr , r . weight , royalty_amt , r . order_id ) ;
}
2025-01-05 22:34:32 -05:00
}
2025-01-11 01:02:43 -05:00
// Heavy Debug for (k,v) in &rt.order_finder { println!(" Order ID {} at position {}",k,v); }
2025-01-05 22:34:32 -05:00
}
2024-08-02 12:50:25 -04:00
println! ( " Trader Balances: " ) ;
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 )
}
}
2024-05-09 09:52:52 -10:00
}
2024-07-26 16:54:03 -04:00
fn replay_file ( & mut self , fname :& str ) {
println! ( " Replaying {} " , fname ) ;
let f = File ::open ( Path ::new ( fname ) ) ;
let reader = io ::BufReader ::new ( f . unwrap ( ) ) ;
for line in reader . lines ( ) {
if let Ok ( line ) = line {
2025-01-13 21:52:33 -05:00
let line = clean_replay ( & line ) ;
2024-07-26 16:54:03 -04:00
let cmd = Command ::deserialize ( line ) ;
2024-07-29 16:45:47 -04:00
if let Command ::NOP ( comment ) = cmd { println! ( " {} " , comment ) ; }
else {
2025-01-13 13:34:26 -05:00
println! ( " {} " , cmd . explain ( self ) ) ;
2024-07-29 16:45:47 -04:00
let res = self . execute ( & cmd ) ;
println! ( " {} " , res . describe ( ) ) ;
}
2024-07-26 16:54:03 -04:00
}
}
2024-07-30 19:11:14 -04:00
}
2025-01-05 22:34:32 -05:00
/* fn execute_batch(&mut self, asset_type0: usize, strike0: FiNum, asset_type1: usize, strike1: FiNum) -> Result {
2024-07-31 19:45:35 -04:00
let mut log :Vec < String > = Vec ::new ( ) ;
let asset0 = self . number_to_asset ( asset_type0 ) ;
let asset1 = self . number_to_asset ( asset_type1 ) ;
2024-08-02 08:29:24 -04:00
log . push ( format! ( " Executing batch {} {} <-> {} {} " , strike0 , asset0 . name , strike1 , asset1 . name ) ) ;
2024-08-06 18:10:48 -04:00
println! ( " Executing batch {} {} <-> {} {} " , strike0 , asset0 . name , strike1 , asset1 . name ) ;
2024-07-31 19:45:35 -04:00
let ap0 = ( asset_type0 , asset_type1 ) ;
let ap1 = ( asset_type1 , asset_type0 ) ;
2025-01-05 22:34:32 -05:00
let [ bids0 , bids1 ] = self . orders . get_many_mut ( [ & ap0 , & ap1 ] ) . unwrap ( ) ; // There's a workaround for this where you remove and reinsert the keys.
2025-04-21 23:37:02 -04:00
let bids0p = bids0 . peek ( ) ;
let bids1p = bids1 . peek ( ) ;
2024-08-02 08:29:24 -04:00
while ! bids0 . empty ( ) & & ! bids1 . empty ( )
2024-08-02 12:50:25 -04:00
& & bids0 . peek ( ) . sell_qty / bids0 . peek ( ) . buy_qty > = strike0 / strike1
& & bids1 . peek ( ) . sell_qty / bids1 . peek ( ) . buy_qty > = strike1 / strike0 {
2024-08-06 18:10:48 -04:00
println! ( " For Asset0 ( {} ) paying either {} or {} " , asset0 . name , bids0 . peek ( ) . sell_remain , strike0 * bids1 . peek ( ) . sell_remain / strike1 ) ;
println! ( " For Asset1 ( {} ) paying either {} or {} " , asset1 . name , bids1 . peek ( ) . sell_remain , strike1 * bids0 . peek ( ) . sell_remain / strike0 ) ;
let asset0_paying = std ::cmp ::min ( bids0 . peek ( ) . sell_remain , strike0 * bids1 . peek ( ) . sell_remain / strike1 ) ;
let asset1_paying = std ::cmp ::min ( bids1 . peek ( ) . sell_remain , strike1 * bids0 . peek ( ) . sell_remain / strike0 ) ;
2024-08-02 12:50:25 -04:00
bids0 . peek ( ) . sell_remain - = asset0_paying ;
bids1 . peek ( ) . sell_remain - = asset1_paying ;
self . traders [ bids0 . peek ( ) . owner ] . add_balance ( asset_type1 , asset1_paying ) ;
self . traders [ bids1 . peek ( ) . owner ] . add_balance ( asset_type0 , asset0_paying ) ;
log . push ( format! ( " {} got {} {} , {} got {} {} " , self . traders [ bids0 . peek ( ) . owner ] . name , asset1_paying , asset1 . name ,
self . traders [ bids1 . peek ( ) . owner ] . name , asset0_paying , asset0 . name ) ) ;
2024-10-13 16:55:06 -04:00
if bids0 . peek ( ) . sell_remain = = FiNum ::zero ( ) { println! ( " Nope0!!! " ) ; bids0 . pop ( ) ; self . order_count - = 1 ; } // Stopped work here. Must fixup all the data structures for these pops.
if bids1 . peek ( ) . sell_remain = = FiNum ::zero ( ) { println! ( " Nope1!!! " ) ; bids1 . pop ( ) ; self . order_count - = 1 ; }
2024-08-02 08:29:24 -04:00
}
Result ::ExecutedBatch ( log )
2024-07-30 19:11:14 -04:00
}
2025-01-05 22:34:32 -05:00
* /
2025-01-13 13:34:26 -05:00
fn retract_order ( & mut self , order_id :usize ) -> Result { // Still need to credit back funds!
2024-10-13 16:55:06 -04:00
let pair = self . order_finder . get ( & order_id ) . unwrap ( ) ;
2025-01-13 13:34:26 -05:00
let sell_type = pair . 0 ;
2025-01-20 16:16:43 -05:00
let buy_type = pair . 1 ;
2024-10-13 16:55:06 -04:00
let queue = self . orders . get_mut ( pair ) . unwrap ( ) ;
let queue_index = * queue . order_finder . get ( & order_id ) . unwrap ( ) ;
let order = & queue . v [ queue_index ] ;
2025-01-13 13:34:26 -05:00
let credit = order . sell_remain + order . royalty_remain + order . commission_remain ;
self . traders [ order . owner ] . add_balance ( sell_type , credit ) ;
2024-10-13 16:55:06 -04:00
println! ( " Order owner is {} " , order . owner ) ;
// Remove from royalties
let rt = self . royalties . get_mut ( & pair . 0 ) . unwrap ( ) ;
2025-01-20 16:16:43 -05:00
let dist = rt . remove_order_id ( order_id ) ;
self . traders [ order . owner ] . add_balance ( buy_type , dist ) ;
2024-10-13 16:55:06 -04:00
// Remove from order_finder
self . order_finder . remove ( & order_id ) ;
// Remove from orders
queue . remove ( queue_index ) ;
self . order_count - = 1 ;
Result ::RetractedOrder ( order_id )
}
2025-04-29 22:39:44 -04:00
fn make_smart_order ( & mut self , owner :usize , sell_type :usize , buy_type :usize , sell_qty_initial :FiNum , buy_qty_initial :FiNum , max0 : FiNum , max1 : FiNum , execute_if_possible :bool ) -> Result {
Result ::Error ( format! ( " make_smart_order not Implemented. " ) )
}
2024-07-30 15:27:25 -04:00
fn make_order ( & mut self , owner :usize , sell_type :usize , buy_type :usize , sell_qty_initial :FiNum , buy_qty_initial :FiNum , execute_if_possible :bool ) -> Result {
2024-07-26 16:54:03 -04:00
let mut log :Vec < String > = Vec ::new ( ) ;
let initial_balance = self . traders [ owner ] . get_balance ( sell_type ) ;
let asset = self . number_to_asset ( sell_type ) ;
let mut royalty0_qty = asset . royalty0_rate * sell_qty_initial ;
let mut royalty1_qty = asset . royalty1_rate * sell_qty_initial ;
let mut commission0_qty = asset . commission0_rate * sell_qty_initial ;
let mut commission1_qty = asset . commission1_rate * sell_qty_initial ;
let sell_qty_plus = sell_qty_initial + royalty0_qty + royalty1_qty + commission0_qty + commission1_qty ; // This is the maximum amount we are going to sell
let sell_qty = sell_qty_initial ;
if initial_balance < sell_qty_plus { return Result ::Error ( format! ( " Funds not available " ) ) }
2024-05-18 23:59:58 -04:00
let mut buy_qty = buy_qty_initial ;
2024-05-09 09:52:52 -10:00
let ap = ( buy_type , sell_type ) ;
2024-07-26 16:54:03 -04:00
let mut royalty_acc = FiNum ::zero ( ) ;
let mut commission_acc = FiNum ::zero ( ) ;
2024-07-29 19:30:36 -04:00
let new_order_id :usize ;
2024-07-30 15:27:25 -04:00
while execute_if_possible & & buy_qty > FiNum ::new ( 0 ) & & self . orders . contains_key ( & ap ) & & self . orders . get ( & ap ) . unwrap ( ) . v . len ( ) > 0 {
2025-01-13 13:34:26 -05:00
let elt = self . orders . get ( & ap ) . unwrap ( ) . v [ 0 ] . clone ( ) ;
2024-07-26 16:54:03 -04:00
if sell_qty / buy_qty_initial > = elt . buy_qty / elt . sell_qty { // Transact at ask_rate
2024-05-18 23:59:58 -04:00
let qty = std ::cmp ::min ( elt . sell_remain , buy_qty ) ;
buy_qty - = qty ;
2024-07-26 16:54:03 -04:00
let pay_qty = qty * elt . buy_qty / elt . sell_qty ;
self . traders [ owner ] . sub_balance ( sell_type , pay_qty ) ;
self . traders [ owner ] . add_balance ( buy_type , qty ) ;
self . traders [ elt . owner ] . add_balance ( sell_type , pay_qty ) ;
log . push ( format! ( " Sold {} {} ( {} ) to buy {} {} ( {} ) " , pay_qty , self . number_to_name ( sell_type ) , self . traders [ owner ] . name ,
qty , self . number_to_name ( buy_type ) , self . traders [ elt . owner ] . name ) ) ;
let rq0 = royalty0_qty * pay_qty / sell_qty_initial ;
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 ;
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 ( ) } ;
2025-01-24 20:20:37 -05:00
self . royalties . entry ( sell_type ) . or_insert ( RoyaltyTree ::new ( ) ) ;
self . royalties . entry ( buy_type ) . or_insert ( RoyaltyTree ::new ( ) ) ;
let rtb = self . royalties . entry ( buy_type ) . or_insert ( RoyaltyTree ::new ( ) ) ;
let rts = self . royalties . entry ( sell_type ) . or_insert ( RoyaltyTree ::new ( ) ) ;
if let Some ( & cap_index ) = rts . order_finder . get ( & elt . order_id ) {
let wh0 = rts . weight_here ( cap_index ) ;
let acc0 = rts . tree [ cap_index ] . acc ;
let cap = rts . capture_by_order_id ( elt . owner ) ;
rts . sub_weight ( cap_index , pay_qty ) ;
let wh1 = rts . weight_here ( cap_index ) ;
let acc1 = rts . tree [ cap_index ] . acc ;
self . traders [ elt . owner ] . add_balance ( sell_type , cap ) ;
}
2024-07-26 16:54:03 -04:00
royalty_acc + = rq0 + rq1 ;
commission_acc + = cq0 + cq1 ;
royalty0_qty - = rq0 ;
royalty1_qty - = rq1 ;
commission0_qty - = cq0 ;
commission1_qty - = cq1 ;
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 ) ;
2024-08-02 12:50:25 -04:00
let top_order = self . orders . get_mut ( & ap ) . unwrap ( ) . peek ( ) ;
top_order . royalty_remain - = rq2 ;
top_order . commission_remain - = cq2 ;
2025-01-13 13:34:26 -05:00
self . traders [ 0 ] . add_balance ( sell_type , cq0 + cq1 ) ;
2024-07-26 16:54:03 -04:00
self . traders [ 0 ] . add_balance ( buy_type , cq2 ) ;
2025-01-13 13:34:26 -05:00
if elt . sell_remain = = qty { // deal with pennies stored in royalty_remain and commission_remain
let rq3 = top_order . royalty_remain ;
let cq3 = top_order . commission_remain ;
2025-01-24 20:20:37 -05:00
let dist = self . royalties . get_mut ( & buy_type ) . unwrap ( ) . remove_order_id ( top_order . order_id ) ;
2025-01-20 16:16:43 -05:00
self . traders [ elt . owner ] . add_balance ( sell_type , dist ) ;
2024-08-07 18:20:42 -04:00
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
2024-10-13 16:55:06 -04:00
self . order_count - = 1 ;
2025-01-13 13:34:26 -05:00
self . traders [ 0 ] . add_balance ( buy_type , cq3 ) ;
self . royalties . entry ( buy_type ) . or_insert ( RoyaltyTree ::new ( ) ) . add_royalty ( rq3 ) ;
2024-07-26 16:54:03 -04:00
} else {
2024-08-02 12:50:25 -04:00
top_order . sell_remain - = qty ;
2024-07-26 16:54:03 -04:00
}
2024-05-19 11:24:59 -04:00
} else { break ; }
2024-05-18 23:59:58 -04:00
}
2024-08-07 18:20:42 -04:00
let mut id = 0 ;
2024-05-18 23:59:58 -04:00
if buy_qty > 0. into ( ) {
2024-05-09 09:52:52 -10:00
let ap = ( sell_type , buy_type ) ;
2024-08-02 12:50:25 -04:00
if let None = self . orders . get_mut ( & ap ) { self . orders . insert ( ap , OrderQueue ::new ( ) ) ; }
2024-05-26 08:19:55 -04:00
let bids = self . orders . get_mut ( & ap ) . unwrap ( ) ;
2024-07-26 16:54:03 -04:00
let sell_qty_remain = sell_qty * buy_qty / buy_qty_initial ;
2024-08-07 18:20:42 -04:00
if sell_qty_remain > 0. into ( ) { // This block can not fail
// Allocate the id for this new order
id = self . next_order_id ;
self . next_order_id + = 1 ;
// Pay royalties to existing orders on the sell size
2025-01-13 13:34:26 -05:00
self . royalties . entry ( sell_type ) . or_insert ( RoyaltyTree ::new ( ) ) . add_royalty ( royalty0_qty ) ;
royalty_acc + = royalty0_qty ;
2024-08-07 18:20:42 -04:00
// Create a new entry in the RoyaltyTree to accumulate for this Order
2024-07-26 16:54:03 -04:00
let rt = self . royalties . entry ( buy_type ) . or_insert ( RoyaltyTree ::new ( ) ) ;
2024-08-07 18:20:42 -04:00
rt . add_weight ( rt . next_entry , buy_qty ) ; // Weight should really be the quantity you would be able to immediately transact rather than how much you wish for.
rt . set_location ( id , rt . next_entry ) ;
2024-07-26 16:54:03 -04:00
rt . next_entry + = 1 ;
2024-08-07 18:20:42 -04:00
// 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 ) ;
2025-01-13 13:34:26 -05:00
royalty_acc + = royalty1_qty ;
commission_acc + = commission1_qty ;
2024-10-13 16:55:06 -04:00
self . order_count + = 1 ;
2024-08-07 18:20:42 -04:00
self . order_finder . insert ( id , ( sell_type , buy_type ) ) ;
2024-07-26 16:54:03 -04:00
self . traders [ owner ] . sub_balance ( sell_type , sell_qty_remain ) ;
2025-01-13 13:34:26 -05:00
self . traders [ owner ] . sub_balance ( sell_type , commission0_qty ) ;
self . traders [ 0 ] . add_balance ( sell_type , commission0_qty ) ;
2024-08-07 18:20:42 -04:00
self . traders [ owner ] . order_finder . insert ( id , ( sell_type , buy_type ) ) ;
2024-07-26 16:54:03 -04:00
log . push ( format! ( " Moved {} {} ( {} ) to market " , sell_qty_remain , self . number_to_name ( sell_type ) , self . traders [ owner ] . name ) ) ;
2024-08-07 18:20:42 -04:00
}
}
2025-01-13 13:34:26 -05:00
// self.traders[0].add_balance(sell_type,commission_acc);
2024-07-26 16:54:03 -04:00
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 ) ) ;
2024-08-07 18:20:42 -04:00
Result ::PlacedOrder ( id , log )
2024-05-13 12:03:03 -04:00
}
2025-02-04 10:59:39 -05:00
fn path_cost ( & self , src : usize , dest : usize , depth_left : usize ) -> TradePath {
let mut cheapest = vec! [ TradePath ::new ( ) ; self . assets . len ( ) ] ;
cheapest [ src ] . cost = FiNum ::new_i32 ( 1 ) ;
cheapest [ src ] . path [ 0 ] = src ;
cheapest [ src ] . len = 1 ;
for i in 0 .. 6 {
let mut progress = false ;
for ass0 in 0 .. self . assets . len ( ) {
for ass1 in 0 .. self . assets . len ( ) {
if true | | ass0 ! = ass1 {
if let Some ( topq ) = self . orders . get ( & ( ass1 , ass0 ) ) {
2025-04-29 22:39:44 -04:00
let top = topq . peek_nomut ( ) ; if true {
2025-02-04 10:59:39 -05:00
println! ( " There is a direct path from {} to {} with a cost of {} {} " ,
self . number_to_name ( ass0 ) , self . number_to_name ( ass1 ) , top . buy_qty / top . sell_qty , self . number_to_name ( ass1 ) ) ;
let candidate = cheapest [ ass0 ] . cost * top . buy_qty / top . sell_qty ;
if candidate < cheapest [ ass1 ] . cost {
// Extend cheapest[ass1]
println! ( " Extending Length {} {} because {} < {} " , cheapest [ ass0 ] . len , self . number_to_name ( ass1 ) , candidate , cheapest [ ass1 ] . cost ) ;
cheapest [ ass1 ] = cheapest [ ass0 ] ;
let ext = & mut cheapest [ ass1 ] ;
let arbitrage = ext . path . contains ( & ass1 ) ;
ext . path [ ext . len ] = ass1 ;
ext . len + = 1 ;
ext . cost = candidate ;
ext . cap = min ( ext . cap , ext . cost * top . sell_remain ) ;
progress = true ;
if arbitrage {
2025-04-21 23:37:02 -04:00
ext . arbitrage = true ;
2025-02-04 10:59:39 -05:00
return ext . clone ( ) ;
}
}
}
}
}
}
}
if ! progress { println! ( " Definitely found the cheapest path. " ) ; break ; }
}
for ass in 0 .. self . assets . len ( ) { println! ( " Cheapest path from {} to {} is {} " , self . number_to_name ( src ) , self . number_to_name ( ass ) , cheapest [ ass ] . cost ) ; }
cheapest [ dest ] . clone ( )
2024-05-13 12:03:03 -04:00
}
2025-02-04 10:59:39 -05:00
}
#[ derive(Copy,Clone) ]
struct TradePath {
cost : FiNum ,
path : [ usize ; 600 ] ,
len : usize ,
cap : FiNum ,
arbitrage : bool ,
}
impl TradePath {
fn new ( ) -> Self { TradePath {
cost : FiNum ::infinity ( ) ,
path : [ 0 ; 600 ] ,
len : 0 ,
cap : FiNum ::infinity ( ) ,
arbitrage : false ,
} }
}
2024-05-09 09:52:52 -10:00
2024-05-26 08:19:55 -04:00
impl PartialOrd for Order {
2024-05-09 09:52:52 -10:00
fn partial_cmp ( & self , other :& Self ) -> Option < Ordering > {
2024-05-09 12:08:26 -10:00
let ord0 = self . sell_qty / self . buy_qty ;
2024-05-20 11:00:45 -04:00
let ord1 = other . sell_qty / other . buy_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 ) }
}
}
2024-05-26 08:19:55 -04:00
impl PartialEq for Order {
2024-05-09 09:52:52 -10:00
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
}
}
2025-04-09 14:54:13 -04:00
//
// If Some(shadow) then peek should return a reference to shadow, and pop should replace shadow with v[next[0]]
//
2024-08-02 12:50:25 -04:00
struct OrderQueue {
v : Vec < Order > ,
2025-04-09 14:54:13 -04:00
shadowing : bool ,
shadow : Option < Order > ,
2025-04-21 23:37:02 -04:00
next : Vec < usize > , // A priority queue
2024-10-13 16:55:06 -04:00
order_finder : HashMap < usize , usize > , // Maps OrderIDs to locations in v
2024-05-09 09:52:52 -10:00
}
2025-04-09 14:54:13 -04:00
2024-08-02 12:50:25 -04:00
impl OrderQueue {
2024-05-09 09:52:52 -10:00
fn new ( ) ->Self {
2024-08-02 12:50:25 -04:00
OrderQueue {
2024-08-07 18:20:42 -04:00
v : Vec ::new ( ) ,
2025-04-09 14:54:13 -04:00
shadowing : false ,
shadow : None ,
next : Vec ::new ( ) ,
2024-08-07 18:20:42 -04:00
order_finder :HashMap ::new ( ) ,
2024-05-09 09:52:52 -10:00
}
}
2025-04-21 23:37:02 -04:00
fn assure_shadowing ( & mut self ) {
if ! self . shadowing {
assert! ( self . next . len ( ) = = 0 ) ;
self . shadowing = true ;
if self . v . len ( ) > 0 { self . shadow = Some ( self . v [ 0 ] . clone ( ) ) ; }
else { self . shadow = None ; }
self . queue_shadow ( 0 ) ;
}
}
fn stop_shadowing ( & mut self ) {
if self . shadowing {
self . next . clear ( ) ;
self . shadowing = false ;
self . shadow = None ;
self . next = Vec ::new ( ) ;
}
}
fn queue_shadow ( & mut self , parent : usize ) {
if parent * 2 + 1 < self . v . len ( ) { self . next . push ( parent * 2 + 1 ) ; self . bubble_up_next ( self . next . len ( ) - 1 ) ; }
if parent * 2 + 2 < self . v . len ( ) { self . next . push ( parent * 2 + 2 ) ; self . bubble_up_next ( self . next . len ( ) - 1 ) ; }
2025-04-09 14:54:13 -04:00
}
2024-08-02 12:50:25 -04:00
fn peek ( & mut self ) -> & mut Order {
2025-04-21 23:37:02 -04:00
if ! self . shadowing { self . v . first_mut ( ) . unwrap ( ) }
else { self . shadow . as_mut ( ) . expect ( " Shadowing mismatch " ) }
2024-05-09 09:52:52 -10:00
}
2025-04-29 22:39:44 -04:00
fn peek_nomut ( & self ) -> & Order {
if ! self . shadowing { self . v . first ( ) . unwrap ( ) }
else { self . shadow . as_ref ( ) . expect ( " Shadowing mismatch " ) }
}
2025-02-04 10:59:39 -05:00
fn peekn ( & self ) -> Option < & Order > {
2025-04-21 23:37:02 -04:00
match & self . shadow {
Some ( order ) = > Some ( order ) ,
None = > { self . v . first ( ) }
}
2025-02-04 10:59:39 -05:00
}
2024-07-31 19:45:35 -04:00
fn empty ( & self ) -> bool {
2024-08-02 08:29:24 -04:00
self . v . len ( ) = = 0
2024-07-31 19:45:35 -04:00
}
2025-04-21 23:37:02 -04:00
fn pop ( & mut self ) -> Option < Order > {
if ! self . shadowing { return self . remove ( 0 ) ; }
let rval = self . shadow . clone ( ) ;
if self . next . len ( ) = = 0 { self . shadow = None ; }
else {
self . shadow = Some ( self . v [ self . next [ 0 ] ] . clone ( ) ) ;
self . queue_shadow ( self . next [ 0 ] ) ;
if self . next . len ( ) > 1 { self . next [ 0 ] = self . next . pop ( ) . unwrap ( ) ; }
else { self . next . pop ( ) ; }
self . trickle_down_next ( 0 ) ;
}
rval
}
fn bubble_up_next ( & mut self , pos : usize ) {
if pos > 0 {
let parent = ( pos - 1 ) / 2 ;
if self . v [ self . next [ parent ] ] < self . v [ self . next [ pos ] ] {
self . next . swap ( parent , pos ) ;
self . bubble_up_next ( parent ) ;
}
}
}
2024-08-07 18:20:42 -04:00
fn bubble_up ( & mut self , pos : usize ) {
2024-05-09 09:52:52 -10:00
if pos > 0 {
let parent = ( pos - 1 ) / 2 ;
2024-05-20 11:00:45 -04:00
if self . v [ parent ] < self . v [ pos ] {
2024-08-07 18:20:42 -04:00
self . swap ( parent , pos ) ;
self . bubble_up ( parent ) ;
2024-05-09 09:52:52 -10:00
}
}
2024-08-07 18:20:42 -04:00
}
fn swap ( & mut self , pos0 : usize , pos1 : usize ) {
self . order_finder . insert ( self . v [ pos0 ] . order_id , pos1 ) ;
self . order_finder . insert ( self . v [ pos1 ] . order_id , pos0 ) ;
self . v . swap ( pos0 , pos1 ) ;
2024-05-09 09:52:52 -10:00
}
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 ( ) {
2024-05-20 11:00:45 -04:00
if self . v [ pos ] < self . v [ child0 ] { pivot = child0 ; }
2024-05-09 09:52:52 -10:00
if child1 < self . v . len ( ) {
2024-05-20 11:00:45 -04:00
if self . v [ pivot ] < self . v [ child1 ] { pivot = child1 ; }
2024-05-09 09:52:52 -10:00
}
if pivot ! = pos {
2024-08-07 18:20:42 -04:00
self . swap ( pivot , pos ) ;
2024-05-09 09:52:52 -10:00
self . trickle_down ( pivot ) ;
}
}
}
2025-04-21 23:37:02 -04:00
fn trickle_down_next ( & mut self , pos : usize ) {
let mut pivot = pos ;
let child0 = pos * 2 + 1 ;
let child1 = pos * 2 + 2 ;
if child0 < self . next . len ( ) {
if self . v [ self . next [ pos ] ] < self . v [ self . next [ child0 ] ] { pivot = child0 ; }
if child1 < self . next . len ( ) {
if self . v [ self . next [ pivot ] ] < self . v [ self . next [ child1 ] ] { pivot = child1 ; }
}
if pivot ! = pos {
self . next . swap ( pivot , pos ) ;
self . trickle_down_next ( pivot ) ;
}
}
}
2024-08-07 18:20:42 -04:00
fn insert ( & mut self , item : Order ) {
2025-04-21 23:37:02 -04:00
assert! ( ! self . shadowing ) ;
2024-08-07 18:20:42 -04:00
let id = item . order_id ;
2024-05-09 09:52:52 -10:00
self . v . push ( item ) ;
2024-08-07 18:20:42 -04:00
self . order_finder . insert ( id , self . v . len ( ) - 1 ) ;
self . bubble_up ( self . v . len ( ) - 1 ) ;
2024-05-09 09:52:52 -10:00
}
2024-10-13 16:55:06 -04:00
fn remove ( & mut self , pos : usize ) -> Option < Order > {
2025-04-21 23:37:02 -04:00
assert! ( ! self . shadowing ) ;
2024-10-13 16:55:06 -04:00
if self . v . len ( ) < = pos { None }
2024-05-09 09:52:52 -10:00
else {
let end = self . v . len ( ) - 1 ;
2024-10-13 16:55:06 -04:00
self . v . swap ( pos , end ) ;
2024-08-07 18:20:42 -04:00
self . order_finder . remove ( & self . v [ end ] . order_id ) ;
2024-05-09 09:52:52 -10:00
let rval = self . v . pop ( ) ;
2024-10-13 16:55:06 -04:00
self . trickle_down ( pos ) ;
2024-05-09 09:52:52 -10:00
rval
}
}
fn dump ( & self ) {
for index in 0 .. self . v . len ( ) {
self . v [ index ] . dump ( ) ;
}
2025-04-21 23:37:02 -04:00
if self . shadowing { for index in 0 .. self . next . len ( ) { println! ( " Shadow {} " , self . next [ index ] ) ; } }
2024-05-09 09:52:52 -10:00
}
}
2024-05-26 08:19:55 -04:00
impl Market {
2024-08-14 16:56:14 -04:00
fn seed_random ( & mut self , seed : u64 ) {
self . rng = StdRng ::seed_from_u64 ( seed ) ;
}
fn random_command ( & mut self ) -> Command {
2025-01-05 22:34:32 -05:00
match self . rng . gen_range ( 1 .. 4 ) {
2024-08-14 16:56:14 -04:00
1 = > Command ::AddFunds { user_id : self . rng . gen_range ( 1 .. self . traders . len ( ) ) ,
asset_id : self . rng . gen_range ( 0 .. self . assets . len ( ) ) ,
amt : FiNum ::new ( self . rng . gen_range ( 1 < < 32 .. 20 < < 32 + 1 ) ) , } ,
2024-10-13 16:55:06 -04:00
2 = > Command ::AddFunds { user_id : self . rng . gen_range ( 1 .. self . traders . len ( ) ) ,
asset_id : self . rng . gen_range ( 0 .. self . assets . len ( ) ) ,
amt : FiNum ::new ( self . rng . gen_range ( 1 < < 32 .. 20 < < 32 + 1 ) ) , } ,
2025-01-10 21:13:04 -05:00
3 = > {
let a0 = self . rng . gen_range ( 0 .. self . assets . len ( ) ) ;
let a1 = ( a0 + self . rng . gen_range ( 1 .. self . assets . len ( ) ) ) % self . assets . len ( ) ;
Command ::Order { user_id : self . rng . gen_range ( 1 .. self . traders . len ( ) ) ,
sell_type : a0 ,
sell_qty : FiNum ::new ( self . rng . gen_range ( 1 < < 32 .. 20 < < 32 + 1 ) ) ,
buy_type : a1 ,
buy_qty : FiNum ::new ( self . rng . gen_range ( 1 < < 32 .. 20 < < 32 + 1 ) ) , }
} ,
2025-01-05 22:34:32 -05:00
4 = > if let Some ( id ) = self . random_order_id ( ) { Command ::RetractOrder { order_id :id } } else { Command ::None } ,
2024-08-14 16:56:14 -04:00
_ = > Command ::Error ( " This can never happen " . to_string ( ) ) ,
2024-08-10 16:52:07 -04:00
}
}
2024-05-26 08:19:55 -04:00
fn exercise ( & mut self ) {
let mut rng : StdRng = StdRng ::seed_from_u64 ( 13 u64 ) ;
2024-05-30 22:22:02 -04:00
let teppy = self . register_trader ( " Teppy " ) ;
let luni = self . register_trader ( " Luni " ) ;
2024-07-26 16:54:03 -04:00
let usd = self . register_asset ( " USD " ) . unwrap ( ) ;
let btc = self . register_asset ( " BTC " ) . unwrap ( ) ;
2024-05-30 22:22:02 -04:00
self . add_trader_balance ( teppy , btc , 100000. into ( ) ) ;
self . add_trader_balance ( luni , usd , 650000000. into ( ) ) ;
let mut count = 0 ;
let mut tries = 0 ;
2024-06-03 19:36:51 -04:00
for _i in 1 ..= 1000000 {
2024-05-26 08:19:55 -04:00
let seller = if rng . gen_bool ( 0.5 ) { teppy } else { luni } ;
2024-05-30 22:22:02 -04:00
let ( buy_type , sell_type , buy_qty , sell_qty ) :( usize , usize , FiNum , FiNum ) ;
2024-05-26 08:19:55 -04:00
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 ( ) ;
}
2024-05-26 20:37:08 -04:00
let mut _success = false ;
2024-07-30 15:27:25 -04:00
if let Result ::Ok = self . make_order ( seller , sell_type , buy_type , sell_qty , buy_qty , true ) { count + = 1 ; _success = true ; } ;
2024-05-30 22:22:02 -04:00
if count > = 1000000 { break ; }
2024-05-26 08:19:55 -04:00
tries + = 1 ;
}
println! ( " Tries: {} Trades: {} " , tries , count ) ;
self . sanity_check ( ) ;
}
}
2024-05-30 22:22:02 -04:00
fn wt_level ( index :usize ) -> usize {
( index ^ ( index + 1 ) ) . trailing_ones ( ) as usize - 1
2024-05-26 08:19:55 -04:00
}
2024-06-03 19:36:51 -04:00
fn wt_leaf ( index :usize ) -> bool {
index & 1 = = 0
}
2024-05-30 22:22:02 -04:00
fn wt_left ( index :usize ) -> Option < usize > {
2024-05-26 08:19:55 -04:00
let level = wt_level ( index ) ;
if level > 0 { Some ( index - ( 1 < < ( wt_level ( index ) - 1 ) ) ) } else { None }
}
2024-07-26 16:54:03 -04:00
fn wt_right_edge ( index : usize , edge : usize ) -> Option < usize > { // Less than edge
if index & 1 = = 0 { None }
else {
let mut r = wt_right ( index ) . unwrap ( ) ;
if r < edge { Some ( r ) }
else {
while r & 1 = = 1 {
if r < edge { return Some ( r ) ; }
else { r = wt_left ( r ) . unwrap ( ) ; }
}
if r < edge { Some ( r ) } else { None }
}
}
}
2024-05-30 22:22:02 -04:00
fn wt_right ( index :usize ) -> Option < usize > {
2024-05-26 08:19:55 -04:00
let level = wt_level ( index ) ;
if level > 0 { Some ( index + ( 1 < < ( wt_level ( index ) - 1 ) ) ) } else { None }
}
2024-05-30 22:22:02 -04:00
fn wt_parent ( index :usize ) -> usize {
2024-05-26 08:19:55 -04:00
let lev = wt_level ( index ) ;
let first_in_row = index % ( 1 < < lev ) ;
let skip = 2 < < lev ;
let nth_in_row = ( index - first_in_row ) / skip ;
let first_in_parent_row = ( 2 < < lev ) - 1 ;
let skip_in_parent_row = 4 < < lev ;
let nth_in_parent_row = nth_in_row > > 1 ;
first_in_parent_row + nth_in_parent_row * skip_in_parent_row
}
2024-05-13 12:03:03 -04:00
2024-05-30 22:22:02 -04:00
fn wt_forefather ( max_index :usize ) -> usize {
2024-05-26 20:37:08 -04:00
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 }
}
2024-05-09 09:52:52 -10:00
2024-06-03 19:36:51 -04:00
fn royalty_stuff ( ) {
2024-08-14 18:53:55 -04:00
let mut rng : StdRng = StdRng ::seed_from_u64 ( 13 u64 ) ;
2024-06-03 19:36:51 -04:00
let mut rt = RoyaltyTree ::new ( ) ;
2024-09-18 15:54:59 -04:00
rt . add_weight ( 4 , FiNum ::new_i32 ( 1 ) ) ;
2024-09-18 20:11:38 -04:00
rt . dump ( ) ;
2024-09-18 15:54:59 -04:00
rt . add_weight ( 5 , FiNum ::new_i32 ( 1 ) ) ;
rt . dump ( ) ;
2024-09-30 15:06:22 -04:00
// return;
2024-10-06 22:02:25 -04:00
for i in 1 .. 1000000 {
2024-09-30 15:06:22 -04:00
// rt.dump();
let node = rng . gen_range ( 0 .. 10 ) ;
2024-08-14 18:53:55 -04:00
match rng . gen_range ( 0 .. 10 ) {
2024-10-06 22:02:25 -04:00
0 = > rt . add_weight ( node , FiNum ::new_i32 ( rng . gen_range ( 0 .. 101 ) ) ) ,
2025-01-20 16:16:43 -05:00
1 = > { rt . sub_weight ( node , FiNum ::new_i32 ( rng . gen_range ( 0 .. 101 ) ) ) ; } ,
2024-09-30 15:06:22 -04:00
2 = > rt . add_royalty ( FiNum ::new_i32 ( rng . gen_range ( 0 .. 10 ) ) ) ,
2024-10-06 22:02:25 -04:00
// 3 => println!("Random Order ID: {:?}",rt.random_order_id(&mut rng)),
2024-08-14 18:53:55 -04:00
_ = > ( ) ,
}
2024-06-03 19:36:51 -04:00
}
2024-09-30 15:06:22 -04:00
rt . dump ( ) ;
2024-06-03 19:36:51 -04:00
}
2024-07-26 16:54:03 -04:00
enum Result {
2024-08-02 08:29:24 -04:00
AddedTrader ( usize , String ) ,
AddedAsset ( usize , String ) ,
2024-07-26 16:54:03 -04:00
PlacedOrder ( usize , Vec < String > ) ,
2024-10-10 21:53:48 -04:00
RetractedOrder ( usize ) ,
2024-07-26 16:54:03 -04:00
FundsRemaining ( FiNum ) ,
2024-08-02 08:29:24 -04:00
ExecutedBatch ( Vec < String > ) ,
2024-07-26 16:54:03 -04:00
Error ( String ) ,
Ok
}
enum Command {
AddTrader { user : String } ,
AddAsset { asset : String } ,
SetRoyalty { asset_id : usize , roy0 : FiNum , com0 : FiNum , roy1 : FiNum , com1 : FiNum } ,
AddFunds { user_id : usize , asset_id : usize , amt : FiNum } ,
SubFunds { user_id : usize , asset_id : usize , amt : FiNum } ,
Order { user_id : usize , sell_type : usize , sell_qty : FiNum , buy_type : usize , buy_qty : FiNum } ,
2025-04-29 22:39:44 -04:00
SmartOrder { user_id : usize , sell_type : usize , sell_qty : FiNum , buy_type : usize , buy_qty : FiNum , max0 : FiNum , max1 : FiNum } ,
2024-07-30 15:27:25 -04:00
OrderBatch { user_id : usize , sell_type : usize , sell_qty : FiNum , buy_type : usize , buy_qty : FiNum } ,
2024-07-30 19:11:14 -04:00
ExecuteBatch { asset_type0 : usize , strike0 : FiNum , asset_type1 : usize , strike1 : FiNum } ,
2024-10-10 21:53:48 -04:00
RetractOrder { order_id : usize } ,
2024-07-26 16:54:03 -04:00
Error ( String ) ,
NOP ( String ) ,
None ,
}
fn clean ( s : & str ) -> String { s . to_string ( ) }
2025-01-13 21:52:33 -05:00
fn clean_replay ( input : & str ) -> String {
let before_semicolon = input . split ( ';' ) . next ( ) . unwrap_or ( " " ) ; // Split on semicolon; take first chunk (before semicolon), or "" if none
before_semicolon . trim_end ( ) . to_string ( ) // Trim trailing whitespace, then convert to owned String
}
2024-07-26 16:54:03 -04:00
impl Command {
fn serialize ( & self ) -> String {
match self {
Self ::AddTrader { user : name } = > format! ( " AT {} " , name ) ,
Self ::AddAsset { asset : name } = > format! ( " AA {} " , name ) ,
Self ::SetRoyalty { asset_id , roy0 , com0 , roy1 , com1 }
= > format! ( " SR {} {} {} {} {} " , asset_id , roy0 . serialize ( ) , com0 . serialize ( ) , roy1 . serialize ( ) , com1 . serialize ( ) ) ,
Self ::AddFunds { user_id , asset_id , amt }
= > format! ( " AF {} {} {} " , user_id , asset_id , amt . serialize ( ) ) ,
Self ::SubFunds { user_id , asset_id , amt }
= > format! ( " SF {} {} {} " , user_id , asset_id , amt . serialize ( ) ) ,
Self ::Order { user_id , sell_type , sell_qty , buy_type , buy_qty }
= > format! ( " OR {} {} {} {} {} " , user_id , sell_type , sell_qty . serialize ( ) , buy_type , buy_qty . serialize ( ) ) ,
2024-07-30 15:27:25 -04:00
Self ::OrderBatch { user_id , sell_type , sell_qty , buy_type , buy_qty }
= > format! ( " ORB {} {} {} {} {} " , user_id , sell_type , sell_qty . serialize ( ) , buy_type , buy_qty . serialize ( ) ) ,
2024-07-30 19:11:14 -04:00
Self ::ExecuteBatch { asset_type0 , strike0 , asset_type1 , strike1 }
= > format! ( " EXE {} {} {} {} " , asset_type0 , strike0 . serialize ( ) , asset_type1 , strike1 . serialize ( ) ) ,
2024-10-10 21:53:48 -04:00
Self ::RetractOrder { order_id } = > format! ( " RE {} " , order_id ) ,
2024-07-29 16:45:47 -04:00
Self ::Error ( str ) = > format! ( " NOP Error: {} " , str ) ,
Self ::NOP ( str ) = > format! ( " NOP {} " , clean ( str ) ) ,
_ = > format! ( " NOP (This should never happen) " ) ,
2024-07-26 16:54:03 -04:00
}
}
2025-01-13 13:34:26 -05:00
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) " ) ,
}
}
2024-07-26 16:54:03 -04:00
fn deserialize ( line : String ) -> Self {
let tokens : Vec < & str > = line . split_whitespace ( ) . collect ( ) ;
match tokens . as_slice ( ) {
[ " AT " , name ] = > Self ::AddTrader { user : name . to_string ( ) } ,
[ " AA " , name ] = > Self ::AddAsset { asset : name . to_string ( ) } ,
[ " SR " , asset_id , roy0 , com0 , roy1 , com1 ] = >
Self ::SetRoyalty { asset_id : asset_id . parse ::< usize > ( ) . unwrap ( ) , roy0 : FiNum ::new_deserialize ( roy0 ) , com0 : FiNum ::new_deserialize ( com0 ) ,
roy1 : FiNum ::new_deserialize ( roy1 ) , com1 : FiNum ::new_deserialize ( com1 ) } ,
[ " AF " , user_id , asset_id , amt ] = >
Self ::AddFunds { user_id : user_id . parse ::< usize > ( ) . unwrap ( ) , asset_id : asset_id . parse ::< usize > ( ) . unwrap ( ) , amt : FiNum ::new_deserialize ( amt ) } ,
[ " SF " , user_id , asset_id , amt ] = >
Self ::SubFunds { user_id : user_id . parse ::< usize > ( ) . unwrap ( ) , asset_id : asset_id . parse ::< usize > ( ) . unwrap ( ) , amt : FiNum ::new_deserialize ( amt ) } ,
[ " OR " , user_id , sell_type , sell_qty , buy_type , buy_qty ] = >
2024-07-30 15:27:25 -04:00
Self ::Order { user_id : user_id . parse ::< usize > ( ) . unwrap ( ) , sell_type : sell_type . parse ::< usize > ( ) . unwrap ( ) , sell_qty : FiNum ::new_deserialize ( sell_qty ) ,
buy_type : buy_type . parse ::< usize > ( ) . unwrap ( ) , buy_qty : FiNum ::new_deserialize ( buy_qty ) } ,
[ " ORB " , user_id , sell_type , sell_qty , buy_type , buy_qty ] = >
Self ::OrderBatch { user_id : user_id . parse ::< usize > ( ) . unwrap ( ) , sell_type : sell_type . parse ::< usize > ( ) . unwrap ( ) , sell_qty : FiNum ::new_deserialize ( sell_qty ) ,
2024-07-26 16:54:03 -04:00
buy_type : buy_type . parse ::< usize > ( ) . unwrap ( ) , buy_qty : FiNum ::new_deserialize ( buy_qty ) } ,
2024-10-06 22:02:25 -04:00
[ " RE " , order_id ] = >
2024-10-10 21:53:48 -04:00
Self ::RetractOrder { order_id : order_id . parse ::< usize > ( ) . unwrap ( ) } ,
2024-07-30 19:11:14 -04:00
[ " EXE " , asset_type0 , strike0 , asset_type1 , strike1 ] = >
Self ::ExecuteBatch { asset_type0 : asset_type0 . parse ::< usize > ( ) . unwrap ( ) , strike0 : FiNum ::new_deserialize ( strike0 ) , asset_type1 : asset_type1 . parse ::< usize > ( ) . unwrap ( ) , strike1 : FiNum ::new_deserialize ( strike1 ) } ,
2024-07-26 16:54:03 -04:00
[ " NOP " , many_things @ .. ] = > Self ::NOP ( clean ( & line ) ) ,
_ = > Self ::Error ( " Unimplemented Parse " . to_string ( ) ) ,
}
}
}
2024-05-26 20:37:08 -04:00
2024-07-26 16:54:03 -04:00
impl Result {
fn describe ( & self ) -> String {
match self {
Self ::PlacedOrder ( order_id , vec ) = > format! ( " Placed order {} : \n {} " , order_id , vec . join ( " \n " ) ) ,
2024-08-02 08:29:24 -04:00
Self ::AddedTrader ( id , name ) = > format! ( " Added Trader Id {} : {} " , id , name ) ,
Self ::AddedAsset ( id , name ) = > format! ( " Added Asset Id {} : {} " , id , name ) ,
Self ::ExecutedBatch ( vec ) = > format! ( " {} " , vec . join ( " \n " ) ) ,
2024-07-26 16:54:03 -04:00
Self ::FundsRemaining ( amt ) = > format! ( " Funds Remaining: {} " , amt ) ,
2024-10-06 22:02:25 -04:00
Self ::RetractedOrder ( order_id ) = > format! ( " Retracted Order {} " , order_id ) ,
2024-07-26 16:54:03 -04:00
Self ::Ok = > format! ( " Ok " ) ,
Self ::Error ( str ) = > format! ( " Error: {} " , str ) ,
// _ => "Some other result".to_string(),
}
}
fn print ( & self ) -> & Self {
println! ( " {} " , self . describe ( ) ) ;
self
}
}
impl Market {
fn execute ( & mut self , cmd : & Command ) -> Result {
match cmd {
Command ::AddTrader { user : user_name } = > {
let id = self . register_trader ( user_name ) ;
2024-08-02 08:29:24 -04:00
Result ::AddedTrader ( id , user_name . to_string ( ) )
2024-07-26 16:54:03 -04:00
}
Command ::AddAsset { asset : asset_name } = > {
if let Some ( cur ) = self . register_asset ( asset_name ) {
2024-08-02 08:29:24 -04:00
Result ::AddedAsset ( cur , asset_name . to_string ( ) )
2024-07-26 16:54:03 -04:00
} else { Result ::Error ( format! ( " Asset {} already exists. " , asset_name ) ) }
}
Command ::SetRoyalty { asset_id , roy0 , com0 , roy1 , com1 } = > {
if * roy0 + * com0 + * roy1 + * com1 < FiNum ::new_i32 ( 1 ) {
self . set_royalty ( * asset_id , * roy0 , * com0 , * roy1 , * com1 ) ;
Result ::Ok
}
else { Result ::Error ( format! ( " Sum of royalties and commissions should be less than 1.00 " ) ) }
}
Command ::AddFunds { user_id , asset_id , amt } = > {
self . add_trader_balance ( * user_id , * asset_id , * amt ) ;
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 ) ;
Result ::FundsRemaining ( self . get_trader_balance ( * user_id , * asset_id ) )
}
}
Command ::Order { user_id , sell_type , sell_qty , buy_type , buy_qty } = > {
2024-07-30 15:27:25 -04:00
self . make_order ( * user_id , * sell_type , * buy_type , * sell_qty , * buy_qty , true )
}
2025-04-29 22:39:44 -04:00
Command ::SmartOrder { user_id , sell_type , sell_qty , buy_type , buy_qty , max0 , max1 } = > {
self . make_smart_order ( * user_id , * sell_type , * buy_type , * sell_qty , * buy_qty , * max0 , * max1 , true )
}
2024-07-30 15:27:25 -04:00
Command ::OrderBatch { user_id , sell_type , sell_qty , buy_type , buy_qty } = > {
self . make_order ( * user_id , * sell_type , * buy_type , * sell_qty , * buy_qty , false )
2024-07-26 16:54:03 -04:00
}
2024-10-06 22:02:25 -04:00
Command ::RetractOrder { order_id } = > {
2024-10-13 16:55:06 -04:00
self . retract_order ( * order_id )
2024-10-06 22:02:25 -04:00
}
2024-07-30 19:11:14 -04:00
Command ::ExecuteBatch { asset_type0 , strike0 , asset_type1 , strike1 } = > {
2025-01-05 22:34:32 -05:00
// self.execute_batch(*asset_type0, *strike0, *asset_type1, *strike1)
Result ::Error ( format! ( " ExecuteBatch not Implemented. " ) )
2024-07-30 19:11:14 -04:00
}
2024-07-26 16:54:03 -04:00
Command ::Error ( str ) = > Result ::Error ( format! ( " Command Error " ) ) ,
Command ::NOP ( str ) = > Result ::Ok ,
_ = > Result ::Error ( format! ( " Tried to execute an unimplemented Command. This is a bug because all Commands should be implemented, even NOPs and Errors. " ) ) ,
}
}
}
2024-07-26 17:17:27 -04:00
2024-06-23 18:50:48 -04:00
fn erase ( filename : & str ) {
// Stub function for erasing a file
println! ( " Erasing file: {} " , filename ) ;
}
fn copy ( source : & str , destination : & str ) {
// Stub function for copying a file
println! ( " Copying file from {} to {} " , source , destination ) ;
}
2024-06-20 16:22:08 -04:00
2024-07-26 16:54:03 -04:00
2024-07-29 16:45:47 -04:00
fn tokens_to_command ( m : & Market , logged_in : usize , tokens : Vec < & str > , line : & str ) -> Command {
2024-07-26 16:54:03 -04:00
let cmd :Command = match & tokens [ .. ] {
[ " addtrader " , username ] = > Command ::AddTrader { user : username . to_string ( ) } ,
[ " addasset " , assetname ] = > Command ::AddAsset { asset : assetname . to_string ( ) } ,
[ " setroyalty " , assetname , roy0 , com0 , roy1 , com1 ] = > {
if let Some ( cur ) = m . name_to_number ( assetname ) {
let roy0 = FiNum ::new_str ( roy0 ) ;
let com0 = FiNum ::new_str ( com0 ) ;
let roy1 = FiNum ::new_str ( roy1 ) ;
let com1 = FiNum ::new_str ( com1 ) ;
if roy0 + com0 + roy1 + com1 < FiNum ::new_i32 ( 1 ) {
Command ::SetRoyalty { asset_id : * cur , roy0 : roy0 , com0 : com0 , roy1 : roy1 , com1 : com1 }
} else { Command ::Error ( " Sum of royalties must be less than 1 " . to_string ( ) ) }
} else {
Command ::Error ( " Unknown Asset " . to_string ( ) )
}
}
[ " addfunds " , username , qty0 , cur0 ] = > {
let user = m . trader_name2num . get ( * username ) ;
let qty = FiNum ::new_str ( qty0 ) ;
let cur = m . name_to_number ( cur0 ) ;
if user . is_none ( ) { Command ::Error ( format! ( " Could not find trader {} " , username ) ) }
else if qty . is_zero ( ) { Command ::Error ( format! ( " Could not parse quantity {} " , qty0 ) ) }
else if cur . is_none ( ) { Command ::Error ( format! ( " Could not find asset {} " , cur0 ) ) }
else {
let user = * user . unwrap ( ) ;
let cur = * cur . unwrap ( ) ;
Command ::AddFunds { user_id : user , asset_id : cur , amt : qty }
}
}
[ " subfunds " , username , qty0 , cur0 ] = > {
let user = m . trader_name2num . get ( * username ) ;
let qty = FiNum ::new_str ( qty0 ) ;
let cur = m . name_to_number ( cur0 ) ;
if user . is_none ( ) { Command ::Error ( format! ( " Could not find trader {} " , username ) ) }
else if qty . is_zero ( ) { Command ::Error ( format! ( " Could not parse quantity {} " , qty0 ) ) }
else if cur . is_none ( ) { Command ::Error ( format! ( " Could not find asset {} " , cur0 ) ) }
else {
let user = * user . unwrap ( ) ;
let cur = * cur . unwrap ( ) ;
Command ::SubFunds { user_id : user , asset_id : cur , amt : qty }
}
}
2024-10-10 21:53:48 -04:00
[ " retract " , p_order_id ] = > {
let order_id :usize = p_order_id . parse ( ) . unwrap ( ) ;
if ! m . order_finder . contains_key ( & order_id ) { return Command ::Error ( format! ( " Order not found: {} " , p_order_id ) ) }
let trader = & m . traders [ logged_in ] ;
2025-01-05 22:34:32 -05:00
let order_type = m . order_finder . get ( & order_id ) ;
2024-10-13 16:55:06 -04:00
println! ( " Order_type {:?} " , order_type ) ;
2024-10-10 21:53:48 -04:00
let order_queue = m . orders . get ( order_type . expect ( " Retrieving Order Queue " ) ) ;
Command ::RetractOrder { order_id : order_id }
2024-10-06 22:02:25 -04:00
}
2024-07-26 16:54:03 -04:00
[ " order " , qty0 , cur0 , qty1 , cur1 ] = > {
let qty0 = FiNum ::new_str ( qty0 ) ;
let cur0 = m . name_to_number ( cur0 ) ;
let qty1 = FiNum ::new_str ( qty1 ) ;
let cur1 = m . name_to_number ( cur1 ) ;
if ! cur0 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if ! cur1 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if qty0 . is_zero ( ) { Command ::Error ( " Qty0 is must be > 0 " . to_string ( ) ) }
else if qty1 . is_zero ( ) { Command ::Error ( " Qty1 is must be > 0 " . to_string ( ) ) }
else { Command ::Order { user_id : logged_in , sell_type : * cur0 . unwrap ( ) , sell_qty : qty0 , buy_type : * cur1 . unwrap ( ) , buy_qty : qty1 } }
}
2025-04-29 22:39:44 -04:00
// Sell up to qty0 cur0 to buy up to qty1 cur1 where no suborder exceeds maxsub (expressed as a fraction)
[ " smart " , qty0 , cur0 , qty1 , cur1 , max0 , " / " , max1 ] = > {
let qty0 = FiNum ::new_str ( qty0 ) ;
let cur0 = m . name_to_number ( cur0 ) ;
let qty1 = FiNum ::new_str ( qty1 ) ;
let cur1 = m . name_to_number ( cur1 ) ;
let max0 = FiNum ::new_str ( max0 ) ;
let max1 = FiNum ::new_str ( max1 ) ;
if ! cur0 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if ! cur1 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if qty0 . is_zero ( ) { Command ::Error ( " Qty0 is must be > 0 " . to_string ( ) ) }
else if qty1 . is_zero ( ) { Command ::Error ( " Qty1 is must be > 0 " . to_string ( ) ) }
else if max0 . is_zero ( ) { Command ::Error ( " Max0 is must be > 0 " . to_string ( ) ) }
else if max1 . is_zero ( ) { Command ::Error ( " Max1 is must be > 0 " . to_string ( ) ) }
else { Command ::SmartOrder { user_id : logged_in , sell_type : * cur0 . unwrap ( ) , sell_qty : qty0 , buy_type : * cur1 . unwrap ( ) , buy_qty : qty1 , max0 : max0 , max1 : max1 } }
}
2024-07-30 15:27:25 -04:00
[ " orderbatch " , qty0 , cur0 , qty1 , cur1 ] = > {
let qty0 = FiNum ::new_str ( qty0 ) ;
let cur0 = m . name_to_number ( cur0 ) ;
let qty1 = FiNum ::new_str ( qty1 ) ;
let cur1 = m . name_to_number ( cur1 ) ;
if ! cur0 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if ! cur1 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if qty0 . is_zero ( ) { Command ::Error ( " Qty0 is must be > 0 " . to_string ( ) ) }
else if qty1 . is_zero ( ) { Command ::Error ( " Qty1 is must be > 0 " . to_string ( ) ) }
2024-07-30 19:11:14 -04:00
else { Command ::OrderBatch { user_id : logged_in , sell_type : * cur0 . unwrap ( ) , sell_qty : qty0 , buy_type : * cur1 . unwrap ( ) , buy_qty : qty1 } }
}
[ " execute " , strike0 , asset_type0 , strike1 , asset_type1 ] = > {
let strike0 = FiNum ::new_str ( strike0 ) ;
let asset_type0 = m . name_to_number ( asset_type0 ) ;
let strike1 = FiNum ::new_str ( strike1 ) ;
let asset_type1 = m . name_to_number ( asset_type1 ) ;
if ! asset_type0 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if ! asset_type1 . is_some ( ) { Command ::Error ( " Count not find currency " . to_string ( ) ) }
else if strike0 . is_zero ( ) { Command ::Error ( " Strike0 is must be > 0 " . to_string ( ) ) }
else if strike1 . is_zero ( ) { Command ::Error ( " Strike1 is must be > 0 " . to_string ( ) ) }
else { Command ::ExecuteBatch { asset_type0 : * asset_type0 . unwrap ( ) , strike0 :strike0 , asset_type1 : * asset_type1 . unwrap ( ) , strike1 : strike1 } }
2024-07-30 15:27:25 -04:00
}
2024-07-29 16:45:47 -04:00
_ = > { Command ::Error ( line . to_string ( ) ) } ,
2024-07-26 16:54:03 -04:00
} ;
cmd
}
fn interactive ( m : & mut Market , mut out : Option < File > ) {
println! ( " Trading interactively in Tuesday Markets (demo) " ) ;
let stdin = io ::stdin ( ) ;
let mut trader :usize = 0 ;
for line in stdin . lock ( ) . lines ( ) {
match line {
Ok ( input ) = > {
let tokens : Vec < & str > = input . split_whitespace ( ) . collect ( ) ;
let cmd :Command = match tokens . as_slice ( ) {
2025-01-13 13:34:26 -05:00
[ " dump " ] = > { m . dump ( ) ; Command ::None } ,
[ " sanity " ] = > { m . sanity_check ( ) ; Command ::None } ,
2025-02-04 10:59:39 -05:00
[ " path " , src , dest ] = > {
let cur0 = * m . name_to_number ( src ) . unwrap ( ) ;
let cur1 = * m . name_to_number ( dest ) . unwrap ( ) ;
let path = m . path_cost ( cur0 , cur1 , 6 ) ;
let mut str = format! ( " Cost: {} Cap: {} " , path . cost , path . cap ) ;
if path . len > 0 { str = format! ( " {} {} " , str , m . number_to_name ( path . path [ 0 ] ) ) }
for i in 1 .. path . len { str = format! ( " {} -> {} " , str , m . number_to_name ( path . path [ i ] ) ) }
println! ( " {} " , str ) ;
Command ::None
} ,
2024-10-13 16:55:06 -04:00
[ " randomorder " ] = > { println! ( " Random OrderID: {:?} " , m . random_order_id ( ) ) ; Command ::None }
2024-07-26 16:54:03 -04:00
[ " 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 ) }
Command ::None
2024-08-14 18:53:55 -04:00
} ,
2024-07-26 16:54:03 -04:00
[ " whoami " ] = > { println! ( " Logged in as {} , id {} " , m . traders [ trader ] . name , trader ) ; Command ::None }
2024-08-07 18:50:46 -04:00
[ " showorders " ] = > {
println! ( " Showing all orders for {} " , m . traders [ trader ] . name ) ;
for ( key0 , value0 ) in & m . traders [ trader ] . order_finder {
let oq = m . orders . get ( value0 ) . unwrap ( ) ; // OrderQueue
2024-08-07 18:55:22 -04:00
println! ( " About to find key {} " , key0 ) ;
2024-08-07 18:50:46 -04:00
let oqi = * oq . order_finder . get ( key0 ) . unwrap ( ) ;
let ord = oq . v [ oqi ] . clone ( ) ; // Order
println! ( " OrderID {} is selling {} {} to buy {} {} " , key0 , ord . sell_remain , m . number_to_asset ( value0 . 0 ) . name , ord . buy_qty * ord . sell_remain / ord . sell_qty , m . number_to_asset ( value0 . 1 ) . name ) ;
}
Command ::None
}
2024-07-26 16:54:03 -04:00
[ " wallet " ] = > {
for ( key , value ) in & m . traders [ trader ] . balances { println! ( " {} {} " , m . number_to_name ( * key ) , value ) ; }
Command ::None
}
[ " balances " , username ] = > {
if let Some ( user ) = m . trader_name2num . get ( * username ) {
println! ( " Balances for trader {} " , m . traders [ * user ] . name ) ;
for ( key , value ) in & m . traders [ * user ] . balances { println! ( " {} {} " , m . number_to_name ( * key ) , value ) ; }
} else { println! ( " Could not find trader {} " , username ) ; }
Command ::None
} ,
2025-01-24 20:20:37 -05:00
[ " seedrandom " | " sr " , seed ] = > {
2024-08-14 16:56:14 -04:00
let seed = seed . parse ( ) . unwrap ( ) ;
m . seed_random ( seed ) ;
println! ( " Seeded RNG with {} " , seed ) ;
Command ::None
}
2025-04-21 23:37:02 -04:00
[ " testshadow " , src , dst ] = > {
if let Some ( cur0 ) = m . name_to_number ( src ) {
if let Some ( cur1 ) = m . name_to_number ( dst ) {
println! ( " Currencies are {} and {} " , cur0 , cur1 ) ;
if let Some ( q ) = m . orders . get_mut ( & ( * cur0 , * cur1 ) ) {
println! ( " Dumping queue " ) ;
q . dump ( ) ;
println! ( " Dumping shadow " ) ;
q . assure_shadowing ( ) ;
while let Some ( ord ) = q . pop ( ) {
ord . dump ( ) ;
}
q . stop_shadowing ( ) ;
}
}
}
Command ::None
}
2025-01-24 20:20:37 -05:00
[ " randomcommands " | " rc " , qty ] = > {
2024-08-14 16:56:14 -04:00
let qty :u32 = qty . parse ( ) . unwrap ( ) ;
2025-01-13 13:34:26 -05:00
let start = Instant ::now ( ) ;
2024-08-14 16:56:14 -04:00
for i in 0 .. qty {
let cmd = m . random_command ( ) ;
2025-01-05 22:34:32 -05:00
// println!("RandomCommand #{}: {}",1+i,cmd.serialize());
2024-08-14 16:56:14 -04:00
if let Some ( ref mut f ) = out {
let ser = cmd . serialize ( ) ;
if let Err ( e ) = writeln! ( f , " {} " , ser ) { eprintln! ( " An error occurred while writing {} " , e ) ; }
2025-01-05 22:34:32 -05:00
} else {
// println!("{}",cmd.serialize());
}
2024-08-14 16:56:14 -04:00
let res = m . execute ( & cmd ) ;
2025-01-05 22:34:32 -05:00
// println!("Result: {}",res.describe());
2024-08-14 16:56:14 -04:00
}
2025-01-13 13:34:26 -05:00
let duration = start . elapsed ( ) ;
println! ( " Ran {} commands in {:?} " , qty , duration ) ;
2024-08-14 16:56:14 -04:00
Command ::None
} ,
2024-07-26 16:54:03 -04:00
[ " quit " ] = > { return } ,
_ = > {
2024-07-29 16:45:47 -04:00
let cmd = tokens_to_command ( m , trader , tokens , & input ) ;
2024-07-26 16:54:03 -04:00
if let Some ( ref mut f ) = out {
let ser = cmd . serialize ( ) ;
if let Err ( e ) = writeln! ( f , " {} " , ser ) { eprintln! ( " An error occurred while writing {} " , e ) ; }
2024-08-06 18:10:48 -04:00
} else { println! ( " {} " , cmd . serialize ( ) ) ; }
2024-07-26 16:54:03 -04:00
let res = m . execute ( & cmd ) ;
println! ( " Result: {} " , res . describe ( ) ) ;
Command ::None
} ,
} ;
}
Err ( error ) = > println! ( " Error reading input: {} " , error ) ,
}
}
}
fn numbers_stuff ( ) {
println! ( " Numbers_stuff " ) ;
let n = FiNum ::new_i32 ( 7 ) / FiNum ::new_i32 ( 2 ) ;
let n_s = n . serialize ( ) ;
let n_d = FiNum ::new_deserialize ( & n_s ) ;
println! ( " N is {} , Serialized to {} , Deserialized to {} " , n , n_s , n_d ) ;
}
fn paths_match ( path0 : & str , path1 : & str ) -> bool
{
let path0 = Path ::new ( path0 ) ;
let path1 = Path ::new ( path1 ) ;
path0 = = path1
}
//
// Use cases:
// Replace logfile
// Replay logfile and then append to it
// Replay one logfile and then replace a different one
// Future additional use cases:
// Replay logfile1+logfile2+... and then replace a different log file
// Replay logfile1+logfile2+logfileN and then append to logfileN
//
2024-05-09 09:52:52 -10:00
fn main ( ) {
2024-05-26 08:19:55 -04:00
let args : Vec < String > = env ::args ( ) . collect ( ) ;
2024-07-26 16:54:03 -04:00
let mut options :HashMap < & str , String > = HashMap ::new ( ) ;
let mut i = 1 ;
enum Mode { Help , Royalty , Interactive , Exercise , Numbers , None }
let mut mode_count = 0 ;
let mut mode = Mode ::None ;
while i < args . len ( ) {
match args [ i ] . as_str ( ) {
" --help " = > { mode = Mode ::Help ; mode_count + = 1 ; i + = 1 ; }
" --interactive " = > { mode = Mode ::Interactive ; mode_count + = 1 ; i + = 1 ; }
" --royalty " = > { mode = Mode ::Royalty ; mode_count + = 1 ; i + = 1 ; }
" --exercise " = > { mode = Mode ::Exercise ; mode_count + = 1 ; i + = 1 ; }
" --numbers " = > { mode = Mode ::Numbers ; mode_count + = 1 ; i + = 1 ; }
" --log " = > {
if i + 1 > = args . len ( ) { println! ( " No log file specified. " ) ; return ; }
else { options . insert ( " logfile " , args [ i + 1 ] . clone ( ) ) ; i + = 2 ; }
}
" --replay " = > {
if i + 1 > = args . len ( ) { println! ( " No replay file specified. " ) ; return ; }
else { options . insert ( " replay " , args [ i + 1 ] . clone ( ) ) ; i + = 2 ; }
}
_ = > { println! ( " Unknown option. " ) ; return ; }
}
}
if mode_count = = 0 { mode = Mode ::Interactive ; mode_count + = 1 ; }
if mode_count ! = 1 { println! ( " You may only select one mode to run in. " ) ; return ; }
2025-01-05 22:34:32 -05:00
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-07-26 16:54:03 -04:00
if options . contains_key ( " replay " ) { m . replay_file ( options . get ( " replay " ) . unwrap ( ) ) ; }
2024-05-26 08:19:55 -04:00
match mode {
2024-07-26 16:54:03 -04:00
Mode ::Interactive = > {
if options . contains_key ( " logfile " ) {
let ap = options . contains_key ( " replay " ) & & paths_match ( options . get ( " replay " ) . unwrap ( ) , options . get ( " logfile " ) . unwrap ( ) ) ;
let f = OpenOptions ::new ( ) . write ( true ) . append ( ap ) . truncate ( ! ap ) . create ( true ) . open ( options . get ( " logfile " ) . unwrap ( ) ) ;
if let Ok ( f ) = f { interactive ( & mut m , Some ( f ) ) ; }
else { println! ( " Could not open logfile for writing. " ) ; }
} else { interactive ( & mut m , None ) ; }
}
Mode ::Exercise = > m . exercise ( ) ,
Mode ::Numbers = > numbers_stuff ( ) ,
Mode ::Royalty = > royalty_stuff ( ) ,
_ = > println! ( " Unspecified mode " ) ,
}
2024-05-26 08:19:55 -04:00
}