WAGERBABE DOCS
All Stories
7-5-2-futures-odds-displayDoneEpic 7.5

Story 7.5.2: Futures Odds Display

Status: done

Tasks

  • **Task 1: Add get_futures_odds() to API Client** (AC: #1)
    • 1.1: Locate existing `OpticOddsAPIClient` in `/server/app/services/odds/optic_odds_api_client.py`
    • 1.2: Add `get_futures_odds()` async method with parameters: market_id, sportsbook_ids
    • 1.3: Call GET `/futures/odds` endpoint with proper params
    • 1.4: Add logging for request and response
    • 1.5: Return odds data from response
    • 1.6: Handle API errors with proper exception handling
  • **Task 2: Create Pydantic Models** (AC: #1)
    • 2.1: Locate existing models in `/server/app/models/optic_odds.py`
    • 2.2: Create `FuturesOddsOutcome` model with fields: team/player, odds, sportsbook, implied_probability
    • 2.3: Create `FuturesOddsResponse` model with market_id, market_name, outcomes array
    • 2.4: Add Field() descriptions for API documentation
    • 2.5: Add model_config with JSON schema examples
    • 2.6: Test model validation with sample futures odds data
  • **Task 3: Implement Best Odds Calculation** (AC: #2)
    • 3.1: Create `calculate_best_futures_odds()` utility function
    • 3.2: Group odds by outcome (team/player)
    • 3.3: Find highest odds value for each outcome across sportsbooks
    • 3.4: Add `is_best_odds` boolean flag to each outcome
    • 3.5: Handle edge cases (single sportsbook, tied odds)
    • 3.6: Add unit tests for best odds calculation
  • **Task 4: Create FastAPI Endpoint** (AC: #1, #5)
    • 4.1: Create new file `/server/app/api/v1/endpoints/odds/futures.py`
    • 4.2: Add GET `/futures/markets/{market_id}/odds` route
    • 4.3: Add path parameter: market_id (required)
    • 4.4: Add query parameters: sportsbook_ids (optional), force_refresh (optional)
    • 4.5: Add JWT authentication with get_current_user dependency
    • 4.6: Check Redis cache first (key: `futures:odds:{market_id}:{hash}`)
    • 4.7: If cache miss, call client.get_futures_odds()
    • 4.8: Calculate best odds using utility function
    • 4.9: Cache response for 30 minutes (WARM tier)
    • 4.10: Return response with cache metadata
    • 4.11: Add error handling (400 for invalid market_id, 500 for API errors)
    • 4.12: Test endpoint via /docs interface
  • **Task 5: Implement Implied Probability Calculation** (AC: #4)
    • 5.1: Create `calculate_implied_probability()` utility function
    • 5.2: Handle positive American odds: probability = 100 / (odds + 100)
    • 5.3: Handle negative American odds: probability = |odds| / (|odds| + 100)
    • 5.4: Convert decimal to percentage (multiply by 100)
    • 5.5: Format to 1 decimal place (e.g., "18.2%")
    • 5.6: Add unit tests for probability calculation
    • 5.7: Integrate into FuturesOddsOutcome model
  • **Task 6: Create FuturesMarketDisplay Component** (AC: #2, #3, #4)
    • 6.1: Create `/client/src/components/composite/betting/futures-market-display.tsx`
    • 6.2: Add props: marketId, marketName
    • 6.3: Implement useFuturesOdds hook for data fetching
    • 6.4: Create sortable table with columns: Team/Player, Odds, Implied Probability
    • 6.5: Implement sort functionality (by odds, by name)
    • 6.6: Highlight best odds with visual indicator (badge, background color)
    • 6.7: Display sportsbook names/logos for each odds value
    • 6.8: Format American odds with + prefix for positive odds
    • 6.9: Add loading state with skeleton
    • 6.10: Add error state with retry option
    • 6.11: Make responsive for mobile (card layout on small screens)
  • **Task 7: Create useFuturesOdds Hook** (AC: #1)
    • 7.1: Create new file `/client/src/hooks/use-futures-odds.ts`
    • 7.2: Define FuturesOdds TypeScript interfaces
    • 7.3: Implement `useFuturesOdds(marketId, sportsbookIds?)` with TanStack Query
    • 7.4: Build query params and fetch from `/api/v1/optic-odds/futures/markets/{marketId}/odds`
    • 7.5: Add JWT token from localStorage to Authorization header
    • 7.6: Set staleTime to 30 minutes (matches server cache)
    • 7.7: Add enabled condition: only run if marketId provided
    • 7.8: Add error handling for failed requests
    • 7.9: Export helper hooks: `useFuturesOddsByMarket()`
  • **Task 8: Create OddsBadge Component** (AC: #2, #4)
    • 8.1: Create `/client/src/components/composite/betting/odds-badge.tsx`
    • 8.2: Add props: odds (number), isBest (boolean), showProbability (boolean)
    • 8.3: Format American odds with + prefix for positive
    • 8.4: Calculate and display implied probability
    • 8.5: Apply visual highlighting for best odds (green background, star icon)
    • 8.6: Add size variants (sm, md, lg)
    • 8.7: Make clickable for bet slip integration
    • 8.8: Add accessibility labels
  • **Task 9: Implement Sort Functionality** (AC: #3)
    • 9.1: Create useSortableTable hook for generic table sorting
    • 9.2: Support sort by odds (numeric comparison)
    • 9.3: Support sort by name (alphabetical comparison)
    • 9.4: Toggle ascending/descending on column header click
    • 9.5: Show sort indicator (up/down arrow) on active column
    • 9.6: Persist sort preference in sessionStorage
    • 9.7: Set default sort to odds ascending (favorites first)
  • **Task 10: Redis Caching Strategy** (AC: #5)
    • 10.1: Implement cache key hashing for sportsbook_ids combinations
    • 10.2: Set TTL to 30 minutes (1800 seconds) via CacheStrategy.WARM
    • 10.3: Store odds data as JSON via set_cached/get_cached helpers
    • 10.4: Include cache metadata in response (cached, timestamp)
    • 10.5: Implement force_refresh parameter to bypass cache
    • 10.6: Add cache key invalidation on market closure
    • 10.7: Log cache hit/miss rates for monitoring
  • **Task 11: Testing & Validation** (AC: All)
    • 11.1: Test endpoint with valid market_id (e.g., "nfl_super_bowl_winner_2025")
    • 11.2: Test with sportsbook_ids filter (specific sportsbooks only)
    • 11.3: Test without sportsbook_ids (all sportsbooks)
    • 11.4: Test 400 error for invalid market_id
    • 11.5: Verify cache hit on second request (cached: true)
    • 11.6: Test force_refresh bypasses cache
    • 11.7: Verify best odds calculation is correct
    • 11.8: Verify implied probability calculations match expectations
    • 11.9: Test sort functionality (by odds, by name, ascending/descending)
    • 11.10: Test component rendering on desktop and mobile
    • 11.11: Test loading and error states
    • 11.12: Verify 30-minute cache TTL in Redis

Progress

Tasks11/11
Acceptance Criteria0
Total Tasks11