FAREPLAY: Prediction Markets for Toronto Transit Delays

Inspiration

On our team, a lot of us take the TTC, and a lot of the time it's late. So we put two and
two together and said: why don't we make prediction markets for how late your streetcar,
subway, or bus will be to your stop?

Real-time prediction accuracy varies significantly across routes and times of day. Instead
of just complaining about delays, we thought—what if we gamified it? What if users could put money on their predictions and earn rewards when they're right?

What it does

FarePlay is a real-time prediction market platform where users predict TTC vehicle arrival
times and place bets (in SOL) on their predictions.

Core Features:

  1. Interactive Transit Map - Real-time visualization of TTC route 501 (Queen Streetcar) and 503 (King Streetcar) with live vehicle positions updated every 5 seconds
  2. Smart Stop Selection - Users browse routes, select stops, and see arriving vehicles in
    real-time
  3. Prediction Betting - Choose a vehicle, predict its arrival time in seconds, and place a
    bet in SOL
  4. Community Analytics - See what other players are betting on the same stop, average
    prediction accuracy, and vehicle-specific statistics
  5. Frozen Predictions - Historical comparison showing what was predicted 5 minutes ago vs.
    actual arrival times
  6. Solana Wallet Integration - Connect your wallet to track balance and bet winnings

User Flow:

  • Route 501/503 → Select stop → Choose vehicle → Enter prediction (seconds) → Enter bet
    amount → Place bet
  • Backend verifies predictions against actual TTC data
  • Winners earn SOL; losers lose their bet amount

How we built it

Frontend (/frontend)

  • Tech Stack: React, Next.js, TypeScript, Tailwind CSS
  • Map Visualization: Mapbox GL with real-time vehicle markers and route geometry
  • State Management: Centralized React state for route/stop/vehicle selection with two-way
    synchronization between map and sidebar
  • Real-time Updates: Vehicle positions fetched every 5 seconds from backend API

Backend (/backend/ttc-predictor)

  • Core Components:
    • Collector Daemon (collector_daemon.py) - Continuously fetches TTC predictions every 10
      seconds, records "frozen" snapshots at different time windows (0-5min, 5-10min, etc.)
    • Database Layer (database.py) - SQLite database tracking frozen predictions, verified
      arrivals, and pre-calculated statistics
    • REST API (api_v2.py) - Flask server exposing endpoints for current predictions,
      historical accuracy data, and route information

Data Pipeline:

  1. TTC NextBus API → Collector (every 10s) → Database
  2. Predictions "frozen" at capture time for later comparison
  3. Vehicles tracked between stops to verify actual arrivals
  4. Accuracy metrics calculated: early/late by how many seconds

Solana Integration:

  • Wallet connection for SOL deposits/withdrawals
  • Frontend placeholder for transaction signing (backend integration in progress)

APIs Used:

  • TTC NextBus XML API for real-time predictions
  • Custom Flask API for historical prediction accuracy

Challenges we ran into

  1. Real-time Data Consistency - TTC API predictions change constantly. We had to implement
    "frozen predictions" to capture a moment in time for later comparison against actual
    arrivals.
  2. Arrival Verification - The TTC API only gives predictions, not confirmations when
    vehicles actually pass. We solved this by tracking vehicle IDs between consecutive stops and calculating elapsed time.
  3. Database Schema Complexity - Modeling the relationship between current predictions,
    frozen snapshots, actual arrivals, and accuracy metrics required careful planning to avoid
    data redundancy.
  4. CORS & API Integration - Connecting frontend to backend with real-time data required
    proper CORS configuration and handling race conditions in prediction updates.
  5. Scaling Database - Storing predictions for every vehicle at every stop every 10 seconds
    generates 1000s of records/day. We implemented statistics caching to keep queries fast.
  6. Mapbox Route Rendering - Getting GeoJSON route geometry to render correctly with proper
    filtering between route lines and stop points required debugging Mapbox GL layer
    configuration.

Accomplishments that we're proud of

  • Full-stack working prototype - Frontend displays real-time map with live vehicle updates; backend has been collecting actual TTC data for weeks

  • Intelligent prediction system - "Frozen predictions" at multiple time windows (0-5min,
    5-10min, 10-20min, 20-30min) allow sophisticated accuracy analysis

  • Beautiful UI/UX - Interactive Mapbox visualization with color-coded routes, smooth
    sidebar interactions, and intuitive betting flow

  • Real data pipeline - Not mocked—we're actually querying TTC's real API and storing
    verified accuracy data

  • Verified transit routes - Confirmed all configured stops exist on their routes with
    proper spacing (5-15 min between stops for Route 501, 3-10 min for Route 503)

  • Two-way component synchronization - Clicking vehicles on the map updates the sidebar;
    selecting in the sidebar highlights on the map

What we learned

  1. Transit data is complex - Vehicle IDs change, routes have multiple directions,
    predictions are probabilistic not deterministic. Real systems require careful data
    validation.
  2. Real-time systems are hard - Managing race conditions between API calls, database
    updates, and UI state is non-trivial. A 1-second delay can make your data stale.
  3. Prediction accuracy varies wildly - Some vehicles consistently arrive early; others
    consistently late. Time of day, weather, and route geometry all matter. This is what makes
    the prediction market interesting!
  4. SQLite can handle moderate load - For a hackathon project, SQLite with proper indexing
    and statistics caching scales surprisingly well.
  5. Mapbox GL is powerful but requires finesse - Layer ordering, feature filtering, and click handlers all need careful coordination.

What's next for fareplay? More transit lines!

Built With

Share this project:

Updates