This project contains a large amount of custom infrastructure that would be burdensome to understand at a granular level but is essential to understand at a functional level.
This document aims to clarify the design choices of the core infrastructure and assist in the implementation of trading agents and analysis scripts.
The simulation design can be briefly summarized with the following diagram
There are 4 core functional components:
-
Trading Agents: Processes market data and submits order requests
-
Broker: Tracks the account states (cash, position) of each agent and validates order requests
-
Matching Engine: Manages the order books and executes orders
-
Simulation
NOTE: Currently, only a limited version of this design that uses exactly one order book is implemented.
Trading agents are the most flexible component of the market.
There are, however, a few properties of trading agents that this project enforces:
-
Uniqueness
-
Can access information about every resting order
-
Can access information about its own account state
-
Can request any number of any type of order
Enforcement of these properties is through the abstract base class (ABC) TradingAgent in agents/base_agent.py.
This project requires that the implementation of every trading agent is its own class that extends the TradingAgent class, forcing the inheritence of the interface that guarantees the above properties.
User-defined TradingAgents are intended to be located in the user_agents directory, though this is neither enforced nor required.
As an example, we can implement the trivial trading agent that never places orders:
# user_agents/trivial_agent.py
from agents.base_agent import TradingAgent
class TrivialAgent(TradingAgent):
def propose_trades(self, market_data, my_account_state):
return []Notice that TrivialAgent doesn't need to implement __init__ because TradingAgent already implements a minimal __init__ and the design of this particular agent doesn't need any additional parameters.
Despite the very simple implementation, the TrivialAgent is completely functional:
# Other imports are hidden
from core.types import AgentId
from user_agents import TrivialAgent
market_data = ... # Assume a valid MarketData object
account_state = ... # Assume a valid AccountState object
my_agent = TrivialAgent(AgentId(1))
# This correctly gets my_agent's OrderRequests
order_requests = my_agent.propose_trades(
market_data,
account_state
)The broker is the only line of defense between an illegal or impossible order request and the market.
NOTE: In real markets, brokers have a much broader set of important responsibilities than just being a gatekeeper of the market. Implementing all of these complexities would have minimal returns for this project, so we use a heavily limited adaptation of the concept of a broker.
The broker is entirely implemented in core/broker.py, which contains the main Broker class, supporting dataclasses (AccountState and RiskViolation), and the RiskViolationType enum.
The broker enforces the following constraints on every OrderRequest submitted by a TradingAgent:
-
Orders may not exceed a predetermined size
-
Orders may not allow the possibility of a
TradingAgenttrading with itself -
Bids may not cost more to execute than the
TradingAgent's available cash -
Orders may not allow the possibility a
TradingAgentaccumulating a position greater thannor less than-nfor some predetermined quantityn
When the Broker detects a violation of any of these constraints, it produces a RiskViolation object that is passed to the market and is made available to the user.
A side effect of enforcing the position limits constraint is careful tracking of each TradingAgent's cash and position, which is itself a productive task.
As a result, the Broker is also the internal recordkeeper for all AccountStates.
This, however, is an implementation detail and isn't important to a user developing TradingAgents.
Users designing and implementing TradingAgents should be able to do so without knowledge of the specific implementation of Broker, though it is very helpful to know which constraints it enforces.
The matching engine maintains an ordered record of all Orders in an implementation of a limit order book (LOB) and matches orders accoring to price-time priority.
NOTE: The implementation of the matching engine in its current state is very inefficient and will likely be significantly redesigned soon. Fortunately, these changes will not change the function or interface of the engine, so everything in this section will still be true after any changes. (UPDATE: most of the matching engine issues have been resolved. There are still a few issues left, but the focus is currently on improving the Broker.)
[THIS SECTION IS IN PROGRESS]
[THIS SECTION IS IN PROGRESS]
[THIS SECTION IS IN PROGRESS]
[THIS SECTION IS IN PROGRESS]
