Trade custom prediction market baskets across Polymarket and Kalshi with a single click. Built with Flask, deployed on Railway, featuring a complete REST API, API key management, and real-time price tracking.
- Custom Baskets: Define multi-leg trades spanning both platforms
- One-Click Execution: Bid YES or NO on entire baskets
- Dual Platform Support: Polymarket (crypto) + Kalshi (regulated)
- Weighted Allocation: Automatically split capital across legs
- Real-time Price Tracking: Automatic price snapshots and history
- 18 System Baskets: Pre-built risk factor baskets (Energy, Politics, Commodities, etc.)
- Automated Categorization: 10-stage ML pipeline for market classification
- 70+ Risk Factors: Comprehensive risk factor taxonomy
- Market Builder UI: 2-column interface for admins and users
- User Suggestions: Regular users can suggest new baskets or modifications
- Admin Review: Administrators review and approve/reject suggestions
- Role-Based UI: Different interfaces for admins vs regular users
- REST API v1: Complete API with authentication
- API Key Management: Secure API key generation and management
- User Authentication: Flask-Login with persistent sessions
- Responsive Design: Mobile-optimized interface
Itô Markets is a Flask-based web application with a modular architecture designed for scalability and maintainability. The application is deployed on Railway and uses PostgreSQL (via Neon) for production or SQLite for local development.
graph TB
subgraph "Client Layer"
Browser[Web Browser]
API_Client[API Clients<br/>Python/JavaScript]
end
subgraph "Vercel Serverless"
subgraph "Flask Application (app.py)"
Router[Flask Router]
subgraph "Web Routes"
Landing[Landing Page]
Dashboard[Dashboard]
MarketBuilder[Market Builder<br/>Role-Based UI]
Baskets[Baskets Page]
Profile[User Profile]
Login[Auth Routes]
end
subgraph "API Routes (Blueprints)"
CronBP[Cron Jobs<br/>/api/cron]
PublicV1[Public API v1<br/>/api/v1]
SuggestionsAPI[Suggestions API<br/>/api/suggestions]
InternalAPI[Internal API<br/>/api/internal]
end
end
subgraph "API Endpoints"
BasketsAPI["/api/v1/baskets"]
MarketsAPI["/api/v1/markets"]
SuggestionsEndpoint["/api/suggestions/baskets"]
APIKeysAPI["/api/internal/api-keys"]
CronEndpoints["/api/cron/*"]
end
subgraph "Middleware"
AuthMW[API Key Auth<br/>@require_api_key]
SessionMW[Session Management<br/>Flask-Login]
RoleCheck[Admin Role Check<br/>is_admin]
end
subgraph "V2 Services (Basket Creation Pipeline)"
MarketFetchers[Market Fetchers<br/>Native Kalshi/Polymarket APIs]
CategorizationSvc[Categorization Service<br/>10-Stage ML Pipeline]
BasketMappingSvc[Basket Mapping Service<br/>70+ Risk Factors → 18 Baskets]
MarketExpansionSvc[Market Expansion Service<br/>Categorical Expansion]
SyncServiceV2[Sync Service<br/>2-Pass Add/Remove]
end
subgraph "Core Services"
PriceService[Price Service<br/>Snapshots & Tracking]
BackfillService[Backfill Service<br/>Historical Data]
end
subgraph "External APIs"
PolymarketAPI[Polymarket<br/>CLOB API]
KalshiAPI[Kalshi<br/>Trading API v2]
end
end
subgraph "Data Layer"
PostgreSQL[(PostgreSQL<br/>Neon/Vercel)]
end
subgraph "Database Models"
UserModel[User<br/>is_admin flag]
BasketModel[Basket]
MarketModelV2[Market<br/>V2 fields: basket_name,<br/>risk_factor_id, direction]
BasketSuggestionModel[BasketSuggestion<br/>pending/approved/rejected]
BasketOverrideModel[BasketOverride<br/>Manual reassignments]
TradeModel[Trade]
APIKeyModel[APIKey]
PriceHistory[BasketPriceHistory]
end
subgraph "GitHub Actions (Automation)"
DailySync[Daily Market Sync<br/>2AM UTC]
DBMigration[Database Migration<br/>Manual]
GenerateBaskets[Generate System Baskets<br/>Manual]
BackfillAll[Backfill All Baskets<br/>Manual]
DeleteMarkets[Delete Market Data<br/>Manual + Confirm]
end
%% Client connections
Browser --> Router
API_Client --> AuthMW
%% Router connections
Router --> Landing
Router --> Dashboard
Router --> MarketBuilder
Router --> Baskets
Router --> Profile
Router --> Login
%% Middleware flow
AuthMW --> PublicV1
AuthMW --> InternalAPI
SessionMW --> Router
SessionMW --> SuggestionsAPI
RoleCheck --> MarketBuilder
RoleCheck --> SuggestionsAPI
%% API routing
PublicV1 --> BasketsAPI
PublicV1 --> MarketsAPI
SuggestionsAPI --> SuggestionsEndpoint
InternalAPI --> APIKeysAPI
CronBP --> CronEndpoints
%% V2 Pipeline flow
CronEndpoints --> SyncServiceV2
SyncServiceV2 --> MarketFetchers
MarketFetchers --> PolymarketAPI
MarketFetchers --> KalshiAPI
SyncServiceV2 --> CategorizationSvc
CategorizationSvc --> BasketMappingSvc
BasketMappingSvc --> MarketExpansionSvc
%% Suggestions workflow
MarketBuilder --> SuggestionsEndpoint
SuggestionsEndpoint --> BasketSuggestionModel
SuggestionsEndpoint --> BasketModel
%% Service to DB connections
BasketsAPI --> PriceService
BasketsAPI --> BackfillService
MarketsAPI --> MarketModelV2
PriceService --> PriceHistory
BackfillService --> PriceHistory
SyncServiceV2 --> MarketModelV2
MarketExpansionSvc --> MarketModelV2
BasketMappingSvc --> BasketOverrideModel
%% Database connections
PostgreSQL --> UserModel
PostgreSQL --> BasketModel
PostgreSQL --> MarketModelV2
PostgreSQL --> BasketSuggestionModel
PostgreSQL --> BasketOverrideModel
PostgreSQL --> TradeModel
PostgreSQL --> APIKeyModel
PostgreSQL --> PriceHistory
%% GitHub Actions
DailySync --> SyncServiceV2
DBMigration --> PostgreSQL
GenerateBaskets --> BasketModel
GenerateBaskets --> MarketModelV2
BackfillAll --> BackfillService
DeleteMarkets --> MarketModelV2
%% Styling
style Browser fill:#4a9eff
style API_Client fill:#4a9eff
style PostgreSQL fill:#00d4aa
style PolymarketAPI fill:#7c3aed
style KalshiAPI fill:#f97316
style MarketBuilder fill:#00d4aa
style SuggestionsAPI fill:#00d4aa
style BasketSuggestionModel fill:#fbbf24
style BasketOverrideModel fill:#fbbf24
style MarketModelV2 fill:#fbbf24
CB_Pred2/
app.py # Main Flask application
api/ # API layer
index.py # Vercel serverless entry point
middleware/ # API middleware
__init__.py
api_key_auth.py # API key authentication decorators
routes/ # Route blueprints
cron.py # Cron job endpoints
internal/ # Internal API routes
api_keys.py # API key management
v1/ # Public API v1 routes
baskets.py # Basket endpoints
markets.py # Market search endpoints
services/ # Business logic services
price_service.py # Price fetching and snapshots
sync_service.py # Market data synchronization
backfill_service.py # Historical price data backfill
utils/ # API utilities
responses.py # Standardized API responses
models/ # Database models
__init__.py # Model exports
models.py # Core models (User, Basket, Trade, Market, etc.)
api_key.py # APIKey model
templates/ # Jinja2 templates
index.html # Baskets page
api_docs.html # API documentation
dashboard.html # Dashboard
landing.html # Landing page
profile.html # User profile
login.html # Login page
developer_portal/
api_keys.html # API key management UI
scripts/ # Standalone scripts (GitHub Actions)
export_all_markets.py # Export all markets to CSV
backfill_history.py # Backfill historical price data
config/ # Configuration
settings.py # App settings
.github/workflows/ # GitHub Actions workflows
vercel.json # Vercel deployment config
requirements.txt # Python dependencies
RESTful API for external consumers with API key authentication:
Endpoints:
GET /api/v1/baskets- List all baskets (paginated)GET /api/v1/baskets/{id}- Get basket detailsGET /api/v1/baskets/{id}/price- Get current basket pricePOST /api/v1/baskets/{id}/backfill- Manually trigger historical backfill for a basketGET /api/v1/markets/search- Search markets across platforms
Authentication:
- Bearer token authentication using API keys
- Scopes:
baskets:read,baskets:write,markets:read,trades:read
Internal endpoints for authenticated web users (session-based):
Endpoints:
GET /api/internal/api-keys- List user's API keysPOST /api/internal/api-keys- Create new API keyDELETE /api/internal/api-keys/{id}- Revoke API keyGET /api/internal/api-keys/{id}/usage- Get API key usage stats
Scheduled tasks for data synchronization:
Endpoints:
POST /api/cron/sync-markets- Sync market data from external APIsPOST /api/cron/record-prices- Record price snapshots for baskets
Trigger:
- Vercel Cron Jobs (configured in
vercel.json) - Or manual triggers via GitHub Actions
-
User: User accounts with authentication
- Fields:
id,username,email,password_hash,is_admin,created_at
- Fields:
-
Basket: Custom prediction market baskets
- Fields:
id,name,description,user_id,underlyers_count,created_at - Relationships:
prices(BasketPriceHistory),trades(Trade)
- Fields:
-
Market: Individual markets from Polymarket/Kalshi
- Fields:
id,platform,market_id,ticker,name,category,expiration,created_at - Indexed by
platformandmarket_id/ticker
- Fields:
-
BasketPriceHistory: Historical price snapshots
- Fields:
id,basket_id,price,timestamp - Used for price tracking and analytics
- Fields:
-
Trade: User trade history
- Fields:
id,user_id,basket_id,direction,amount,status,created_at
- Fields:
-
UserAPIKeys: User's API keys for Polymarket/Kalshi
- Fields:
user_id,platform,key_data(encrypted),demo_mode
- Fields:
- APIKey: Public API keys for external consumers
- Fields:
id,user_id,key_hash,key_prefix,name,scopes,rate_limit_per_hour,last_used_at,total_requests,created_at,expires_at - Methods:
generate_key(),verify_key(),has_scope()
- Fields:
-
BasketOverride: Manual market-to-basket reassignments
- Fields:
id,market_id,original_basket_name,override_basket_name,reason,created_by,created_at - Indexed by
market_idfor fast lookups
- Fields:
-
BasketSuggestion: User suggestions for baskets
- Fields:
id,suggestion_type,suggested_name,suggested_description,suggested_legs,target_basket_id,markets_to_add,markets_to_remove,suggested_by,status,reviewed_by,reviewed_at,admin_notes,created_at - Status values:
pending,approved,rejected
- Fields:
Handles basket price calculations and snapshots:
calculate_basket_price(basket_id): Calculate current basket price from market pricesrecord_price_snapshot(basket_id): Record price snapshot to historyget_latest_price(basket_id): Get most recent price snapshot
Synchronizes market data from external APIs:
sync_markets(platform): Sync markets from Polymarket/Kalshi via Dome APIupdate_market_prices(): Update current market prices- Uses Dome API SDK for unified market search across platforms
Backfills historical price data for baskets using Dome API:
backfill_basket_history(basket, days=365): Backfill historical data for a single basketget_kalshi_history(ticker, days): Fetch historical trades from Kalshi via Dome APIget_polymarket_history(token_id, days): Fetch historical orders from Polymarket via Dome APIaggregate_to_daily(trades): Aggregate trades to daily average prices- Automatic Backfill: Automatically triggers when a basket is created (365 days of history)
- Manual Backfill: Can be triggered via API endpoint or script
- Uses Dome API for historical data retrieval
Decorators for API route protection:
@require_api_key: Require valid API key@require_read_key: Require key with read scopes@require_write_key: Require key with write scopes
Validates API keys, checks scopes, and tracks usage statistics.
- Flask-Login: Session-based authentication for web users
- Session Duration: 30 days (persistent sessions)
- Password Hashing: Secure password hashing for user accounts
- Routes:
/login,/register,/logout,/profile
- API Keys: Bearer token authentication (
Authorization: Bearer {key}) - Key Format:
bkt_live_{random}orbkt_test_{random} - Key Storage: Hashed in database (SHA-256)
- Key Scopes: Fine-grained permissions (read/write per resource)
- Rate Limiting: Per-key rate limits (configurable per hour)
The application is deployed on Vercel as serverless functions:
Configuration (vercel.json):
- Serverless function routing for Flask app
- Cron job schedules for background tasks
- Environment variable configuration
Key Features:
- Automatic HTTPS
- Global CDN
- Serverless scaling
- Cron job support
Database:
- Production: PostgreSQL (Neon or Vercel Postgres)
- Local: SQLite (
instance/basket_trader.db)
Required environment variables:
# Flask
SECRET_KEY=your-secret-key-here
# Database (Production)
DATABASE_URL=postgresql://user:pass@host:port/dbname
# Dome API (Optional - has default)
DOME_API_KEY=your-dome-api-key
# Vercel
VERCEL=1 # Automatically set by Vercel# Install dependencies
pip install -r requirements.txt
# Set environment variables
export SECRET_KEY=dev-secret-key
# Optional: export DATABASE_URL for PostgreSQL
# Run Flask app
python app.py
# App runs on http://localhost:5000When a new basket is created, historical price data is automatically backfilled:
- Trigger: Automatically triggered when
save_basket_to_db()is called - Duration: Defaults to 365 days of historical data
- Non-blocking: Backfill runs asynchronously - basket creation succeeds even if backfill fails
- Service: Uses
api/services/backfill_service.pyfor backfill logic - Data Source: Dome API (Kalshi trades and Polymarket orders)
Basket history can be manually backfilled via:
-
API Endpoint:
POST /api/v1/baskets/{basket_id}/backfill?days=365- Authentication: API Key (baskets:read scope)
- Query Parameter:
days(default: 365) - Returns: Number of snapshots created
-
Script:
scripts/backfill_history.py- Scheduled: Manual trigger via GitHub Actions
- Processes: All baskets or individual basket via service
Long-running tasks that exceed Vercel's function timeout:
Scripts:
-
scripts/export_all_markets.py: Export all markets to CSV- Scheduled: Manual trigger or weekly
- Output: CSV files in
bin/directory
-
scripts/backfill_history.py: Backfill historical price data for all baskets- Scheduled: Manual trigger via GitHub Actions
- Uses:
api/services/backfill_service.pyfor individual basket backfill - Processes: Historical price snapshots from database
Workflows:
.github/workflows/export_markets.yml: Export markets workflow.github/workflows/backfill_history.yml: Backfill history workflow (all baskets)
Short-running scheduled tasks (configured in vercel.json):
- Record Prices: Periodic price snapshots for baskets (every minute)
Automated workflows for backend operations:
| Workflow | Trigger | Description |
|---|---|---|
daily-market-sync.yml |
Daily 2AM UTC | Add new markets (no deletions) |
db-migration.yml |
Manual | Apply SQL migration files |
delete-market-data.yml |
Manual | Delete all markets (with confirmation) |
generate-system-baskets.yml |
Manual | Create 18 system baskets |
backfill_all_baskets.yml |
Manual | Backfill historical prices |
backfill_history.yml |
Manual | Backfill individual basket |
export_markets.yml |
Manual | Export market data to CSV |
DATABASE_URL=postgresql://...
KALSHI_API_KEY=your-kalshi-key
SECRET_KEY=your-flask-secret
CRON_SECRET=your-cron-secret- API: CLOB (Central Limit Order Book) API
- Client:
py-clob-client(official Python client) - Auth: EIP-712 signed messages
- Network: Polygon (Chain ID: 137)
- Currency: USDC
- API: Trading API v2
- Auth: RSA/Ed25519 signed requests
- Environments: Demo (
demo-api.kalshi.co) and Production (api.elections.kalshi.com) - Regulation: CFTC-regulated (US available)
- Purpose: Unified market search across Polymarket and Kalshi
- SDK:
dome_api_sdkPython package - Features: Market search, filtering, category browsing
Interactive API documentation is available at /api-docs:
- Comprehensive endpoint documentation
- Interactive "Try it out" functionality
- Code examples in Python, JavaScript, and cURL
- Authentication guide
- Response format documentation
- Session Security: HTTPOnly, Secure (HTTPS), SameSite cookies
- Password Hashing: Secure password hashing (Flask security)
- CSRF Protection: SameSite cookie policy
- XSS Prevention: Template escaping (Jinja2)
- API Key Hashing: Keys stored as SHA-256 hashes
- Key Prefixes: Only key prefix visible (e.g.,
bkt_live_abc...) - Scope Validation: Fine-grained permission checks
- Rate Limiting: Per-key rate limits (prevent abuse)
- Encryption: User API keys encrypted in database (UserAPIKeys)
- No Key Exposure: Full API keys only shown once on creation
- Audit Logging: API key usage tracking (
last_used_at,total_requests)
# Clone repository
git clone <repository-url>
cd CB_Pred2
# Create virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Set up environment variables
cp .env.example .env # Create .env file with your config
# Initialize database (SQLite for local dev)
python -c "from app import app, db; app.app_context().push(); db.create_all()"
# Run development server
python app.py# Run tests (when test suite is added)
pytest
# Run with coverage
pytest --cov=.- Routes: Keep route handlers thin, delegate to services
- Services: Business logic and external API interactions
- Models: Database models and relationships
- Middleware: Reusable decorators and request handlers
- Utils: Helper functions and utilities
For more detailed implementation guides, see the docs/ folder:
- Complete REST API v1: Public API with API key authentication
/api/v1/baskets- List and retrieve baskets/api/v1/baskets/{id}/price- Get basket prices/api/v1/markets/search- Market search across platforms
- Internal API: Session-based API for authenticated users
/api/internal/api-keys- API key management (CRUD operations)
- API Key Management: Secure API key generation, hashing, and scope-based permissions
- Interactive Documentation: Comprehensive API docs at
/api-docswith "Try it out" functionality
- Modular Architecture: Reorganized codebase into clear layers
api/routes/- Route blueprints (v1, internal, cron)api/services/- Business logic services (price, sync)api/middleware/- Authentication and request middlewaremodels/- Database models (separated from app.py)scripts/- Standalone scripts for GitHub Actions
- Separation of Concerns: Clear separation between routes, services, and data access
- Blueprint-Based Routes: Flask blueprints for better organization
- Responsive Design: Fully responsive across all pages (mobile, tablet, desktop)
- Navigation Enhancement: Consistent left sidebar navigation across main pages
- API Documentation Page: Professional, interactive API documentation
- Developer Portal: API key management UI integrated with site design
- Session Management: Improved session persistence (30-day sessions)
- Auto-login: Automatic login redirect for authenticated users
- Database-Backed Markets: Markets stored in database instead of real-time API calls
- Market Model: Comprehensive
Marketmodel with platform, ticker, metadata - Market Synchronization: Automated sync service (
sync_service.py) to update markets from external APIs - Efficient Filtering: Fast database queries for market search and filtering
- Market History: Track market additions and updates over time
- Category Organization: Markets organized by category for easy filtering
- Performance Optimization: Markets served from database instead of external API calls
- Reduced Latency: Faster market search and retrieval
- Offline Capability: Markets available even when external APIs are slow/unavailable
- Cron-Based Updates: Automated market updates via cron jobs
- Data Consistency: Single source of truth for market data
- Backfill Service: Centralized
api/services/backfill_service.pyfor historical price data backfill - Automatic Trigger: Historical data automatically backfilled when baskets are created (365 days)
- Manual Backfill API: New endpoint
POST /api/v1/baskets/{id}/backfillfor manual backfill triggers - Reusable Logic: Backfill logic extracted from script into reusable service module
- Non-blocking: Backfill doesn't block basket creation - failures are logged but don't affect basket creation
- Dome API Integration: Uses Dome API for historical Kalshi trades and Polymarket orders
- Daily Aggregation: Historical trades/orders aggregated to daily average prices
MIT
For issues, questions, or contributions, please open an issue on GitHub.
