Agent Based Modeling: A Simple Market

I was reading Nate Silver’s The Signal and the Noise today and I ran across his idea of Bayesland, a place where people walk around with sandwich boards listing things on which they would place a bet. If they meet a person whose odds on an event differs substantially from their own odds on that event, then the two will make a bet.

I thought that it would be neat to simulate this idea and see what would happen. In my example, we have three agents that bet on the probability of a coin coming up heads. The coin is simulated through a Bernoulli process, and the true probability or heads is 60%.

I created an agent class that would be initialized with a key, a hypothesis regarding the probability of heads, a bankroll, and some idea of the past events. When two agents meet, they compare keys. If they have the same keys, and a difference of opinion regarding the probability of a heads, then they make a bet. Each participant is willing to bet 5% of their bankroll, and when they meet and bet, they just bet the average of whatever they’re willing to wager. (That’s not a very important detail.)

Another arbitrary implementation detail was the fact that each agent has a slightly different recollection of past events. This is controlled in a probabilistic manner. I thought that agents with perfect knowledge would be less interesting. This could also be interpreted as a form of irrationality. Anyway, what I was hoping to see was the bettors converging around the actual value of the probability of heads, which I set at 60%. This sort of happened most of the time.

Code

import scipy.stats
import numpy as np
import random

Define an Agent class with a number of randomly assigned parameters, and a means of learning about the environment.

class Agent:
    def __init__( self ):
        # two agents will interact if they have the same key
        self.key = random.randint(1,5)
        # select a random probability of heads
        self.hypothesis = scipy.stats.uniform(0,1).rvs()
        # start with a hundred dolalrs
        self.bank = 100
        # set the bet at 5% of the bankroll
        self.bet = self.bank * 0.05
        # initialize the "memory"
        self.memory = [ scipy.stats.uniform(0,1).rvs() ]
        # set some recollection accuracy
        self.accuracy = scipy.stats.uniform(0,0.1).rvs()
    def inform( self, outcome ):
        # if u is less than the recollection accuracy
        # then the agent "forgets" the outcome of the flip
        u = scipy.stats.uniform(0,1).rvs()
        if u < self.accuracy:
            self.memory.append( outcome )
        # take the mean of the observations
        observations = np.mean( self.memory )
        # update the hypothesis based on the observations
        self.hypothesis = np.mean( [ self.hypothesis, observations ] )
    def update( self ):
        # reset the key
        self.key = random.randint(1,5)
        # reset the recollection accuracy
        self.accuracy = scipy.stats.uniform(0,0.1).rvs()
        # update the bet
        self.bet = self.bank * 0.05

A loop to pit the agents against each other in a random fashion according to the “key” each agent holds, which is changed at the end of each iteration.

# create a list of three agents
a = [ Agent() for i in range(3) ]

# create three clunky lists
A, B, C = list(), list(), list()

# go for 500 turns
for i in range( 500 ):
    
    # flip a coin with a 60% chance of landing heads up
    flip = scipy.stats.bernoulli(0.6).rvs()
    
    # for each agent..
    for j in range( len( a ) ):
        # for each remaining agent..
        for k in range( j+1, len(a) ):
            
            # if they meet by chance (have the same key)
            if a[j].key == a[k].key:
                
                # and if their opinions differ greatly enough..
                h0, h1 = a[j].hypothesis, a[k].hypothesis
                if np.abs( h0 - h1 ) > 0.1:
                    
                    # the greater hypothesis bets heads
                    # the lesser hypothesis bets tails
                    if h0 > h1:
                        head = a[j]
                        tail = a[k]
                        bet = np.mean( [ a[j].bet, a[k].bet ] )
                    elif h1 > h0:
                        head = a[k]
                        tail = a[j]
                        bet = np.mean( [ a[j].bet, a[k].bet ] )
                
                # if the flip was a heads..
                if flip == 1:
                    head.bank += bet
                    tail.bank -= bet
                elif flip == 0:
                    head.bank -= bet
                    tail.bank += bet
    
    # inform the agents of the flip
    # change their keys and recollection probability
    for agent in a:
        agent.inform( flip )
        agent.update()
        
    # clunkily record the results
    A.append( [ a[0].hypothesis, a[0].bank ] )
    B.append( [ a[1].hypothesis, a[1].bank ] )
    C.append( [ a[2].hypothesis, a[2].bank ] )

Now, plot the results!

# convert to NumPy arrays
A = np.array( A ).T
B = np.array( B ).T
C = np.array( C ).T

# begin plotting
fig, axs = subplots(2,1)
axs[0].set_ylabel("Prediction")
axs[0].plot( A[0] )
axs[0].plot( B[0] )
axs[0].plot( C[0] )
axs[0].set_ylim(0,1)

axs[1].set_ylabel('Bankroll')
axs[1].plot( A[1] )
axs[1].plot( B[1] )
axs[1].plot( C[1] )

savefig("market_bets.png",dpi=200,bbox_inches='tight')