People do not maximize expected utility. They overweight losses, anchor on irrelevant numbers, and chase trends. Behavioral finance studies these systematic deviations and the market anomalies they produce.
Prospect theory and loss aversion
Kahneman and Tversky showed that people evaluate outcomes relative to a reference point, not in absolute terms. Losses hurt roughly twice as much as equivalent gains feel good. This asymmetry — loss aversion — explains why investors hold losers too long and sell winners too early (the disposition effect).
Scheme
; Prospect theory value function; v(x) = x^0.88 for gains, -lambda * |x|^0.88 for losses; lambda โ 2.25 (loss aversion coefficient)
(define lambda-loss 2.25)
(define alpha 0.88)
(define (prospect-value x)
(if (>= x 0)
(expt x alpha)
(* (- lambda-loss) (expt (abs x) alpha))))
(display "Gain of $100: v = ") (display (prospect-value 100)) (newline)
(display "Loss of $100: v = ") (display (prospect-value -100)) (newline)
(display "Gain of $1000: v = ") (display (prospect-value 1000)) (newline)
(display "Loss of $1000: v = ") (display (prospect-value -1000)) (newline)
; The ratio shows loss aversion: |v(-x)| / v(x) โ 2.25
(display "Loss/gain ratio at $100: ")
(display (/ (abs (prospect-value -100)) (prospect-value 100)))
Python
# Prospect theory value function
LAMBDA = 2.25# loss aversion coefficient
ALPHA = 0.88def prospect_value(x):
if x >= 0:
return x ** ALPHA
else:
return -LAMBDA * abs(x) ** ALPHA
print(f"Gain of $100: v = {prospect_value(100):.2f}")
print(f"Loss of $100: v = {prospect_value(-100):.2f}")
print(f"Gain of $1000: v = {prospect_value(1000):.2f}")
print(f"Loss of $1000: v = {prospect_value(-1000):.2f}")
print(f"Loss/gain ratio at $100: {abs(prospect_value(-100)) / prospect_value(100):.2f}")
Overconfidence and anchoring biases
Overconfidence makes investors trade too much. They set confidence intervals too narrow — what they call a 90% range captures reality maybe 50% of the time. Anchoring locks judgments to irrelevant starting points: a stock's 52-week high, a round number, the price you paid. Both biases distort information processing.
Scheme
; Anchoring bias simulation; An investor anchors on a stock's purchase price; and adjusts insufficiently toward new information
(define (anchored-estimate anchor new-info adjustment-factor)
; People adjust from the anchor, but not enough; adjustment-factor < 1 means under-adjustment (bias)
(+ anchor (* adjustment-factor (- new-info anchor))))
(define purchase-price 50)
(define fair-value 35) ; analyst estimate after bad earnings; Rational investor: full adjustment
(display "Rational estimate: $")
(display (anchored-estimate purchase-price fair-value 1.0)) (newline)
; Anchored investor: only adjusts ~40% of the way
(display "Anchored estimate: $")
(display (anchored-estimate purchase-price fair-value 0.4)) (newline)
; Overconfidence: true 90% CI vs. stated 90% CI
(define true-std 15)
(define perceived-std 8)
(display "True 90% CI: $") (display (- 50 (* 1.645 true-std)))
(display " to $") (display (+ 50 (* 1.645 true-std))) (newline)
(display "Perceived 90% CI: $") (display (- 50 (* 1.645 perceived-std)))
(display " to $") (display (+ 50 (* 1.645 perceived-std)))
Even when mispricings exist, rational traders may not correct them. Short-selling is costly and risky. Noise traders can push prices further from fundamentals before they revert. Synchronization risk means you can be right about value but wrong about timing. These frictions let behavioral anomalies persist.
Scheme
; Limits to arbitrage: cost of short-selling; An arbitrageur spots a $5 overpricing but faces costs
(define (arbitrage-profit overpricing borrow-cost margin-cost time-months)
; Net profit after costs of maintaining the short position
(let ((total-cost (* (+ borrow-cost margin-cost) (/ time-months 12))))
(- overpricing total-cost)))
; Stock at $105, fair value $100, overpriced by $5
(display "Overpricing: $5.00") (newline)
; Scenario 1: corrects in 1 month โ profitable
(display "Corrects in 1mo: profit = $")
(display (arbitrage-profit 5.00.020.031)) (newline)
; Scenario 2: takes 6 months โ barely worth it
(display "Corrects in 6mo: profit = $")
(display (arbitrage-profit 5.00.020.036)) (newline)
; Scenario 3: takes 18 months โ net loss
(display "Corrects in 18mo: profit = $")
(display (arbitrage-profit 5.00.020.0318)) (newline)
; Scenario 4: gets worse before correcting (noise trader risk)
(define (noise-trader-loss initial-short price-moves-against margin-call-cost)
(+ initial-short price-moves-against margin-call-cost))
(display "If price rises $10 before correcting, total capital at risk: $")
(display (noise-trader-loss 105105))
Python
# Limits to arbitrage: costs erode mispricing profitsdef arbitrage_profit(overpricing, borrow_cost, margin_cost, time_months):
total_cost = (borrow_cost + margin_cost) * (time_months / 12)
return overpricing - total_cost
print("Overpricing: $5.00")
print(f"Corrects in 1mo: profit = ${arbitrage_profit(5, 0.02, 0.03, 1):.2f}")
print(f"Corrects in 6mo: profit = ${arbitrage_profit(5, 0.02, 0.03, 6):.2f}")
print(f"Corrects in 18mo: profit = ${arbitrage_profit(5, 0.02, 0.03, 18):.2f}")
# Noise trader risk: price can move against youprint(f"If price rises $10 before correcting, capital at risk: ${105 + 10 + 5}")
Bubbles and crashes
A bubble forms when prices detach from fundamentals and keep rising because participants expect to sell to a greater fool. Feedback loops amplify: rising prices attract more buyers, which raises prices. The crash comes when the supply of new money runs out. Historically, every bubble follows the same pattern: displacement, boom, euphoria, profit-taking, panic.