openapi: 3.0.3
info:
  title: DSTA Trading Service
  description: Strategy management, order execution, positions, risk, signals, and portfolio.
  version: 0.1.0
servers:
  - url: http://localhost:8002
    description: Local development
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    PageResponse:
      type: object
      properties:
        items:
          type: array
          items: {}
        total:
          type: integer
        page:
          type: integer
        size:
          type: integer
    OrderRead:
      type: object
      properties:
        id:
          type: integer
        symbol:
          type: string
        side:
          type: string
          enum: [buy, sell]
        order_type:
          type: string
          enum: [market, limit, stop]
        quantity:
          type: string
        price:
          type: string
          nullable: true
        status:
          type: string
          enum: [pending, open, filled, cancelled, rejected]
        created_at:
          type: string
          format: date-time
    PositionRead:
      type: object
      properties:
        id:
          type: integer
        symbol:
          type: string
        side:
          type: string
          enum: [long, short]
        quantity:
          type: string
        avg_entry_price:
          type: string
        unrealized_pnl:
          type: string
          nullable: true
        status:
          type: string
          enum: [open, closed]
        opened_at:
          type: string
          format: date-time
    StrategyConfigRead:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        strategy_class:
          type: string
        is_active:
          type: boolean
        parameters:
          type: object
        created_at:
          type: string
          format: date-time
    RiskLimitRead:
      type: object
      properties:
        id:
          type: integer
        limit_type:
          type: string
        value:
          type: string
        is_active:
          type: boolean
    AggregatedSignal:
      type: object
      properties:
        symbol:
          type: string
        signal:
          type: string
          enum: [buy, sell, hold]
        confidence:
          type: number
        components:
          type: array
          items:
            type: object
        computed_at:
          type: string
          format: date-time
    Error:
      type: object
      properties:
        detail:
          type: string
security:
  - bearerAuth: []
paths:
  /health:
    get:
      operationId: health
      summary: Liveness probe
      tags: [health]
      security: []
      responses:
        '200':
          description: Service is alive
  /ready:
    get:
      operationId: ready
      summary: Readiness probe
      tags: [health]
      security: []
      responses:
        '200':
          description: Service is ready

  # ── Strategies ────────────────────────────────────────────────────────────
  /api/v1/strategies:
    get:
      operationId: listStrategies
      summary: List strategy configurations
      tags: [strategies]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: size
          in: query
          schema:
            type: integer
            default: 20
        - name: is_active
          in: query
          schema:
            type: boolean
      responses:
        '200':
          description: Paginated list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PageResponse'
    post:
      operationId: createStrategy
      summary: Create a strategy configuration
      tags: [strategies]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, strategy_class]
              properties:
                name:
                  type: string
                strategy_class:
                  type: string
                parameters:
                  type: object
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StrategyConfigRead'
  /api/v1/strategies/{strategy_id}:
    get:
      operationId: getStrategy
      summary: Get a strategy configuration
      tags: [strategies]
      parameters:
        - name: strategy_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Strategy configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StrategyConfigRead'
        '404':
          description: Not found
    patch:
      operationId: updateStrategy
      summary: Update a strategy configuration
      tags: [strategies]
      parameters:
        - name: strategy_id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StrategyConfigRead'
    delete:
      operationId: deactivateStrategy
      summary: Deactivate a strategy
      tags: [strategies]
      parameters:
        - name: strategy_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: Deactivated

  # ── Execution ─────────────────────────────────────────────────────────────
  /api/v1/execution/orders:
    get:
      operationId: listOrders
      summary: List orders
      tags: [execution]
      parameters:
        - name: symbol
          in: query
          schema:
            type: string
        - name: status
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: size
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Paginated list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PageResponse'
    post:
      operationId: createOrder
      summary: Place a new order
      tags: [execution]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [symbol, side, order_type, quantity]
              properties:
                symbol:
                  type: string
                side:
                  type: string
                  enum: [buy, sell]
                order_type:
                  type: string
                  enum: [market, limit, stop]
                quantity:
                  type: string
                price:
                  type: string
                strategy_id:
                  type: integer
      responses:
        '201':
          description: Order created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderRead'
        '422':
          description: Risk limit violation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api/v1/execution/orders/{order_pk}:
    get:
      operationId: getOrder
      summary: Get an order
      tags: [execution]
      parameters:
        - name: order_pk
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Order
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderRead'
        '404':
          description: Not found
    patch:
      operationId: updateOrder
      summary: Update an order
      tags: [execution]
      parameters:
        - name: order_pk
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderRead'
    delete:
      operationId: deleteOrder
      summary: Cancel/delete an order
      tags: [execution]
      parameters:
        - name: order_pk
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: Deleted

  # ── Positions ─────────────────────────────────────────────────────────────
  /api/v1/positions:
    get:
      operationId: listPositions
      summary: List positions
      tags: [positions]
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [open, closed]
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: size
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Paginated list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PageResponse'
    post:
      operationId: createPosition
      summary: Create a position record
      tags: [positions]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [symbol, side, quantity, avg_entry_price]
              properties:
                symbol:
                  type: string
                side:
                  type: string
                  enum: [long, short]
                quantity:
                  type: string
                avg_entry_price:
                  type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PositionRead'
  /api/v1/positions/{position_pk}:
    get:
      operationId: getPosition
      summary: Get a position
      tags: [positions]
      parameters:
        - name: position_pk
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Position with fills
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PositionRead'
        '404':
          description: Not found
    patch:
      operationId: updatePosition
      summary: Update a position
      tags: [positions]
      parameters:
        - name: position_pk
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated
    delete:
      operationId: deletePosition
      summary: Delete a position
      tags: [positions]
      parameters:
        - name: position_pk
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: Deleted

  # ── Risk ──────────────────────────────────────────────────────────────────
  /api/v1/risk/limits:
    get:
      operationId: listRiskLimits
      summary: List risk limits
      tags: [risk]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: size
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Paginated list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PageResponse'
    post:
      operationId: createRiskLimit
      summary: Create a risk limit
      tags: [risk]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [limit_type, value]
              properties:
                limit_type:
                  type: string
                value:
                  type: string
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RiskLimitRead'
  /api/v1/risk/limits/{limit_pk}:
    get:
      operationId: getRiskLimit
      summary: Get a risk limit
      tags: [risk]
      parameters:
        - name: limit_pk
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Risk limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RiskLimitRead'
        '404':
          description: Not found
    patch:
      operationId: updateRiskLimit
      summary: Update a risk limit
      tags: [risk]
      parameters:
        - name: limit_pk
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated
    delete:
      operationId: deleteRiskLimit
      summary: Delete a risk limit
      tags: [risk]
      parameters:
        - name: limit_pk
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: Deleted
  /api/v1/risk/circuit-breaker:
    get:
      operationId: listCircuitBreakerEvents
      summary: List circuit breaker events
      tags: [risk]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: size
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: Paginated list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PageResponse'

  # ── Signals ───────────────────────────────────────────────────────────────
  /api/v1/signals/aggregate:
    post:
      operationId: aggregateSignals
      summary: Aggregate signals for a symbol
      tags: [signals]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [symbol]
              properties:
                symbol:
                  type: string
                signals:
                  type: array
                  items:
                    type: object
      responses:
        '200':
          description: Aggregated signal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AggregatedSignal'
  /api/v1/signals/composite:
    post:
      operationId: compositeSignals
      summary: Compute composite signal
      tags: [signals]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Composite signal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AggregatedSignal'
  /api/v1/signals/latest/{symbol}:
    get:
      operationId: getLatestSignal
      summary: Get latest signal for a symbol
      tags: [signals]
      parameters:
        - name: symbol
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Latest aggregated signal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AggregatedSignal'
  /api/v1/signals/regime-check:
    post:
      operationId: checkRegimeGate
      summary: Check if regime gate allows trading
      tags: [signals]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [symbol]
              properties:
                symbol:
                  type: string
      responses:
        '200':
          description: Regime gate check result
          content:
            application/json:
              schema:
                type: object
                properties:
                  allowed:
                    type: boolean
                  regime:
                    type: string
                  reason:
                    type: string

  # ── Optimization ──────────────────────────────────────────────────────────
  /api/v1/optimization/start:
    post:
      operationId: startOptimization
      summary: Start a strategy parameter optimization job
      tags: [optimization]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [strategy_id]
              properties:
                strategy_id:
                  type: integer
                n_trials:
                  type: integer
                  default: 100
                objective:
                  type: string
                  default: sharpe
      responses:
        '202':
          description: Job enqueued
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id:
                    type: string
                  status:
                    type: string
  /api/v1/optimization/{job_id}:
    get:
      operationId: getOptimizationStatus
      summary: Get optimization job status
      tags: [optimization]
      parameters:
        - name: job_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Job status
          content:
            application/json:
              schema:
                type: object
                properties:
                  job_id:
                    type: string
                  status:
                    type: string
                  result:
                    type: object
                    nullable: true

  # ── Portfolio ─────────────────────────────────────────────────────────────
  /api/v1/portfolio/positions:
    get:
      operationId: getPortfolioPositions
      summary: Get aggregated portfolio positions
      tags: [portfolio]
      responses:
        '200':
          description: Portfolio positions summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  positions:
                    type: array
                    items:
                      $ref: '#/components/schemas/PositionRead'
                  total_equity:
                    type: string
                  total_unrealized_pnl:
                    type: string
  /api/v1/portfolio/kelly/{strategy_id}:
    get:
      operationId: getKellyFraction
      summary: Get Kelly criterion fraction for a strategy
      tags: [portfolio]
      parameters:
        - name: strategy_id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Kelly fraction
          content:
            application/json:
              schema:
                type: object
                properties:
                  strategy_id:
                    type: integer
                  kelly_fraction:
                    type: number
  /api/v1/portfolio/optimize:
    post:
      operationId: optimizePortfolio
      summary: Run portfolio optimization
      tags: [portfolio]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Optimization result
          content:
            application/json:
              schema:
                type: object

  # ── Control ───────────────────────────────────────────────────────────────
  /api/v1/control/emergency-stop:
    post:
      operationId: emergencyStop
      summary: Trigger emergency stop — cancel all open orders
      tags: [control]
      responses:
        '200':
          description: Emergency stop executed
          content:
            application/json:
              schema:
                type: object
                properties:
                  cancelled:
                    type: integer
                  failed:
                    type: integer
