Workspace IndexMath › Day 41

LMSR / Market Scoring Rules (Connection to Verex) TODO

Math · Day 41 / 52 · November — Probability, Statistics & Financial Math (Day 35-43)

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


한국어

LMSR/마켓 스코어링(Verex 연결) TODO

Math · Day 41 / 52 · 11월 — 확률·통계·금융수학 (Day 35–43)

개념

마켓 스코어링 규칙은 적정 스코어링 규칙을 자동화된 마켓 메이커로 바꾼 것으로, 참여자가 현재 분포를 자기 믿음으로 갱신하고 그 개선분만큼 보상받는 구조다. LMSR은 로그 스코어링 규칙에서 유도되며, 각 결과에 대해 지금까지 팔린 수량 벡터 q에 대한 비용함수를 로그-합-지수 형태로 정의한다. 어떤 거래의 비용은 거래 후 비용함수 값에서 거래 전 값을 뺀 차이이므로, 같은 최종 상태에 도달하는 모든 경로의 총비용이 동일한 경로 독립성이 성립한다. 순간 가격은 비용함수의 편미분이고 이는 수량을 유동성 파라미터로 나눈 값의 softmax 형태여서, 가격들의 합이 항상 1이 되어 확률로 해석된다. 유동성 파라미터가 클수록 같은 거래량에 대한 가격 변동이 작아지지만 마켓 메이커가 감수하는 최대 손실의 상한은 커지며, 이 상한은 유한하게 정해진다.

LMSR은 상대 주문이 없어도 항상 호가를 제시하므로 초기 유동성 부트스트래핑에 쓰이고, 유동성 파라미터 선택이 곧 슬리피지와 운영 손실 예산 사이의 직접적인 교환이 된다.

코드 · 수식

# 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}")

연습

두 결과 마켓에 대해 LMSR 비용함수와 가격 함수를 코드로 구현하고, 유동성 파라미터를 바꿔 가며 같은 수량 매수 시의 평균 체결가와 누적 최대 손실을 시뮬레이션해 표로 정리하라.

실무 · Verex 연결

Verex가 LMSR과 CLOB를 함께 쓰는 이상, 어느 시점에 마켓 메이커 가격이 오더북 최우선 호가와 어긋나는지, 그리고 고정소수점 연산에서 로그-합-지수를 안전하게 계산하며 반올림 오차가 마켓 메이커에 불리하게만 쌓이도록 만드는지가 실제 구현 쟁점이다.

공부한 날 원본 커리큘럼(docs/knowledge/math-50-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.

← 40. VaR·꼬리리스크42. 마르코프 체인(개념) →