|
|
Software Release Disclaimer:
All software provided below is unsupported and provided as-is, without warranty of any kind.
Table of Contents:
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

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:
The K-value determines where the price is set between the bid and the ask price. For example, a value of 0.5 means that the price is set half way between the bid and the ask price. Furthermore, if k is set to 0.3, and the bid and ask prices are $10 and $20, respectively, then the final price will be set to $13.
[10 + (20 - 10) * 0.3 = $13]
This market works by randomly selecting a buyer agent and a seller agent, and attempting a trade. (A trade only occurs if neither agent has traded before and the bid price exceeds the ask price.) While agents can only trade once per round, this market keeps pairing buyer and seller agents, regardless of how many successful trades have occurred, until the Maximum Number of Attempted Trades Per Round is reached. Note: since each TradingMarket object attempts to trade the maximum number of trades each round, setting both this value and the number of batched runs significantly higher than the default values may result in the simulation either running very slowly or running out of memory.
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 Constant learning style always bids or asks the OfferPrice, as specified by the user. (This learning style doesn’t actually learn.)
The Zero Intelligence trader picks a price randomly. The bid/ask price is set randomly within its action domain. (This learning style doesn’t actually learn.)
The ModifiedRothErev learning style uses the Modified Roth-Erev reinforcement learning algorithm to select bid or ask prices. The algorithm’s initial propensity, experimentation and recency parameter values can be set by the user. (See implementation for more detail)
The Belief Based learning style uses a Simplified Belief-Based learning algorithm to select bid or ask prices. (See implementation for more detail)
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).
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.
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:
Randomly choose a BuyerAgent and a SellerAgent, and call their formBidPrice() and formAskPrice() methods, respectively.
Check if they have traded yet.
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.
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.
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.

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.

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.
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):
Ian Guffy and Yu Nanpeng, DA-MAOS --Version 0.91 Release Date: 6 Aug 2008
Download the executable JAR file (154 KB) To run, unzip the zip file into a folder, and double-click on the JAR file within it.
Download the source code and necessary libraries (17.5 MB) To set this up as a project in NetBeans, see below.
The basic features of DA-MAOS (Version 0.91) are described in the above software overview.
Version 0.91: Published August 4th, 2008
The following free software are used in the development of DA-MAOS.
Java platform: Java SE Development Kit (JDK) 6 update 1 (6u1) (Download page). You can download JDK 6u1 (or higher) either alone or in combination with the NetBeans Integrated Development Environment (IDE) 5.5.1 (or higher).
Java IDE: NetBeans IDE 5.5.1 (or higher) (Download Page). You can also download the Java SE Development Kit (JDK) 6u1 (or higher) with the NetBeans IDE from this page.
Java Chart Library: JFreeChart (Download Page).
Repast J: A Java Agent-Based Toolkit, Version 3.1 (Download page). For an on-line self-study guide for Repast J and Java, visit here.
RNGPack: A pseudorandom number generator package for Java, Version 1.1a. The home page can be found here.
The first step is to install JDK 6u1 (or higher) either separately or in combination with the NetBeans IDE (5.5.1 or higher). Please note that JDK 6u1 (or higher) is required for the DA-MAOS code to run correctly. Error messages will be generated if you attempt to compile the DA-MAOS code with any earlier JDK release.
The second step is to install the NetBeans IDE (5.5.1 or higher) if you have not already done so in step one.
The final step is then to use the NetBeans IDE to open the project folder. To do this, got to the File menu, and select the Open Project option. Navigate to the location of the (the source code should be unzipped before performing this step) open the DA-MAOSv0.9 Source folder on your machine, and sect the DA-MAOS v0.9 folder, and hit the Open Project button.
RepastJ and JFreeChart must each also be added to NetBeans as a library, and included in this project.
To do so, go to "Tools" on the menu bar, select "Library Manager", and then click the "New Library" button and give your library a name. Then click the "Add JAR/Folder..." button on the Classpath tab and select the the jar files for the library you want to create ( To add RepastJ you need to select everything in the C:/.../RepastJ/lib folder AND add the C:/.../RepastJ/repast.jar file ).
To add the library to your project, right click on the project name, select "properties", and then "Libraries", and on the "Compile" tab click the "Add Library..." button.
These same two steps must also be performed for JFreeChart
Note in particular that a jar file for Repast J 3.1 (repast.jar) is included in the required library directory extracted from DA-MAOS zip file. Consequently, Repast J 3.1 does not have to be separately downloaded and installed unless you are planning to undertake code development for parts of the DA-MAOS code involving Repast J and you would like to have access to RePast J debugging facilities.
Once you get DA-MAOS to compile, you are ready to perform groundbreaking experimental simulations.
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.
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
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.