A professional, real-time monitoring and analytics platform for automated weather prediction market trading on Kalshi. Built with React 19, Django 4.2, and WebSockets for seamless live updates.
This dashboard provides comprehensive visibility into trading performance, position management, P&L analytics, and reliability metrics—all through a clean, responsive interface powered by the Frost design system.
The Kalshi Weather Trading Dashboard is designed to monitor and analyze automated trading activity from the Kalshi Weather Arbitrage Bot. It reads structured data files (JSON, JSONL, and text logs) generated by the trading daemon and presents them through an intuitive web interface with real-time updates via WebSockets.
Key Capabilities:
- Live Position Tracking: Monitor all active positions with ensemble forecast details, edge calculations, and settlement dates
- P&L Analytics: Daily, weekly, and total profit/loss tracking with interactive charts and city-level breakdowns
- Backtest Analysis: Review every trade opportunity the bot evaluated—what was traded, what was skipped, and why
- Reliability Metrics: 27 analytics endpoints measuring win rates, edge calibration, provider accuracy, and cost efficiency
- Paper Trading Monitor: Track simulated trades separately from live positions
- WebSocket Log Streaming: Real-time log tailing with color-coded formatting and automatic scroll
- Health Monitoring: Daemon status indicators, staleness detection, and circuit breaker alerts
- Real-time Status Panel: Balance, daily P&L, position count, daemon health indicator
- Position Table: All active positions with city, side, strike, edge, confidence, cost, and settlement date
- P&L Chart: Interactive time-series visualization of daily profit/loss with hover details
- City Heatmap: Geographical performance breakdown showing which cities are most profitable
- Backtest Log Viewer: Paginated table of all evaluated opportunities with filters for date, city, and action
- Win Rate Analysis: Breakdown by city, side (yes/no), confidence bucket, and edge bracket
- Edge Calibration: Verify that predicted edges match actual win rates (calibration curve)
- Confidence Calibration: Assess whether forecast confidence scores are well-calibrated
- Bias Detection: Measure systematic over/under-estimation of edges
- Provider Accuracy: Compare performance across NOAA, GFS, ICON, ECMWF, and GEM forecast providers
- Staleness Impact: Quantify win rate degradation when NOAA forecasts are stale
- Dropout Analysis: Performance when provider count drops (missing data scenarios)
- Cost Efficiency: ROI by edge bucket, average profit per trade, break-even win rate
- Streak Tracking: Current and longest winning/losing streaks
- Separate Tab: Dedicated view for paper trades (simulated positions)
- Trade History: All paper trades with timestamps, edge, confidence, and cost
- Summary Metrics: Total paper trades, total cost, cities traded, average edge/confidence
- Side Distribution: Breakdown of yes/no paper trades
- Frost Design System: Clean, modern UI with Tailwind CSS v4 and consistent color palette
- Responsive Layout: Optimized for desktop (1920px+), laptop (1440px), and tablet (1024px+)
- Dark Mode Ready: Color scheme designed for low-light environments (future enhancement)
- Toast Notifications: Non-intrusive alerts for daemon status changes and data refresh events
- Settings Drawer: Configure API endpoint, enable/disable WebSocket logs, adjust refresh intervals
- React 19.0: Latest React with Concurrent Mode and automatic batching
- TypeScript 5.6: Full type safety across components, stores, and API calls
- Vite 6.0: Lightning-fast HMR (Hot Module Replacement) and optimized production builds
- Tailwind CSS 4.0: Utility-first styling with custom Frost theme configuration
- Zustand 5.0: Lightweight state management for dashboard data and UI state
- Recharts 3.7: Declarative charting library for P&L and analytics visualizations
- Axios 1.7: Promise-based HTTP client with interceptors for error handling
- Django 4.2: Robust Python web framework with ORM and middleware support
- Django REST Framework 3.14: RESTful API toolkit with serializers and viewsets
- Django Channels 4.0: WebSocket support with ASGI protocol server (Daphne)
- Daphne 4.0: HTTP/WebSocket ASGI server for production deployment
- Python 3.10+: Modern Python with async/await and type hints
- WebSockets: Bi-directional communication for log streaming
- ASGI Protocol: Async server gateway interface for handling HTTP and WebSocket connections
- Redis (optional): Channel layer backend for horizontal scaling (future enhancement)
- File-based Architecture: No database required—reads directly from daemon output files
- JSONL Format: Append-only log files for backtests, settlements, and paper trades
- JSON State Files:
kalshi_unified_state.json,kalshi_pnl.jsonfor current status - Text Logs:
kalshi_unified_log.txtfor real-time log streaming
The primary view displays live positions in a sortable table with ensemble forecast details, a P&L chart showing daily performance trends, and a city heatmap highlighting geographic profitability. The status panel at the top shows current balance, today's P&L, active position count, and daemon health indicator (green dot when running, red when stale).
The backtest tab presents a paginated log of all evaluated trading opportunities. Each row shows the timestamp, city, action (trade/skip), skip reason, edge, confidence, market price, and ensemble forecast. Filters allow narrowing by date, city, or action type. Summary stats display the funnel: opportunities scanned → traded → skipped (with reason breakdown).
A dedicated view for simulated trades shows all paper positions with normalized schema (compatible with two historical formats). The summary panel displays total paper trades, aggregate cost, cities traded, and average edge/confidence. A side distribution chart shows yes/no trade balance.
A planned enhancement will add a visual analytics suite displaying calibration curves, provider accuracy comparisons, and reliability metrics in chart form. Currently, these 27 endpoints are accessible via REST API.
A slide-out panel allows configuring the backend API URL (useful for remote daemon monitoring), enabling/disabling WebSocket log streaming, and adjusting auto-refresh intervals for position and P&L data.
- Python 3.10+ (3.11 or 3.12 recommended)
- Node.js 18+ (20.x LTS recommended)
- Trading Daemon: The Kalshi Weather Bot must be running and generating data files
git clone https://github.com/Tyler-Irving/kalshi-dashboard.git
cd kalshi-dashboardcd backend
# Create virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Create .env file
cp .env.example .env
nano .env # Edit configuration (see below)
# Generate SECRET_KEY
python manage.py generate_secret_key
# Run migrations (optional, no DB required but good practice)
python manage.py migrate
# Start development server
python manage.py runserver 0.0.0.0:8000Backend .env Configuration:
SECRET_KEY=your-generated-secret-key-here
DEBUG=false
ALLOWED_HOSTS=localhost,127.0.0.1
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
# Path to daemon data files
TRADING_DIR=../../kalshi-weather-bot
# Path to daemon codebase (for optional integrations)
KALSHI_DAEMON_DIR=../../kalshi-weather-bot
# API credentials directory (optional, for future enhancements)
KALSHI_SECRETS_DIR=~/.openclaw/.secretsImportant: TRADING_DIR must point to the directory containing:
kalshi_unified_state.jsonkalshi_pnl.jsonkalshi_unified_log.txtkalshi_backtest_log.jsonlkalshi_settlement_log.jsonlpaper_trades.jsonl
cd ../frontend
# Install dependencies
npm install
# Start development server
npm run devThe frontend will open at http://localhost:5173 and automatically proxy API requests to http://localhost:8000.
Open your browser to:
http://localhost:5173
You should see:
- Status Panel: Balance, P&L, position count (data from
kalshi_unified_state.json) - Position Table: Active positions (may be empty if no trades are open)
- P&L Chart: Daily performance (populated if daemon has been running)
- Logs Tab: Real-time log streaming via WebSocket
Troubleshooting Quick Start:
- "API Error": Verify backend is running on port 8000 and
CORS_ALLOWED_ORIGINSincludeshttp://localhost:5173 - "No Positions": This is normal if the trading bot hasn't opened any positions yet
- "Daemon Offline": The health indicator shows red if
kalshi_unified_log.txthasn't been modified in 20+ minutes - "WebSocket Disconnected": Check that Daphne/Channels is running (not just Django
runserver)
The dashboard follows a file-reader architecture where the backend acts as a REST API gateway to structured files generated by the trading daemon. There is no database—all data is read on-demand from JSON, JSONL, and text files.
Trading Daemon (kalshi_unified.py)
↓ Writes files every 15 minutes
[Data Files: JSON, JSONL, TXT]
↓ Read by Django views
Django REST API (7 core endpoints, 27 analytics)
↓ HTTP/WebSocket
React Frontend (Zustand state management)
↓ Renders
Browser (User)
Key Components:
- Trading Daemon: Autonomous bot running as a background process, polling Kalshi API and weather providers
- Data Files: Append-only logs (JSONL) and overwrite-on-update state files (JSON)
- Django Backend: Stateless API server reading files and serving REST endpoints
- WebSocket Server: Daphne/Channels for real-time log streaming
- React Frontend: SPA (Single Page Application) with client-side routing and Zustand stores
Daemon → Files → Backend → Frontend
-
Daemon Writes: Every poll cycle (15 minutes), the daemon:
- Updates
kalshi_unified_state.json(positions, balance, daily trades) - Appends to
kalshi_backtest_log.jsonl(all evaluated opportunities) - Appends to
kalshi_settlement_log.jsonl(when positions settle) - Updates
kalshi_pnl.json(daily/weekly P&L aggregations) - Appends to
kalshi_unified_log.txt(timestamped log entries)
- Updates
-
Backend Reads: On each API request:
file_readers.pyopens files, parses JSON/JSONL, and returns Python dicts- Views aggregate, filter, and format data into REST responses
- No caching—always fresh data (appropriate for 15-minute update cycles)
-
Frontend Fetches: React components:
- Call Axios-wrapped API methods from
src/config/api.ts - Store responses in Zustand stores (
dashboardStore,paperStore) - Re-render components when store state changes
- Poll critical endpoints (status, positions) every 10 seconds
- Call Axios-wrapped API methods from
-
WebSocket Push: For log streaming:
- Daphne opens WebSocket connection at
/ws/logs/ - Consumer (
LogConsumer) tailskalshi_unified_log.txtand pushes new lines - Frontend receives and appends to scrollable log viewer
- Daphne opens WebSocket connection at
Django Apps:
config/: Project settings, ASGI/WSGI configuration, root URL routingdashboard/: Core REST API views, file readers, analytics engine, WebSocket consumerskalshi/: (Optional) Future integration for live Kalshi API calls
Key Modules:
dashboard/views.py: 20+ REST API endpoints (status, positions, P&L, backtest, analytics)dashboard/file_readers.py:FileReaderclass with JSON/JSONL parsers, log tailing, mtime checksdashboard/analytics.py:ReliabilityAnalyticsclass for 27 analytics endpoints (win rates, calibration, bias)dashboard/consumers.py:LogConsumerWebSocket handler for real-time log streamingdashboard/routing.py: WebSocket URL routing (separate from HTTP routes)
File Reader Design:
- Singleton Pattern: Single
FileReaderinstance configured withTRADING_DIRfrom.env - On-demand Parsing: Files are read and parsed on each request (no caching)
- Error Handling: Returns empty dicts/arrays if files missing or malformed
- Performance: JSONL files filtered by date to limit memory usage (e.g., only today's backtests)
Component Structure:
src/
├── main.tsx # Entry point, React root
├── App.tsx # Root component, routing logic
├── config/
│ └── api.ts # Axios instance, API method wrappers
├── stores/
│ ├── dashboardStore.ts # Zustand store for positions, P&L, status
│ └── paperStore.ts # Zustand store for paper trades
├── components/
│ ├── layout/
│ │ ├── Header.tsx # Top banner with app title
│ │ └── NavBar.tsx # Tab navigation
│ ├── PositionsTable.tsx # Sortable table of active positions
│ ├── PnLChart.tsx # Recharts area chart for daily P&L
│ ├── PnLPanel.tsx # Summary cards: balance, P&L, win rate
│ ├── CityHeatmap.tsx # Color-coded city performance grid
│ ├── BacktestStats.tsx # Funnel stats for backtest log
│ ├── HeroBanner.tsx # Large status panel with daemon health
│ ├── SettingsDrawer.tsx # Slide-out settings panel
│ ├── ErrorBanner.tsx # Error notifications
│ └── AlertBanner.tsx # Info/warning banners
└── types/
├── position.ts # TypeScript interfaces for positions
├── pnl.ts # P&L data types
├── backtest.ts # Backtest entry types
└── paper.ts # Paper trade types
State Management:
- Zustand Stores: Lightweight, hook-based state containers (no Context API overhead)
- Store Pattern: Each store has:
- State fields (e.g.,
positions,pnl,loading,error) - Actions (e.g.,
fetchPositions(),refreshPnL()) - Computed selectors (e.g.,
getTotalPnL())
- State fields (e.g.,
- Polling Logic:
useEffecthooks in components triggerfetchactions on mount and intervals
Routing:
- Client-side Routing: Simple tab-based navigation (no React Router—just conditional rendering)
- Tabs: Dashboard, Backtest, Paper Trades, Analytics (future), Settings
- URL Sync: (Optional future enhancement) Sync tab state to URL hash
WebSocket Flow:
- Frontend connects:
new WebSocket('ws://localhost:8000/ws/logs/') - Daphne routes to
LogConsumer.connect() - Consumer spawns background task that tails
kalshi_unified_log.txt - On new lines detected (via
mtimepolling):- Consumer sends JSON message:
{"type": "log_line", "line": "...", "timestamp": "..."}
- Consumer sends JSON message:
- Frontend appends line to scrollable
<div>, auto-scrolls to bottom - Disconnect: Consumer cancels background task, closes file handle
WebSocket Reconnection:
- Frontend implements exponential backoff reconnection (1s, 2s, 4s, 8s, max 30s)
- Shows "Reconnecting..." indicator in log viewer
- Automatically resumes streaming when connection restored
All configuration is via .env file in backend/ directory.
Security:
SECRET_KEY= # REQUIRED: Generate with `python manage.py generate_secret_key`
DEBUG=false # Set to true only for local development (exposes stack traces)
ALLOWED_HOSTS=localhost,127.0.0.1 # Comma-separated hostnames (add your domain for production)CORS (Cross-Origin Resource Sharing):
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
# Add production frontend URL:
# CORS_ALLOWED_ORIGINS=http://localhost:5173,https://dashboard.example.comData Source Paths:
TRADING_DIR=../../kalshi-weather-bot
# Absolute paths work too:
# TRADING_DIR=/home/user/trading/kalshi-weather-bot
KALSHI_DAEMON_DIR=../../kalshi-weather-bot
# Used for optional integrations (currently unused)
KALSHI_SECRETS_DIR=~/.openclaw/.secrets
# Path to kalshi.json and kalshi_private.pem (for future API features)Logging:
LOG_LEVEL=INFO
# Options: DEBUG, INFO, WARNING, ERROR, CRITICAL
# DEBUG logs all SQL queries and API calls (verbose)Database (Optional):
# Currently unused (file-based architecture)
# Future enhancement: cache analytics results in PostgreSQL
# DATABASE_URL=postgresql://user:pass@localhost:5432/kalshi_dashboardFrontend configuration is via src/config/api.ts (no .env file needed for development).
API Endpoint:
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';To override for production:
# In frontend/.env.production
VITE_API_URL=https://api.example.comWebSocket URL:
const WS_BASE_URL = API_BASE_URL.replace('http', 'ws');
// Automatically converts http://localhost:8000 → ws://localhost:8000The FileReader class in backend/dashboard/file_readers.py is configured via TRADING_DIR environment variable.
Default File Locations:
TRADING_DIR = os.getenv('TRADING_DIR', '../trading')
FILES = {
'kalshi_unified_state.json': f'{TRADING_DIR}/kalshi_unified_state.json',
'kalshi_pnl.json': f'{TRADING_DIR}/kalshi_pnl.json',
'kalshi_unified_log.txt': f'{TRADING_DIR}/kalshi_unified_log.txt',
'kalshi_backtest_log.jsonl': f'{TRADING_DIR}/kalshi_backtest_log.jsonl',
'kalshi_settlement_log.jsonl': f'{TRADING_DIR}/kalshi_settlement_log.jsonl',
'paper_trades.jsonl': f'{TRADING_DIR}/paper_trades.jsonl',
}Handling Missing Files:
- Missing JSON files return
{} - Missing JSONL files return
[] - Missing log files return
[](no crash—graceful degradation)
Hot Reload (Both Servers):
# Terminal 1: Backend (Django auto-reloads on .py changes)
cd backend
source venv/bin/activate
python manage.py runserver
# Terminal 2: Frontend (Vite HMR on .tsx changes)
cd frontend
npm run devWebSocket Development:
For WebSocket support in development, use Daphne instead of runserver:
cd backend
source venv/bin/activate
daphne -b 0.0.0.0 -p 8000 config.asgi:applicationBackend (Python):
- Formatter: Black (line length 120)
- Linter: Flake8
- Type Hints: Encouraged but not enforced
# Format all Python files
black backend/
# Lint
flake8 backend/Frontend (TypeScript/React):
- Formatter: Prettier (2 spaces, single quotes)
- Linter: ESLint (React hooks rules, TypeScript rules)
- Style Guide: Airbnb React style guide (adapted)
# Lint
npm run lint
# Auto-fix
npm run lint -- --fixBackend Tests:
cd backend
python manage.py test dashboard.testsTest coverage includes:
- File reader unit tests (mocked file I/O)
- Analytics engine tests (edge calibration, win rate calculations)
- API endpoint tests (response schema validation)
Frontend Tests: Currently no test suite (future enhancement: Vitest + React Testing Library).
Backend Debugging:
# Enable DEBUG mode
echo "DEBUG=true" >> backend/.env
# View SQL queries (if using DB)
LOG_LEVEL=DEBUG python manage.py runserverFrontend Debugging:
- React DevTools extension (inspect component tree, props, state)
- Zustand DevTools: Install
zustand/middlewareand enable Redux DevTools integration
import { devtools } from 'zustand/middleware';
export const useDashboardStore = create<DashboardState>()(
devtools(
(set) => ({
// ...state and actions
}),
{ name: 'DashboardStore' }
)
);WebSocket Debugging:
// In browser console
const ws = new WebSocket('ws://localhost:8000/ws/logs/');
ws.onmessage = (event) => console.log('WS:', JSON.parse(event.data));
ws.onerror = (error) => console.error('WS Error:', error);New REST Endpoint:
- Add view function to
backend/dashboard/views.py:
@api_view(['GET'])
def my_new_view(request):
data = file_reader.read_json('some_file.json')
# Process data...
return Response({'result': data})- Add URL route to
backend/dashboard/urls.py:
path('my-endpoint/', views.my_new_view, name='my-endpoint'),- Add API call to
frontend/src/config/api.ts:
export const fetchMyData = () => api.get('/api/v1/my-endpoint/');- Use in component:
const [data, setData] = useState(null);
useEffect(() => {
fetchMyData().then(response => setData(response.data));
}, []);New WebSocket Consumer:
- Create consumer in
backend/dashboard/consumers.py - Add route to
backend/dashboard/routing.py - Connect from frontend:
const ws = new WebSocket('ws://localhost:8000/ws/my-channel/');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// Handle message...
};Before deploying to production:
- Set
DEBUG=falseinbackend/.env - Generate strong
SECRET_KEY(64+ characters) - Configure
ALLOWED_HOSTSwith production domain - Set
CORS_ALLOWED_ORIGINSto production frontend URL - Use HTTPS for both frontend and backend
- Configure reverse proxy (Nginx or Caddy)
- Set up systemd services for Daphne and Gunicorn
- Configure log rotation for
kalshi_unified_log.txt - Set up monitoring (health check endpoint at
/api/v1/health/) - Build frontend with
npm run build - Serve frontend static files via CDN or Nginx
1. Install dependencies on server:
sudo apt update
sudo apt install python3.11 python3.11-venv nginx redis-server2. Deploy backend code:
cd /opt
sudo git clone https://github.com/Tyler-Irving/kalshi-dashboard.git
cd kalshi-dashboard/backend
sudo python3.11 -m venv venv
sudo venv/bin/pip install -r requirements.txt3. Configure .env:
sudo nano /opt/kalshi-dashboard/backend/.env
# Set production values (see Configuration section)4. Create systemd service for Daphne:
# /etc/systemd/system/kalshi-dashboard.service
[Unit]
Description=Kalshi Dashboard (Daphne WebSocket Server)
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/kalshi-dashboard/backend
Environment="PATH=/opt/kalshi-dashboard/backend/venv/bin"
ExecStart=/opt/kalshi-dashboard/backend/venv/bin/daphne -b 127.0.0.1 -p 8000 config.asgi:application
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target5. Start and enable service:
sudo systemctl daemon-reload
sudo systemctl start kalshi-dashboard
sudo systemctl enable kalshi-dashboard
sudo systemctl status kalshi-dashboard6. Configure Nginx reverse proxy:
# /etc/nginx/sites-available/kalshi-dashboard
upstream dashboard_backend {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name dashboard.example.com;
# Redirect to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name dashboard.example.com;
ssl_certificate /etc/letsencrypt/live/dashboard.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dashboard.example.com/privkey.pem;
# API and WebSocket proxy
location /api/ {
proxy_pass http://dashboard_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ws/ {
proxy_pass http://dashboard_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400; # 24 hours for long-lived WS connections
}
# Frontend static files
location / {
root /opt/kalshi-dashboard/frontend/dist;
try_files $uri $uri/ /index.html;
}
}7. Enable site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/kalshi-dashboard /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx1. Build production bundle:
cd /opt/kalshi-dashboard/frontend
VITE_API_URL=https://dashboard.example.com npm run build2. Serve via Nginx (already configured above):
The dist/ directory contains:
index.html(entry point)assets/(JS, CSS, images with hashed filenames)
3. Enable gzip compression:
# Add to nginx server block
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml+rss text/javascript;docker-compose.yml:
version: '3.8'
services:
backend:
build: ./backend
volumes:
- ./backend/.env:/app/.env
- /path/to/trading/data:/data
ports:
- "8000:8000"
command: daphne -b 0.0.0.0 -p 8000 config.asgi:application
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backendbackend/Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["daphne", "-b", "0.0.0.0", "-p", "8000", "config.asgi:application"]frontend/Dockerfile:
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.confHealth Check Endpoint:
curl https://dashboard.example.com/api/v1/health/
# {"status": "ok", "timestamp": "2026-02-16T12:00:00"}Systemd Logs:
sudo journalctl -u kalshi-dashboard -fNginx Access Logs:
tail -f /var/log/nginx/access.logApplication Logs: Django logs are sent to stdout (captured by systemd). To log to file:
# backend/config/settings.py
LOGGING = {
'version': 1,
'handlers': {
'file': {
'class': 'logging.FileHandler',
'filename': '/var/log/kalshi-dashboard/app.log',
},
},
'root': {
'handlers': ['file'],
'level': 'INFO',
},
}Secret Key Rotation:
# Generate new SECRET_KEY
python manage.py generate_secret_key
# Update .env and restart
sudo systemctl restart kalshi-dashboardHTTPS Enforcement:
- Use Let's Encrypt for free SSL certificates:
sudo certbot --nginx - Set
SECURE_SSL_REDIRECT=Truein Django settings for production - Enable HSTS headers in Nginx
CORS Restrictions:
- Only whitelist your frontend domain in
CORS_ALLOWED_ORIGINS - Do NOT use
*wildcard in production
File Permissions:
- Ensure
TRADING_DIRfiles are readable bywww-datauser - Use
chmod 640for.envfiles (owner read/write, group read)
Cause: Frontend cannot reach backend API.
Solutions:
- Verify backend is running:
curl http://localhost:8000/api/v1/health/ - Check
CORS_ALLOWED_ORIGINSincludes frontend URL - Inspect browser console for CORS errors
- Verify Nginx proxy configuration if using reverse proxy
Cause: Dashboard loads but shows empty tables/charts.
Solutions:
- Check
TRADING_DIRpath inbackend/.envpoints to daemon output directory - Verify daemon files exist:
ls -la /path/to/trading/kalshi_unified_state.json - Check file permissions:
sudo -u www-data cat /path/to/trading/kalshi_unified_state.json - Inspect backend logs:
sudo journalctl -u kalshi-dashboard -n 50
Cause: kalshi_unified_log.txt mtime is >20 minutes old.
Solutions:
- Verify daemon is running:
ps aux | grep kalshi_unified - Check daemon logs for errors:
tail -f /path/to/trading/kalshi_unified_log.txt - Restart daemon if crashed
Cause: WebSocket connection to /ws/logs/ failed or dropped.
Solutions:
- Verify Daphne is running (not Django
runserver):systemctl status kalshi-dashboard - Check Nginx WebSocket proxy config (must include
UpgradeandConnectionheaders) - Inspect browser console for WebSocket errors
- Test WebSocket directly:
wscat -c ws://localhost:8000/ws/logs/
Cause: Pre-TICK-020a bug where paper trades weren't filtered out.
Solution:
This was fixed in TICK-020a. Update to latest version. If still occurring, verify paper_trade field exists in kalshi_unified_state.json position entries.
Cause: No settlement data (trades haven't closed yet).
Solution:
Analytics require settled trades logged in kalshi_settlement_log.jsonl. Wait for positions to settle (after market close on settlement date) and daemon to log settlements.
Cause: TypeScript type errors or missing dependencies.
Solutions:
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install
# Check for type errors
npm run build
# If Tailwind CSS errors, verify tailwind.config.tsCause: Large JSONL files being read into memory.
Solutions:
- JSONL readers use date filters to limit rows (e.g.,
?date=2026-02-16) - For very large logs (>100K lines), implement pagination in
read_jsonl() - Archive old JSONL entries:
split -l 10000 kalshi_backtest_log.jsonl backtest_archive_
Contributions are welcome! Whether bug fixes, new features, documentation improvements, or analytics enhancements—your help is appreciated.
1. Fork and Clone:
git clone https://github.com/YourUsername/kalshi-dashboard.git
cd kalshi-dashboard2. Create Feature Branch:
git checkout -b feature/my-new-feature3. Make Changes:
- Follow code style guidelines (Black for Python, Prettier for TypeScript)
- Add tests for new functionality
- Update documentation if adding features
4. Test Locally:
# Backend tests
cd backend
python manage.py test
# Frontend lint
cd frontend
npm run lint5. Commit with Clear Messages:
git add .
git commit -m "feat: Add city-level reliability metrics endpoint"Follow Conventional Commits:
feat:New featurefix:Bug fixdocs:Documentation changesstyle:Code style (formatting, no logic change)refactor:Code restructuring (no behavior change)test:Adding testschore:Build process, dependencies
6. Push and Open PR:
git push origin feature/my-new-featureOpen a Pull Request on GitHub with:
- Title: Brief description (e.g., "Add provider accuracy timeline chart")
- Description: What changed, why, and any breaking changes
- Screenshots: If UI changes
- Testing: How you tested it
Use the GitHub Issues page.
Good Bug Report Includes:
- Title: Brief, specific (e.g., "P&L chart shows incorrect data for Feb 15")
- Steps to Reproduce: Numbered list of actions leading to bug
- Expected Behavior: What should happen
- Actual Behavior: What actually happened
- Environment: OS, Python version, Node version, browser
- Screenshots/Logs: Error messages or console output
Open an issue with [Feature Request] prefix.
Good Feature Request Includes:
- Use Case: Why this feature is needed
- Proposed Solution: How it might work
- Alternatives Considered: Other approaches you thought about
- Priority: How important is this to you?
Python (Black + Flake8):
black backend/ --line-length 120
flake8 backend/ --max-line-length 120TypeScript (Prettier + ESLint):
cd frontend
npm run lint -- --fix
npx prettier --write "src/**/*.{ts,tsx}"Commit Hooks (Optional):
Install pre-commit to auto-format on commit:
pip install pre-commit
pre-commit install- One Feature Per PR: Easier to review and merge
- Update CHANGELOG.md: Add entry under
[Unreleased] - Add Tests: For new endpoints or components
- Update Docs: If adding features, update README.md and API.md
- Rebase on Main: Before opening PR, rebase to avoid merge conflicts
git fetch upstream
git rebase upstream/mainMIT License
Copyright (c) 2026 Tyler Irving
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- Kalshi: Prediction market platform providing API access
- Trading Daemon: This dashboard is designed to complement the Kalshi Weather Arbitrage Bot
- Open Source Libraries: React, Django, Tailwind CSS, Recharts, Zustand, and the entire open source ecosystem
Current Version: 1.0.0 (Initial Public Release)
Active Development: This project is actively maintained. Expect regular updates for:
- New analytics visualizations
- Performance optimizations
- Additional WebSocket channels (position updates, P&L streaming)
- Dark mode theme
- Mobile responsive improvements
Roadmap (planned enhancements):
- Dark mode toggle
- Advanced filtering (date range picker, multi-city selection)
- Export to CSV (positions, P&L, analytics)
- Email/SMS alerts for trade events
- Historical performance comparison (month-over-month)
- Provider forecast accuracy timeline chart
- Redis channel layer for horizontal scaling
- PostgreSQL integration for analytics caching
- Docker Compose production setup
- Kubernetes deployment manifests
- Documentation: This README and API.md
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: [your-email@example.com] (for security issues only)
Built with ❄️ by the Frost design system
Professional trading dashboard for Kalshi weather markets—real-time monitoring, analytics, and insights.