Chapter 32: Key Takeaways — Building a Platform from Scratch
Architecture and Design
- Use a layered architecture separating routers (HTTP), services (business logic), engines (market mechanisms), and models (data). This makes each layer independently testable and replaceable.
- Start with FastAPI for prediction market backends. Its native Pydantic validation, automatic OpenAPI documentation, and async support accelerate development significantly.
- Version your API from day one (
/api/v1/). Breaking changes happen; versioning lets you evolve without disrupting existing clients. - Use SQLite for prototyping, PostgreSQL for production. SQLAlchemy makes the switch a one-line configuration change.
Order Book Engine
- Price-time priority is the standard matching rule: best price first, then earliest order among ties.
- Limit orders rest in the book and provide liquidity; market orders consume liquidity and execute immediately.
- Lazy cancellation (setting remaining quantity to zero) is cheaper than heap removal and works well when cancellations are infrequent relative to new orders.
- The trade executes at the maker's (resting order's) price, rewarding liquidity providers.
LMSR Automated Market Maker
- Cost function: $C(\mathbf{q}) = b \cdot \ln\!\left(\sum_{i=1}^{n} e^{q_i / b}\right)$ determines the running total spent by all traders.
- Price function: $p_i = e^{q_i / b} / \sum_j e^{q_j / b}$ (softmax) produces probabilities that always sum to 1.
- Trade cost: $C(\mathbf{q'}) - C(\mathbf{q})$ is the cost difference before and after the share vector changes.
- Maximum loss: $b \cdot \ln(n)$ bounds the market maker's worst-case subsidy. For binary markets: $\approx 0.693 \cdot b$.
- The liquidity parameter $b$ controls the trade-off between price stability (high $b$) and subsidy cost (also high $b$). For binary markets with 40-100 traders, $b = 100$ is a reasonable starting point.
- Always use the log-sum-exp trick to prevent numerical overflow in the exponential calculations.
REST API Design
- Keep routers thin: They should validate input, call the service, and format the response. Business logic belongs in services.
- Use Pydantic models for all request and response schemas. This provides automatic validation, documentation, and serialization.
- Design endpoints around resources (markets, orders, positions) with standard HTTP methods (GET, POST, DELETE).
- Return appropriate HTTP status codes: 201 for creation, 400 for bad requests, 401 for authentication failures, 404 for not found.
Authentication
- JWT tokens are stateless: no database lookup needed per request, which scales well.
- Hash passwords with bcrypt (via passlib). Never store plaintext passwords.
- Use dependency injection (
Depends(get_current_user)) to protect endpoints cleanly. - Store secrets in environment variables, never in source code.
- Set token expiration appropriately (1 hour is common for access tokens).
Market Resolution
- Markets follow a lifecycle: Open (trading active) -> Closed (trading stopped) -> Resolved (payouts distributed).
- **Winning positions pay $1 per share**; losing positions pay $0.
- Voiding refunds participants at cost basis when a market cannot be properly resolved.
- Pre-specify resolution criteria in the market description to avoid disputes.
- Record closing prices for calibration analysis—this proves the platform's forecasting value.
Order Book vs. LMSR Decision Framework
- Use LMSR when: few traders, niche topics, need guaranteed liquidity, want simple UX.
- Use order book when: many traders, high volume, want best price discovery, cost-sensitive.
- Consider a hybrid that defaults to LMSR and adds an order book overlay when volume justifies it.
Frontend Considerations
- API-first development: Use FastAPI's
/docsto explore endpoints before building the frontend. - Use WebSockets for real-time price updates instead of polling.
- Optimistic updates make the UI feel responsive: update immediately, roll back on error.
- Token management: Store JWTs in
httpOnlycookies for production security.
Common Pitfalls to Avoid
- Do not use floating-point for money in production. Use
Decimalor integer-cent representation. - Do not store engine state only in memory. Server restarts will lose all order books and AMM states. Persist to database or Redis.
- Do not allow
allow_origins=["*"]in production CORS configuration. - Do not set $b$ too low for LMSR. This creates excessive price volatility that discourages participation.
- Do not skip input validation. Always validate that prices are in [0, 1], quantities are positive, and markets are open before executing trades.