openapi: 3.1.0
info:
  title: Prediction Trading Agent API
  version: 1.0.0
  description: MVP API contracts for Kalshi + Polymarket multi-service trading agent.
servers:
  - url: http://localhost:8080
paths:
  /v1/markets/snapshots:
    get:
      summary: Get normalized market snapshots
      parameters:
        - in: query
          name: venue
          schema: { type: string, enum: [kalshi, polymarket] }
        - in: query
          name: marketId
          schema: { type: string }
      responses:
        '200':
          description: Latest snapshots
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/MarketSnapshot'
  /v1/signals/evaluate:
    post:
      summary: Evaluate signal for a market
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SignalEvaluateRequest'
      responses:
        '200':
          description: Signal output
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Signal'
  /v1/risk/check-intent:
    post:
      summary: Check and adjust order intent against risk policy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderIntent'
      responses:
        '200':
          description: Risk decision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RiskDecision'
  /v1/execution/submit:
    post:
      summary: Submit approved order to venue
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExecutionSubmitRequest'
      responses:
        '200':
          description: Submission status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecutionSubmitResponse'
  /v1/execution/cancel:
    post:
      summary: Cancel existing venue order
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExecutionCancelRequest'
      responses:
        '200':
          description: Cancellation status
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, example: cancelled }
                  venueOrderId: { type: string }
  /v1/recon/run:
    post:
      summary: Run reconciliation check
      responses:
        '200':
          description: Reconciliation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReconciliationResult'
  /v1/system/state:
    get:
      summary: Get system state
      responses:
        '200':
          description: Current safety state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemState'
  /v1/system/safe-mode:
    post:
      summary: Toggle safe mode
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [enabled, reason]
              properties:
                enabled: { type: boolean }
                reason: { type: string }
      responses:
        '200':
          description: Updated state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemState'
components:
  schemas:
    MarketSnapshot:
      type: object
      required: [ts, venue, marketId, bid, ask, mid, last, depthBid, depthAsk, spreadBps]
      properties:
        ts: { type: string, format: date-time }
        venue: { type: string, enum: [kalshi, polymarket] }
        marketId: { type: string }
        bid: { type: number }
        ask: { type: number }
        mid: { type: number }
        last: { type: number }
        depthBid: { type: number }
        depthAsk: { type: number }
        spreadBps: { type: integer }
    SignalEvaluateRequest:
      type: object
      required: [marketId, venue, features]
      properties:
        marketId: { type: string }
        venue: { type: string, enum: [kalshi, polymarket] }
        features:
          type: object
          required: [pModel, pMarket]
          properties:
            pModel: { type: number }
            pMarket: { type: number }
    Signal:
      type: object
      required: [signalId, ts, marketId, pModel, pMarket, edge, confidence, passesThreshold]
      properties:
        signalId: { type: string }
        ts: { type: string, format: date-time }
        marketId: { type: string }
        pModel: { type: number }
        pMarket: { type: number }
        edge: { type: number }
        confidence: { type: number }
        passesThreshold: { type: boolean }
    OrderIntent:
      type: object
      required: [intentId, marketId, side, sizeUsd, limitPrice, signalId]
      properties:
        intentId: { type: string }
        marketId: { type: string }
        side: { type: string, enum: [buy_yes, buy_no, sell_yes, sell_no] }
        sizeUsd: { type: number }
        limitPrice: { type: number }
        reason: { type: string }
        signalId: { type: string }
    RiskDecision:
      type: object
      required: [approved, adjustedSizeUsd, rulesTriggered, riskDecisionId]
      properties:
        approved: { type: boolean }
        adjustedSizeUsd: { type: number }
        rulesTriggered:
          type: array
          items: { type: string }
        riskDecisionId: { type: string }
    ExecutionSubmitRequest:
      type: object
      required: [intentId, riskDecisionId, venue, marketId, side, sizeUsd, limitPrice, idempotencyKey]
      properties:
        intentId: { type: string }
        riskDecisionId: { type: string }
        venue: { type: string, enum: [kalshi, polymarket] }
        marketId: { type: string }
        side: { type: string, enum: [buy_yes, buy_no, sell_yes, sell_no] }
        sizeUsd: { type: number }
        limitPrice: { type: number }
        idempotencyKey: { type: string }
    ExecutionSubmitResponse:
      type: object
      required: [status, venueOrderId, executionId]
      properties:
        status: { type: string, enum: [submitted, rejected] }
        venueOrderId: { type: string }
        executionId: { type: string }
    ExecutionCancelRequest:
      type: object
      required: [venue, venueOrderId, reason]
      properties:
        venue: { type: string, enum: [kalshi, polymarket] }
        venueOrderId: { type: string }
        reason: { type: string }
    ReconciliationResult:
      type: object
      required: [ok, mismatches, safeModeActivated]
      properties:
        ok: { type: boolean }
        mismatches:
          type: array
          items: { type: string }
        safeModeActivated: { type: boolean }
    SystemState:
      type: object
      required: [safeMode, killSwitch, lastHeartbeatTs]
      properties:
        safeMode: { type: boolean }
        killSwitch: { type: boolean }
        lastHeartbeatTs: { type: string, format: date-time }
