Agent-based modeling and simulation in US equity markets – Part 3

This is the final post of an introduction to agent-based models in US equity markets. The first post provided a definition of a model and a brief overview of how economists use models to simplify reality. The second post introduced agent-based models, simulation, and how they are related. We will conclude with an introduction to the polar types of agents: zero-intelligence and learning agents.

Zero intelligence versus learning agents

Gode and Sunder (1993) define a zero intelligence (ZI) trader as a trader that “has no intelligence, does not seek or maximize profits, and does not observe, remember, or learn.” Farmer et al. (2005) also provide a description: “The model makes the simple assumption that agents place orders to buy or sell at random, subject to constraints imposed by current prices.” They go on to explain that their ZI traders do observe current prices – a deviation from the previous definition. The constraints are essentially dynamic bounds on limit order prices.

The agents in my Tick Pilot paper are ZI as characterized in Farmer et al. (2005): they observe the top of book (price and size) and place orders to buy or sell consistent with their pricing heuristic. The buying and selling choice is random subject to pricing constraints.

Holland et al. (1986) provide a description of learning agents. This description is summarized in Beinhocker (2006):

  1. Agents interact with other agents and the environment.
  2. The agent has a goal or set of goals and can perceive the gap between its current state and desired state.
  3. The agent has a set of heuristics (rules of thumb) that map the current state into decisions. This is called the agent’s mental model.
  4. The agent’s mental model tracks which rules have helped it achieve its goals. Historically successful rules are used more often than less successful rules. Feedback from the environment causes the agent to learn over time.

Holland and Miller (1991) define complex (1, 2, 3) adaptive (4, 5) systems:

  1. A network of interacting agents.
  2. Exhibits dynamic behavior that emerges from the individual agent activities.
  3. Aggregate behavior can be described without detailed knowledge of the individual agents.
  4. Agent actions can be assigned a value (payoff, fitness, utility).
  5. Agent behaves so as to increase this value over time.

Beinhocker (2006) summarizes the evolutionary (learning) approach: differentiate, select, amplify. The evolutionary approach is implemented on a computer with genetic algorithms.

Genetic algorithm

Wikipedia gets the final word: In computer science and operations research, a genetic algorithm (GA) is a metaheuristic inspired by the process of natural selection that belongs to the larger class of evolutionary algorithms (EA). Genetic algorithms are commonly used to generate high-quality solutions to optimization and search problems by relying on bio-inspired operators such as mutation, crossover and selection.

There’s a lot going on in this definition. The links are helpful – especially to folks with a hard science background. But, I suspect it is all a bit of a mystery to those who never studied evolutionary biology, computer science, physics, etc. How would you explain zero intelligence, learning agents and genetic algorithms to an accountant, attorney or MBA?

In an upcoming post, I will provide some specific examples of failed attempts to communicate with a wider audience about simulation and agent-based modeling applied to US equity markets.

Agent-based modeling and simulation in US equity markets – Part 2

This is the second of an occasional series of posts on the application of agent-based modeling to US equity markets. We left off with the definition of a model and a brief overview of how economists use models to simplify reality. Agent-based models can be used to simplify reality as well. This post will tackle the nuts and bolts of ABMs.

Simulation

According to Wikipedia:

  1. Simulation is the imitation of the operation of a real-world process or system.
  2. Monte Carlo methods (or Monte Carlo experiments) are a broad class of computational algorithms that rely on repeated random sampling to obtain numerical results.
  3. discrete-event simulation (DES) models the operation of a system as a discrete sequence of events in time.

In my agent-based model of the US equity Tick Pilot, I employ simulation to imitate the real world process of order submission to a centralized limit order book and the system that book uses to match orders and generate prices and sizes for trades. Note: the US equity markets are not centralized – they are fragmented. The model simplification makes the simulation much easier to code and faster to run without harming any inference regarding market-maker profits or participation. I also employ Monte Carlo methods with pseudo random number generators to produce distributions of results, thereby facilitating statistical tests for significance. And finally, the model employs discrete-event simulation. The first discrete event is the time step. For each step, a subset of the traders is chosen and randomly queued. Only one agent trader can be interacting with the order book at a particular instant in computer time. This is a very simple discrete-event simulation. In reality, many traders are attempting to interact with the order book. But, there too, only one event can be processed at any very small point in time. Otherwise, there is indeterminacy in the book – much like when a database is being updated and accessed at the same time.

How would you explain simulation to an accountant, attorney or MBA?

Agent-based modeling

According to Wikipedia, an agent-based model (ABM) is a class of computational models for simulating the actions and interactions of autonomous agents (both individual or collective entities such as organizations or groups) with a view to assessing their effects on the system as a whole. This definition is simple enough but doesn’t really describe what an agent-based model is or even what an agent is. Bookstaber (2017) distills the essence of ABMs:

  1. A set of agents that are typically heterogeneous and that can act with some degree of independence or autonomy. No centralized control.
  2. At the start of each time period each agent observes its environment and acts according to its own heuristic. The agent’s environment is only a local view of the overall system.
  3. Agent’s actions change the environment.
  4. In the next period, each agent sees its new environment, altered based on the actions of the previous period, and takes action again. Thus there is an interaction between the agents and the environment, and between agents.
  5. The components are the agents, the environment, heuristics, interactions, and dynamics.

Let’s apply the essence of ABMs to my Tick Pilot model to see if it satisfies Bookstaber’s view of an ABM.

  1. The agents in my model are heterogeneous. There are a variety of agents, including generic liquidity providers, market makers, liquidity takers, and penny jumpers. Many of these agents have randomly generated arrival times (i.e., there is heterogeneity within agent classes in addition to among agent classes). Some agents choose limit order prices at random. The choice of their arrival times is independent and no agent is constrained by the activities of other agents.
  2. All of the agents view the top of the book (i.e., the best bid and ask prices and associated sizes at those prices are the agent’s environment). The agents take actions based on that specific view of the book. The agents have no insight into what other agents have done or will do in the future.
  3. When liquidity providers, market makers and penny jumpers arrive, they (usually) add orders to the book, thereby altering the state of the environment. These agents potentially cancel outstanding orders, which also alters the state of the environment. And finally, liquidity takers alter the book when they trade with standing limit orders.
  4. After each discrete event in the queue during one time step, the selected agents view any changes to the top of the book and act accordingly. The takers and providers interact with each other via the book.
  5. The components are the trader agents, the limit order book, the rules that the traders use to generate orders, the interactions with the limit order book, and the dynamics of price and size traded and available at each discrete step.

My model varies somewhat from Bookstaber’s essence of ABMs because of the artificiality of the time step and the fact that several events can occur in one time step. It is best to view each discrete event as occurring at a specific fraction of a time step. This is consistent with how real market data is generated: if two or more messages or transactions occur within the minimum time step (a microsecond in most US equity market feeds), then we do not know their exact time stamps, but we do know the order of the events within that microsecond. And, in my model, each selected (queued) agent receives an up-to-date view of the top of book before they act within each time step.

Tesfatsion provides seven requirements for agents and agent-based models (MP1 – MP7 are taken directly from the linked website without alteration):

 (MP1) Agent Definition: An agent is a software entity within a computationally constructed world capable of acting over time on the basis of its own state, i.e., its own internal data, attributes, and methods.

(MP2) Agent Scope: Agents can represent individuals, social groupings, institutions, biological entities, and/or physical entities.

(MP3) Agent Local Constructivity: The action of an agent at any given time is determined as a function of the agent’s own state at that time.

(MP4) Agent Autonomy: Coordination of agent interactions cannot be externally imposed by means of free-floating restrictions, i.e., restrictions not embodied within agent states.

(MP5) System Constructivity: The state of the modeled system at any given time is determined by the ensemble of agent states at that time.

(MP6) System Historicity: Given initial agent states, all subsequent events in the modeled system are determined solely by agent interactions.

(MP7) Modeler as Culture-Dish Experimenter: The role of the modeler is limited to the setting of initial agent states and to the non-perturbational observation, analysis, and reporting of model outcomes.

Many of these are consistent with Bookstaber’s characterization of ABMs. But there doesn’t appear to be any role for the environment. I think if we re-characterize the limit order book as an institutional agent as defined in MP2, then there is some consistency.

How would you explain agents and agent-based modeling to an accountant, attorney or MBA?

Agent-based modeling and simulation in US equity markets – Part 1

This is the first of an occasional series of posts on the application of agent-based modeling to US equity markets. I chose the US equity markets because it serves as the motivating paradigm for the vast majority of historical academic work on agent-based modeling of equity markets. Despite the appearance of such a narrow focus, this particular perspective is relevant because the history of US equity markets reflects and occasionally foretells the variety of market structures observed around the globe today. For example, prior to Regulation National Market System (Reg NMS), US equity markets were a combination of fragmented dealer-intermediated continuous two-sided auctions, call auctions, and over-the-counter networks. Nowadays, they are a combination of fragmented order-driven two-sided continuous auctions, electronically-intermediated call auctions, and a variety of dark pools.

There are three primary sources of agent-based models and/or simulation-based models of equity markets: academics (including academics in government), for-profit companies and not-for-profit government contractors. My discussion and examples will be focused on academic work because academics are the ones most likely to publish their results. It would come as no surprise to learn that hedge funds and other market participants employ simulations as a part of their R&D apparatus. For example, backtesting trading strategies with historical data requires a technical infrastructure that would also support some forms of ABM or simulation. However, for-profit trading firms are not very likely to publish their findings. Even not-for-profit government contractors (and academics who aspire to be) have an incentive to withhold critical model details, thereby making validation and replication difficult or impossible.

In the introduction to this website, I set out my true aims. One of these is increasing awareness of the utility of applying agent-based modeling to financial markets. Toward that end, I will begin with some definitions and provide some examples of how I think we can do better at communicating with market practitioners, regulators, and other professionals who are not susceptible to deep and lengthy discussions of the scientific method. I will source many of the definitions from Wikipedia; not because I think they are the final arbiter of scientific rigor, but because I don’t necessarily want to argue about definitions. I just want a common source everyone can agree is the source – even if they don’t agree with the precision (or lack thereof) contained within.

A model

Wikipedia defines a conceptual model as a representation of a system using general rules and concepts and a scientific model as a simplified and idealized understanding of physical systems. A model is a simplification of reality. Economists use a variety of models to simplify our true complex economy. Theoretical models are mathematical frameworks. Simplifications are introduced to facilitate analytical tractability. In other words, the framework (i.e., the construction of the model) is simplified in order to get answers, right or wrong. Econometric models are empirical statistical models, typically applied to real economic data. Simplifications involve specification (choosing a type of model that can be estimated or that suits the available data), variable selection (choosing variables that are consistent with the available data), and imposing (or ignoring) untested assumptions regarding the unbiasedness or consistency of parameter estimates. Yet economists continue to use these models because they can be useful despite their shortcomings. George Box, a statistician, summarized the practical use of statistical models in a paper with a section entitled “All models are wrong but some are useful.”

Agent-based models are simplifications of reality, too. To maintain usefulness, the agent-based modeler abstracts away features of the real economy that are unnecessary and accentuates features that are necessary for generating insights into the proposed problem. If the agent-based modeler is successful, the model is useful. But the model is still a simplification, and, if Box is right, it is wrong, too!

How would you explain economic modeling to an accountant, attorney or MBA?

In the next post, I will introduce simulation and agent-based modeling and discuss the relationship between them.

Coding the agent-based model simulation loop with Python

This blog continues the coding project in support of replicating the results in my Tick Pilot Agent-Based Modeling paper. The first and second blogs created and tested the limit order book. The third and fourth blogs created and tested the traders. The next step is to pull the book and traders together and run a simulation. The strategy is designed to enable a user to install a package, import the package, and instantiate from a Runner class. For example, from the command line:

~$ conda install pyziabm

The user might have to specify the conda repo or download and install from local. See the Tick Pilot ABM project website for more details. Then from IPython or a Jupyter Notebook:

import pyziabm as pzi
pzi.Runner()

This command would run the simulation with a set of defaults and store some results in a table in an hdf5 file. The defaults are all keywords. The user can change the defaults by calling Runner with the keywords updated – in the spirit of how matplotlib gets things done:

pzi.Runner(mpi=1, h5filename='test2.h5', pj=True, alpha_pj=0.01)

The full code is available on GitHub as runner2017mpi_r4.py. As usual, the first step is to import some python packages. The traders and the orderbook were designed to be imported by the simulation module. We will import those as well.

import random
import numpy as np
import pandas as pd

from pyziabm.orderbook3 import Orderbook
from pyziabm.trader2017_r3 import Provider, Provider5, Taker, MarketMaker, MarketMaker5, PennyJumper

The __init__() method does all of the work in four major steps: create some useful attributes for later use, create the traders, orderbook and information environment, set up and run the simulation, and save some output. The first portion of __init__() demonstrates the keyword strategy and creates some attributes.

    def __init__(self, prime1=20, num_mms=1, mm_maxq=1, mm_quotes=12, mm_quote_range=60, mm_delta=0.025, 
                 num_takers=50, taker_maxq=1, num_providers=38, provider_maxq=1, q_provide=0.5,
                 alpha=0.0375, mu=0.001, delta=0.025, lambda0=100, wn=0.001, c_lambda=1.0, run_steps=100000,
                 mpi=5, h5filename='test.h5', pj=False, alpha_pj=0):
        self.alpha_pj = alpha_pj
        self.q_provide = q_provide
        self.lambda0 = lambda0
        self.run_steps = run_steps+1
        self.h5filename = h5filename

The second portion creates the traders and their arrival intervals, the order book and the information environment.

        self.t_delta_t, self.taker_array = self.make_taker_array(taker_maxq, num_takers, mu)
        self.t_delta_p, self.provider_array = self.make_provider_array(provider_maxq, num_providers, delta, mpi, alpha)
        self.t_delta_m, self.marketmaker_array = self.make_marketmaker_array(mm_maxq, num_mms, mm_quotes, mm_quote_range, mm_delta, mpi)
        self.pennyjumper = self.make_pennyjumper(mpi)
        self.exchange = Orderbook()
        self.q_take, self.lambda_t = self.make_q_take(wn, c_lambda)
        self.trader_dict = self.make_traders(num_takers, num_providers, num_mms)

The final portion prepares and runs the simulation and then saves output.

        self.seed_orderbook()
        self.make_setup(prime1)
        if pj:
            self.run_mcsPJ(prime1)
        else:
            self.run_mcs(prime1)
        self.exchange.trade_book_to_h5(h5filename)
        self.out_to_h5()

We will take each of these steps in order and I will provide a brief overview of what’s going on in each of the methods. See the Tick Pilot Agent-Based Modeling paper for further details and a full description of the agents and the simulation strategy.

The Traders

The make_taker_array(…) method creates the taker agents and their arrival intervals. The first three lines of code determine the trade size (size = 1 in the paper). The fourth line creates the random arrival intervals and the fifth and sixth lines prepare and create the Taker instances and store them in a numpy array. The arrival intervals are permanently associated with specific Taker instances via numpy arrays. We will make use of this later.

    def make_taker_array(self, maxq, num_takers, mu):
        default_arr = np.array([1, 5, 10, 25, 50])
        actual_arr = default_arr[default_arr<=maxq]
        taker_size = np.random.choice(actual_arr, num_takers)
        t_delta_t = np.floor(np.random.exponential(1/mu, num_takers)+1)*taker_size
        takers_list = ['t%i' % i for i in range(num_takers)]
        takers = np.array([Taker(t,i) for t,i in zip(takers_list,taker_size)])
        return t_delta_t, takers

The make_provider_array(…) method follows a similar strategy while using an if block to specify whether the provider should use a unit (penny) pricing increment or a 5 unit increment.

    def make_provider_array(self, maxq, num_providers, delta, mpi, alpha):
        default_arr = np.array([1, 5, 10, 25, 50])
        actual_arr = default_arr[default_arr<=maxq]
        provider_size = np.random.choice(actual_arr, num_providers)
        t_delta_p = np.floor(np.random.exponential(1/alpha, num_providers)+1)*provider_size
        providers_list = ['p%i' % i for i in range(num_providers)]
        if mpi==1:
            providers = np.array([Provider(p,i,mpi,delta) for p,i in zip(providers_list,provider_size)])
        else:
            providers = np.array([Provider5(p,i,mpi,delta) for p,i in zip(providers_list,provider_size)])
        return t_delta_p, providers

The make_marketmaker_array(…) method also follows the same strategy. The market maker arrival interval is the same as the trade size. In the paper, the single market maker has a trade size of one and therefore appears once every simulation step.

    def make_marketmaker_array(self, maxq, num_mms, mm_quotes, mm_quote_range, mm_delta, mpi):
        default_arr = np.array([1, 5, 10, 25, 50])
        actual_arr = default_arr[default_arr<=maxq]
        provider_size = np.random.choice(actual_arr, num_mms)
        t_delta_m = maxq
        marketmakers_list = ['m%i' % i for i in range(num_mms)]
        if mpi==1:
            marketmakers = np.array([MarketMaker(p,i,mpi,mm_delta,mm_quotes,mm_quote_range) for p,i in zip(marketmakers_list,provider_size)])
        else:
            marketmakers = np.array([MarketMaker5(p,i,mpi,mm_delta,mm_quotes,mm_quote_range) for p,i in zip(marketmakers_list,provider_size)])
        return t_delta_m, marketmakers

The make_pennyjumper(…) method merely returns the single instance of the Penny Jumper.

    def make_pennyjumper(self, mpi):
        return PennyJumper('j0', 1, mpi)

The Information Environment

The information environment includes a vector, q_take, that determines the probability a taker will submit a buy order and a vector, lambda_t, that serves as a parameter for a method that modifies the exponential distribution from which the Providers choose their prices.

    def make_q_take(self, s, c_lambda):
        noise = np.random.rand(2,self.run_steps)
        qt_take = np.empty_like(noise)
        qt_take[:,0] = 0.5
        for i in range(1,self.run_steps):
            qt_take[:,i] = qt_take[:,i-1] + (noise[:,i-1]>qt_take[:,i-1])*s - (noise[:,i-1]<qt_take[:,i-1])*s
        lambda_t = -self.lambda0*(1 + (np.abs(qt_take[1] - 0.5)/np.sqrt(np.mean(np.square(qt_take[0] - 0.5))))*c_lambda)
        return qt_take[1], lambda_t

Preparing the Orderbook

Preparing the order book for the simulation involves seeding the book with one ask order and one bid order and then priming the book for twenty steps with just the Providers participating. The seed_orderbook(…) method accomplishes the seeding. The make_setup(…) method calls make_providers(…) to prime the book. For each time step, make_setup(…) calls make_providers(…) and loops through the returned active Providers: the Provider processes the top-of-book signal; the Exchange (orderbook) processes the Provider order and then updates the top-of-book, which serves as an input for the next step through the list of active Providers. make_providers(…) uses np.remainder() on the arrival interval vector to determine which Providers are active in any particular step. We will re-use this strategy in the main simulation loop to follow.

    def seed_orderbook(self):
        seed_provider = Provider('p999999', 1, 5, 0.05)
        self.trader_dict.update({'p999999': seed_provider})
        ba = random.choice(range(1000005, 1002001, 5))
        bb = random.choice(range(997995, 999996, 5))
        qask = {'order_id': 'p999999_a', 'timestamp': 0, 'type': 'add', 'quantity': 1, 'side': 'sell',
              'price': ba, 'exid': 99999999}
        qbid = {'order_id': 'p999999_b', 'timestamp': 0, 'type': 'add', 'quantity': 1, 'side': 'buy',
              'price': bb, 'exid': 99999999}
        seed_provider.local_book['p999999_a'] = qask
        self.exchange.add_order_to_book(qask)
        self.exchange.order_history.append(qask)
        seed_provider.local_book['p999999_b'] = qbid
        self.exchange.add_order_to_book(qbid)
        self.exchange.order_history.append(qbid)

    def make_setup(self, prime1):
        top_of_book = self.exchange.report_top_of_book(0)
        for current_time in range(1, prime1):
            for p in self.make_providers(current_time):
                p.process_signal(current_time, top_of_book, self.q_provide, -self.lambda0)
                self.exchange.process_order(p.quote_collector[-1])
                top_of_book = self.exchange.report_top_of_book(current_time)

    def make_providers(self, step):
        providers = self.provider_array[np.remainder(step, self.t_delta_p)==0]
        np.random.shuffle(providers)
        return providers

Running the Simulation

The run_mcs(…) method steps through the remaining time, calling make_both(…) to determine which traders will participate in the time step and to randomize them (a misnomer, should name the method make_all(…)). A series of actions are specified as a function of trader type. Providers and MarketMakers add orders if their arrival interval matches the time step (that’s what “if row[1]:” determines) and potentially cancel orders regardless of whether their interval matches the time step or not. Takers add orders, too. A make_traders(…) method creates a dictionary of trader objects and their ids, thereby enabling liquidity provider lookup when a taker takes liquidity. This facilitates sending confirm messages to the liquidity providers when one of their resting orders is hit. The final block of code stores some of the larger history objects to an hdf5 file and resets the containers to empty.

    def run_mcs(self, prime1):
        top_of_book = self.exchange.report_top_of_book(prime1)
        for current_time in range(prime1, self.run_steps):
            for row in self.make_both(current_time):
                if row[0].trader_type == 'Provider':
                    if row[1]:
                        row[0].process_signal(current_time, top_of_book, self.q_provide, self.lambda_t[current_time])
                        self.exchange.process_order(row[0].quote_collector[-1])
                        top_of_book = self.exchange.report_top_of_book(current_time)
                    row[0].bulk_cancel(current_time)
                    if row[0].cancel_collector:
                        for c in row[0].cancel_collector:
                            self.exchange.process_order(c)
                            if self.exchange.confirm_modify_collector:
                                row[0].confirm_cancel_local(self.exchange.confirm_modify_collector[0])
                        top_of_book = self.exchange.report_top_of_book(current_time)
                elif row[0].trader_type == 'MarketMaker':
                    if row[1]:
                        row[0].process_signal(current_time, top_of_book, self.q_provide)
                        for q in row[0].quote_collector:
                            self.exchange.process_order(q)
                        top_of_book = self.exchange.report_top_of_book(current_time)
                    row[0].bulk_cancel(current_time)
                    if row[0].cancel_collector:
                        for c in row[0].cancel_collector:
                            self.exchange.process_order(c)
                            if self.exchange.confirm_modify_collector:
                                row[0].confirm_cancel_local(self.exchange.confirm_modify_collector[0])
                        top_of_book = self.exchange.report_top_of_book(current_time)
                else:
                    row[0].process_signal(current_time, self.q_take[current_time])
                    self.exchange.process_order(row[0].quote_collector[-1])
                    if self.exchange.traded:
                        for c in self.exchange.confirm_trade_collector:
                            trader = self.trader_dict[c['trader']]
                            trader.confirm_trade_local(c)
                        top_of_book = self.exchange.report_top_of_book(current_time)
            if not np.remainder(current_time, 2000):
                self.exchange.order_history_to_h5(self.h5filename)
                self.exchange.sip_to_h5(self.h5filename)

    def make_both(self, step):
        providers_mask = np.remainder(step, self.t_delta_p)==0
        takers_mask = np.remainder(step, self.t_delta_t)==0
        marketmakers_mask = np.remainder(step, self.t_delta_m)==0
        providers = np.vstack((self.provider_array, providers_mask)).T
        takers = np.vstack((self.taker_array, takers_mask)).T
        marketmakers = np.vstack((self.marketmaker_array, marketmakers_mask)).T
        traders = np.vstack((providers, marketmakers, takers[takers_mask]))
        np.random.shuffle(traders)
        return traders

    def make_traders(self, num_takers, num_providers, num_mms):
        takers_dict = dict(zip(['t%i' % i for i in range(num_takers)], list(self.taker_array)))
        providers_dict = dict(zip(['p%i' % i for i in range(num_providers)], list(self.provider_array)))
        takers_dict.update(providers_dict)
        marketmakers_dict = dict(zip(['m%i' % i for i in range(num_mms)], list(self.marketmaker_array)))
        takers_dict.update(marketmakers_dict)
        if self.alpha_pj > 0:
            takers_dict.update({'j0': self.pennyjumper})
        return takers_dict

The run_mcsPJ(…) method has an additional block of code at the end of each step through the traders. This code determines whether a PennyJumper will be active after a trader shows up. If so, the PennyJumper has an opportunity to add and/or cancel orders. Note that the PennyJumper can participate zero, one, or many times during each time step.

    def run_mcsPJ(self, prime1):

...

                if random.uniform(0,1) < self.alpha_pj:
                    self.pennyjumper.process_signal(current_time, top_of_book, self.q_take[current_time])
                    if self.pennyjumper.cancel_collector:
                        for c in self.pennyjumper.cancel_collector:
                            self.exchange.process_order(c)
                    if self.pennyjumper.quote_collector:
                        for q in self.pennyjumper.quote_collector:
                            self.exchange.process_order(q)
                    top_of_book = self.exchange.report_top_of_book(current_time)

...

The final step saves some results.

    def qtake_to_h5(self):
        temp_df = pd.DataFrame({'qt_take': self.q_take, 'lambda_t': self.lambda_t})
        temp_df.to_hdf(self.h5filename, 'qtl', append=True, format='table', complevel=5, complib='blosc')
        
    def mm_profitability_to_h5(self):
        for m in self.marketmaker_array:
            temp_df = pd.DataFrame(m.cash_flow_collector)
            temp_df.to_hdf(self.h5filename, 'mmp', append=True, format='table', complevel=5, complib='blosc')
            
    def out_to_h5(self):
        self.qtake_to_h5()
        self.mm_profitability_to_h5()

If you have comments or suggestions, feel free to post them. Coming up are posts describing the wrapper file to replicate the results in the Working Paper and some notes on a simple Conda build process for creating a package.

New ABM White Paper on SEC Market Structure Website

An application of agent-based modeling to market structure policy: the case of the U.S. Tick Size Pilot Program and market maker profitability

Abstract

I demonstrate the feasibility of applying a heterogeneous agent-based model of a modern limit order market to inform U.S. equity market structure policy. The model specifically addresses matters related to the U.S. Tick Size Pilot Program as an example to demonstrate the utility of agent-based models in general. In this case, the model provides quantitative results on changes in market maker participation and profitability as well as quoted spreads when the minimum pricing increment is raised from one tick to five ticks. Perhaps more importantly, the very act of applying the agent-based model and analyzing the results generates additional insights into the nuances of the impact of tick size on small cap equity market quality. Agent-based modeling should be included in the regulatory toolbox because it can not only provide answers to specific policy-related questions but can also help policy-makers ask better questions before implementing the policy.

Why would anyone write about agent-based modeling?

Agent-based modeling offers an opportunity to experiment with and learn about how markets work without interfering with real markets. To this very day, experimenting with our financial markets means altering the actual market structure, typically via some mixture of policy and incentives designed to influence or curtail the activities of market participants. These experiments impose real costs on a variety of participants, sometimes for years. Why not try agent-based modeling before experimenting with actual markets? A variety of excuses are proffered by the people who stand to gain the most: regulators and market participants. These folks regularly claim that agent-based models are untested, difficult to implement, and too unrealistic. They are wrong about the models being untested, wrong about implementation, and wrong about the necessity for complete realism in any model. The truth is agent-based modeling is not well understood. We fear what we do not understand. And we distrust what we fear.

radicalmarketsimulation.com is devoted to increasing awareness of the utility of applying agent-based modeling to financial markets. To increase awareness, skilled agent-based modelers must engage market practitioners, academics, and regulators in an open discussion of the pros and cons of agent-based models. Not only do we need to build better models, target important policy issues and publicize our results more extensively, we need to learn how to communicate effectively with each member of the tripartite audience. I say “we” because I want to invite other agent-based modelers to contribute to this site in the spirit of an open-source community of concerned practitioners. More details below. But first, let’s get some obligatory preliminaries out of the way.

Who am I?

My name is Charles Collver and I am a financial economist at the US Securities and Exchange Commission where I have spent the last five years working with financial market big data. My LinkedIn profile provides more historical details and a way to contact me (for now). While I have been noodling around with agent-based models and genetic algorithms since my grad school days, I have only recently begun focusing on applying the models to financial market policy.

What will I write about?

In the blogs to follow, I will write about:
1. Agent-based modeling for financial markets
2. Simulation
3. Python/Cython for building an API (aka a “test bed”)
4. Genetic algorithms
5. Packaging, Conda, GitHub
6. Large scale and distributed computing
7. I will write about what works as if it took a few hours that morning. I will also write about some of the things I tried and didn’t work (and took a lot more than a few hours).

What will I not write about?

I will not write about the US Securities and Exchange Commission or opine on any policy-related issues. However, if a policy issue becomes a matter of public discussion, then I might write about how to think about designing models to address the issue.

Why I write (and why you should, too)?

Or, what’s in it for me? I want to establish expertise and super-credibility, to become a better modeler and have other like-minded modelers notice. Of course, as an ex-academic, I believe you haven’t really fully understood a topic/subject/skill/dirty trick until you have taught it to others. More importantly, I want to engage with a like-minded community and build a network for myself, my colleagues and for you. We can learn from each other – which means I will get better, you will get better, and, most importantly, the models will get better.

When?

I have a day job! At first, I will shoot for weekly posts, maybe more frequently and occasionally less. When others begin contributing, publication frequency will necessarily increase.

Where?

radicalmarketsimulation.com