Bulls and Bears
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
BULLS & BEARS - A MINIMALIST ARTIFICIAL STOCK MARKET
WHAT IS IT?
The model explores the mechanism of price formation in a stock market. The model is relatively simple, yet generates all the emergent properties of real stock markets. It also shows under what circumstances one can get pathological price behaviour such as monotonic moves to zero or infinity, or permanent oscillations between two price levels.
THE MODEL
There is one asset (a stock) in the market. The model is non-spatial. Agents (investors) do not interact with each other directly but with the stock price, which is determined by the total demand for the stock, i.e., they interact indirectly in their aggregate. So net aggregate demand determines the price movement in each time period (tick), and this price movement then determines the investors' demand in the next time period. The price formation mechanism is thus highly recursive.
Investor types
There is a variable number of investors. These investors are of two types, followers and contrarians, with two diametrically opposite strategies. If the stock’s price moved up in the last time period, followers want to buy while contrarians wish to sell, and vice versa for a downward move in price. There are therefore always buyers and sellers, but with differing demands of the quantity desired to transact. There is no leverage, but short selling is allowed.
Sales of the stock generate cash for an investor and purchases require cash. An investor's wealth is the sum of the value of their shareholding and their cash balance.
Demand function
In every time period there will be demand from both followers and contrarians. The magnitude of their demand represents "how big do I want to bet" and is determined by a demand function. It is always a positive number or zero. The sign of this demand, i.e. buy or sell, is then determined separately by the previous price move.
For each investor, their demand function contains three components:
Risk demand. The value of stock demanded is proportional to the moving average (m.a.) of returns over the last m returns. (Investors have a memory of the last m time periods.) So risk demand = (k4) (r) (s) (m.a.) where: k4 is an empirical constant, r is a scale factor: r = 1 if m.a. >=0 and r = 2.5 if m.a. <0, since studies have shown that investors hate losses ~2.5x more than they love gains. s is the sign. If the m.a. is positive, investors know there is a a higher probability of future negative returns, so the risk appetite of contrarians will increase and that of followers will decrease. So if m.a. is +/-, the sign for contrarians will be +/- and for followers will be -/+.
The value of the risk demand is the same for all investors in one time period. Individual investors are then randomly assigned an individual risk demand in the range [0, risk demand].
Herd demand. The value of stock demanded is proportional to the aggregate stock demanded by other investors of the same type. It is assumed that followers and contrarians have different propensities to herd, and are given two different empirical constants.
So herd demand = (k2) (total demand of followers) for followers, and ... herd demand = - (k3) (total demand of contrarians) for contrarians Note that the herd demand can only be of positive sign.
This value of herd demand is therefore different for each type of investor in a time period. Individual investors of each type, i.e. followers and contrarians, are then randomly assigned an individual herd demand in the range [0, herd demand - followers] and [0, herd demand - contrarians] respectively.
Wealth demand. The value of stock demanded is proportional to the investor's wealth. So wealth demand = (k1) (wealth), where k1 is an empirical constant.
This demand component is the same for all investors. Note "wealth demand" will be of the same sign as wealth, which we assume can be negative as well as positive. This value of wealth demand is the same for all investors in one time period. Individual investors are then randomly assigned an individual wealth demand in the range [0, wealth demand].
Investors' wealth is dependent on how many shares they buy or sell in each time period. The value of their shares will then change with the new share price in the next period, in turn changing their wealth.
The total demand for each investor is then the sum of these three demands. Its sign must always be positive as it is the "size of desired bet", and its sign will simply be determined by the direction of the previous price move. Therefore, if the magnitude of total demand is less than zero, it is set to zero.
Price, price return, value and volume traded
The net share value demanded is D = (demand from followers + demand from contrarians) where the two demands are appropriately signed.
The change in stock price is then proportional to this net demand. So, at time t the price is Pt = Pt-1 + (k5) (D) where k5 is an empirical constant.
Note that changing the number of investors is not neutral - a larger number of investors will tend to result in larger net demands D, which will cause larger price changes.
The stock price return will be Rt = (Pt-1 / Pt) - 1 (log returns are not strictly necessary here).
Followers and contrarians will usually wish to transact with different amounts of demand, so to clear the market the total share value traded is Tt = min (|demand from followers|, |demand from contrarians|)
Note that all the model's relationships are linear, while in the real world everything is non-linear!
HOW TO USE IT
The input items in the interface tab are largely self-explanatory, consisting of the number of investors, the fraction of investors who are contrarians, the persistence of investors' memory (m time periods) and the empirical parameters k1...k5. The command buttons are the usual ones. "Go once" is useful for examining the causes of a price change from tick to tick and for debugging. The "go slowly" button uses one tick every 0.5 seconds, to slow things down.
OUTPUTS
The prime output is the share price graph. The value of stock demanded (in dollars) by followers and contrarians, and their total, is also shown. The next plot shows the percentage return in each period, together with the moving average of the previous m periods, i.e. it shows if the share has generally gone up or down in investors' recent memory, and by how much. The distribution of these returns is also shown. Market returns are not random and their distribution is not normal. The volatility of price movements over the past 36 periods is calculated and displayed. Trade, in both dollars and number of shares, is also plotted, as is the dollar wealth of followers, contrarians and their total. The Demand/Wealth graph is discussed in NETLOGO FEATURES.
This data can then be extracted with BehaviorSpace and analysed to see if it accords with the known emergent phenomena of markets, which include:
- persistence of returns
- volatility clustering
- low autocorrelation
- excess kurtosis
- volume correlation with price volatility
THINGS TO NOTICE
A market consisting of only these two types of investors is intrinsically unstable. This is exacerbated by the fact that as an investor's wealth grows, they take larger buying or selling positions.
There are two bounding unstable price behaviors: * If the demand of followers is greater than contrarians (e.g., there are no contrarians) and the price, for example, declines, then it will continue declining to zero. Conversely if the price moves up it will continue to infinity. * If the demand of contrarians is greater than that of followers, for either initial price movement, the price subsequently will become cyclical, oscillating indefinitely between two values.
Most combinations of parameter settings eventually lead to these bounding behaviors.
Also, in the case of monotonic increases or decreases in price, the wealth of followers will become infinite, while price oscillations will see the wealth of contrarians rise to infinity, as these investors will make the correct decision in every time period.
These behaviors are legitimate and would happen in a real market as modeled. They are not a code flaw. They demonstrate the intrinsic instability of markets. Market crashes, for example, often occur without any obvious proximate cause - they are simply emergent behavior. (And, inevitably, if the price goes to zero, many variables tend to blow up.)
THINGS TO TRY
Best explored with the "go once" or "go slowly" buttons. Try adjusting the parameters under various settings. How sensitive is the stability of price behavior to these parameters? Which parameters counteract each other and which reinforce each other? If there are exactly equal numbers of followers and contrarians, what does the stock price do? See the extreme situations where there are only followers or contrarians, which breaks the market. Do the returns look random or is there clustering? Can you see fat tails and kurtosis in the return distributions? Does the price volatility correlate with anything?
EXTENDING THE MODEL
Possible embellishments of the model are limitless. Additional investor categories such as long-term and short-term players and insider traders could be added. Individual investors could be allowed to use leverage. Also, risk appetite (as determined by the moving average) has been the same for each investor type. Ideally, each investor should use the moving average of their individual return history. Additional assets, as well as transaction costs could be incorporated. Most importantly, investors could be given a range of different and possibly competing strategies, including adaptive ones - which is closest to the actual behaviour in real markets. (This was the original Santa Fe Artificial Stock Market Model.) Note that complicating the model does not change the market's emergent properties materially. It can however be useful in examining the sensitivity to these additional parameters.
NETLOGO FEATURES
Because the model is non-spatial, the main plot has been transformed into a graph. This graph shows 5 dimensions for each agent on the same plot using shape, coordinates, size and color intensity to indicate agent type, wealth, demand, herd influence and risk aversion, respectively.
The histogram is of a global variable, not a turtle property. In this case, 'histogram' operates on a list. So the variable's values were accumulated in a list, with the list being extended by one element at each tick. Lists were also required to accumulate histories for the moving average of the last m returns and 36-period price volatility.
They were also used for the dynamic scaling of some graphs. Smart scaling is implemented on the 'Returns distribution', 'Price volatility' and 'Trade' graphs via plot update commands.
A zero axis is drawn on the Demand and Period return & MA graphs. The Price volatility graph only begins drawing after 36 periods, using the plot-pen-up command in the plot update commands.
RELATED MODELS
The seminal model in this genre was the original Santa Fe Artificial Stock Market Model: Arthur, W. B., Holland, J. H., LeBaron, B., Palmer, R., & Taylor, P. (1996). Asset pricing under endogenous expectation in an artificial stock market. (No. 96-12-093).
A later retrospection on it was: LeBaron, B. (2002). Building the Santa Fe artificial stock market. Physica A, 1-20.
Various improvements were suggested in the book: Ehrentreich, N. (2007). Agent-based modeling: The Santa Fe Institute artificial stock market model revisited. (Vol. 602). Springer Science & Business Media.
There has been surprisingly little highly-cited research since then. Two studies are: Šperka, R., & Spišák, M. (2013). Transaction costs influence on the stability of financial market: agent-based simulation. Journal of Business Economics and Management, 14(sup1), S1-S12. Oldham, M. (2017). Introducing a multi-asset stock market to test the power of investor networks. Journal of Artificial Societies and Social Simulation, 20(4).
HOW TO CITE
If you mention this model or the NetLogo software in a publication, we ask that you include the citations below.
For the model itself: * Busetti, F. R., (2021). NetLogo Bulls & Bears model. http://ccl.northwestern.edu/netlogo/models/BullsBears. Center for Connected Learning and Computer-Based Modeling, Northwestern Institute on Complex Systems, Northwestern University, Evanston, IL.
Please cite the NetLogo software as: * Wilensky, U. (1999). NetLogo. http://ccl.northwestern.edu/netlogo/. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
COPYRIGHT AND LICENSE
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
Comments and Questions
;; Bulls & Bears ;; A Minimalist Artificial Stock Market globals [ ;; From sliders: ;; investors ;; total number of investors/agents ;; fraction-contrarians ;; percentage of investors that are contrarians ;; memory ;; number of periods m that price is remembered ;; wealth-factor ;; coefficient k1 ;; maximum-herd-effect-followers ;; coefficient k2 ;; maximum-herd-effect-contrarians ;; coefficient k3 ;; maximum-risk-appetite ;; coefficient k4 ;; price-sensitivity-to-demand ;; coefficient k5 ;; Others: num-contrarians ;; total number of contrarians num-followers ;; total number of followers risk-appetite-big ;; used for ma >=0 risk-appetite-small ;; used for ma < 0 tot-demand-followers ;; total demand of followers tot-demand-contrarians ;; total demand of contrarians tot-demand ;; total value of shares demanded ;; tot-share-demand ;; total number of shares demanded price ;; current calculated price last-price ;; price at time t-1 return ;; percentage price change from t-1 to t value-traded ;; equal to smaller of demands, to clear market volume-traded ;; value traded divided by share price followers-wealth ;; total wealth of followers contrarians-wealth ;; total wealth of contrarians total-wealth ;; total wealth of all investors max-wealth ;; highest wealth of all investors min-wealth ;; lowest wealth of all investors max-demand-c ;; maximum demand of contrarians max-demand-f ;; maximum demand of followers max-demand ;; highest demand of all investors min-demand ;; lowest demand of all investors all-return-list ;; collects all returns return-list ;; collects last m returns moving-average ;; average return over last m periods all-volatility-list ;; collects all volatilities volatility-price-list ;; collects last 36 prices volatility ;; standard deviation of returns over last 36 periods value-traded-list ;; collects values traded volume-traded-list ;; collects volumes traded graph-max ;; maximum of previous two lists graph-min ;; minimum of previous two lists ] turtles-own [ follower contrarian cash shares-value shares wealth wealth-effect ;; part of investors' demand function herd-effect-follower ;; part of followers' demand function herd-effect-contrarian ;; part of contrarians' demand function risk-appetite ;; part of investors' demand function demand-follower demand-contrarian shares-value-transacted ] to setup ca random-seed 1051100757 ;; if required ask patches [ set pcolor white ] ;; create a blank background create-turtles investors [ setxy random-xcor random-ycor set size 3 ] ;; Create empty lists for return histogram, moving average of last m returns, 36-period price volatility, trade set all-return-list [ 0 ] ;; for histogram scaling set return-list [] ;; for moving average while [ length return-list < memory ] [ set return-list lput 0 return-list ] set all-volatility-list [ 0 0.2] ;; for volatility graph scaling set volatility-price-list [] while [ length volatility-price-list < 36 ] [ set volatility-price-list lput 100 volatility-price-list ] set value-traded-list [] ;; for trade graph scaling set volume-traded-list [] ;; for trade graph scaling set num-contrarians round ( ( fraction-contrarians ) / ( 100 ) * ( investors ) ) set num-followers ( investors - num-contrarians ) ;; Initialise some variables set price ( 100 ) set moving-average ( 0 ) set graph-min ( 0 ) set graph-max ( 1 ) ;; Divide into two investor types ask turtles [ set cash 50 set shares-value 50 set wealth cash + shares-value ifelse who < num-contrarians [ set contrarian 1 set follower 0 set shape "wolf 3" set color red ] [ set contrarian 0 set follower 1 set shape "cow skull" set color blue ] ] reset-ticks end to go ;; For each investor calculate *magnitudes* of demands, i.e. "desired size of bet" set risk-appetite-big maximum-risk-appetite / 1 * moving-average set risk-appetite-small maximum-risk-appetite / 2.5 * moving-average ;; investors hate losses ~2.5 times as much as they love gains ask turtles [ ;; Wealth: range of wealth parameter (i.e. on slider) and other parameters need to be determined empirically set wealth-effect ( ( wealth-factor ) * ( wealth ) ) ;; this is per investor ifelse contrarian = 1 [ ;; Herding: the susceptibility of investors to herding by their own type ranges randomly from zero to the maximum set herd-effect-contrarian random-float abs ( ( maximum-herd-effect-contrarians ) * ( tot-demand-contrarians ) / ( num-contrarians ) ) ;; normalize per investor ;; Risk appetite: ifelse moving-average >= 0 [ set risk-appetite random-float risk-appetite-small ] [ set risk-appetite random-float ( - ( risk-appetite-big ) ) ] ;; this is per investor set demand-contrarian max list 0 ( wealth-effect + herd-effect-contrarian + risk-appetite ) ;; Full demand function: is "desired size of bet" so cannot be less than zero; the sign is then determined purely by type of investor set demand-contrarian min list demand-contrarian wealth ;; Can't bet more than one's wealth ;; Scaling for main graph if ticks > 2 [set color scale-color blue risk-appetite ( max [ risk-appetite ] of turtles + 1 ) ( min [ risk-appetite ] of turtles) ] ;; + 1 is error trap for when m.a. = 0 set size min list ( 0.5 * herd-effect-contrarian + 1.3 ) 7 ] [ set herd-effect-follower random-float abs ( ( maximum-herd-effect-followers ) * ( tot-demand-followers ) / ( num-followers) ) ifelse moving-average >= 0 [ set risk-appetite random-float ( - ( risk-appetite-big ) ) ] [ set risk-appetite random-float risk-appetite-small ] set demand-follower max list 0 ( wealth-effect + herd-effect-follower + risk-appetite ) set demand-follower min list demand-follower wealth if ticks > 2 [ set color scale-color red risk-appetite ( max [ risk-appetite ] of turtles + 1 ) ( min [ risk-appetite ] of turtles ) ] set size min list ( 0.5 * herd-effect-follower + 1.3 ) 7 ] ] ;; In the risk appetite calculation above it is assumed that if e.g. moving-average >= 0 followers would have largely been long, so their ;; risk appetite will be big, with the converse for contrarians. Ideally, each investor should have their own personal moving-average. ;; For each investor type, aggregate demand set tot-demand-followers sum [ demand-follower ] of turtles if tot-demand-followers = 0 [ set tot-demand-followers (10) ] ;; error trap for division by zero set tot-demand-contrarians sum [ demand-contrarian ] of turtles if tot-demand-contrarians = 0 [ set tot-demand-contrarians (10) ] ;; error trap for division by zero ;; For each investor type now calculate *sign* of aggregate demand, i.e. direction of aggregate bet ifelse return > 0 [ set tot-demand-contrarians (- tot-demand-contrarians) ] [ set tot-demand-followers (- tot-demand-followers) ] set tot-demand ( tot-demand-followers ) + ( tot-demand-contrarians ) ;; i.e. is *net* demand ;; Calculate new price set last-price price set price ( ( last-price ) + ( price-sensitivity-to-demand ) * ( tot-demand ) ) if price <= 0 [set price (1)] ;; error trap - price floor ;; Calculate return over period set return ( ( price ) / ( last-price ) - ( 1 ) ) * ( 100 ) ;; Add return to the all-return list, then the moving-average return list and take average of this list set all-return-list lput return all-return-list set return-list lput return return-list set return-list remove-item 0 return-list set moving-average ( mean return-list ) ;; Add price to the volatility price list, take standard deviation of list, cumulate volatilities set volatility-price-list lput price volatility-price-list set volatility-price-list remove-item 0 volatility-price-list set volatility ( standard-deviation volatility-price-list ) if ticks > 36 [ set all-volatility-list lput volatility all-volatility-list ] ;; start to cumulate volatilities when past initialized dummy data ;; Calculate value traded (equal to smaller of demands, to clear market) and volume set value-traded min list abs tot-demand-followers abs tot-demand-contrarians set volume-traded ( value-traded ) / ( price ) * ( 100 ) ;; For trade graph scaling set value-traded-list lput value-traded value-traded-list set volume-traded-list lput volume-traded volume-traded-list set graph-max max list ( max value-traded-list ) ( max volume-traded-list ) set graph-min min list ( min value-traded-list ) ( min volume-traded-list ) ;; Recalculate investors' wealth ask turtles [ ifelse contrarian = 1 [ set shares-value-transacted ( demand-contrarian ) / ( tot-demand-contrarians ) * ( value-traded ) ;; get share value allocated pro-rata to relative demand ;; change investors' cash and share balances ifelse return >= 0 [ set shares-value shares-value - shares-value-transacted set shares ( shares-value ) / ( last-price ) set cash cash + shares-value-transacted ] [ set shares-value shares-value + shares-value-transacted set shares ( shares-value ) / ( last-price ) set cash cash - shares-value-transacted ] ] [ set shares-value-transacted ( demand-follower ) / ( tot-demand-followers ) * ( value-traded ) ifelse return >= 0 [ set shares-value shares-value + shares-value-transacted set shares ( shares-value ) / ( last-price ) set cash cash - shares-value-transacted ] [ set shares-value shares-value - shares-value-transacted set shares ( shares-value ) / ( last-price ) set cash cash + shares-value-transacted ] ] set wealth ( shares ) * ( price ) + ( cash ) ;; update investors' wealth ] ;; Scaling of main graph set followers-wealth sum [ wealth ] of turtles with [ follower = 1 ] set contrarians-wealth sum [ wealth ] of turtles with [ contrarian = 1 ] set total-wealth sum [ wealth ] of turtles set max-wealth max [ wealth] of turtles set min-wealth min [ wealth] of turtles if max-wealth = min-wealth [ set max-wealth ( max-wealth + random ( 10 ) ) set min-wealth ( min-wealth - random ( 10 ) ) ] ;; error trap to stop division by zero in plot set max-demand-c max [ demand-contrarian ] of turtles set max-demand-f max [ demand-follower ] of turtles set max-demand max list max-demand-c max-demand-f ;; must be a cleverer way to do this set min-demand min list min [ demand-contrarian ] of turtles min [ demand-follower ] of turtles ask turtles ;; [ if wealth >= 0 [ ifelse contrarian = 1 [ setxy ((( wealth - min-wealth ) / ( max-wealth - min-wealth ) * ( max-pxcor - min-pxcor)) + min-pxcor ) ((( demand-contrarian - min-demand ) / ( max-demand - min-demand ) * ( max-pycor - min-pycor)) + min-pycor ) ] [ setxy ((( wealth - min-wealth ) * ( max-pxcor - min-pxcor) / ( max-wealth - min-wealth )) + min-pxcor ) ((( demand-follower - min-demand ) / ( max-demand - min-demand ) * ( max-pycor - min-pycor)) + min-pycor ) ] ;; ] ] tick end
There are 2 versions of this model.
Attached files
File | Type | Description | Last updated | |
---|---|---|---|---|
Bulls and Bears.png | preview | Preview for 'Bulls and Bears' | about 4 years ago, by Franco Busetti | Download |
This model does not have any ancestors.
This model does not have any descendants.