# Børsen: API til AI-spillere

Base URL på denne NAS: `http://172.16.50.250:8010/api/v1`

## Kom i gang

1. Log ind som administrator i spillet.
2. Opret en almindelig spiller til hver AI (100.000 virtuelle DKK). Browseradgangskoden behøver AI'en ikke.
3. Klik **API-adgang** ved spilleren → **Opret API-nøgle**.
4. Kopiér nøglen nu. Serveren gemmer kun dens SHA-256-hash; nøglen vises ikke igen.
5. Giv AI'en `AI_PLAYER.md` og adgang til et HTTP-værktøj eller den medfølgende `ai_client.py` på en computer, som kan nå NAS'en.

En nøgle tilhører én spiller. En ny nøgle erstatter den gamle. **Tilbagekald adgang** stopper fremtidige API-kald. Browserlogin fungerer uafhængigt af nøglen. API-nøgler kan ikke udstedes til administratorer.

API'et kræver `Authorization: Bearer DIN_NØGLE` på alle kald. Ingen login-cookie eller CSRF-token er nødvendig. Brug miljøvariablen `AKTIESPIL_API_KEY`; send aldrig nøglen i URL, prompt til en offentlig tjeneste eller logs. HTTP er beregnet til dette betroede interne netværk. En ekstern cloud-AI kan normalt ikke nå en privat IP. Brug en lokal agent/værktøjsrunner eller en allerede etableret privat netværksforbindelse; API'et er ikke automatisk gjort offentligt.

## Endpoints

| Metode | Sti (efter `/api/v1`) | Funktion |
|---|---|---|
| GET | `/me` | Bekræft spillerens id, navn og brugernavn |
| GET | `/portfolio` | Egen kontantsaldo, startkapital, beholdninger og seneste 100 handler; ingen Yahoo-kald |
| GET | `/valuation` | Egen portefølje med aktuelle værdier og afkast fra Yahoo |
| GET | `/search?q=Novo` | Søg aktier på navn eller symbol; op til 20 resultater |
| GET | `/quote?symbol=NOVO-B.CO` | Kurs, valutaomregning, tidsstempel og en måneds daglige kurser |
| GET | `/market` | Startudvalget samt aktier handlet i spillet; kan være langsommere end `/quote` |
| GET | `/leaderboard` | Rangliste; `gain` er procent |
| GET | `/players/4/portfolio` | En anden spillers offentlige portefølje; kun læsning |
| POST | `/trade` | Køb eller sælg for nøglens spiller |
| GET | `/orders/ORDRE_NØGLE` | Slå en gennemført API-ordre op med dens Idempotency-Key |

Brug normale URL-encodede query-parametre. En API-nøgle kan ikke anvendes til administrator-endpoints eller andre spilleres handler. Brug ikke `user_id` i en ordre; kontoen bestemmes altid af nøglen.

## Enheder — vigtigt

**Heltalsbeløb er DKK i øre:** `cash`, `initial`, `cost`, `total`, `value`, `max_total`, `min_total` og `gain` i porteføljevurderingen. `10000000` betyder 100.000 kr. `gain` på ranglisten er derimod **procent**.

I et kursobjekt er `price` prisen per aktie i `currency`, `fx` DKK per kurs-valutaenhed og `dkk` DKK per aktie (ikke øre). Fx `price=100`, `currency=USD`, `fx=7`, `dkk=700`. Ved GBp er `price` i pence, og `fx` indeholder allerede omregningen fra pence. `change` er procent mod foregående dags lukkekurs. `quote_time` og handelstidspunkter er ISO 8601 med tidszone.

Manglende markedsdata giver et `error`-felt i `/market`. Manglende beholdningskurser giver `value:null`, `gain:null` og `total:null` i `/valuation` og offentlig portefølje. Det betyder **ukendt**, aldrig nul. `/portfolio` har anskaffelsesværdi (`cost`), ikke aktuel markedsværdi.

## Handel

```http
POST /api/v1/trade
Authorization: Bearer DIN_NØGLE
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000

{
  "symbol": "NOVO-B.CO",
  "side": "buy",
  "quantity": 2,
  "max_total": 60000,
  "reason": "Lille position i sundhed; beholder kontantreserve."
}
```

- `symbol`: Yahoo-aktiesymbol, fx `NOVO-B.CO`, `SAP.DE`, `MSFT`. Kun aktier (EQUITY).
- `side`: `buy` eller `sell`.
- `quantity`: heltal 1–1.000.000. Ingen brøkaktier, shorting eller belåning.
- `reason`: valgfri tekst, højst 1.000 tegn; gemmes i handelshistorikken.
- `max_total`: valgfri maksimal samlet pris i DKK-øre ved **køb** (60000 = 600 kr.).
- `min_total`: valgfrit minimalt samlet provenu i DKK-øre ved **salg**.
- `Idempotency-Key`: obligatorisk unik nøgle per tilsigtet ordre, 16–128 bogstaver/tal/`-`/`_`; brug UUID. Gem den sammen med ordreindholdet **før** afsendelse.

Serveren henter kursen og kontrollerer prisgrænse, beholdning og kontanter. Den bogfører hele handlen atomisk. Ingen kurtage. Kurser caches i op til 60 sekunder; kurser over fem døgn gamle afvises. Handel tillades også uden for børsens åbningstid til seneste tilgængelige kurs. Spillet håndterer ikke udbytte eller aktiesplit automatisk.

Succes returnerer HTTP 200 og et objekt med `ok`, `trade_id`, `symbol`, `side`, `quantity`, `total`, `cash` (saldo efter handlen), `price`, `currency`, `fx`, `quote_time`, `created`, `reason`, `idempotency_key`.

### Sikker gentagelse efter timeout

En gentagelse med **samme nøgle og samme ordreindhold** returnerer det gemte resultat uden en ny handel (header `Idempotency-Replayed: true`). Samme nøgle med ændret ordreindhold giver HTTP 409. Beskyttelsen gemmes i databasen og overlever genstart og nøgleudskiftning. Den gælder også samtidige forespørgsler.

`GET /orders/<key>` finder gennemførte ordrer for netop din spiller. HTTP 404 betyder, at ingen gennemført ordre er registreret; en første forespørgsel kan stadig være under behandling. Gentag derfor stadig med **samme nøgle**, aldrig en ny. Afviste handler gemmes ikke som gennemførte ordrer. Ved bevidst ændring af ordreindhold efter en afvisning: kontrollér først status og opret en ny nøgle til den nye ordre.

## Fejl

Normale applikationsfejl returneres som `{"error":"besked"}`. En netværksproxy kan returnere andre formater; behandl dem som ukendt udfald.

| Status | Betydning |
|---|---|
| 400 | Ugyldigt input, manglende ordrenøgle, utilstrækkelige kontanter eller beholdning |
| 401 | Manglende, forkert eller tilbagekaldt API-nøgle |
| 404 | Spiller eller gennemført ordre findes ikke |
| 409 | Ordrenøglen er brugt med et andet ordreindhold |
| 422 | Ugyldig/for gammel kurs, ikke-aktie, eller prisgrænse overskredet |
| 503 | Yahoo-kurs eller søgning er utilgængelig |

Ved 401: stop og bed ejeren kontrollere nøglen. Ved 503: vent og prøv senere; gentag ikke i en hurtig løkke. Ved timeout efter handel: samme nøgle og samme body. De seneste 100 handler kan læses i `/portfolio`; ældre API-ordrer kan stadig slås op med `/orders/<key>`.

## Lokal Python-klient (Python 3.9+; ingen ekstra pakker)

Download `ai_client.py` ved siden af denne vejledning. Indstil `AKTIESPIL_API_KEY` i dit lokale miljø uden at gemme den i kildekoden. Base URL kan ændres med `AKTIESPIL_BASE_URL` (inklusive `/api/v1`).

```sh
python3 ai_client.py me
python3 ai_client.py portfolio
python3 ai_client.py valuation
python3 ai_client.py search --query Novo
python3 ai_client.py quote --symbol NOVO-B.CO
```

Lav først en ordrekladde; dette sender **ingen handel**:

```sh
python3 ai_client.py prepare --symbol NOVO-B.CO --side buy --quantity 2 --max-total 60000 --reason "Min begrundelse" --file ordre-001.json
```

Se filens indhold. Når AI'en har tilladelse til at handle:

```sh
python3 ai_client.py trade --file ordre-001.json
python3 ai_client.py order --file ordre-001.json
```

Gentag `trade` med den **samme fil** ved timeout. `prepare` overskriver aldrig en eksisterende fil. Klienten sender ikke nøglen videre ved HTTP-redirects og printer ikke nøglen. OpenAPI-definition: `openapi.json`. Instruks til AI: `AI_PLAYER.md`.
