# SimTrade Per-Ticker Fun Fact Endpoint — Investigation

**Author:** backend-integration agent
**Date:** 2026-04-07
**Status:** Endpoint does NOT exist. Proposal below.

## Findings

### Existing state
- **No fun-fact, did-you-know, or trivia endpoint exists in the backend.** Searched `services/main/app` for `fun.?fact|did.?you.?know|fun_fact|trivia` — zero matches.
- Mobile's `OrderConfirmedModal` hardcoded "Did you know?" Apple text is the only source of this content.
- The closest existing AI-per-ticker pattern is `openai_service.get_stock_overview(ticker)` which returns a structured company overview dict (already cached). It's used by other endpoints for descriptive content.

### Related infrastructure already available
- `app/services/openai_service.py` — has helpers like `get_analyst_ai_summary`, `get_stock_overview` that wrap OpenAI calls with caching.
- `app/infrastructure/cache.py` — in-memory cache with TTL helpers.
- All SimTrade routes are under `app/routers/simtrade.py` with `Depends(get_current_user)` auth.

## Proposed Endpoint

### Contract

```
GET /api/simtrade/stocks/{symbol}/fun-fact
Auth: required (Bearer JWT)
```

**Path params:**
- `symbol` — ticker symbol, uppercased server-side, e.g. `AAPL`, `TSLA`.

**Response (200):**
```json
{
  "symbol": "AAPL",
  "fact": "Apple's first product, the Apple I, was sold in 1976 for $666.66 — a price Steve Wozniak picked because he liked repeating digits.",
  "cached": true
}
```

**Errors:**
- `404` — unknown ticker (validated against existing ticker store)
- `500` — OpenAI failure (after retries)

### Behavior
- AI-generated via `openai_service` using a single short prompt: "Give me ONE surprising, kid-friendly fun fact about {company_name} ({symbol}). One sentence, max 140 characters. No fluff."
- Cached **per symbol** for **24 hours** in the in-memory cache (`cache_key=f"simtrade:funfact:{symbol}"`).
- `cached: true` when served from cache, `cached: false` on first generation.
- Idempotent: same symbol always returns the same fact within the cache TTL.
- The endpoint resolves company name from the existing FMP/Massive ticker details before sending to OpenAI, so the fact is grounded in the right company.

### Pydantic model
```python
class SimTradeFunFactResponse(BaseModel):
    symbol: str = Field(..., description="Ticker symbol (uppercased)")
    fact: str = Field(..., description="One-sentence fun fact about the company")
    cached: bool = Field(..., description="True if served from cache")
```

### Implementation sketch
```python
@router.get(
    "/stocks/{symbol}/fun-fact",
    response_model=SimTradeFunFactResponse,
    summary="Per-ticker fun fact",
)
async def get_stock_fun_fact(
    symbol: str = Path(..., min_length=1, max_length=10),
    current_user: dict[str, Any] = Depends(get_current_user),
) -> SimTradeFunFactResponse:
    """Return a single short fun fact about the company behind the ticker.
    Used by the SimTrade order-confirmed celebration sheet. AI-generated,
    cached per symbol for 24h.
    """
    sym = symbol.upper()
    fact, cached = await simtrade_funfact_service.get_fun_fact(sym)
    return SimTradeFunFactResponse(symbol=sym, fact=fact, cached=cached)
```

A small new service `app/services/simtrade_funfact_service.py` wraps the
cache lookup + OpenAI call + grounding (company name fetch).

### Cost / scale notes
- 24h cache means worst case ~10 OpenAI calls per ticker per year.
- Top 100 tickers = ~1000 OpenAI calls/year — negligible cost.
- Cache is per-instance; with multiple instances we'd see slightly more calls but still trivial.
- Could be promoted to a Supabase-backed cache later if we want cross-instance sharing.

## Recommendation

**Land this endpoint in a separate PR after PR #274 merges.** PR #274 is already bundled with three SimTrade fixes — adding a new endpoint would muddy the diff. The mobile change (read endpoint instead of hardcoded text) can ship in parallel.

## Next steps
1. Wait for human approval
2. Implement as new branch `feat/simtrade-fun-fact-endpoint`
3. Coordinate response shape with 135-provision before merging
