Skip to content

Commit

Permalink
Sphinx doctests and more Rust doc examples (#3)
Browse files Browse the repository at this point in the history
* Use doctest for sphinx examples

* Test docstring examples

* Add env crate examples

* Additional examples

* Switch docs task ordering
  • Loading branch information
zombie-einstein authored Feb 21, 2024
1 parent a255b96 commit b1f9164
Show file tree
Hide file tree
Showing 16 changed files with 312 additions and 43 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pre_merge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,5 @@ jobs:
run: pip install hatch
- name: Install bourse and build docs 📚
run: hatch run docs:build
- name: Test doc examples
run: hatch run docs:test
8 changes: 8 additions & 0 deletions crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
use proc_macro::TokenStream;
use quote::quote;

/// Agent iteration macro
///
/// Implements the `AgentSet` trait for a struct
/// with fields of agent types. It's often the case
/// we want to implement `update` function that
/// iterates over a heterogeneous set of agents,
/// which this macro automates.
///
#[proc_macro_derive(Agents)]
pub fn agents_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
Expand Down
26 changes: 25 additions & 1 deletion crates/order_book/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,38 @@
//! use bourse_book::types;
//!
//! let mut book = bourse_book::OrderBook::new(0, true);
//!
//! // Create a new order
//! let order_id = book.create_order(
//! types::Side::Bid, 50, 101, Some(50)
//! );
//!
//! // Place the order on the market
//! book.place_order(order_id);
//!
//! // Get the current touch prices
//! let (bid, ask) = book.bid_ask();
//!
//! // Cancel the order
//! book.cancel_order(order_id);
//! ```
//! # Notes
//!
//! - Orders are sorted by price-time priority. To
//! reduce any ambiguity in ordering the simulated
//! time of the market should be updated in
//! between placing orders on the market.
//! - For accuracy prices are stored as unsigned
//! integers (as opposed to a float type), hence
//! prices from data should be scaled based on
//! market tick-size
//! - Simulated orders are intended to be owned by
//! the order book, from which agents/users can
//! retrieve order data. Creating an order with
//! [OrderBook::create_order] initialises a new
//! order entry, but does not immediately place
//! the order on the market.
//!
mod orderbook;
mod side;
pub mod types;
Expand Down
21 changes: 19 additions & 2 deletions crates/order_book/src/orderbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ pub struct OrderEntry {
}

/// Order book with order and trade history
///
/// # Examples
///
/// ```
/// use bourse_book;
/// use bourse_book::types;
///
/// let mut book = bourse_book::OrderBook::new(0, true);
/// let order_id = book.create_order(
/// types::Side::Bid, 50, 101, Some(50)
/// );
/// book.place_order(order_id);
/// let (bid, ask) = book.bid_ask();
/// book.cancel_order(order_id);
/// ```
///
pub struct OrderBook {
/// Simulated time, intended to represent
/// nano-seconds, but arbitrary units can
Expand Down Expand Up @@ -69,8 +85,9 @@ impl OrderBook {
///
/// # Arguments
///
/// - `start_time` - Time to assign to the order book
/// - `trading` - FLag to indicate if trades will be
/// - `start_time` - Simulated time to assign to the
/// order book
/// - `trading` - Flag to indicate if trades will be
/// executed
pub fn new(start_time: Nanos, trading: bool) -> Self {
Self {
Expand Down
2 changes: 1 addition & 1 deletion crates/order_book/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Type aliases and order data structures
//! Type aliases and order data-structures
/// Order-id
pub type OrderId = usize;
Expand Down
94 changes: 93 additions & 1 deletion crates/step_sim/src/agents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,103 @@ pub use bourse_macros::Agents;
pub use momentum_agent::MomentumAgent;
pub use noise_agent::NoiseAgent;

/// Homogeneous agent set functionality
///
/// A set of agents that implement this trait
/// can then be included in a stuct using the
/// [Agents] macro to combine multiple agent
/// types.
///
/// # Examples
///
/// ```
/// use bourse_de::Env;
/// use bourse_de::agents::{Agent, Agents, AgentSet};
/// use fastrand::Rng;
///
/// struct AgentType{}
///
/// impl Agent for AgentType {
/// fn update(
/// &mut self, env: &mut Env, _rng: &mut Rng
/// ) {}
/// }
///
/// #[derive(Agents)]
/// struct MixedAgents {
/// a: AgentType, b: AgentType
/// }
/// ```
pub trait Agent {
/// Update the state of the agent(s)
///
/// # Argument
///
/// - `env` - Reference to a [Env] simulation environment
/// - `rng` - Fastrand random generator
///
fn update(&mut self, env: &mut Env, rng: &mut Rng);
}

/// Functionality required for simulation agents
///
/// Simulation agents provided as an argument to
/// [crate::sim_runner] must implement this trait,
/// but the details of the implementation are
/// left to the user.
///
/// It's a common case that we want update to update
/// a heterogeneous set of agents which can be
/// automatically implemented with the [Agents] macro
/// as long as the agent types implement the [Agent]
/// trait.
///
/// # Examples
///
/// ```
/// use bourse_de::Env;
/// use bourse_de::agents::{Agent, Agents, AgentSet};
/// use fastrand::Rng;
///
/// struct AgentType{}
///
/// impl Agent for AgentType {
/// fn update(
/// &mut self, env: &mut Env, _rng: &mut Rng
/// ) {}
/// }
///
/// #[derive(Agents)]
/// struct MixedAgents {
/// a: AgentType,
/// b: AgentType
/// }
/// ```
///
/// this is equivelant to
///
/// ```
/// # use bourse_de::Env;
/// # use bourse_de::agents::{Agent, Agents, AgentSet};
/// # use fastrand::Rng;
/// # struct AgentType{}
/// # impl Agent for AgentType {
/// # fn update(
/// # &mut self, env: &mut Env, _rng: &mut Rng
/// # ) {}
/// # }
/// struct MixedAgents {
/// a: AgentType,
/// b: AgentType
/// }
///
/// impl AgentSet for MixedAgents {
/// fn update(&mut self, env: &mut Env, rng: &mut Rng){
/// self.a.update(env, rng);
/// self.b.update(env, rng);
/// }
/// }
/// ```
pub trait AgentSet {
/// Update function called each simulated step
///
Expand All @@ -26,7 +118,7 @@ pub trait AgentSet {
///
/// The implementing struct is flexible in what
/// it represent, from a single agent to a group
/// of multiple agent types
/// of multiple agent types.
///
/// # Arguments
///
Expand Down
30 changes: 29 additions & 1 deletion crates/step_sim/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,35 @@ use bourse_book::OrderBook;
use fastrand::Rng;
use std::mem;

// Simulation environment
/// Discrete event simulation environment
///
/// Simulation environment designed for use in a
/// discrete event simulation. Allows agents/users
/// to submit order instructions, update
/// the state of the simulation, and record the
/// market data.
///
/// # Examples
///
/// ```
/// use bourse_de;
/// use bourse_de::types;
/// use fastrand::Rng;
///
/// let mut env = bourse_de::Env::new(0, 1_000, true);
/// let mut rng = Rng::with_seed(101);
///
/// // Submit a new order instruction
/// let order_id = env.place_order(
/// types::Side::Ask,
/// 100,
/// 101,
/// Some(50),
/// );
///
/// // Update the state of the market
/// env.step(&mut rng)
/// ```
pub struct Env {
/// Time-length of each simulation step
step_size: Nanos,
Expand Down
52 changes: 41 additions & 11 deletions crates/step_sim/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,49 @@
//! # Examples
//!
//! ```
//! use bourse_de;
//! use bourse_de::types;
//! use bourse_book::types::{Price, Side, Vol};
//! use bourse_de::agents::AgentSet;
//! use bourse_de::{sim_runner, Env};
//! use fastrand::Rng;
//!
//! let mut env = bourse_de::Env::new(0, 1_000, true);
//! let mut rng = Rng::with_seed(101);
//! let order_id = env.place_order(
//! types::Side::Ask,
//! 100,
//! 101,
//! Some(50),
//! );
//! env.step(&mut rng)
//! struct Agents {
//! pub offset: Price,
//! pub vol: Vol,
//! pub n_agents: usize,
//! }
//!
//! impl AgentSet for Agents {
//! // Agents place an order on a random side
//! // a fixed distance above/below the mid
//! fn update(
//! &mut self, env: &mut Env, rng: &mut Rng
//! ) {
//! let (bid, ask) = env.get_orderbook().bid_ask();
//! let mid = (ask - bid) / 2;
//! let mid_price = bid + mid;
//! println!("{}", mid_price);
//! for _ in (0..self.n_agents) {
//! let side = rng.choice([Side::Bid, Side::Ask]).unwrap();
//! match side {
//! Side::Ask => {
//! let p = mid_price - self.offset;
//! println!("==> {}", p);
//! env.place_order(side, self.vol, 101, Some(p));
//! }
//! Side::Bid => {
//! let p = mid_price + self.offset;
//! println!("==< {}", p);
//! env.place_order(side, self.vol, 101, Some(p));
//! }
//! }
//! }
//! }
//! }
//!
//! let mut env = Env::new(0, 1_000_000, true);
//! let mut agents = Agents{offset: 5, vol: 50, n_agents: 10};
//!
//! sim_runner(&mut env, &mut agents, 101, 50);
//! ```
pub mod agents;
Expand Down
25 changes: 24 additions & 1 deletion crates/step_sim/src/runner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// Simulation execution functionality
//! Simulation execution functionality
use super::agents::AgentSet;
use super::env::Env;
use kdam::tqdm;
Expand All @@ -9,6 +9,29 @@ use kdam::tqdm;
/// in turn can submit instructions to the environment
/// and then update the environment state)
///
/// # Examples
///
/// ```
/// use bourse_de::{Env, sim_runner};
/// use bourse_de::agents::AgentSet;
/// use fastrand::Rng;
///
/// // Dummy agent-type
/// struct Agents{}
///
/// impl AgentSet for Agents {
/// fn update(
/// &mut self, env: &mut Env, _rng: &mut Rng
/// ) {}
/// }
///
/// let mut env = bourse_de::Env::new(0, 1_000, true);
/// let mut agents = Agents{};
///
/// // Run for 100 steps from seed 101
/// sim_runner(&mut env, &mut agents, 101, 100)
/// ```
///
/// # Arguments
///
/// - `env` - Simulation environment
Expand Down
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"sphinx_immaterial",
"sphinx_immaterial.apidoc.python.apigen",
"sphinx.ext.intersphinx",
"sphinx.ext.doctest",
]

napoleon_google_docstring = False
Expand Down
Loading

0 comments on commit b1f9164

Please sign in to comment.