← back to Watches
tasks/prd-real-time-market-data.md
234 lines
# PRD: Real-Time Market Data Integration (Frontend MVP)
## Introduction
Integrate real-time market data from Chrono24 and WatchCharts into the Omega Watch Price History frontend. Currently, the platform displays static historical data from a JSON file. This feature adds live market prices, recent sales, and trend indicators so users see accurate, fresh pricing information without manual updates.
The MVP focuses on frontend UI components that consume market data, with cost-effective 5-minute polling (using free API tiers where available).
## Goals
- Display current market prices from Chrono24 and WatchCharts alongside historical data
- Show recent sales history with actual transaction prices
- Provide visual price trend indicators (up/down, % change)
- Indicate data freshness clearly (timestamps, live/stale badges)
- Auto-refresh data every 5 minutes without page reload
- Maintain performance with efficient polling and caching
- Use free API tiers to keep costs at zero
## User Stories
### US-001: Create Market Data Service Layer
**Description:** As a developer, I need a frontend service to fetch and cache market data so components can access live prices without redundant API calls.
**Acceptance Criteria:**
- [ ] Create `src/services/marketDataService.js` with fetch functions for Chrono24 and WatchCharts
- [ ] Implement in-memory cache with 5-minute TTL
- [ ] Handle API errors gracefully (return cached/stale data on failure)
- [ ] Export functions: `getMarketPrice(watchId)`, `getRecentSales(watchId)`, `getTrends(watchId)`
- [ ] Typecheck passes (if using TypeScript) or ESLint passes
### US-002: Add Current Market Price Display
**Description:** As a user, I want to see the current market price range for a watch so I know what it's selling for today.
**Acceptance Criteria:**
- [ ] Display "Market Price: $X,XXX - $X,XXX" on watch detail cards
- [ ] Show price source attribution (e.g., "via Chrono24")
- [ ] Handle missing data gracefully (show "Price unavailable" or fall back to historical)
- [ ] Price updates without full page refresh
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
### US-003: Add Recent Sales History Component
**Description:** As a user, I want to see recent actual sales so I can understand real transaction prices, not just listings.
**Acceptance Criteria:**
- [ ] Create `RecentSales.jsx` component showing last 5-10 transactions
- [ ] Display: date, price, condition, source for each sale
- [ ] Sort by most recent first
- [ ] Show "No recent sales data" when unavailable
- [ ] Responsive layout (stacks on mobile)
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
### US-004: Add Price Trend Indicators
**Description:** As a user, I want to see at a glance whether a watch's price is trending up or down so I can make informed decisions.
**Acceptance Criteria:**
- [ ] Display trend arrow (↑ green, ↓ red, → gray for stable)
- [ ] Show percentage change (e.g., "+5.2% (30d)")
- [ ] Include trend periods: 7-day, 30-day, 90-day
- [ ] Tooltip explains calculation method on hover
- [ ] Works on watch cards and detail views
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
### US-005: Add Data Freshness Indicators
**Description:** As a user, I want to know how fresh the market data is so I can trust the information displayed.
**Acceptance Criteria:**
- [ ] Display "Last updated X minutes ago" timestamp
- [ ] Show freshness badge: green (<5 min), yellow (5-30 min), red (>30 min/stale)
- [ ] Badge appears next to market price on all views
- [ ] Timestamp uses relative format ("2 min ago", "1 hour ago")
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
### US-006: Implement Auto-Refresh Mechanism
**Description:** As a user, I want market data to refresh automatically so I always see current prices without manually reloading.
**Acceptance Criteria:**
- [ ] Poll for new data every 5 minutes
- [ ] Show subtle loading indicator during refresh (not full-page loader)
- [ ] Update UI smoothly without scroll position reset
- [ ] Pause polling when browser tab is inactive (visibility API)
- [ ] Resume polling when tab becomes active
- [ ] Clear interval on component unmount (no memory leaks)
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
### US-007: Add Manual Refresh Button
**Description:** As a user, I want to manually refresh market data when I need the latest prices immediately.
**Acceptance Criteria:**
- [ ] Refresh button/icon near market data section
- [ ] Button shows loading spinner while fetching
- [ ] Disable button during refresh to prevent spam
- [ ] Update freshness timestamp after successful refresh
- [ ] Show toast/notification on refresh success or failure
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
### US-008: Integrate Market Data into Watch Detail View
**Description:** As a user, I want to see all market data consolidated on the watch detail page so I have a complete picture.
**Acceptance Criteria:**
- [ ] Add "Market Data" section to watch detail view
- [ ] Include: current price, recent sales, trend indicators, freshness badge
- [ ] Section is collapsible on mobile
- [ ] Falls back gracefully when APIs unavailable
- [ ] Matches existing UI design patterns (luxury theme)
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
### US-009: Add Market Data to Watch List Cards
**Description:** As a user, I want to see key market metrics on watch list cards so I can quickly scan multiple watches.
**Acceptance Criteria:**
- [ ] Show current market price on each card
- [ ] Display trend indicator (arrow + percentage)
- [ ] Show freshness dot (green/yellow/red, no text)
- [ ] Maintain card layout balance (not cluttered)
- [ ] Typecheck/ESLint passes
- [ ] Verify in browser using dev-browser skill
## Functional Requirements
- FR-1: The system must fetch market data from Chrono24 API for watch listings and prices
- FR-2: The system must fetch market data from WatchCharts API for price indices and trends
- FR-3: The system must cache API responses for 5 minutes to reduce API calls
- FR-4: The system must display current market price range (low-high) for each watch
- FR-5: The system must show the 5 most recent sales with date, price, and condition
- FR-6: The system must calculate and display 7d, 30d, and 90d price trend percentages
- FR-7: The system must show a visual freshness indicator (timestamp + colored badge)
- FR-8: The system must auto-refresh data every 5 minutes when the tab is active
- FR-9: The system must pause auto-refresh when the browser tab is inactive
- FR-10: The system must provide a manual refresh button for on-demand updates
- FR-11: The system must gracefully handle API failures (show stale data, not errors)
- FR-12: The system must attribute data sources (e.g., "Prices via Chrono24")
## Non-Goals (Out of Scope)
- Backend API proxy for market data (frontend calls APIs directly for MVP)
- User authentication for personalized data
- Price alerts or notifications
- Historical market data storage (beyond session cache)
- Paid API tier features
- WebSocket real-time streaming (polling only for MVP)
- Data aggregation across multiple sources (display separately)
- Admin dashboard for API monitoring
## Design Considerations
### UI Components to Create/Modify
- `src/components/MarketPrice.jsx` - Current price display with source
- `src/components/RecentSales.jsx` - Sales history list
- `src/components/TrendIndicator.jsx` - Arrow + percentage badge
- `src/components/FreshnessIndicator.jsx` - Timestamp + colored dot
- `src/components/RefreshButton.jsx` - Manual refresh with loading state
- Modify `LuxuryWatchCard.jsx` - Add market data to cards
- Modify watch detail view - Add market data section
### Design Patterns
- Follow existing luxury theme (dark mode compatible)
- Use Tailwind CSS classes consistent with current components
- Match existing loading states (PremiumLoader pattern)
- Use Framer Motion for smooth data transitions
- Responsive: stack on mobile, side-by-side on desktop
### Color Scheme for Indicators
- Trend Up: `text-green-500` / `bg-green-500`
- Trend Down: `text-red-500` / `bg-red-500`
- Trend Stable: `text-gray-400` / `bg-gray-400`
- Fresh Data: `bg-green-500` (dot)
- Stale Data: `bg-yellow-500` (dot)
- Very Stale: `bg-red-500` (dot)
## Technical Considerations
### API Integration
- **Chrono24:** Check for public API availability or use web scraping fallback
- **WatchCharts:** Verify free tier limits and rate limiting
- Handle CORS: May need backend proxy if APIs don't allow browser requests
- Implement exponential backoff for failed requests
### Performance
- Cache in React state or Context (not localStorage for session data)
- Use `useMemo` for computed trend calculations
- Debounce manual refresh button (prevent rapid clicks)
- Consider React Query or SWR for data fetching (optional enhancement)
### Error Handling
- Network failures: Show last cached data with "stale" indicator
- API rate limits: Back off and show user-friendly message
- Invalid data: Validate response shape, fallback to historical data
- Partial failures: If one API fails, still show data from the other
### Dependencies (Existing)
- React 19.2.0
- Tailwind CSS 4.1.17
- Framer Motion 12.23.24 (for transitions)
- Fuse.js (not needed for this feature)
### Dependencies (New - Optional)
- `date-fns` or `dayjs` for relative timestamps (or use native Intl.RelativeTimeFormat)
## Success Metrics
- Market data displays for 80%+ of watches (API coverage)
- Data refresh completes in <2 seconds
- Auto-refresh runs without memory leaks (stable heap over time)
- Zero cost (stays within free API tiers)
- Users see fresh data (<5 min old) 90%+ of the time when tab is active
- Graceful degradation: UI never shows errors, always shows some price data
## Open Questions
1. **API Access:** Do Chrono24 and WatchCharts have free public APIs, or do we need to scrape/proxy?
2. **CORS:** Will these APIs allow browser-based requests, or do we need a backend proxy?
3. **Rate Limits:** What are the free tier limits? May need to adjust polling frequency.
4. **Watch ID Mapping:** How do we map our watch IDs to Chrono24/WatchCharts identifiers?
5. **Data Normalization:** Chrono24 prices may be in different currencies - do we need conversion?
6. **Fallback Strategy:** If both APIs fail, should we show a prominent warning or silently use historical data?
---
## Checklist
- [x] Asked clarifying questions with lettered options
- [x] Incorporated user's answers
- [x] User stories are small and specific
- [x] Functional requirements are numbered and unambiguous
- [x] Non-goals section defines clear boundaries
- [x] Saved to `tasks/prd-real-time-market-data.md`