Added and tested Shadowing at the OrderQueue level

This commit is contained in:
2025-04-21 23:37:02 -04:00
parent d4cbf229a6
commit 771d18aeaa
145 changed files with 10119 additions and 31 deletions

View File

@@ -54,6 +54,7 @@ use std::path::Path;
use std::io::{self, BufRead, Write};
use std::env;
use std::collections::HashMap;
use std::collections::HashSet;
use std::cmp::Ordering;
use std::cmp::min;
use rand::prelude::*;
@@ -370,7 +371,7 @@ impl Dumpable for usize {
impl Dumpable for Order {
fn dump(&self) {
println!("Giving {}/{} to get {}",self.sell_remain,self.sell_qty,self.buy_qty);
println!("Giving {}/{} ({} each) to get {}",self.sell_remain,self.sell_qty,self.buy_qty/self.sell_qty,self.buy_qty);
}
}
@@ -382,6 +383,7 @@ struct Market {
traders: Vec<Trader>,
trader_name2num: HashMap<String,usize>,
orders: HashMap<(usize,usize),OrderQueue>,
shadows: HashSet<(usize,usize)>,
royalties: HashMap<usize,RoyaltyTree>, // Active orders that are accepting asset X. They receive royalties when someone makes an order to sell X
royalty_rate: HashMap<usize,FiNum>,
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
@@ -399,6 +401,7 @@ impl Market {
traders: Vec::new(),
trader_name2num: HashMap::new(),
orders: HashMap::new(),
shadows: HashSet::new(),
royalties: HashMap::new(),
royalty_rate: HashMap::new(),
order_finder: HashMap::new(),
@@ -573,6 +576,8 @@ impl Market {
let ap0=(asset_type0,asset_type1);
let ap1=(asset_type1,asset_type0);
let [bids0,bids1]=self.orders.get_many_mut([&ap0, &ap1]).unwrap(); // There's a workaround for this where you remove and reinsert the keys.
let bids0p=bids0.peek();
let bids1p=bids1.peek();
while !bids0.empty() && !bids1.empty()
&& bids0.peek().sell_qty/bids0.peek().buy_qty>=strike0/strike1
&& bids1.peek().sell_qty/bids1.peek().buy_qty>=strike1/strike0 {
@@ -763,7 +768,7 @@ impl Market {
ext.cap=min(ext.cap,ext.cost*top.sell_remain);
progress=true;
if arbitrage {
ext.arbitrage=true;
return ext.clone();
}
}
@@ -824,7 +829,7 @@ struct OrderQueue {
v: Vec<Order>,
shadowing: bool,
shadow: Option<Order>,
next: Vec<usize>,
next: Vec<usize>, // A priority queue
order_finder: HashMap<usize,usize>, // Maps OrderIDs to locations in v
}
@@ -839,20 +844,62 @@ impl OrderQueue {
order_finder:HashMap::new(),
}
}
fn start_shadow(&mut self) {
assert!(self.shadowing==false);
self.shadowing=true;
self.shadow=if self.v.len()>0 { Some(self.v[0].clone()) } else { None }
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); }
}
fn peek(&mut self) -> &mut Order {
self.v.first_mut().unwrap()
if !self.shadowing { self.v.first_mut().unwrap() }
else { self.shadow.as_mut().expect("Shadowing mismatch") }
}
fn peekn(&self) -> Option<&Order> {
self.v.first()
match &self.shadow {
Some(order) => Some(order),
None => { self.v.first() }
}
}
fn empty(&self) -> bool {
self.v.len()==0
}
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);
}
}
}
fn bubble_up(&mut self, pos: usize) {
if pos>0 {
let parent=(pos-1)/2;
@@ -882,16 +929,30 @@ impl OrderQueue {
}
}
}
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);
}
}
}
fn insert(&mut self, item: Order) {
assert!(!self.shadowing);
let id=item.order_id;
self.v.push(item);
self.order_finder.insert(id,self.v.len()-1);
self.bubble_up(self.v.len()-1);
}
fn pop(&mut self) -> Option<Order> {
self.remove(0)
}
fn remove(&mut self, pos: usize) -> Option<Order> {
assert!(!self.shadowing);
if self.v.len()<=pos { None }
else {
let end=self.v.len()-1;
@@ -906,6 +967,7 @@ impl OrderQueue {
for index in 0..self.v.len() {
self.v[index].dump();
}
if self.shadowing { for index in 0..self.next.len() { println!(" Shadow {}",self.next[index]); } }
}
}
@@ -1382,6 +1444,24 @@ fn interactive(m: &mut Market, mut out: Option<File>) {
println!("Seeded RNG with {}",seed);
Command::None
}
["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
}
["randomcommands"|"rc", qty] => {
let qty:u32=qty.parse().unwrap();
let start = Instant::now();