bourse_de/agents/
random_agent.rs

1use super::Agent;
2use crate::types::{OrderId, Price, Side, Status, TraderId, Vol};
3use crate::Env;
4use rand::seq::SliceRandom;
5use rand::Rng;
6use rand::RngCore;
7
8/// Agents that place orders with uniformly sampled parameters
9///
10/// A set of agents that place orders on a random side with
11/// random volume and price within a given range. Sides,
12/// volumes and prices are uniformly sampled.
13///
14/// Each step each agent:
15///
16/// - Are randomly activated with a given activity rate
17/// - If active and they have an active order on the
18///   market they try to cancel that order
19/// - If they don't have an active order then they
20///   place a new order on a random side, with a
21///   random volume and price
22///
23/// <div id="doc-warning-1" class="warning">
24/// The behaviour of this agent is not based on anything
25/// 'realistic' and should probably only be used for
26/// testing and benchmarking.
27/// </div>
28///
29/// # Examples
30///
31/// ```
32/// use bourse_de::agents::{Agent, RandomAgents, Agents};
33/// use bourse_de::{sim_runner, Env};
34///
35/// #[derive(Agents)]
36/// struct SimAgents {
37///     pub a: RandomAgents,
38/// }
39///
40/// let mut env = Env::new(0, 1, 1_000_000, true);
41///
42/// let mut agents = SimAgents {
43///     a: RandomAgents::new(10, (40, 60), (10, 20), 2, 0.8),
44/// };
45///
46/// sim_runner(&mut env, &mut agents, 101, 10, false);
47/// ```
48pub struct RandomAgents {
49    orders: Vec<Option<OrderId>>,
50    tick_range: (Price, Price),
51    vol_range: (Vol, Vol),
52    tick_size: Price,
53    activity_rate: f32,
54}
55
56impl RandomAgents {
57    /// Initialise a set of random agents
58    ///
59    /// # Arguments
60    ///
61    /// - `n_agents` - Number of agents in the set
62    /// - `tick_range` - Range of ticks to place orders over
63    /// - `vol_range` - Order volume range to sample from
64    /// - `tick_size` - Market tick size
65    /// - `activity_rate` - Agent activity rate
66    ///
67    pub fn new(
68        n_agents: usize,
69        tick_range: (Price, Price),
70        vol_range: (Vol, Vol),
71        tick_size: Price,
72        activity_rate: f32,
73    ) -> Self {
74        Self {
75            orders: vec![None; n_agents],
76            tick_range,
77            vol_range,
78            tick_size,
79            activity_rate,
80        }
81    }
82}
83
84impl Agent for RandomAgents {
85    fn update<R: RngCore>(&mut self, env: &mut Env, rng: &mut R) {
86        let new_orders: Vec<Option<OrderId>> = self
87            .orders
88            .iter_mut()
89            .enumerate()
90            .map(|(n, i)| {
91                let p = rng.gen::<f32>();
92
93                match p < self.activity_rate {
94                    true => {
95                        if (i.is_some()) && (env.order_status(i.unwrap()) == Status::Active) {
96                            env.cancel_order(i.unwrap());
97                            None
98                        } else {
99                            let side = [Side::Ask, Side::Bid].choose(rng).unwrap();
100                            let tick = rng.gen_range(self.tick_range.0..self.tick_range.1);
101                            let vol = rng.gen_range(self.vol_range.0..self.vol_range.1);
102                            Some(
103                                env.place_order(
104                                    *side,
105                                    vol,
106                                    TraderId::try_from(n).unwrap(),
107                                    Some(tick * self.tick_size),
108                                )
109                                .unwrap(),
110                            )
111                        }
112                    }
113                    false => *i,
114                }
115            })
116            .collect();
117
118        self.orders = new_orders;
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use bourse_book::types::Event;
126    use rand::SeedableRng;
127    use rand_xoshiro::Xoroshiro128StarStar;
128
129    #[test]
130    fn test_activity_rate() {
131        let mut env = Env::new(0, 1, 1000, true);
132        let mut rng = Xoroshiro128StarStar::seed_from_u64(101);
133
134        let mut agents = RandomAgents::new(2, (10, 20), (20, 30), 1, 0.0);
135
136        agents.update(&mut env, &mut rng);
137        assert!(env.get_transactions().len() == 0);
138
139        agents.activity_rate = 1.0;
140        agents.update(&mut env, &mut rng);
141        assert!(env.get_transactions().len() == 2);
142    }
143
144    #[test]
145    fn test_order_place_then_cancel() {
146        let mut env = Env::new(0, 1, 1000, true);
147        let mut rng = Xoroshiro128StarStar::seed_from_u64(101);
148
149        let mut agents = RandomAgents::new(1, (10, 20), (20, 30), 1, 1.0);
150
151        agents.update(&mut env, &mut rng);
152        assert!(env.get_transactions().len() == 1);
153        assert!(matches!(env.get_transactions()[0], Event::New { .. }));
154        assert!(agents.orders == vec![Some(0)]);
155
156        env.step(&mut rng);
157
158        agents.update(&mut env, &mut rng);
159        assert!(env.get_transactions().len() == 1);
160        assert!(matches!(
161            env.get_transactions()[0],
162            Event::Cancellation { .. }
163        ));
164
165        env.step(&mut rng);
166
167        agents.update(&mut env, &mut rng);
168        assert!(env.get_transactions().len() == 1);
169        assert!(matches!(env.get_transactions()[0], Event::New { .. }));
170        assert!(agents.orders == vec![Some(1)]);
171    }
172}