Bulls and Bears

Bulls and Bears preview image

1 collaborator

Default-person Franco Busetti (Author)

Tags

price formation 

Tagged by Franco Busetti about 4 years ago

stock market 

Tagged by Franco Busetti about 4 years ago

Visible to everyone | Changeable by the author
Model was written in NetLogo 6.2.0 • Viewed 602 times • Downloaded 55 times • Run 0 times
Download the 'Bulls and Bears' modelDownload this modelEmbed this model

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

CC BY-NC-SA 3.0

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

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

;; 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.

Uploaded by When Description Download
Franco Busetti almost 2 years ago Refreshed Download this version
Franco Busetti about 4 years ago Initial upload Download this version

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.