{"components":{"schemas":{"AppSetting":{"properties":{"key":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"value":{"type":"string"}},"type":"object"},"ArbitrageDryRun":{"properties":{"close":{"$ref":"#/components/schemas/DryRunResult"},"mode":{"example":"dry_run","type":"string"},"notes":{"items":{"type":"string"},"type":"array"},"open":{"$ref":"#/components/schemas/DryRunResult"},"valid":{"type":"boolean"}},"type":"object"},"AuthPrincipal":{"properties":{"auth_type":{"enum":["jwt","api_key"],"type":"string"},"email":{"format":"email","type":"string"},"role":{"description":"Role for the \"kimchi\" service.","enum":["user","admin","superuser"],"type":"string"},"services":{"additionalProperties":{"type":"string"},"example":{"kimchi":"admin","warehouse":"user"},"type":"object"},"user_id":{"format":"uuid","type":"string"},"username":{"type":"string"}},"type":"object"},"Credentials":{"properties":{"api_key":{"type":"string"},"api_secret":{"type":"string"},"passphrase":{"type":"string"}},"type":"object"},"DryRunResult":{"properties":{"book_age_ms":{"type":"integer"},"est_fee_pct":{"type":"number"},"est_fee_quote":{"type":"number"},"exchange":{"type":"string"},"exchange_symbol":{"description":"Venue-native symbol the real order would use (e.g. BTCUSDT, KRW-BTC).","type":"string"},"marketable":{"description":"For limit orders — whether it would fill immediately.","type":"boolean"},"mode":{"example":"dry_run","type":"string"},"net_cost":{"description":"Cost incl. fee (buy) or proceeds net of fee (sell).","type":"number"},"projected_cost":{"description":"Gross quote spent (buy) or received (sell).","type":"number"},"projected_price":{"type":"number"},"projected_qty":{"type":"number"},"quote_asset":{"type":"string"},"reference_ask":{"type":"number"},"reference_bid":{"type":"number"},"side":{"type":"string"},"symbol":{"type":"string"},"type":{"type":"string"},"valid":{"type":"boolean"},"warnings":{"items":{"type":"string"},"type":"array"}},"type":"object"},"ExchangeInfo":{"properties":{"live_trading":{"description":"true when live placement is enabled for this venue.","type":"boolean"},"min_notional":{"additionalProperties":{"type":"number"},"description":"Minimum order value keyed by quote asset.","type":"object"},"name":{"example":"binance","type":"string"},"order_types":{"items":{"enum":["market","limit"],"type":"string"},"type":"array"},"quote_assets":{"items":{"type":"string"},"type":"array"},"taker_fee_pct":{"example":0.1,"type":"number"}},"type":"object"},"FeedConnection":{"properties":{"connected_at":{"format":"date-time","type":"string"},"detail":{"type":"string"},"endpoint":{"type":"string"},"exchange":{"type":"string"},"id":{"type":"string"},"last_activity":{"format":"date-time","type":"string"},"messages_approx":{"type":"integer"},"reconnects":{"type":"integer"},"status":{"type":"string"},"transport":{"enum":["websocket","http_poll","internal_mock"],"type":"string"}},"type":"object"},"LatencyStats":{"properties":{"count":{"type":"integer"},"max":{"type":"integer"},"mean":{"type":"integer"},"min":{"type":"integer"},"p50":{"type":"integer"},"p75":{"type":"integer"},"p90":{"type":"integer"},"p95":{"type":"integer"},"p99":{"type":"integer"}},"type":"object"},"OrderResult":{"properties":{"est_fee_pct":{"type":"number"},"est_fee_quote":{"type":"number"},"exchange":{"type":"string"},"exchange_symbol":{"type":"string"},"mode":{"example":"live","type":"string"},"net_cost":{"type":"number"},"order_id":{"type":"string"},"projected_cost":{"type":"number"},"projected_price":{"type":"number"},"projected_qty":{"type":"number"},"raw_response":{"description":"Raw JSON response from the exchange.","type":"string"},"side":{"type":"string"},"status":{"type":"string"},"symbol":{"type":"string"},"type":{"type":"string"},"valid":{"type":"boolean"},"warnings":{"items":{"type":"string"},"type":"array"}},"type":"object"},"PremiumRow":{"properties":{"anomaly":{"type":"boolean"},"asset":{"type":"string"},"asset_vol_pct":{"description":"Worst-case volatility across every venue for this base coin — high values flag a thin / shitcoin market.","type":"number"},"close_exchange":{"type":"string"},"close_price_usdt":{"type":"number"},"close_symbol":{"type":"string"},"close_vol_pct":{"description":"Windowed high-low range (%) of the close leg's market.","type":"number"},"fx_rate":{"type":"number"},"latency_ms":{"type":"integer"},"open_exchange":{"type":"string"},"open_price_usdt":{"type":"number"},"open_symbol":{"type":"string"},"open_vol_pct":{"description":"Windowed high-low range (%) of the open leg's market.","type":"number"},"simulation_at":{"format":"date-time","type":"string"},"simulation_notional_usdt":{"type":"number"},"simulation_pnl_usdt":{"type":"number"},"spread_pct":{"type":"number"},"spread_usdt":{"type":"number"},"symbol":{"type":"string"},"updated_at":{"format":"date-time","type":"string"},"volume_median_usd":{"type":"number"}},"type":"object"},"SimulateRequest":{"properties":{"close_exchange":{"type":"string"},"close_symbol":{"type":"string"},"notional_usdt":{"type":"number"},"open_exchange":{"type":"string"},"open_symbol":{"type":"string"}},"required":["open_exchange","close_exchange","open_symbol","close_symbol"],"type":"object"},"SimulationResult":{"properties":{"close_exchange":{"type":"string"},"close_price_usdt":{"type":"number"},"close_symbol":{"type":"string"},"computed_at":{"format":"date-time","type":"string"},"notional_usdt":{"type":"number"},"open_exchange":{"type":"string"},"open_price_usdt":{"type":"number"},"open_symbol":{"type":"string"},"pnl_usdt":{"type":"number"}},"type":"object"},"TradeOrder":{"properties":{"exchange":{"enum":["binance","upbit"],"type":"string"},"notional":{"description":"Quote amount — alternative sizing for market orders.","type":"number"},"price":{"description":"Limit price — required for limit orders.","type":"number"},"quantity":{"description":"Base amount. Provide this or notional.","type":"number"},"side":{"enum":["buy","sell"],"type":"string"},"symbol":{"description":"Canonical BASE/QUOTE.","example":"BTC/USDT","type":"string"},"type":{"enum":["market","limit"],"type":"string"}},"required":["exchange","symbol","side","type"],"type":"object"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"JWT or mk_live_\u003chex\u003e","scheme":"bearer","type":"http"}}},"info":{"description":"Realtime Kimchi premium matrix with FX-normalized spreads across Korean and\nWestern exchanges.\n\n### Authentication\n\nAll authenticated endpoints accept either a **JWT** or an **API key** on\nthe `Authorization: Bearer \u003ctoken\u003e` header. Both are issued by\n[auth.marketmaker.cc](https://auth.marketmaker.cc):\n\n- `POST {AUTH_BASE_URL}/auth/login` — exchange email/password for a JWT.\n- `POST {AUTH_BASE_URL}/keys` — create an API key (string `mk_live_\u003chex\u003e`).\n\nThe kimchi-specific role lives under `services[\"kimchi\"]` in the JWT\nclaims, and `admin`/`superuser` unlocks the /api/v1/admin/* routes.\n\n### WebSocket\n\n`GET /ws/premium` upgrades to a server-pushed JSON stream\n(`{rows, ts, market_data_mode}` per frame, default 1 Hz).\n","title":"Kimchi Premium API","version":"1.1.0"},"openapi":"3.0.3","paths":{"/api/docs":{"get":{"responses":{"200":{"description":"HTML"}},"summary":"Redoc UI","tags":["docs"]}},"/api/v1/admin/feed-connections":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"connections":{"items":{"$ref":"#/components/schemas/FeedConnection"},"type":"array"}},"type":"object"}}}}},"security":[{"bearerAuth":[]}],"summary":"WS/REST feed connection snapshot","tags":["admin"]}},"/api/v1/admin/settings":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AppSetting"},"type":"array"}}}}},"security":[{"bearerAuth":[]}],"summary":"Get app settings","tags":["admin"]},"put":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppSetting"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppSetting"}}}}},"security":[{"bearerAuth":[]}],"summary":"Update app setting","tags":["admin"]}},"/api/v1/auth/me":{"get":{"description":"Returns the resolved principal for the supplied bearer token. The\nkimchi-specific role is in `role`; the raw multi-service map is in\n`services`. Useful for the frontend to render its admin UI gates.\n","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthPrincipal"}}}},"401":{"description":"Unauthorized"}},"security":[{"bearerAuth":[]}],"summary":"Current principal (JWT or API key)","tags":["auth"]}},"/api/v1/meta/exchanges":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"korean":{"items":{"type":"string"},"type":"array"},"western":{"items":{"type":"string"},"type":"array"}},"type":"object"}}}}},"summary":"Configured exchange ids","tags":["meta"]}},"/api/v1/meta/status":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"auth_base_url":{"example":"https://auth.marketmaker.cc/api/v1","type":"string"},"backend_version":{"type":"string"},"market_data_mode":{"example":"realtime","type":"string"}},"type":"object"}}}}},"summary":"Backend mode, version, and auth base URL","tags":["meta"]}},"/api/v1/meta/symbols":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"symbols":{"items":{"type":"string"},"type":"array"}},"type":"object"}}}}},"summary":"Discovered base symbols (Korean ∩ Western)","tags":["meta"]}},"/api/v1/openapi.json":{"get":{"responses":{"200":{"content":{"application/json":{}}}},"summary":"Download this spec as JSON","tags":["docs"]}},"/api/v1/openapi.yaml":{"get":{"responses":{"200":{"content":{"application/yaml":{}}}},"summary":"Download this spec as YAML","tags":["docs"]}},"/api/v1/premium/latency-stats":{"get":{"description":"Aggregate stats over the `latency_ms` column of the current table snapshot.\nPercentiles use the nearest-rank method on integer milliseconds.\n","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LatencyStats"}}},"description":"Latency distribution"}},"summary":"Latency percentiles across the current premium table","tags":["premium"]}},"/api/v1/premium/simulate":{"post":{"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationResult"}}},"description":"Result"},"400":{"description":"Bad request"}},"summary":"Simulate buy open / sell close (USDT notional)","tags":["premium"]}},"/api/v1/premium/table":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/PremiumRow"},"type":"array"}},"type":"object"}}},"description":"Rows"}},"summary":"Premium table rows (USDT-normalized)","tags":["premium"]}},"/api/v1/trade/arbitrage/validate":{"post":{"description":"Validates an open leg and a close leg together. Each leg is projected\nindependently; cross-leg checks flag a base-asset mismatch, same-side\nlegs, or both legs on one exchange.\n","requestBody":{"content":{"application/json":{"schema":{"properties":{"close":{"$ref":"#/components/schemas/TradeOrder"},"open":{"$ref":"#/components/schemas/TradeOrder"}},"required":["open","close"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArbitrageDryRun"}}}},"400":{"description":"Invalid order"},"401":{"description":"Unauthorized"}},"security":[{"bearerAuth":[]}],"summary":"Dry-run validate both legs of an arbitrage trade","tags":["trade"]}},"/api/v1/trade/exchanges":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"exchanges":{"items":{"$ref":"#/components/schemas/ExchangeInfo"},"type":"array"},"mode":{"example":"dry_run","type":"string"},"note":{"type":"string"}},"type":"object"}}}}},"summary":"Supported trading venues and their capabilities","tags":["trade"]}},"/api/v1/trade/order":{"post":{"description":"Sends a real order to the exchange using API credentials vaulted in\nauth.marketmaker.cc. Requires the user to have stored credentials for\nthe target exchange. Returns the exchange-assigned order ID on success.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeOrder"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResult"}}}},"400":{"description":"Invalid order"},"401":{"description":"Unauthorized"},"503":{"description":"Live trading disabled or credentials missing"}},"security":[{"bearerAuth":[]}],"summary":"Place a live order (when TRADING_LIVE_ENABLED=true)","tags":["trade"]}},"/api/v1/trade/order/validate":{"post":{"description":"Validates an order and projects the expected fill from the current\ntop-of-book: projected price, quantity, gross cost, estimated taker\nfee, and any warnings (stale book, below minimum notional, a limit\norder that would not fill immediately). No order is placed.\n","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TradeOrder"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DryRunResult"}}}},"400":{"description":"Invalid order"},"401":{"description":"Unauthorized"}},"security":[{"bearerAuth":[]}],"summary":"Dry-run validate a single order","tags":["trade"]}},"/healthz":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"example":"ok","type":"string"}},"type":"object"}}},"description":"OK"}},"summary":"Liveness probe","tags":["health"]}},"/ws/premium":{"get":{"description":"Upgrades to a WebSocket. The server pushes a JSON frame\n`{rows, ts, market_data_mode}` once per `WS_PUSH_INTERVAL_MS`\n(default 1000). A single broadcast hub fans out the same payload to\nevery connected client, so CPU does not scale with subscriber count.\n","responses":{"101":{"description":"Switching Protocols"}},"summary":"WebSocket stream of premium rows","tags":["premium"]}}},"servers":[{"description":"Same origin (or backend origin in dev)","url":"/"}],"tags":[{"name":"health"},{"name":"premium"},{"name":"meta"},{"name":"auth"},{"description":"Order validation for placing arbitrage trades. Currently dry-run only:\norders are validated and a fill is projected from the live order book,\nbut nothing is sent to the exchange. Live placement will read\nper-user exchange credentials vaulted in auth.marketmaker.cc.\n","name":"trade"},{"name":"admin"},{"name":"docs"}]}
