Numpy Discrete Event EnvironmentΒΆ
This environment allows market state and market instructions to be returned/submitted as Numpy arrays. This has the potential for higher performance (over native Python) using vectorisation (with some limitations on functionality) in particular for ML and RL use-cases.
The simulation environment can be initialised from a random seed, start-time, tick-size, and step-size (i.e. how long in time each simulated step is)
import numpy as np
import bourse
seed = 101
start_time = 0
tick_size = 2
step_size = 100_000
env = bourse.core.StepEnvNumpy(seed, start_time, tick_size, step_size)
New order and cancellations can be submitted as Numpy arrays
new_orders = (
# Order sides
np.array([True, False]),
# Order volumes
np.array([10, 20], dtype=np.uint32),
# Trader ids
np.array([101, 202], dtype=np.uint32),
# Order prices
np.array([50, 60], dtype=np.uint32),
)
new_order_ids = env.submit_limit_orders(new_orders)
# Update the environment state (placing orders)
env.step()
# Cancel the new orders
env.submit_cancellations(new_order_ids)
Multiple instruction types can be submitted as a tuple of arrays:
The instruction type where
0 = no action
,1 = new-order
, and2 = cancellation
.Order sides (as bool,
True
for bid side) (used for new orders)Order volumes (used for new orders)
Trader ids (used for new orders)
Order prices (used for new orders)
Order ids (used for cancellations)
Note
Values that are not used for a given action (e.g. order-ids for new orders) are ignored, so can be set to an arbitrary default.
For example, if we want to submit one instruction with no change and one new-order we could use:
instructions = (
np.array([0, 1], dtype=np.uint32),
np.array([True, True]),
np.array([0, 20], dtype=np.uint32),
np.array([0, 101], dtype=np.uint32),
np.array([0, 50], dtype=np.uint32),
np.array([0, 0], dtype=np.uint64)
)
new_order_ids = env.submit_instructions(instructions)
env.step()
Warning
This method currently only supports submitting limit orders and cancelling orders.
The state of the order book can be retrieved as an array of values representing the current touch-prices, volumes and volumes and orders at price levels
level_1_data = env.level_1_data()
level_2_data = env.level_2_data()
where the level-1 data only contains the touch volume and number of orders, and level-2 data contains the volume and number of orders for the first 10 price levels from the touch.
See bourse.core.StepEnvNumpy
for full details
of the API.
Agents that interact with the Numpy API can implement
bourse.step_sim.agents.base_agent.BaseNumpyAgent
with an
update
method that takes a random number generator
and array representing the current level 2 data of the
order book (the current touch price, and volumes and orders
at the top 10 price levels). It should return a tuple of
arrays encoding market instructions, for example this
agent simply places new orders either side of the spread
from bourse.step_sim.agents import BaseNumpyAgent
class Agent(BaseNumpyAgent):
def update(self, rng, level_2_data):
bid = max(level_2_data[1], 20)
ask = min(level_2_data[2], 40)
return (
np.array([1, 1], dtype=np.uint32),
np.array([True, False]),
np.array([10, 20], dtype=np.uint32),
np.array([101, 202], dtype=np.uint32),
np.array([bid, ask], dtype=np.uint32),
np.array([0, 0], dtype=np.uint64),
)
These agents can be used in simulation by setting the
use_numpy
argument, and passing an array
of agents implementing bourse.step_sim.agents.base_agent.BaseNumpyAgent
,
for example
agents = [Agent()]
n_steps = 50
seed = 101
market_data = bourse.step_sim.run(
env, agents, n_steps, seed, use_numpy = True
)