Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions atelier-data/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@ description = "Core data structures and I/O tools for the atelier-rs engine"
publish = true
readme = "README.md"

version = "0.0.1"
version = "0.0.10"
rust-version = "1.84.1"
edition = "2021"

exclude = ["assets/*", ".github", "Makefile.toml", "*.log", "tags"]
include = [
"src/**/*",
"Cargo.toml",
"README.md",
"../katex-header.html"
]
include = ["src/**/*", "Cargo.toml", "README.md", "../katex-header.html"]

authors = ["IteraLabs.ai"]
documentation = "https://docs.rs/atelier-rs/"
Expand Down Expand Up @@ -50,6 +45,7 @@ csv = { version = "1.3" }

# AI/ML with LibTorch from C++
tch = { version = "0.20.0" }
tokio.workspace = true

# ------------------------------------------------------------------------- Examples -- #
# ------------------------------------------------------------------------- -------- -- #
Expand All @@ -65,4 +61,3 @@ path = "examples/orderbook.rs"
[[example]]
name = "basic_orderbook_progressions"
path = "examples/progressions.rs"

16 changes: 10 additions & 6 deletions atelier-data/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@

#![allow(clippy::too_many_arguments)]

/// Configurations and experiments
pub mod templates;

/// Dataset defintion and tools
pub mod data;

/// Implementation of orders
pub mod orders;

/// Orders-Price-Volume levels for Orderbooks.
pub mod levels;

/// Single thread Orderbook structure.
pub mod orderbooks;

/// Implementation of orders
pub mod orders;

/// Configurations and experiments
pub mod templates;

/// Implementation of trades
pub mod trades;

46 changes: 46 additions & 0 deletions atelier-data/src/templates/agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use serde::Deserialize;

use super::{exchanges::OrderbookConfig, models::ModelConfig};

#[derive(Debug, Deserialize)]
pub struct Agents {
#[allow(unused)]
agents: Vec<Config>,
}

#[derive(Debug, Deserialize, Clone)]
pub struct Config {
pub name: String,
pub model: ModelConfig,
pub orderbook: OrderbookConfig,
}

#[cfg(test)]
mod test {
use super::*;

static TOML: &str = r#"[[agents]]
name = "a"
[agents.orderbook]
update_freq = 20
bid_price = 3_000.00
bid_levels = [4, 12]
bid_orders = [5, 15]
ticksize = [0.2, 2.0]
ask_price = 3_001.00
ask_levels = [3, 13]
ask_orders = [6, 16]
[agents.model]
id = "mod_00"
label = "GBM"
description = "Geometric Brownian Motion"
params_labels = ["mu", "sigma"]
params_values = [1e-3, 1e-6]
seed = 20995"#;

#[test]
fn toml_works() {
let x: Agents = toml::from_str(TOML).unwrap();
println!("{x:#?}");
}
}
1 change: 1 addition & 0 deletions atelier-data/src/templates/exchanges/centralized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct ExchangeConfig {
pub orderbook: Option<OrderbookConfig>,
}

// TODO refactor
#[derive(Debug, Deserialize, Clone)]
pub struct OrderbookConfig {
pub update_freq: Option<u64>,
Expand Down
1 change: 1 addition & 0 deletions atelier-data/src/templates/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::templates::{
features::FeatureConfig, models::ModelConfig,
};

pub mod agent;
/// Load exchanges config files
pub mod exchanges;
/// Load experiments config files
Expand Down
19 changes: 5 additions & 14 deletions atelier-data/src/templates/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct ModelConfig {
pub description: Option<String>,
pub params_labels: Option<Vec<String>>,
pub params_values: Option<Vec<f64>>,
pub seed: Option<u64>,
}

impl ModelConfig {
Expand All @@ -23,30 +24,19 @@ impl ModelConfig {
}
}

#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize, Clone, Default)]
pub struct ModelConfigBuilder {
pub id: Option<String>,
pub label: Option<Models>,
pub description: Option<String>,
pub params_labels: Option<Vec<String>>,
pub params_values: Option<Vec<f64>>,
}

impl Default for ModelConfigBuilder {
fn default() -> Self {
Self::new()
}
pub seed: Option<u64>,
}

impl ModelConfigBuilder {
pub fn new() -> Self {
ModelConfigBuilder {
id: None,
label: None,
description: None,
params_labels: None,
params_values: None,
}
Self::default()
}

pub fn id(mut self, id: String) -> Self {
Expand Down Expand Up @@ -87,6 +77,7 @@ impl ModelConfigBuilder {
description: Some(description),
params_labels: Some(params_labels),
params_values: Some(params_values),
seed: self.seed,
})
}
}
216 changes: 216 additions & 0 deletions atelier-data/src/trades/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
pub use serde::{Deserialize, Serialize};
pub use std::time::{SystemTime, UNIX_EPOCH};

/// TradeSide
///
/// Enum for identification of either a buy or sell side
/// used to describe the Trade side.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum TradeSide {
Bids,
Asks,
}

impl TradeSide {
///
/// Creates a random choice of the Side enum variants, which currently
/// has implemented: {Bids, Asks}
pub fn random() -> Self {
let now_ts = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();

if now_ts % 2 == 0 {
TradeSide::Bids
} else {
TradeSide::Asks
}
}
}

/// trade_id
///
/// Method to generate unique `trade_id` values for individual trades.
/// currently taking the trade timestamp, the trade symbol and the trade side to deliver
/// a hashed u64 value.
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct TradeId(u64);

impl TradeId {
const TIME_MASK: u64 = 0x0FFF_FFFF_FFFF_FFFF;

pub fn new(trade_ts: u64, trade_side: TradeSide, reserved: u64) -> Self {
let mut val = trade_ts & Self::TIME_MASK;
val |= (trade_side as u64) << 63;
val |= (reserved) << 62;
TradeId(val)
}

pub fn timestamp(&self) -> u64 {
self.0 & Self::TIME_MASK
}

pub fn reserved(&self) -> u64 {
if self.0 >> 62 & 1 == 0 {
1
} else {
2
}
}

pub fn side(&self) -> TradeSide {
if self.0 >> 63 & 1 == 0 {
TradeSide::Bids
} else {
TradeSide::Asks
}
}
}

// Represents a single public trade
#[derive(Debug, Clone)]
pub struct Trade {
pub trade_id: u64,
pub trade_ts: u64,
pub symbol: String,
pub price: f64,
pub side: TradeSide,
pub amount: f64,
}

impl Trade {
pub fn builder() -> TradeBuilder {
TradeBuilder::new()
}
}

#[derive(Debug, Clone)]
pub struct TradeBuilder {
trade_id: Option<u64>,
trade_ts: Option<u64>,
symbol: Option<String>,
price: Option<f64>,
side: Option<TradeSide>,
amount: Option<f64>,
}

impl Default for TradeBuilder {
fn default() -> Self {
Self::new()
}
}

impl TradeBuilder {
pub fn new() -> Self {
TradeBuilder {
trade_id: None,
trade_ts: None,
symbol: None,
price: None,
side: None,
amount: None,
}
}

pub fn trade_id(mut self, trade_id: u64) -> Self {
self.trade_id = Some(trade_id);
self
}

pub fn trade_ts(mut self, trade_ts: u64) -> Self {
self.trade_ts = Some(trade_ts);
self
}

pub fn symbol(mut self, symbol: String) -> Self {
self.symbol = Some(symbol);
self
}

pub fn side(mut self, side: TradeSide) -> Self {
self.side = Some(side);
self
}

pub fn price(mut self, price: f64) -> Self {
self.price = Some(price);
self
}

pub fn amount(mut self, amount: f64) -> Self {
self.amount = Some(amount);
self
}

pub fn build(self) -> Result<Trade, &'static str> {
let trade_ts = self.trade_ts.unwrap_or_else(|| {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_micros() as u64
});
let symbol = self.symbol.ok_or("Missing symbol")?;
let price = self.price.ok_or("Missing price")?;
let side = self.side.ok_or("Missing side")?;
let amount = self.amount.ok_or("Missing amount")?;
let trade_id = Trade::encode_trade_id(side, trade_ts, 0);

Ok(Trade {
trade_id,
trade_ts,
symbol,
price,
side,
amount,
})
}
}

impl Trade {
/// Encoded Trade ID formation
///
/// The trade_id field is an u64 containing encoded info about: side, type,
/// timestamp. The Bit allocation is the following:
///
/// 00TTSSSS SSSSSSSS SSSSSSSS SSSSSSSS SSSSSSSS SSSSSSSS SSSSSSSS SSSSSSSS
/// ||└ 60 bits for timestamp (valid until ~2079)
/// |└─ 1 bit for type (0=Market, 1=Limit)
/// └── 1 bit reserved for future implementation.
pub fn encode_trade_id(trade_side: TradeSide, trade_ts: u64, reserved: u64) -> u64 {
// Highest bit
let side_bit = match trade_side {
TradeSide::Bids => 0,
TradeSide::Asks => 1,
} << 63;

// Second highest bit
let reserved_bit = match reserved {
0 => 0,
_ => 1,
} << 62;

// 60 bits starting at position 2
let timestamp_bits = (trade_ts & ((1 << 60) - 1)) << 2;

side_bit | reserved_bit | timestamp_bits
}

/// Decode Order ID encoded formation. check `encoded_order_id` for more
/// details.
pub fn decode_trade_id(order_id: u64) -> (TradeSide, u64, u64) {
// Highest bit
let order_side = if (order_id >> 63) & 1 == 0 {
TradeSide::Bids
} else {
TradeSide::Asks
};
// Second highest bit
let order_type = if (order_id >> 62) & 1 == 0 { 0 } else { 1 };
// 60 bits starting at position 2
let order_ts = (order_id >> 2) & ((1 << 60) - 1);

(order_side, order_type, order_ts)
}

}
Loading