LMSR / Market Scoring Rules (Connection to Verex) TODO
Concept
A market scoring rule turns a proper scoring rule into an automated market maker, where participants update the current distribution toward their own beliefs and are rewarded for the improvement they contribute. LMSR is derived from the logarithmic scoring rule: for each outcome it defines a cost function over the vector q of quantities sold so far, in log-sum-exp form. The cost of a trade is the difference between the cost function's value after the trade and before it, so path independence holds — the total cost to reach the same final state is the same regardless of the path taken. The instantaneous price is the partial derivative of the cost function, which takes the softmax form of quantities divided by the liquidity parameter, so prices always sum to 1 and can be read as probabilities. A larger liquidity parameter reduces price movement for the same trade size, but it also raises the upper bound on the market maker's maximum possible loss — and that bound is always finite.
Because LMSR always quotes a price even without a counter order, it's used for bootstrapping initial liquidity, and choosing the liquidity parameter is a direct trade-off between slippage and the operating loss budget.
Code & Formula
# LMSR/마켓 스코어링(Verex 연결) — 로그-합-지수 비용함수와 경로독립적 가격
# C(q) = b*ln(sum(exp(q_i/b))), price_i = exp(q_i/b) / sum(exp(q_j/b)) (softmax)
import math
def cost(q, b):
m = max(q) # 오버플로 방지를 위한 log-sum-exp 안정화 트릭
return b * (m / b + math.log(sum(math.exp((qi - m) / b) for qi in q)))
def prices(q, b):
m = max(q)
exps = [math.exp((qi - m) / b) for qi in q]
s = sum(exps)
return [e / s for e in exps]
b = 100.0 # 유동성 파라미터: 클수록 가격 변동은 완만해지고 손실 상한은 커진다
q = [0.0, 0.0] # 두 결과(YES/NO) 초기 보유 수량, 시작 가격은 각각 0.5
print("초기 가격:", [round(p, 4) for p in prices(q, b)])
def buy(q, b, outcome, shares):
before = cost(q, b)
q2 = list(q)
q2[outcome] += shares
after = cost(q2, b)
return q2, after - before # 지불해야 할 비용
# YES에 20주 매수
q, paid = buy(q, b, 0, 20)
print(f"YES 20주 매수 비용 = {paid:.4f}, 매수 후 가격 = {[round(p,4) for p in prices(q, b)]}")
# 같은 거래를 유동성이 작은 마켓(b=20)에서 하면 가격이 훨씬 크게 움직인다
q_small, paid_small = buy([0.0, 0.0], 20.0, 0, 20)
print(f"[b=20] 같은 20주 매수 비용 = {paid_small:.4f}, 가격 = {[round(p,4) for p in prices(q_small, 20.0)]}")
# 마켓 메이커의 최대 손실 상한은 b*ln(결과 수)로 유한하다
worst_case_loss = b * math.log(len(q))
print(f"b={b}일 때 마켓 메이커 최대 손실 상한 = {worst_case_loss:.4f}")
Exercise
Implement the LMSR cost function and price function in code for a two-outcome market, vary the liquidity parameter, and simulate the average fill price and cumulative maximum loss for buying the same quantity — tabulate the results.
Practical Connection
Since Verex uses LMSR alongside a CLOB, the real implementation questions are: at what point does the market maker's price diverge from the order book's best quote, and how do you compute log-sum-exp safely in fixed-point arithmetic so that rounding error doesn't accumulate systematically against the market maker.
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/math-50-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.