forked from calumrussell/rotala
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request calumrussell#84 from calumrussell/66-add-source-wi…
…th-input 66 add source with input
- Loading branch information
Showing
6 changed files
with
297 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
#![allow(dead_code)] | ||
|
||
use std::{borrow::Borrow, collections::HashMap}; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
pub enum Side { | ||
Bid, | ||
Ask, | ||
} | ||
|
||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
pub struct Level { | ||
pub price: f64, | ||
pub size: f64, | ||
} | ||
|
||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
pub struct Depth { | ||
pub bids: Vec<Level>, | ||
pub asks: Vec<Level>, | ||
pub date: i64, | ||
} | ||
|
||
impl Depth { | ||
pub fn add_level(&mut self, level: Level, side: Side) { | ||
match side { | ||
Side::Bid => { | ||
self.bids.push(level); | ||
self.bids | ||
.sort_by(|x, y| x.price.partial_cmp(&y.price).unwrap()); | ||
} | ||
Side::Ask => { | ||
self.asks.push(level); | ||
self.asks | ||
.sort_by(|x, y| x.price.partial_cmp(&y.price).unwrap()); | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, Deserialize, Serialize)] | ||
pub struct BBO { | ||
pub bid: f64, | ||
pub bid_volume: f64, | ||
pub ask: f64, | ||
pub ask_volume: f64, | ||
pub symbol: String, | ||
pub date: i64, | ||
} | ||
|
||
pub type DateQuotes = HashMap<String, Depth>; | ||
|
||
pub struct Athena { | ||
dates: Vec<i64>, | ||
inner: HashMap<i64, DateQuotes>, | ||
} | ||
|
||
impl Athena { | ||
fn get_quotes(&self, date: &i64) -> Option<&DateQuotes> { | ||
self.inner.get(date) | ||
} | ||
|
||
fn get_quotes_unchecked(&self, date: &i64) -> &DateQuotes { | ||
self.get_quotes(date).unwrap() | ||
} | ||
|
||
pub fn get_date(&self, pos: usize) -> Option<&i64> { | ||
self.dates.get(pos) | ||
} | ||
|
||
pub fn has_next(&self, pos: usize) -> bool { | ||
self.dates.len() > pos | ||
} | ||
|
||
pub fn get_best_bid( | ||
&self, | ||
date: impl Borrow<i64>, | ||
symbol: impl Into<String>, | ||
) -> Option<&Level> { | ||
if let Some(date_levels) = self.inner.get(date.borrow()) { | ||
if let Some(depth) = date_levels.get(&symbol.into()) { | ||
return depth.bids.last(); | ||
} | ||
} | ||
None | ||
} | ||
|
||
pub fn get_best_ask( | ||
&self, | ||
date: impl Borrow<i64>, | ||
symbol: impl Into<String>, | ||
) -> Option<&Level> { | ||
if let Some(date_levels) = self.inner.get(date.borrow()) { | ||
if let Some(depth) = date_levels.get(&symbol.into()) { | ||
return depth.asks.first(); | ||
} | ||
} | ||
None | ||
} | ||
|
||
pub fn add_price_level( | ||
&mut self, | ||
date: i64, | ||
symbol: impl Into<String>, | ||
level: Level, | ||
side: Side, | ||
) { | ||
self.inner.entry(date).or_default(); | ||
|
||
let symbol_string = symbol.into(); | ||
|
||
//We will always have a value due to the above block so can unwrap safely | ||
let date_levels = self.inner.get_mut(&date).unwrap(); | ||
if let Some(depth) = date_levels.get_mut(&symbol_string) { | ||
depth.add_level(level, side) | ||
} else { | ||
let depth = match side { | ||
Side::Bid => Depth { | ||
bids: vec![level], | ||
asks: vec![], | ||
date, | ||
}, | ||
Side::Ask => Depth { | ||
bids: vec![], | ||
asks: vec![level], | ||
date, | ||
}, | ||
}; | ||
|
||
date_levels.insert(symbol_string, depth); | ||
} | ||
} | ||
|
||
pub fn new() -> Self { | ||
Self { | ||
dates: Vec::new(), | ||
inner: HashMap::new(), | ||
} | ||
} | ||
} | ||
|
||
impl Default for Athena { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::{Athena, Level, Side}; | ||
|
||
#[test] | ||
fn test_that_insertions_are_sorted() { | ||
let mut athena = Athena::new(); | ||
|
||
let level = Level { | ||
price: 100.0, | ||
size: 100.0, | ||
}; | ||
|
||
let level1 = Level { | ||
price: 101.0, | ||
size: 100.0, | ||
}; | ||
|
||
let level2 = Level { | ||
price: 102.0, | ||
size: 100.0, | ||
}; | ||
|
||
athena.add_price_level(100, "ABC", level2, Side::Ask); | ||
athena.add_price_level(100, "ABC", level1, Side::Bid); | ||
athena.add_price_level(100, "ABC", level, Side::Bid); | ||
|
||
assert_eq!(athena.get_best_bid(100, "ABC").unwrap().price, 101.0); | ||
assert_eq!(athena.get_best_ask(100, "ABC").unwrap().price, 102.0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
use std::io::{Cursor, Write}; | ||
|
||
pub struct BinanceKlinesQuote { | ||
pub open_date: i64, | ||
pub open: f64, | ||
pub high: f64, | ||
pub low: f64, | ||
pub close: f64, | ||
pub volume: f64, | ||
pub close_date: i64, | ||
pub quote_volume: f64, | ||
pub trades_number: i64, | ||
pub taker_buy_volume: f64, | ||
pub taker_buy_asset_volume: f64, | ||
} | ||
|
||
// Building this way isn't performant but this is easier to reason about atm | ||
pub fn get_binance_1m_klines() -> Vec<BinanceKlinesQuote> { | ||
let url = | ||
"https://data.binance.vision/data/spot/daily/klines/BTCUSDT/1m/BTCUSDT-1m-2022-08-03.zip"; | ||
|
||
let mut result = Vec::new(); | ||
|
||
if let Ok(resp) = reqwest::blocking::get(url) { | ||
if let Ok(contents) = resp.bytes() { | ||
let mut c = Cursor::new(Vec::new()); | ||
let _res = c.write(&contents); | ||
|
||
if let Ok(mut zip) = zip::ZipArchive::new(c) { | ||
for i in 0..zip.len() { | ||
if let Ok(mut zip_file) = zip.by_index(i) { | ||
let mut rdr = csv::Reader::from_reader(&mut zip_file); | ||
for row in rdr.records().flatten() { | ||
let open_date = (row[0].parse::<i64>().unwrap()) / 1000; | ||
let open = row[1].parse::<f64>().unwrap(); | ||
let high = row[2].parse::<f64>().unwrap(); | ||
let low = row[3].parse::<f64>().unwrap(); | ||
let close = row[4].parse::<f64>().unwrap(); | ||
let volume = row[5].parse::<f64>().unwrap(); | ||
let close_date = (row[6].parse::<i64>().unwrap()) / 1000; | ||
let quote_volume = row[7].parse::<f64>().unwrap(); | ||
let trades_number = row[8].parse::<i64>().unwrap(); | ||
let taker_buy_volume = row[9].parse::<f64>().unwrap(); | ||
let taker_buy_asset_volume = row[10].parse::<f64>().unwrap(); | ||
result.push(BinanceKlinesQuote { | ||
open_date, | ||
open, | ||
high, | ||
low, | ||
close, | ||
volume, | ||
close_date, | ||
quote_volume, | ||
trades_number, | ||
taker_buy_volume, | ||
taker_buy_asset_volume, | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use std::path::Path; | ||
use std::{collections::HashMap, fs::read_to_string}; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
use serde_json::from_str; | ||
|
||
#[derive(Debug, Clone, Deserialize, Serialize)] | ||
pub struct Level { | ||
pub px: String, | ||
pub sz: String, | ||
pub n: i8, | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize, Serialize)] | ||
pub struct PointInTime { | ||
pub coin: String, | ||
pub time: u64, | ||
pub levels: Vec<Vec<Level>>, | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize, Serialize)] | ||
pub struct Data { | ||
pub channel: String, | ||
pub data: PointInTime, | ||
} | ||
|
||
#[derive(Debug, Clone, Deserialize, Serialize)] | ||
pub struct L2Book { | ||
pub time: String, | ||
pub ver_num: u64, | ||
pub raw: Data, | ||
} | ||
|
||
pub fn get_hyperliquid_l2(path: &Path) -> HashMap<u64, L2Book> { | ||
let mut result = HashMap::new(); | ||
if let Ok(file_contents) = read_to_string(path) { | ||
for line in file_contents.split('\n') { | ||
if line.is_empty() { | ||
continue; | ||
} | ||
|
||
let val: L2Book = from_str(line).unwrap(); | ||
let time = val.raw.data.time; | ||
result.insert(time, val); | ||
} | ||
} | ||
result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,5 @@ | ||
//! Sources are external data sources that are used to create Inputs and then Exchanges. Source | ||
//! creation should be hidden from users and embedded within the creation of Inputs. Each Source | ||
//! should have its own internal format that is converted into an Input format within the Input. | ||
use std::io::{Cursor, Write}; | ||
|
||
pub struct BinanceKlinesQuote { | ||
pub open_date: i64, | ||
pub open: f64, | ||
pub high: f64, | ||
pub low: f64, | ||
pub close: f64, | ||
pub volume: f64, | ||
pub close_date: i64, | ||
pub quote_volume: f64, | ||
pub trades_number: i64, | ||
pub taker_buy_volume: f64, | ||
pub taker_buy_asset_volume: f64, | ||
} | ||
|
||
// Building this way isn't performant but this is easier to reason about atm | ||
pub fn get_binance_1m_klines() -> Vec<BinanceKlinesQuote> { | ||
let url = | ||
"https://data.binance.vision/data/spot/daily/klines/BTCUSDT/1m/BTCUSDT-1m-2022-08-03.zip"; | ||
|
||
let mut result = Vec::new(); | ||
|
||
if let Ok(resp) = reqwest::blocking::get(url) { | ||
if let Ok(contents) = resp.bytes() { | ||
let mut c = Cursor::new(Vec::new()); | ||
let _res = c.write(&contents); | ||
|
||
if let Ok(mut zip) = zip::ZipArchive::new(c) { | ||
for i in 0..zip.len() { | ||
if let Ok(mut zip_file) = zip.by_index(i) { | ||
let mut rdr = csv::Reader::from_reader(&mut zip_file); | ||
for row in rdr.records().flatten() { | ||
let open_date = (row[0].parse::<i64>().unwrap()) / 1000; | ||
let open = row[1].parse::<f64>().unwrap(); | ||
let high = row[2].parse::<f64>().unwrap(); | ||
let low = row[3].parse::<f64>().unwrap(); | ||
let close = row[4].parse::<f64>().unwrap(); | ||
let volume = row[5].parse::<f64>().unwrap(); | ||
let close_date = (row[6].parse::<i64>().unwrap()) / 1000; | ||
let quote_volume = row[7].parse::<f64>().unwrap(); | ||
let trades_number = row[8].parse::<i64>().unwrap(); | ||
let taker_buy_volume = row[9].parse::<f64>().unwrap(); | ||
let taker_buy_asset_volume = row[10].parse::<f64>().unwrap(); | ||
result.push(BinanceKlinesQuote { | ||
open_date, | ||
open, | ||
high, | ||
low, | ||
close, | ||
volume, | ||
close_date, | ||
quote_volume, | ||
trades_number, | ||
taker_buy_volume, | ||
taker_buy_asset_volume, | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
result | ||
} | ||
pub mod binance; | ||
pub mod hyperliquid; |