A prediction market platform, implementing the Logarithmic Market Scoring Rule (LMSR) for automated market making. Created for fun and for CodeCell Intra Hack 2025.
- Overview
- Features
- Tech Stack
- The Mathematics Behind LMSR
- System Architecture
- Getting Started
- Project Structure
- API Documentation
- Usage
- Contributing
- Acknowledgments
SattaCell is a centralized prediction market platform inspired by Polymarket, where users can trade shares on various outcomes using a fake token system. The platform uses the Logarithmic Market Scoring Rule (LMSR) as its Automated Market Maker (AMM) to automatically price shares and calculate probabilities based on market activity.
A prediction market is a platform where users can buy and sell shares in the outcome of future events. The price of shares represents the market's collective probability estimate of that outcome occurring. For example, if shares for "Team A wins" are trading at 70%, the market believes there's a 70% chance Team A will win.
The Logarithmic Market Scoring Rule ensures:
- Liquidity: There's always a price to buy or sell shares
- No Order Books: Prices are calculated automatically based on a mathematical formula
- Fair Pricing: Prices reflect the true market probability
- Atomic Trades: All trades are executed atomically and safely
- Market Creation: Admins can create prediction markets with multiple outcomes
- Trading: Buy and sell shares in market outcomes using fake tokens
- Real-time Probabilities: Dynamic probability calculation using LMSR
- User Positions: Track your holdings across all markets
- Analytics Dashboard: Comprehensive market analytics with 10+ visualizations
- Authentication: Google OAuth restricted to
@gmail.comemails - Admin Panel: Password-protected admin interface for market management
- 10 Interactive Charts:
- Top Position Holders
- Top Traders by Volume
- Top Holders by Outcome
- Position Concentration
- Most Active Traders
- Recent Activity by Top Holders
- Probability vs Volume Scatter Plot
- Cumulative Trading Volume
- Buy vs Sell by Outcome
- Buy/Sell Ratio Pie Chart
- Trade History: Complete transaction history with user names
- Orderbook: View all user positions in a market
- Market Analytics: Leading outcomes, volume metrics, trader statistics
- Token Management: Initial 1000 tokens per user, admin can grant more
- Node.js + Express.js: RESTful API server
- MongoDB + Mongoose: Database and ODM
- Google OAuth: Authentication (direct GCP integration)
- LMSR Service: Core pricing algorithm implementation
- React 19: Modern UI framework
- Vite: Fast build tool and dev server
- React Router: Client-side routing
- Recharts: Data visualization library
- Tailwind CSS: Utility-first CSS framework
- Shadcn/ui: Beautiful component library
- Lucide React: Icon library
- Vercel Analytics: Performance tracking
- Microsoft Clarity: User behavior analytics
The Logarithmic Market Scoring Rule (LMSR) is a mathematical mechanism for pricing shares in prediction markets. It was developed by Robin Hanson and ensures liquidity and fair pricing.
For each outcome i, the weight is calculated as:
weight_i = exp(q_i / b)
Where:
q_i= Current state vector (quantity of shares) for outcomeib= Liquidity parameter (controls price sensitivity, default: 100)exp()= Exponential function
The probability of outcome i is:
P_i = weight_i / Σ(weight_j)
This normalizes weights so probabilities sum to 1.
The total cost of the current market state is:
C(q) = b * ln(Σ(exp(q_i / b)))
Where ln() is the natural logarithm.
When a user buys or sells shares, the cost is:
cost = C(q_after) - C(q_before)
- Positive cost = User pays tokens (buying shares)
- Negative cost = User receives tokens (selling shares)
Let's walk through a complete example with actual numbers from the platform:
- Market: "Will it rain tomorrow?" with outcomes ["Yes", "No"]
- Initial state:
q = [0, 0](no shares outstanding) - Liquidity parameter:
b = 100 - Initial probabilities: 50% / 50% (equal)
Before Trade:
q = [0, 0]
C([0, 0]) = 100 × ln(exp(0/100) + exp(0/100))
= 100 × ln(1 + 1)
= 100 × ln(2)
= 100 × 0.693
= 69.31 tokens
After Trade:
q = [100, 0] (added 100 shares to "Yes")
C([100, 0]) = 100 × ln(exp(100/100) + exp(0/100))
= 100 × ln(exp(1) + exp(0))
= 100 × ln(2.718 + 1)
= 100 × ln(3.718)
= 100 × 1.315
= 131.5 tokens
Cost:
Cost = 131.5 - 69.31 = 62.19 tokens
New Probabilities:
weight_yes = exp(100/100) = exp(1) = 2.718
weight_no = exp(0/100) = exp(0) = 1.0
sum = 3.718
P_yes = 2.718 / 3.718 = 0.731 = 73.1%
P_no = 1.0 / 3.718 = 0.269 = 26.9%
Result:
- User pays: 62.19 tokens
- User receives: 100 shares of "Yes"
- If "Yes" wins: User gets 100 tokens (1 token per share)
- Profit if wins: 100 - 62.19 = 37.81 tokens (60.8% return)
Before Trade:
q = [100, 0] (from previous trade)
C([100, 0]) = 131.5 tokens (calculated above)
After Trade:
q = [100, 100] (added 100 shares to "No")
C([100, 100]) = 100 × ln(exp(100/100) + exp(100/100))
= 100 × ln(exp(1) + exp(1))
= 100 × ln(2.718 + 2.718)
= 100 × ln(5.436)
= 100 × 1.693
= 169.3 tokens
Cost:
Cost = 169.3 - 131.5 = 37.8 tokens
New Probabilities:
weight_yes = exp(100/100) = 2.718
weight_no = exp(100/100) = 2.718
sum = 5.436
P_yes = 2.718 / 5.436 = 0.5 = 50.0%
P_no = 2.718 / 5.436 = 0.5 = 50.0%
Result:
- User pays: 37.8 tokens
- User receives: 100 shares of "No"
- If "No" wins: User gets 100 tokens (1 token per share)
- Profit if wins: 100 - 37.8 = 62.2 tokens (164.6% return)
- Market rebalances to 50/50 (equal shares = equal probabilities)
-
Price Impact (Slippage):
- Buying shares increases the price of that outcome
- The first 100 "Yes" shares cost 62.19 tokens
- The first 100 "No" shares cost only 37.8 tokens (cheaper because "No" was at 26.9%)
-
Why Costs Differ from Simple Estimates:
- Simple estimate:
100 shares × 50% = 50 tokens - Actual LMSR cost:
62.19 tokens - The difference (12.19 tokens) is the price impact or slippage
- Simple estimate:
-
Market Rebalancing:
- When both outcomes have equal shares (
q = [100, 100]), probabilities return to 50/50 - This is the equilibrium state of the market
- When both outcomes have equal shares (
-
Profit Calculation:
- Each share pays 1 token if the outcome wins
- Profit =
(Shares × 1) - Cost Paid - Example: 100 shares cost 62.19 tokens, win = 100 tokens, profit = 37.81 tokens
- Automatic Pricing: No need for order books or matching buyers/sellers
- Liquidity: Always a price available (though it may be expensive for large orders)
- Fairness: Prices reflect true market probabilities and account for market depth
- Price Discovery: Probabilities automatically adjust based on trading activity
- Atomic: All trades are executed atomically using MongoDB transactions
- Scalable: Works with any number of outcomes
The platform calculates actual LMSR costs in real-time, not simple estimates:
- Uses current market state (
qarray) - Accounts for liquidity parameter (
b) - Shows exact cost you'll pay
- Updates probabilities after each trade
- Fetch current market state (
qvector) - Calculate LMSR cost BEFORE trade
- Apply delta to selected outcome
- Calculate LMSR cost AFTER trade
- Deduct/add tokens from user balance
- Update outcome shares in market state
- Record trade in database
- Return updated probabilities
All steps are wrapped in a MongoDB transaction to ensure atomicity.
┌─────────────────┐
│ React Client │
│ (Frontend) │
└────────┬────────┘
│ HTTP/REST
│
┌────────▼────────┐
│ Express API │
│ (Backend) │
└────────┬────────┘
│
┌────┴────┐
│ │
┌───▼───┐ ┌──▼────┐
│MongoDB│ │Google │
│ │ │ OAuth │
└───────┘ └───────┘
- Node.js (v18 or higher)
- MongoDB (local or Atlas)
- Google Cloud Project with OAuth credentials
- npm or yarn
- Clone the repository
git clone git@github.com:Percobain/sattacell.git
cd sattacell- Install backend dependencies
cd server
npm install- Install frontend dependencies
cd ../client
npm install- Set up environment variables
Create server/.env:
MONGODB_URI=your_mongodb_connection_string
PORT=3000
ADMIN_PASSWORD=your_secure_admin_password
# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_REDIRECT_URI=http://localhost:5173/auth/callback
GOOGLE_REDIRECT_URI_PROD=https://your-production-url.com/auth/callbackCreate client/.env:
VITE_GOOGLE_CLIENT_ID=your_google_client_id
VITE_API_URL=http://localhost:3000/api
VITE_CLARITY_PROJECT_ID=your_clarity_project_id- Set up Google OAuth
- Go to Google Cloud Console
- Create OAuth 2.0 credentials
- Add authorized redirect URIs:
http://localhost:5173/auth/callback(development)https://your-production-url.com/auth/callback(production)
- Set up admin user
cd server
npm run set-admin
# Edit scripts/setAdmin.js to set your email- Start the development servers
Backend:
cd server
npm run devFrontend:
cd client
npm run devThe application will be available at http://localhost:5173
sattacell/
├── server/
│ ├── config/
│ │ └── database.js # MongoDB connection
│ ├── models/
│ │ ├── User.js # User schema
│ │ ├── Market.js # Market schema
│ │ └── Trade.js # Trade schema
│ ├── routes/
│ │ ├── auth.js # Authentication routes
│ │ ├── markets.js # Market CRUD + analytics
│ │ ├── trades.js # Trade execution
│ │ ├── users.js # User endpoints
│ │ └── admin.js # Admin endpoints
│ ├── services/
│ │ ├── lmsrService.js # LMSR calculations
│ │ ├── tradeService.js # Trade execution logic
│ │ └── settlementService.js # Market settlement
│ ├── middleware/
│ │ ├── auth.js # Authentication middleware
│ │ └── admin.js # Admin protection
│ ├── utils/
│ │ ├── googleAuth.js # Google OAuth utilities
│ │ └── errors.js # Error handling
│ └── server.js # Express app entry point
│
├── client/
│ ├── src/
│ │ ├── components/
│ │ │ ├── markets/ # Market components
│ │ │ ├── trading/ # Trading components
│ │ │ ├── admin/ # Admin components
│ │ │ └── ui/ # UI components
│ │ ├── hooks/
│ │ │ ├── useAuth.js # Auth hook
│ │ │ ├── useMarkets.js # Markets hook
│ │ │ └── useTrades.js # Trades hook
│ │ ├── pages/
│ │ │ ├── Home.jsx
│ │ │ ├── MarketPage.jsx
│ │ │ ├── DashboardPage.jsx
│ │ │ └── AdminPage.jsx
│ │ ├── services/
│ │ │ └── api.js # API client
│ │ └── App.jsx
│ └── public/
│ └── logo.png
│
└── README.md
GET /api/auth/google- Get Google OAuth URLPOST /api/auth/callback- Exchange code for tokensGET /api/auth/me- Get current user
GET /api/markets- List all marketsGET /api/markets/:id- Get market detailsGET /api/markets/:id/analytics- Get market analyticsGET /api/markets/:id/orderbook- Get orderbookGET /api/markets/:id/history- Get trade historyPOST /api/markets- Create market (admin only)
POST /api/trades- Execute a trade
GET /api/users/balance- Get user balanceGET /api/users/portfolio- Get user portfolio
POST /api/admin/settle- Settle a marketPOST /api/admin/grant-tokens- Grant tokens to user
- Sign In: Click "Sign in with Google"
- Browse Markets: View all open markets on the home page
- View Market: Click on a market to see probabilities and analytics
- Trade: Use the trading panel to buy/sell shares
- Track Positions: View your positions in "Your Positions" section
- View History: See all trades in the trade history section
- Access Admin Panel: Navigate to
/adminand enter admin password - Create Markets: Use "Create Market" tab to add new prediction markets
- Settle Markets: After an event, settle the market and select the winning outcome
- Grant Tokens: Send additional tokens to users if needed
- 10 Interactive Charts: Visualize market dynamics
- Real-time Updates: Probabilities update as trades occur
- People-focused: See who's leading, who's trading most, etc.
- Instant Execution: Trades execute atomically
- Cost Preview: See estimated cost before trading
- Position Tracking: Monitor your holdings across markets
- Market Management: Create and settle markets
- Token Distribution: Grant tokens to users
- Password Protection: Secure admin access
This project was created for CodeCell Intra Hack 2025 and is primarily for fun and learning. However, contributions are welcome!
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
- CodeCell Intra Hack 2025 - For the hackathon opportunity
- Robin Hanson - For developing the LMSR mechanism
- Polymarket - For inspiration on prediction market UX
This project is created for educational purposes and the CodeCell Intra Hack 2025. Feel free to use and modify as needed.
Potential improvements for future versions:
- Real-time WebSocket updates
- Historical probability charts
- User reputation system
- Market categories and tags
- Mobile app
- Social features (following traders, comments)
- Advanced analytics and predictions
Made with ❤️ for CodeCell Intra Hack 2025
"Predicting the future, one trade at a time."