# ADR 0011 — Providers de prix : exchanges directs (crypto) + Yahoo Finance best-effort (stocks)

- Status: Accepted
- Date: 2026-04-26
- Successor of: ADR 0009 (architecture proxy) — précise les providers concrets
- Milestone: `spec-price-fetching` + `prices-proxy` (maximus-api)

## Context

ADR 0009 a établi qu'un proxy `maximus-api` mutualisé sert le price-fetching premium pour préserver la privacy (IP cachée, headers strippés). La revue spec du contrat `/v1/prices` (2026-04-26) a soulevé deux risques critiques :

1. **Yahoo Finance n'a pas d'API publique officielle.** Les endpoints `query1/query2.finance.yahoo.com` sont non documentés, leur ToS interdit l'usage commercial et la redistribution. Un IP-ban du VPS coupe le feature pour 100% des premium en même temps.
2. **CoinGecko free tier interdit le proxy commercial.** Seul le plan Demo/Pro payant (~129 $/mo Analyst) le permet contractuellement.

Quatre options ont été considérées (cf. revue inline `docs/api-contract-prices.md` §0) :

| Option | Coût/mois | Légalité commercial | Stabilité | Couverture |
|--------|-----------|---------------------|-----------|------------|
| Polygon.io Starter | 29 $ | ✅ contractuelle | ✅ haute | Stocks NYSE/NASDAQ + crypto |
| Tiingo Power + exchanges directs (crypto) | 10 $ | ✅ Tiingo, ✅ exchanges (public market data OSS-légal) | ✅ haute | Stocks + crypto |
| **Exchanges directs (crypto) + Yahoo best-effort (stocks)** | **0 $** | ⚠️ Yahoo ToS risqué (data publique mais redistribution interdite) ; ✅ exchanges | ⚠️ Yahoo fragile, exchanges stables | Stocks + crypto |
| Polygon Stocks + CoinGecko Pro | 158 $ | ✅ | ✅ | Best-of-both |

## Decision

**Adopter l'option « tout-OSS / best-effort »** pour le MVP :

- **Crypto** : interrogation directe des exchanges majeurs via la lib `ccxt` (MIT). Les données de marché publiques (ticker, OHLC) sont gratuites et explicitement autorisées en commercial par les ToS de Kraken, Coinbase, Binance, etc. Implémentation initiale : Kraken d'abord, Coinbase en fallback si Kraken 404.
- **Stocks** : interrogation de Yahoo Finance via `query1.finance.yahoo.com/v7/finance/quote` (et v8 chart pour historique) avec un User-Agent navigateur. **Best-effort assumé** : peut échouer ou changer sans préavis.

Le client paie pour **l'infrastructure d'anonymisation**, pas pour la donnée. Cette distinction est centrale au modèle économique : la valeur premium = privacy (proxy mutualisé) + commodité (auto-fill), pas la donnée elle-même.

### Garde-fous obligatoires

1. **Label UX explicite** : sur les catégories de bilan en stocks, le bouton fetch affiche un badge « best-effort » + warning au premier usage. Sur crypto : pas de warning.
2. **Circuit breaker côté maximus-api** : seuil `5 erreurs Yahoo / 60 sec → breaker ouvert pour 15 min`. Notification automatique Telegram/email à `maxime2tremblay@protonmail.com`.
3. **Quota baissé** : 200 req/jour/licence (vs 2000 initialement). Suffit pour ~50 actifs × snapshot mensuel. Réduit l'incitation à abuser.
4. **Saisie manuelle toujours active** : aucun chemin d'erreur ne bloque la saisie d'un snapshot.
5. **Headers stripping rigoureux** : tous les headers entrants supprimés avant call sortant. Vers Yahoo : UA browser-like (`Mozilla/5.0 ...`). Vers exchanges : UA `maximus-api/<version>`.
6. **Logs séparés** : pas de log conjoint `(license_id, symbol)`. Implémentation via wrapper logger injectable (`src/logger.ts` pino).

### Plan de migration si Yahoo devient inutilisable

Triggers de migration vers un provider payant :
- Plus de 1 incident IP-ban / mois pendant 2 mois consécutifs, OU
- Plus de 30% des requêtes stocks tombent en circuit-breaker `service_degraded` sur 7 jours, OU
- Plainte légale formelle de Yahoo / Verizon Media.

Provider de bascule prioritaire : **Tiingo plan Power** (~10 $/mo, 1000 req/jour, ToS-clean).
- Implémentation : ajouter un module `providers/tiingo.ts` parallèle à `providers/yahoo.ts`. Switch via env var `STOCKS_PROVIDER=yahoo|tiingo`.
- Délai de bascule : ≤ 30 jours après déclenchement d'un trigger.
- Communication : entrée CHANGELOG explicite + email aux licences premium actives.

Si l'audience grandit (>500 licences premium actives), bascule vers Polygon Starter (~29 $/mo) considérée.

## Consequences

### Positives

- **0 $ de coût récurrent au MVP** — pas de cash burn avant que le produit ait validé son marché.
- **Crypto 100% OSS-légal** — voie pérenne, ne nécessitera jamais de migration.
- **Justification premium cohérente** — privacy comme valeur, pas la donnée. Aligne avec les principes du projet.
- **Plan de bascule pré-engagé** — pas pris au dépourvu si Yahoo devient hostile.

### Négatives / Risques actés

- **ToS Yahoo en zone grise** — le proxy commercial de leur data publique n'est pas formellement autorisé. Yahoo a déjà émis des cease-and-desist contre yfinance (lib Python). Risque légal théorique mais peu probable à petite échelle.
- **IP-ban probable à un moment ou l'autre** — Yahoo bloque les UA non browser et les patterns de requête trop réguliers. Le circuit breaker absorbe l'événement, mais le feature devient temporairement HS pour tous les premium.
- **Pas de garantie de stabilité de schéma** — Yahoo peut renommer un champ JSON sans préavis. Tests d'intégration `nock` ne capturent pas ça (mock = donnée figée).
- **Charge ops accrue** — il faudra surveiller le taux d'erreur Yahoo et réagir vite si dégradation.

### Neutre

- Première implémentation un peu plus complexe côté serveur (deux providers + circuit breaker), mais le code reste contained dans `src/providers/` et est testable.

## Suivi

- ADR à reviewer dans 6 mois (2026-10-26) ou plus tôt si trigger de migration déclenché.
- Métriques à tracker dans le log applicatif maximus-api : `yahoo_success_rate_7d`, `yahoo_breaker_open_count_30d`, `crypto_provider_distribution`.
- Issue de suivi : créer une issue `ops` dans `maximus-api` pour le monitoring continu une fois deployé.

## References

- [Yahoo Finance ToS](https://legal.yahoo.com/us/en/yahoo/terms/index.html) — sec. 7-8 sur l'usage commercial
- [CoinGecko API ToS](https://www.coingecko.com/en/api/terms) — restrictions free tier
- [Kraken API public market data](https://docs.kraken.com/rest/) — explicite : free public tier, commercial OK pour data publique
- [CCXT (MIT)](https://github.com/ccxt/ccxt) — abstraction multi-exchange, lib OSS
- ADR 0009 — Architecture du proxy
- `docs/api-contract-prices.md` — Contrat figé `/v1/prices`
