DA-MAOS (Double Auction Multi-Agent Open Source)

Market Experimentation Platform:

A testbed for simulating double-auction markets which contain

multiple, possibly heterogeneous, agents with either fixed or

randomly specified true reservation values.

 

Last Updated: August 21st, 2008


Site maintained by:
Nanpeng Yu and Ian Guffy
Iowa State University
Ames, IA, 50010
ynp@iastate.edu
ianguffy@iastate.edu
 
Agent-Based Computational Economics (ACE)


Software Release Disclaimer:

All software provided below is unsupported and provided as-is, without warranty of any kind.

Table of Contents:

Software Overview

 

Introduction

DA-MAOS is programmed in Java, and the Repast J toolkit. It is one of the first modular, extensible and open source computer test-beds for a double auction market with multiple learning agents that have either fixed or randomly specified true reservation values. DA-MAOS provides a graphical user interface (GUI), which allows the user to control a wide variety of parameters and to perform a vast array of experiments in an auction market context.

 

Architecture

The general architecture of this demo is depicted in Figure 1.

 

The ‘TradingWorld’ class uses the parameters the user has entered into the GUI to create the ‘TradingMarket’ objects. These ‘TradingMarket’ objects each represent a market composed of buyer agents, seller agents, a data recording module called Stat, and a trading protocol (MarketStyle), that defines how the market is organized and operated.

 

During a time step, each ‘TradingMarket’ performs one round (or day) of trading. While implemented differently in various MarketStyles, a round is generally understood to give each agent at least one opportunity to trade.

 

After every trading market completes a trading round, the average daily market efficiency, and the average daily market surplus are displayed. Optionally, histograms of the buyer market advantage, seller market advantage, market efficiency and market surplus for every batched run (of the most recent trading round) can be displayed as well. These statistics are handled by the Stat object.

 

Information about every single trade performed is also recorded in an output file that is in a standard comma separated format (CSV) so that it can be easily imported into any spreadsheet program. 

 

Figure 1: Architecture of the Demo

 

Using DA-MAOS

 

This section describes all of the user-settable options.

 

Below is a screenshot of the DA-MAOS’s GUI.

 

The Market&AgentOptions tab holds all of the options available for a market simulation run, while the Display Options tab allows the user to select which histograms to display and to name the output file.

The Random Seed is used to seed the master random number generator, which then creates seeds for subsequent random number generators ( See Random Number Generation.)

 

The Number of Batched Runs specifies the number of TradingMarket objects that are created.

 

The Market Type specifies which MarketStyle to use. For example, if you select the Market Type to be UniformPriceAuction, then all of the TradingMarket objects will be of the type UniformPriceAuction.

 

The Discriminatory-Price K-double Auction has two specific user-settable parameters:

       [10 + (20 - 10) * 0.3 = $13]

 

    The UniformPriceAuction has a single parameter, the K-value, which is user-selectable and which works the same way as it does for the Discriminatory-Price K-double Auction.

Agents are defined in groups using the AgentOptions section. There are no limitations on the numbers, types or sizes of groups you can define. Once you set all of the parameters for a group of agents, hit the Add button, after which a shorthand description of that group will be placed in the Existing Agent Groups box. Then you can set the parameters of the next group. Agent groups can also be loaded from an input file. (See Loading & Saving)

AgentType specifies if the group of agents is composed of buyers or sellers.

 

The reservation price is the minimum price a seller agent is willing to sell at, or the maximum price a buyer agent is willing to buy at. Seller agents will never ask a price that is higher than its MaximumReservationValue. The reservation price options works as follows: if the Reservation Value is set to -1, then each agent is randomly and independently assigned a reservation price between zero and the MaximumReservationValue. In this option, each agent’s reservation price is independently selected from each other agent, not only within each batched run, but also across batched run. However, if the Reservation Value is non-negative, then the reservation values for all of the agents in the group are set to the ReservationValue. This means that if you want to quickly set up a simulation with buyers and sellers that have heterogeneous reservation values, then you just need to set the MaximumReservationValue.

 

There are four learning styles for the user to choose from:

 

The Add button will add a group of agents with the parameters specified by the Agent Options section. The Remove button will remove the group of agents selected in the Existing Agent Groups box.

 

The Start button loads the RepastJ controls, which are only used to run and pause the simulation. The blue triangle play button can be used to start the simulation, while the stop and pause buttons work as expected. The PauseAt option allows the user to have the simulation pause after a specific number of trading rounds.

 

Loading, Saving and Output

Definition of terms: In this section, when we use the term 'settings,' we are broadly referring to everything that can be set by the user in the GUI on both the Market&Agent panel and the Display Options panel, as well as the groups of agents that have been defined.

 

Once you have set up your simulation (but before you press the start button), you can save these settings to a file. This file can then be loaded at a later date, the settings modified if desired, and then used to run a simulation. To do so, simply go to the File menu, and go to the Save option. Then navigate to the desired saving location, name the file, and hit save. DA-MAOS has no restriction on the name of this file.  From here, you can continue to modify settings, save simulation settings, or run the simulation.

 

To load a saved file, simply go to the File menu, select the Open option, locate the desired file, and hit the open button. Then you can further modify the parameters, save them and/or run the simulation.

 

The name and location of the output file can be set in the Display Options panel. This output file is in standard comma separated format (CSV), so it should end in ".csv", though DA-MAOS does not enforce this. The output file contains information about the simulation, and records information about what happened in each batched run after every trading round.

(Note: the output file cannot be used to load settings into a new simulation.) By default, the output file resides in your project folder (if you are using the source code) or in the same folder as the JAR file (if you are using the executable).

 

Implementation Details

 

GUI, TradingWorld and RepastJ

When the “start” button is pressed, the GUI takes all of the user-specified parameters, puts them into a SimSpecs object, and passes that object on to the TradingWorld object, and initializes the RepastJ controls.

 

Once the blue triangular RepastJ "play" button is pressed, the TradingWorld object then calls the TradingMarket.buildModel() method, which creates the TradingMarket objects and adds them to the RepastJ agentList. This agentList is used by the TradingWorld.buildSchedule() method, which creates a schedule for the simulation. This schedule specifies the order of the actions to be taken by RepastJ to run the simulation. It specifies that the trade() method needs to be called on each TradingMarket object, and then the histograms need to be updated for the results of this trading round.

 

The simulation is then run until Repast is told to stop.

 

TradingMarket

When created, each TradingMarket must create its component BuyerAgents, SellerAgents, MarketStyle, and Stat objects, using the SimSpecs object it is given. It must also calculate some benchmark values needed for later calculations, such as the maximum possible buyer and seller surpluses. When the trade() method is called on a TradingMarket, the TradingMarket calls MarketStyle.Trade() method, giving the list of buyers, sellers, and a Stat object. Then the results of the MarketStyle.Trade() function are used to compute the values displayed as histograms and output to the data file.

 

MarketStyles & TransInfo

Both MarketStyles, UniformPriceAuction and DiscPriceKDoubleAuction, implement the MarketStyle interface. This interface specifies that the Trade() method must be given a list of BuyerAgents, a list of SellerAgents, and a Stat object.

 

After an attempted trade, the results are given to the BuyerAgent and SellerAgent involved in the trade in the form of a TransInfo object. This object contains fields for the buyerID, sellerID, buyerOffer, sellerOffer, transPrice (the transaction price) and a flag to record if the trade was successful or not. Fields that are not applicable are given an arbitrary negative value.  For example, if an attempted trade was unsuccessful, then there will be no transaction price, so the transPrice will be given a value of -2. Currently, the buyerID and sellerID fields are not utilized by any TradingStyle.

 

UniformPriceAuction

When UniformPriceAuction.Trade() is called, it calls the formBidPrice() method on each BuyerAgent, as well as formAskPrice() on each SellerAgent. These two lists of bid and ask prices are then sorted, and the  competitive market clearing price (CMC) is calculated.  Then, each BuyerAgent and SellerAgent is given a TransInfo object via their setInfo() functions that uses the CMC price as the transPrice. Therefore, the agents that have a bid price above or an ask price below the CMC price successfully trade. Note that because the traders have only one opportunity to trade per trading round, that the value of the maxNumberOfTrades variable is ignored.

 

The k value is used when the CMC point is not unique: in such cases, the CMC point is set between the maximum and minimum possible CMC values, similar to DiscPriceKDoubleAuction (described elsewhere).

 

DiscPriceKDoubleAuction

When DiscPriceKDoubleAuction.Trade() is called, a loop begins with the following steps:

  1. Randomly choose a BuyerAgent and a SellerAgent, and call their formBidPrice() and formAskPrice() methods, respectively.

  2. Check if they have traded yet.

  3. If either of them has traded, do nothing. Otherwise, if the bid price is greater than or equal to the ask price, perform the trade, and give both agents a TransInfo object. If the bid price is less than the ask price, give both agents a TransInfo object with a negative transPrice.

  4. Increment counter.

Note: DiscPriceKDoubleAuction does not remove agents who have traded previously from the pool of potential agents that can be chosen to attempt a trade.

 

This loop continues through these steps until its counter has reached maxNumberOfTrades.

 

BuyerAgents, SellerAgents, and TradingStyles

Wherever possible, the implementations of BuyerAgent and SellerAgent have been kept identical.  When the BuyerAgent.formBidPrice() is called, the getBidPrice() method of the BuyerAgent’s TradingStyle is called. Similarly, when SellerAgent.formAskPrice() is called, the getAskPrice() of its TradingStyle is called. An offer is then returned from the TradingStyle object to the agent, and this offer is then passed back to the MarketStyle.

 

When either kind of agent is created, if a positive resPrice is given, then the agent’s reservationPrice is set to that value. Otherwise, the reservationPrice is set to a randomly selected value in the range of [0, maxReservationValue] for a BuyerAgent, or a SellerAgent. The action domain has a size given by the Domain Size GUI paremeter. For a BuyerAgent, the Bidding Markup Lower Bound and the Bidding Markup Upper Bound are given as a percentage of the reservation value, which are multiplied by the reservation value to get the minimum and maximum values in the action domain, respectively. For a SellerAgent, the Bidding Markup Lower Bound and the Bidding Markup Upper Bound are given as a percentage which is added to 100% and multiplied by the reservation value to arrive at the minimum and maximum values of the action domain, respectively. Note: As specified below, some learning styles do not use part or all of these action domain parameters.

 

BuyerAgents and SellerAgents accept the results of attempted trades through their setInfo() method, which takes a TransInfo object as its only argument. This object is then used as the argument for the TradingStyle.setInfo() method.

 

TradingStyle is an interface that specifies that each TradingStyle must have getBidPrice, getAskPrice, setInfo, setResPrice and setAgentType methods. (Therefore, each TradingStyle must be able to make offers for both BuyerAgents and SellerAgents.) The setAgentType method is used to tell the TradingStyle which type of agent it is making offers for, and the setResPrice method is used to set the reservationPrice of the TradingStyle. The setInfo method is used to accept a TransInfo object which describes the results of an attempted trade. The getBidPrice and getAskPrice methods return bid and ask prices, respectively.

 

The four types of TradingStyle implemented are described by how each of their constructors, getBidPrice, getAskPrice and setInfo methods are implemented.

 

Constant

Constructor: The reservation value is set to resPrice, and a bid/ask price is set to the value given in the LearningStyleParameters object for Constant, ConstantLSParams..

getBidPrice: Returns the bid price.

getAskPrice: Returns the ask price.

setInfo: Does not make use of the TransInfo object.

 

Note: Constant ignores all of the action domain parameters.

 

ZI

Constructor: Sets the reservationPrice and maxReservationValue to the values provided in its arguments.

getBidPrice: Randomly selects a price in the range [ Bidding Markup Lower Bound * reservation value / 100,  Bidding Markup Upper Bound * reservation value / 100 ]

getAskPrice: Randomly selects a price in the range [ (Bidding Markup Lower Bound + 100) * reservation value / 100,  (Bidding Markup Upper Bound + 100) * reservation value / 100 ]

(Neither getBidPrice nor getAskPrice have discretized action domains for this learning style.)

setInfo: Does not make use of the TransInfo object.

 

Note: Because ZI is implemented to have a continuous action domain, the Domain Size parameter is ignored.

 

ModRothErev

Constructor: Set experimentation, recency and the initial propensity parameters to the values given in the arguments. The actionDomain, probability, propensity and accProp arrays are created to be of the length specified in domainSize, so their indices will range from zero to (domainSize - 1).  The actionDomain is initialized such that the values are uniformly distributed within the specified range. The probability and propensity are initialized such that the value of each index is (1 / domainSize ) and INITIAL_PROPENSITY, respectively. The accumulated propensity, accProp, is initialized such that the value at each index n is equal to ( n / domainSize ). These values are passed to ModRothErev in the LearningStyleParameters object for ModRothErev, ModRothErevParams.

getBidPrice: Calls the pickAction method, which returns the picked action that is within the range of the Bidding Markup Upper Bound and Lower Bound. This number is then multiplied by the reservationValue to get the bid price, and this price is then returned to the BuyerAgent.

getAskPrice: Calls the pickAction method, which returns the picked action that is within the range of the Bidding Markup Upper Bound and Lower Bound. This number is then multiplied by the reservationValue, the result of which is summed with the reservationValue to get the ask price, and this price is then returned to the SellerAgent.

pickAction: Update the probability array according to the current propensity array (as per equation (3) below), and then update the accProp array accordingly. Pick a random number in the domain [0,1], and iterate through the accumulated probability array (in ascending order) until the first value greater than or equal to the random number is found. The index of this value is then returned as the index of the action to be taken.  (Because all of the arrays in ModRothErev are of the same length, their indices all correspond to each other.)

setInfo: If the attempted trade was successful, it calculates the previousReward, which is equal to the buyer or seller surplus. Otherwise the previousReward is set to zero. Then, the propensity is updated according to equations (1) and (2) below.

 

 

 

 

SimpBeliefBased

Constructor: Creates the actionDomain and expectedReward arrays to be of size domainSize. The actionDomain is initialized such that the values are uniformly distributed within the specified range. A HashMap object, called observationMap is created to store data pairs representing the price observed and the number of times that price has been observed.

getBidPrice: Calls the pickAction method, which returns the picked action that is within the range of the Bidding Markup Upper Bound and Lower Bound. This number is then multiplied by the reservationValue to get the bid price, and this price is then returned to the BuyerAgent.

getAskPrice: Calls the pickAction method, which returns the picked action that is within the range of the Bidding Markup Upper Bound and Lower Bound. This number is then multiplied by the reservationValue, the result of which is summed with the reservationValue to get the ask price, and this price is then returned to the SellerAgent.

pickAction: Starting at a random index of expectedReward, the entire array is iterated through to find the index which corresponds to the highest value in the array. Each round this iteration is randomly chosen to be in ascending or descending order, beginning at the first or last index of the array, respectively. This index is then used to return the action in the actionDomain with the highest expected reward. (Because the expectedReward is zero for each action the first time SimpBeliefBased is asked for a price, a random action from the actionDomain is chosen in this case.)

setInfo: Using the data provided in the TransInfo object, this method records the value of the other agent’s offer as the offerPrice. The observationMap is then updated to reflect the number of times that the offerPrice has been observed by this agent. (Remember that a TransInfo object is only given to the agent in the case of an attempted trade. In the case of a UniformPriceAuction, the offerPrice will represent the market clearing price.)

calculateExpectedReward: The expected reward for each action in the action domain is calculated based on the new distribution of observed prices in the observationMap. To do this, for each action in the actionDomain, the profits are calculated against each observed price in the observationMap and their probability. This is precisely described by equation (4) below.

     


 

Random Number Generation

DA-MAOS uses the RngPack 1.1a pseudorandom number generation package (see  Development Software) wherever "randomness" is required. Because we are using a pseudorandom number generator, whenever random numbers are referred to in this documentation, they are actually pseudorandom numbers. DA-MAOS uses the RngPack's RANMAR generator, which has a period of 1043.

 

The following description of RANMAR comes from the RngPack documentation:

 

"RANMAR is a lagged Fibonacci generator proposed by Marsaglia and Zaman and is a good research grade generator. This version of RANMAR is based on the paper by James, which is a good reference for the properties of RANMAR and several other generators.
 

REFERENCES:
F. James, Comp. Phys. Comm. 60 (1990) p 329-344
and was originally described in
G. Marsaglia, A. Zaman and W.-W Tsang, Stat. Prob. Lett 9 (1990) p 35."

 

The RANMAR generator is encapsulated in the DA-MAOS RandomNumber class. In order to ensure platform independence, each object that has a need for random numbers is given its own instance of RandomNumbers. In order to ensure that simulations are reproducible given the same master seed and initial parameters, all of these instances of RandomNumbers are seeded, directly or indirectly, by the master seed. This is described in detail below.

 

The master seed is set by the user in the GUI. This seed is used to seed the masterSeedGen generator in TradingWorld. The masterSeedGen is then used to create seeds for each batched run. These seeds are passed via the simSpecs object to the tradingMarket's. The seed from the simSpecs object is then used to seed the tradingMarketAgentSeedGen, which is used to create a seed for the MarketStyle object and to seed the BuyerAgentSeedGen and SellerAgentSeedGen (which both also reside in the TradingMarket object). The seed for the MarketStyle object's random number generator is then passed to the MarketStyle object via its constructor.

 

The random number generators in BuyerAgents and SellerAgents are seeded in a similar manner, so for clarity only BuyerAgent will be described in further detail. When a BuyerAgent is created in TradingMarket, two seeds are generated by the BuyerAgentSeedGen, buyerSeed and tsSeed. The buyerSeed is used to seed the buyer's random number generator, called rand, and the buyer's trading style's random number generator. The seeds for both the BuyerAgent's random number generator and its TradingStyle's random number generator are passed to them through their respective constructors.

 

Software Downloads and Supporting Materials

Detailed instructions are provided below for downloading, compiling, and running the latest version of DA-MAOS. Explanations of the modifications incorporated into successive versions released to date can be obtained at the Version Release History.

DA-MAOS -- Version 0.91 (Guffy and Yu):

Licensing Terms

The DA-MAOS package is licensed by the copyright holders (Ian Guffy and Yu Nanpeng) as free open-source software under the terms of the GNU General Public License (GPL). Anyone who is interested is allowed to view, modify, and/or improve upon the code used to produce this package, but any software generated using all or part of this code must be released as free open-source software in turn. The GNU GPL can be viewed in its entirety here.

          DA-MAOS uses the RNGpack libraries to generate random numbers. RNGpack is licensed under the terms of the BSD License.

          DA-MAOS uses the JFreeChart libraries to display some of the output. JFreeChart is licensed under the terms of the Lesser General Public License (LGPL).

          DA-MAOS used the RepastJ implementation of Repast 3.1 as a multi-agent based platform. RepastJ is licensed under the terms of the BSD License.

Publications & References

 

1. Nicolaisen, J., Petrov, V., and Tesfatsion, L. Market Power and Efficiency in a Computational Electricity Market with Discriminatory Double-auction Pricing. IEEE Transactions on Evolutionary Computing 5, (October 2001), 504–523.

2. Dhananjay K. Gode and Shyam Sunder. 1993. Allocative Efficiency of Markets with Zero-Intelligence Traders: Market as a Partial Substitute for Individual Rationality. The Journal of Political Economy. 101 (Feb. 1993). 119-137.

3. McBride, M. Zero-Intelligence Trading Demo 

4. Houle, P. RngPack version 1.1a. RngPack

5. JFreeChart, version 1.06. JFree.org

6. RepastJ, Version 3.1. RepastJ

Acknowledgements

We would like to thank Professor Tesfatsion for her inspiring discussion and valuable comments during the development process. She has been very generous in sharing her time to help us work through many of the implementation details of this test bed and in providing us invaluable resources.

           Thank you to Matthew Renze for discovering a bug in the calculation of the CMC point in version 0.81, and suggesting potential modifications.

            Thank you to Tyson Williams for his code suggestions.