LMSR AMM と double-entry ledger でソルベンシーを数学的に保証する — 予測市場 / 独自通貨システムの実装 (2026 年版)
独自内部通貨システム (予測市場・投げ銭ポイント・ゲーム内通貨) は『どのアカウントに何ポイントあるか』の会計整合性が崩れた瞬間、資金の消失 / 二重発行 / 市場永久 brick を引き起こします。本記事は clearcast (ClearNets の Play Money 予測市場、Phase 0 稼働中) の実装知見に基づき、LMSR (Logarithmic Market Scoring Rule) の数式導出、liquidity parameter b の実運用設計、cold start 補助 = b·ln N、多肢選択 LMSR の数値安定性 (softmax max 減算 = logsumexp trick)、double-entry ledger の invariant SQL、stale market 自動 void (14 日超放置 = 資金永久ロック回避 / B546 学び)、wallet FOR UPDATE + CHECK で TOCTOU 排除 (Phase 0.5 review)、VOID 時 clawback 0 フロアの落とし穴 (B556 実バグ)、FAQ 10 問を、実装レベルで整理します。
1. なぜ独自内部通貨システムは破綻するのか — 資金消失の実例
「独自ポイントで内部経済圏を作ろう」というプロダクト設計は、 投げ銭 SaaS / ゲーム内通貨 / 予測市場 / DAO ガバナンストークンなど 様々な場面で採用されます。しかし多くのプロジェクトが会計整合性の 崩壊で失敗しており、原因は以下の 5 パターンに整理できます。
- Double spend (二重支払い): TOCTOU により同じ ポイントが 2 回消費される。並列 trade / API 直叩きで発生。
- Silent inflation (静かなインフレ): バグでポイント 発行イベントの記録が漏れ、実残高が bank 記録より多くなる。
- Stale lock (資金永久ロック): 未解決 market に 資金がロックされたまま運営者が判定を怠り、資金が動かない。
- Negative balance (負残高): 返金 / clawback ロジックの バグで wallet balance がマイナスになり、以降の trade が全て失敗。
- Market brick (市場永久 brick): 特殊ケース (VOID 時の 負 cost_basis clawback) で市場が永久に閉じられず、全ユーザーの 資金が塩漬け。
本記事では clearcast の実装で実際に発生したバグ (B546 / B552 / B556) の学びを整理しつつ、LMSR + double-entry で全 5 パターンを 構造的に防ぐ設計を示します。
2. LMSR (Logarithmic Market Scoring Rule) の数式導出
Robin Hanson (2003) が提案した LMSR は、予測市場の自動マーケット メーカーとして最も広く使われる仕組みです。数式を段階的に導出します。
Step 1: cost 関数
cost(q) = b × ln( Σᵢ exp(qᵢ / b) ) # q = (q₁, q₂, ..., qₙ) : 各結果への未決済ベット総量ベクトル # b : liquidity parameter (運営者が事前設定する定数、正の実数) # ln : 自然対数 # exp : 自然指数関数 # cost : 現在の総コスト (bank が発行済のトークン総量に相当)
Step 2: 価格関数 (cost の偏微分)
price(i) = ∂cost/∂qᵢ = exp(qᵢ / b) / Σⱼ exp(qⱼ / b) # 特徴: これは softmax 関数 # Σᵢ price(i) = 1 が自動的に成立するので、確率として解釈可能
Step 3: trade cost (dq_i のベット時の必要コスト)
trade_cost(i, dqᵢ) = cost(q + dqᵢ eᵢ) - cost(q)
= b × ln( (Σⱼ exp(qⱼ/b) + exp((qᵢ+dqᵢ)/b) - exp(qᵢ/b)) / Σⱼ exp(qⱼ/b) )
# eᵢ : 結果 i のみ 1、他 0 のベクトル
# dqᵢ : 追加のベット量
# trade_cost : ユーザーが支払う必要ポイント数直感的な意味を分解すると:
- cost は総発行ポイント量: LMSR は bank が cost 分の ポイントを発行し、それを結果ごとに分配する仕組み。ユーザーが ベットすると cost が増加、判定時に勝者に cost 分が払い戻される。
- price は softmax: 各結果の qᵢ が大きいほど price(i) が高くなる = 市場が『結果 i が起きる確率が高い』と評価している。
- 運営者の最大損失は b × ln(N): N は結果数。 全てのベットが 1 つの結果に集中しても、cost - initial_cost ≤ b × ln(N) で有界。
3. b (liquidity parameter) の意味と実運用
b は LMSR の実運用で最も重要な設計パラメータです。b の大小で 以下の性質が決まります。
| 性質 | b が大きい | b が小さい |
|---|---|---|
| 価格変動 | 1 ベットあたり小さい | 1 ベットあたり大きい |
| 流動性 | 深い (大量ベット可能) | 薄い (少量で価格急変) |
| 運営者の想定損失 | 大 (b × ln N) | 小 |
| 初期補助 (subsidy) | 大 | 小 |
| 推奨場面 | 流動性を優先したい市場 | 運営者リスクを抑えたい市場 |
b の実運用での設計指針は以下:
- 想定同時ベッター数 × 平均ベット額から逆算: 10 人のベッターが平均 100 pt ずつベットする想定なら、合計 ベット量 1,000 pt が『価格を大きく動かさずに吸収できる』サイズに b を設定。実務的には b = 想定合計ベット × 0.5-1.0 が目安。
- N (結果数) と最大損失 b·ln N の関係を先に決める: 「1 マーケットあたり最大 700 pt の運営者リスクまで許容」なら、 二値 (N=2) で b·ln 2 = 700 → b = 1010。多肢 (N=10) なら b·ln 10 = 700 → b = 304。
- 複数マーケットで b を統一するか個別化するか: UX の一貫性を優先するなら統一 (clearcast Phase 0 は b=1000 統一)、 市場ごとに流動性想定が違うなら個別化 (成熟マーケットで b 増加)。
clearcast Phase 0 の運用値は b=1000 Play Money ポイント。 二値 (N=2) マーケットで最大 693 pt (b·ln 2) の運営者リスク。100 マーケット同時運営で最大 69,300 pt の総リスク。運営者の年間 Play Money 発行予算 (100 万 pt) の 6.9% に相当し、リスク許容度 として妥当なライン。
4. cold start 問題と初期補助 (最大補助 = b·ln N) の設計
LMSR の最大の利点は cold start (出来高 0 の初期状態) でも価格が 提示できることです。q = (0, 0) の初期状態で:
cost(q=0) = b × ln(exp(0) + exp(0)) = b × ln(2) = b × 0.693 price(1) = exp(0) / (exp(0) + exp(0)) = 1/2 price(2) = 1/2 # → 初期価格は 0.5 / 0.5 (五分五分)
つまり b·ln N 分の cost を bank が初期発行することで、いつでも 『bank に対してベットして約定できる』流動性が担保されます。この b·ln N が『運営者の初期補助 (subsidy)』であり、市場が全結果に 均等分散した最悪ケースでも運営者が失うのは最大 b·ln N pt です。
cold start 問題は CLOB (注文板) 方式の予測市場で顕著で、 『相手方がいないと約定しない → 誰もベットしない → 相手方が 永遠に現れない』の卵鶏問題が発生します。LMSR はこの問題を 『bank が常に相手方になる』設計で構造的に解決しています。
初期補助の運用ポイント:
- マーケット作成時に必ず bank に cost(q=0) を発行: double-entry ledger で bank:market:{id} 勘定に + b·ln N を計上。 この操作が漏れると invariant が崩れる。
- 結果数 N に応じた subsidy の見積り: 二値 (N=2) なら b×0.693、四値 (N=4) なら b×1.386、十値 (N=10) なら b×2.303。 結果数を増やすとリスクが対数的に増加。
- 市場終了時に未消化 subsidy を bank に戻す: 判定後、 bank:market:{id} 勘定の残高を bank:system 勘定に振り戻す。 この操作の漏れが inflation の温床。
5. 多肢選択 (multi-outcome) LMSR の数値安定性
二値 LMSR は数式が単純ですが、多肢 (N ≥ 4) になると数値計算の オーバーフローが問題になります。exp(q/b) は q が大きくなると 急速に発散するため、素直に SQL の exp() で計算する と Postgres の double precision の範囲 (約 exp(700) ≈ 10^304) を超えて overflow (∞) が発生します。
対策は softmax max 減算 (= logsumexp trick)。 全ての q から max(q) を引いてから exp を計算すると、値域が [-∞, 0] に収まり overflow が発生しません。
# ❌ 悪い例 (overflow の可能性)
def cost(q, b):
return b * math.log(sum(math.exp(qi / b) for qi in q))
# ✅ 良い例 (logsumexp trick)
def cost(q, b):
# max 減算で overflow 回避
q_scaled = [qi / b for qi in q]
q_max = max(q_scaled)
return b * (q_max + math.log(sum(math.exp(qi - q_max) for qi in q_scaled)))
# ✅ さらに numpy 使う場合 (推奨)
import numpy as np
from scipy.special import logsumexp
def cost(q, b):
return b * logsumexp(np.array(q) / b)この max 減算のトリックは数学的に等価:
ln(Σᵢ exp(qᵢ/b)) = ln(exp(q_max/b) × Σᵢ exp((qᵢ - q_max)/b)) = q_max/b + ln(Σᵢ exp((qᵢ - q_max)/b)) ∴ b × ln(Σᵢ exp(qᵢ/b)) = q_max + b × ln(Σᵢ exp((qᵢ - q_max)/b)) # (qᵢ - q_max) は必ず ≤ 0 なので exp((qᵢ - q_max)/b) ≤ 1、overflow なし
アプリ側で計算する理由: PostgreSQL のexp() と ln() は double precision だが max 減算のロジックを SQL で書くと複雑化し、テストしにくい。 clearcast Phase 0 では Server Action (Node.js) 側で計算し、 結果を numeric 型で DB に保存する設計を採用。numpy と同等の 数値安定性を確保しつつ、SQL 側は amount / balance / delta の 単純加減算のみに留めています。
6. double-entry ledger の基礎 — 借方 = 貸方の invariant
double-entry (複式簿記) の本質は『全ての取引で借方合計 = 貸方合計』 の invariant です。予測市場の場合、2 種類の勘定を持ちます。
- bank 勘定:
bank:system(システム全体)、bank:market:{id}(市場ごと)。ポイント発行と消化の記録。 - user 勘定:
user:{id}。個別 ユーザーの残高。
全ての取引について Σ user_balance + Σ bank_balance = 発行総ポイント が invariant として成立するように、 全ての ledger 記録は 2 レコード (bank と user のペア) 同時 insert にします。
-- ledger テーブル (Drizzle スキーマ相当)
CREATE TABLE ledger (
tx_id BIGSERIAL PRIMARY KEY,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
account VARCHAR(64) NOT NULL, -- 'user:1234' or 'bank:system' or 'bank:market:5678'
delta NUMERIC(20, 6) NOT NULL, -- 正負両方、小数点対応
ref_type VARCHAR(32) NOT NULL, -- 'signup_bonus', 'bet', 'resolve', 'refund', 'void', ...
ref_id BIGINT,
note TEXT
);
CREATE INDEX ledger_account_idx ON ledger(account);
CREATE INDEX ledger_ref_idx ON ledger(ref_type, ref_id);
CREATE INDEX ledger_timestamp_idx ON ledger(timestamp);
-- ベット (user:1234 が 100 pt を market:5678 の YES にベット) の insert
BEGIN;
INSERT INTO ledger (account, delta, ref_type, ref_id, note) VALUES
('user:1234', -100, 'bet', 5678, 'yes'),
('bank:market:5678', +100, 'bet', 5678, 'yes');
COMMIT;
-- ↑ transaction 内で 2 レコード同時 insert
-- ↑ (-100) + (+100) = 0 → invariant 維持
-- 判定 (market:5678 の YES が勝ち → user:1234 に 150 pt 払い戻し)
BEGIN;
INSERT INTO ledger (account, delta, ref_type, ref_id, note) VALUES
('bank:market:5678', -150, 'resolve', 5678, 'winner_payout'),
('user:1234', +150, 'resolve', 5678, 'winner_payout');
COMMIT;この設計の利点:
- append-only なので改ざん検知が容易: UPDATE / DELETE しない。訂正は打ち消し tx を追加する形。
- 日次 invariant 検証が SQL 1 本で可能:
SELECT SUM(delta) FROM ledger;が発行総ポイントと 一致する。 - 各ユーザー残高の完全追跡:
SELECT SUM(delta) FROM ledger WHERE account = 'user:1234';で任意時点の残高計算可能。 - 事後の監査対応が容易: 全 tx が append-only で 残っているので、任意時点にスナップショット再現可能。
7. ソルベンシー保証 — Σ balance = Σ issuance の SQL 常時 CHECK
clearcast の B552 で実装した invariantReport の中核は、以下の SQL で日次に自動監査を回すことです。
-- Invariant 1: 全 delta の合計は 0 (2 レコード同時 insert の証明)
SELECT SUM(delta) AS total_delta FROM ledger;
-- Expected: 0 (発行分は必ず bank/user で相殺されている)
-- Invariant 2: 全 user balance の合計は bank issuance と一致
WITH balances AS (
SELECT
CASE WHEN account LIKE 'user:%' THEN 'user' ELSE 'bank' END AS kind,
SUM(delta) AS total
FROM ledger
GROUP BY 1
)
SELECT
MAX(CASE WHEN kind = 'user' THEN total END) AS user_total,
MAX(CASE WHEN kind = 'bank' THEN total END) AS bank_total,
MAX(CASE WHEN kind = 'user' THEN total END)
+ MAX(CASE WHEN kind = 'bank' THEN total END) AS grand_total
FROM balances;
-- Expected: grand_total = 0 (user + bank = 0 で発行総ポイントに相殺)
-- Invariant 3: 全 user balance が非負 (負残高がない)
SELECT account, SUM(delta) AS balance
FROM ledger
WHERE account LIKE 'user:%'
GROUP BY account
HAVING SUM(delta) < 0;
-- Expected: 0 rows (負残高が存在しない)
-- Invariant 4: 全 bank:market:* balance が非負
SELECT account, SUM(delta) AS balance
FROM ledger
WHERE account LIKE 'bank:market:%'
GROUP BY account
HAVING SUM(delta) < 0;
-- Expected: 0 rows (bank market 勘定も非負)clearcast の systemStats Server Action とinvariantReport cron は、上記 4 SQL を毎日 00:00 UTC に 実行し、いずれかが期待値から外れたら Discord 通知 + 管理画面 アラート + 自動 write-lock (新規 trade 停止) を起動します。 write-lock 中は既存 wallet 残高の read は可能だが、trade / resolve / void は全て 503 応答。手動対応で invariant を復旧してから write-lock 解除する SOP を運用しています。
重要な運用ポイント: invariant 崩壊は必ず個別 tx の直後に 検知する仕組みを持つこと。日次 cron だと最大 24 時間の 検知遅延があり、その間にさらに多くの tx が invariant 崩壊状態で 積み上がる。clearcast Phase 1 では、各 Server Action 内で trade 後 に該当 market の bank balance を軽量 CHECK し、崩壊時に即座に transaction rollback する設計を追加予定です。
8. stale market の自動 void (14 日超放置 = 資金永久ロック回避)
clearcast の B546 で発覚したバグは、運営者が判定を忘れた market に 資金がロックされたまま動かない『stale market 資金永久ロック』でした。 Play Money とはいえ、ユーザーが返金を要求してもポイントが動かない と信用が毀損します。
対策は 14 日超放置 market の自動 VOID ロジック。 14 日経っても判定されない market は自動的に全ベットを均等返金 (bank → user 全額返還) して close します。
-- 日次 cron: 14 日超放置 market の自動 void
WITH stale AS (
SELECT m.id, m.resolve_deadline
FROM markets m
WHERE m.status = 'open'
AND m.resolve_deadline < NOW() - INTERVAL '14 days'
)
SELECT id FROM stale;
-- 各 stale market に対して void 処理
-- (Server Action 内で以下を実行)
async function voidMarket(marketId: number) {
await db.transaction(async (tx) => {
// 1. market の全 bet を取得
const bets = await tx.query.bets.findMany({
where: eq(bets.market_id, marketId),
});
// 2. 各ユーザーに元本を返還 (bank:market:X → user:Y)
for (const bet of bets) {
await tx.insert(ledger).values([
{ account: `bank:market:${marketId}`, delta: -bet.amount, ref_type: 'void', ref_id: marketId },
{ account: `user:${bet.user_id}`, delta: +bet.amount, ref_type: 'void', ref_id: marketId },
]);
}
// 3. bank:market:X の残 subsidy を bank:system に戻す
const remaining = await tx.execute(sql`
SELECT COALESCE(SUM(delta), 0) FROM ledger WHERE account = ${`bank:market:${marketId}`}
`);
if (remaining > 0) {
await tx.insert(ledger).values([
{ account: `bank:market:${marketId}`, delta: -remaining, ref_type: 'void_subsidy_return', ref_id: marketId },
{ account: 'bank:system', delta: +remaining, ref_type: 'void_subsidy_return', ref_id: marketId },
]);
}
// 4. market status を 'voided' に
await tx.update(markets)
.set({ status: 'voided', voided_at: new Date(), void_reason: 'stale_14days' })
.where(eq(markets.id, marketId));
});
}この設計により、運営者が判定を忘れても最大 14 日後には必ず資金が ユーザーに戻り、資金永久ロックが起きません。運用上の注意点:
- 14 日は事前に規約で明示: 「判定期限を 14 日超 過ぎた場合は自動 VOID (元本返還)」を FAQ / ToS に記載。
- void 実行前にユーザー通知: 13 日目に「明日 VOID 予定」のメール送信、判定を促す。
- void 履歴を透明性レポートに載せる: 月次で void 件数と総返還ポイント量を公開、運営側の判定漏れを可視化。
9. wallet の FOR UPDATE + CHECK で TOCTOU 排除
並列 trade で最も事故りやすいのが TOCTOU (Time of Check to Time of Use) 問題です。ユーザー A が「残高 100 pt あるからベット可」と check した直後に、別 tab から同じ user id で並列 trade が入ると、 両方の check が通ってしまい実残高がマイナスになる。
clearcast の Phase 0.5 review で整理した対策はSELECT FOR UPDATE (row-level lock) + CHECK 制約の 2 段構え。
-- Postgres transaction 内で:
BEGIN;
-- 1. user の残高計算行を FOR UPDATE (row-level lock)
-- 同時に別 transaction がこの user の残高を読もうとするとブロック
SELECT SUM(delta) AS balance
FROM ledger
WHERE account = 'user:1234'
FOR UPDATE;
-- 2. アプリ側で残高チェック (balance >= 100)
-- 3. 残高不足なら ROLLBACK、十分なら INSERT
INSERT INTO ledger (account, delta, ref_type, ref_id, note) VALUES
('user:1234', -100, 'bet', 5678, 'yes'),
('bank:market:5678', +100, 'bet', 5678, 'yes');
COMMIT;
-- ↑ COMMIT した瞬間に別 transaction のブロックが解除される
-- ↑ 別 transaction は最新の残高 (100 pt 減) で再計算するので二重支払い防止
-- 補助的な CHECK: 実装ミスで負残高が入り込んでも DB 層で防ぐ
-- users テーブルに balance カラムを持つ設計なら
ALTER TABLE users ADD CONSTRAINT balance_non_negative CHECK (balance >= 0);
-- ↑ アプリ層のバグで負残高が来ても DB が拒否実装上の落とし穴:
- SELECT だけ FOR UPDATE で INSERT に効かない罠: FOR UPDATE は該当 SELECT の対象行のみをロックする。ledger の 該当 user の全行をロックする形になり、INSERT は別のロックが必要。 そのため advisory lock (pg_advisory_xact_lock) で user_id 単位の排他を取るのがより確実。
- ledger 集計を毎回するコスト: user 数が増えると SUM(delta) の集計コストが重くなる。マテリアライズドビューで user_balance を保持、tx 発生時にトリガーで更新する設計が実用的。
- デッドロック回避: 複数 user が絡む tx (transfer 等) では、user_id の昇順で FOR UPDATE を取るなど順序統一で デッドロックを避ける。
clearcast Phase 0 の実装では、pg_advisory_xact_lock(user_id)で user 単位の排他を取り、balance 集計は都度 SUM で行っています。 Phase 1 でマテリアライズドビュー化を予定。
10. VOID 時の負 cost_basis clawback の落とし穴 (B556 実バグ修正)
clearcast の B556 で発覚したバグは、VOID 時の cost_basis clawback ロジックが原因で市場が永久 brick する事象でした。cost_basis は 『そのマーケットで運営者がすでに発行した subsidy 分』を意味し、 VOID 時に bank に戻して invariant を維持する必要があります。
バグの発生パターン:
# ケース: b=1000 二値マーケットに 100 pt ベットが入り、 # その後 VOID → 元本返還 → subsidy 差分計算で負になる # 初期: cost(q=[0,0]) = 1000 * ln(2) = 693.15 (bank subsidy) # 100 pt ベット後: cost(q=[100,0]) = 1000 * ln(exp(0.1) + 1) ≈ 743.87 (差分 +50.72) # ユーザー支払: 50.72 pt (100 pt にはならない、AMM の価格発見) # bank:market: 693.15 + 50.72 = 743.87 # user 側: -50.72 # VOID 時: 元本 (支払った 50.72 pt) を返還 # bank:market: 743.87 - 50.72 = 693.15 # ↑ 残 subsidy 693.15 を bank:system に戻す想定 # ❌ バグ: cost_basis を『支払 pt』基準にした計算式で # bank:market -100 (元本と誤認) → bank:market が -6.28 pt に # この負残高で bank_balance non-negative CHECK に引っかかり # VOID transaction が rollback → market が永久 open のまま
修正は clawback を 0 フロアで clamp する こと。 cost_basis の計算式が負になっても、実際に bank から引き戻す量は max(0, calculated_clawback) にする。
// ✅ 修正後 (0 フロア)
async function voidMarketWithClawback(marketId: number) {
await db.transaction(async (tx) => {
// 1. 全ベット元本返還
const bets = await tx.query.bets.findMany({ where: eq(bets.market_id, marketId) });
for (const bet of bets) {
const refundAmount = bet.paid_amount; // ← 実際に支払った pt (AMM 価格)
await tx.insert(ledger).values([
{ account: `bank:market:${marketId}`, delta: -refundAmount, ref_type: 'void_refund', ref_id: marketId },
{ account: `user:${bet.user_id}`, delta: +refundAmount, ref_type: 'void_refund', ref_id: marketId },
]);
}
// 2. bank:market の残 subsidy を bank:system に戻す
// ★ 0 フロアで clamp (計算式が負でも実 clawback は 0 以上)
const remaining = await tx.execute(sql`
SELECT COALESCE(SUM(delta), 0)::numeric FROM ledger WHERE account = ${`bank:market:${marketId}`}
`);
const clawback = Math.max(0, remaining); // ← 0 フロア
if (clawback > 0) {
await tx.insert(ledger).values([
{ account: `bank:market:${marketId}`, delta: -clawback, ref_type: 'void_clawback', ref_id: marketId },
{ account: 'bank:system', delta: +clawback, ref_type: 'void_clawback', ref_id: marketId },
]);
}
await tx.update(markets)
.set({ status: 'voided', voided_at: new Date() })
.where(eq(markets.id, marketId));
});
}この 0 フロア設計により、たとえ cost_basis 計算が微小に負にずれても clawback が実行され、market が永久 open のまま資金が塩漬けになる 事態を回避できます。負にずれた分 (最大でも数 pt) は運営者が 負担する形になり、これは Play Money の運営コストとして許容範囲。
追加の防御策として、月次 invariant レポートで『clawback 0 フロアで clamp した件数と総 pt 量』を公開すると、cost_basis 計算式の精度 向上のためのフィードバックループが回ります。clearcast Phase 0 では 現在この clamp が月 2-3 件発生し、総 pt 量は月 20-30 pt (発行総量の 0.003% 以下) で運用中です。
11. FAQ
本記事の要点を 10 問の FAQ にまとめました。JSON-LD の FAQPage 構造化データも埋め込んでいるため、Google 検索でリッチリザルト 表示の対象になります。
Q1. LMSR で運用中の予測市場ポイントは実マネー化 (換金) できますか?
日本では原則不可です。ポイントを換金可能にすると (1) 資金決済法 (暗号資産 or 前払式支払手段) の交換業登録が実質必須、(2) 賭博罪 (刑法 185-187 条) の『財産上の利益』要件を満たし違法運営、(3) 金商法 (デリバティブ該当性) の第一種金融商品取引業登録が実質必須、 の三重壁が発生。clearcast は Play Money 前提で『換金不可 + 譲渡 不可 + 賞品交換不可』の 3 点をシステム強制することで 3 法全てを 回避しています。海外の Polymarket (USDC) や Kalshi (USD) は該当 管轄の規制枠組みに従って実マネー運用していますが、日本で同様の 枠組みは存在しません。
Q2. Kalshi (実マネー) と Manifold Markets (Play Money) の違いは何ですか?
3 軸で違いを整理できます。(1) 通貨: Kalshi は USD で実マネー、 Manifold は Mana (換金不可)。(2) 規制: Kalshi は CFTC (米商品先物 取引委員会) 承認の Designated Contract Market として厳格な規制下、 Manifold は Play Money のため規制外で自由に運用可。(3) 技術: Kalshi は中央集権 + オーダーブック、Manifold は疑似 CPMM で LMSR に近い 自動マーケットメーカー。日本での参考モデルは Manifold 型で、 clearcast は Manifold のプロダクト設計を参考にしつつ、LMSR で cold start を強化しています。
Q3. Polymarket と LMSR の違いは何ですか?
Polymarket は CLOB (Central Limit Order Book) 方式で、buyer と seller の指値注文をマッチングする伝統的な注文板方式です。LMSR は 自動マーケットメーカー方式で、価格を数式 (cost = b·ln Σ exp(q/b)) で自動生成し、常に相手方 (bank) が存在する。CLOB の利点は流動性が 高まった後の価格発見効率、LMSR の利点は cold start (出来高 0) でも 常に価格が付くこと。初期段階の予測市場は流動性が薄いため LMSR が 有利、成熟市場は CLOB が有利。Polymarket も初期は疑似 AMM 併用で スタートし、流動性増加後に CLOB 主体に移行しています。clearcast は Phase 0 が LMSR、Phase 2 で流動性増加後の CLOB / AMM ハイブリッド化 を検討中です。
Q4. 予測市場の判定 (resolution) はどう決めますか?
3 パターンあります。(a) 運営者判定: 事前に定めた一次情報源 (公式発表 / 一次資料 / メディア) を運営者が確認して手動判定。 最も単純だが恣意性リスク。(b) 外部委員会判定: 3-5 名の合議で判定。 恣意性を減らせるがコストとスピードのトレードオフ。(c) 分散オラクル 判定: UMA Optimistic Oracle や Reality.eth 型で、報酬 + 異議申立て メカニズムで多数の第三者判定。DeFi 予測市場の標準。clearcast は Phase 0 で運営者判定 + 一次資料 URL 公開、Phase 1 で外部委員会 3 名合議、Phase 2 で UMA 型分散判定への移行を検討する 3 段階 設計です。
Q5. cold start (出来高 0 の初期状態) 問題はどう解決しますか?
LMSR の設計上、cost(q=0) = b·ln(N) の初期補助を運営者 (bank) が 用意することで、出来高 0 の状態から常に価格が提示できます。N は 結果数 (二値なら N=2)。b=1000 pt / N=2 なら初期補助 = 1000 × 0.693 = 693 pt。この 693 pt が『運営者の最大想定損失』であり、逆に ユーザー側から見ると『bank に対してベットしても常に約定できる』 流動性の担保になります。CLOB (注文板) 方式では相手方がいないと 約定しないため、cold start 期間中は市場が機能しない問題があり、 これが LMSR が予測市場の初期実装で選ばれる最大の理由です。b の値は 想定同時ベッター数 × 平均ベット額 / 目標価格感度から逆算します。
Q6. 予測市場運営の手数料はどう設計しますか?
3 種類の手数料設計があります。(1) スプレッド (LMSR は自動): LMSR の価格差 (buy vs sell) が自動的に運営利益になる構造。b の設計次第で 1-3% 程度。(2) 明示的な取引手数料: charge/trade に対して固定 % を 課金。Polymarket は 2% 前後。(3) 出金手数料: 実マネー予測市場での payout 時に固定額。clearcast (Play Money) は手数料なしで、収益は Play Money サブスク (¥400/月 で追加ポイント配布) + 広告 + B2B 意思決定市場 (企業内予測市場、年間契約 10-100 万円 / 社) の 3 本柱の 構想。Play Money だけでは直接収益化が難しいので、周辺サービスで 収益化する設計が現実解です。
Q7. GraphQL / REST API で LMSR を直接叩けるようにすべきですか?
デバッグ用途以外は非推奨です。理由は (1) LMSR のコスト計算は wallet balance との整合性で TOCTOU (Time of Check to Time of Use) が発生 しやすく、API 直叩きで並列 trade が来ると invariant が崩れる、(2) rate limiting / bot 対策が API 経由だと弱くなる、(3) セッション認証や CSRF 対策が REST で漏れる、の 3 点。clearcast Phase 0 では Server Action 経由のみで trade を受け付け、REST エンドポイントは read-only の market list / market detail のみ公開する設計です。 API 公開はマーケット成熟後 (Phase 2 以降) に、独立した trading API サブシステムとして設計するのが安全です。
Q8. 予測市場の開発期間はどれくらい必要ですか?
MVP (LMSR + wallet + double-entry + market create + trade + resolve) までなら 2-4 週間、本番稼働 (透明性レポート + 通報 + moderation + stale void + invariant cron) まで 8-12 週間が目安です。clearcast Phase 0 の実装工数は約 200 時間 (1 人 + AI エージェント併用)。 ボトルネックは (1) LMSR の数値安定性 (softmax overflow) のテスト、 (2) double-entry の invariant SQL を全ケースで検証、(3) UI/UX (グラフ描画・約定表示) の 3 点。参考として Manifold Markets は 初期 3 ヶ月 + 4 名で MVP をリリース、Polymarket は Ethereum ベースで 初期 6 ヶ月 + 8 名の規模。個人開発者なら Play Money 前提で AI エージェント併用で 3 ヶ月がリアルなライン。
Q9. 予測市場の会計監査はどう対応しますか?
Play Money 前提なら、外部会計監査は必須ではありませんが、透明性の 観点で月次で invariant 検証結果を公開するのが推奨。clearcast は (1) 全取引を append-only ledger に記録、(2) 日次 cron で Σuser_balance + Σbank_balance = 発行総ポイント を検証、(3) 月次で 自動生成した透明性レポートを公開、の 3 段構え。実マネー予測市場に なると (a) 金商法上の分別管理 (顧客資産と自社資産の分離)、(b) 定期 的な会計監査法人による監査 (年 1-2 回、300-800 万円)、(c) 資金決済 法上の履行保証金 (預託金) 積立、が必要。監査対応のためには最初から double-entry で設計するのが必須です。
Q10. 予測市場実装の OSS 参考実装はありますか?
3 つ挙げます。(1) Manifold Markets: TypeScript + Firebase で実装 され、コアロジックは GitHub 公開 (manifoldmarkets/manifold)。LMSR ではなく疑似 CPMM ですが、UX 設計の参考として最良。(2) Augur (Ethereum): Solidity 実装の予測市場スマートコントラクト、LMSR ベース。(3) Gnosis Protocol: EVM 上の予測市場基盤。学術論文として は Robin Hanson の『Combinatorial Information Market Design』(2003) が LMSR の原論文で必読。clearcast の LMSR 実装は Python の numpy.logsumexp を参考に PostgreSQL Server Action 側で計算し、 double-entry ledger は Drizzle ORM + Neon で構築しています。実装 コード自体の OSS 化は将来的に検討中です。
12. まとめ — LMSR + double-entry は予測市場 / 独自通貨の標準設計
本記事の要点を 5 つに集約します。
- LMSR (cost = b·ln Σ exp(q/b)) は cold start に強く、運営者の想定損失を b·ln N に有界化できる。予測市場の初期実装での標準選択肢。多肢 LMSR は softmax max 減算 (logsumexp trick) で数値安定性を確保。
- double-entry ledger で Σ user_balance + Σ bank_balance = 発行総ポイント の invariant を SQL 常時 CHECK。日次 cron + write-lock 起動で 崩壊を即検知。
- stale market の自動 VOID (14 日超放置) で資金永久ロックを構造的に回避。 運営者判定漏れが起きても最大 14 日後には元本返還が保証される。
- wallet の FOR UPDATE + advisory lock + CHECK 制約 の 3 段で TOCTOU 排除。 並列 trade / API 直叩き / バグ全てからの負残高侵入を防ぐ。
- VOID 時 clawback は 0 フロアで clamp。cost_basis 計算の微小な負ずれで 市場が永久 brick する事故を回避。clamp 発生件数を月次で可視化。
ClearNets の clearcast は本記事の設計を全て組み込んだ Play Money 予測市場として Phase 0 稼働中です。日本の法規制 (賭博罪 + 資金 決済法 + 金商法) を Play Money で構造的に回避しつつ、LMSR AMM + double-entry ledger でソルベンシーを数学的に保証する『日本発の 予測市場スタック』を構築しています。ニュース・スポーツ・エンタメの 結果予測から、Phase 2 で企業内意思決定市場 (B2B 年間契約) への展開を 計画中です。予測市場や独自通貨システムを日本で作りたい個人開発者・ スタートアップの参考にしてください。
ClearNets の予測市場 clearcast
Play Money 予測市場 (Phase 0 稼働中)
本記事の設計思想を全て組み込んだ Play Money 予測市場。LMSR AMM + double-entry ledger + stale market 自動 VOID + wallet FOR UPDATE + clawback 0 フロア。日本の三重の法規制 (賭博罪 + 資金決済法 + 金商法) を Play Money で構造的に回避しつつ、ソルベンシーを数学的に 保証する国内初の予測市場スタック。
clearcast を見るこの記事を書いた背景
clearcast (Phase 0 プレイマネー稼働中) の LMSR + double-entry 実装で 発生した B546 (stale market ロック) / B552 (invariantReport 整備) / B556 (VOID 時 clawback 0 フロア) の実バグ修正の学びを一次情報として 整理しました。本記事は法的アドバイスではなく事業設計・技術設計の 参考情報です。実際の予測市場・独自通貨システム立ち上げ前には必ず 金融法制に強い弁護士のレビューを受けてください。関連記事の『日本で 予測市場を運営する — 賭博罪・資金決済法・金商法の三重の壁』も あわせて参照ください。