openapi: 3.1.0
info:
  title: Dipsern API
  version: "1.0.0"
  description: |
    Quantitative drawdown-segmented forecast engine. Premium tier only.

    See [the full reference](https://github.com/jruiz000/ARAD_V8/blob/main/docs/API.md)
    for a written guide, code samples and SDK install instructions.
  contact:
    name: Dipsern Support
    email: support@dipsern.com
    url: https://dipsern.com
  license:
    name: Proprietary
    url: https://dipsern.com/terms
servers:
  - url: https://dipsern.com
    description: Production
security:
  - bearerAuth: []

tags:
  - name: Health
  - name: Assets
  - name: Analysis
  - name: Ranking
  - name: Backtest

paths:
  /api/health:
    get:
      tags: [Health]
      summary: Liveness probe
      security: []
      parameters:
        - in: query
          name: debug
          schema: { type: integer, enum: [0, 1] }
          required: false
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, example: ok }
                  service: { type: string, example: dipsern-api }

  /api/assets:
    get:
      tags: [Assets]
      summary: List active assets
      parameters:
        - in: query
          name: category
          schema: { $ref: '#/components/schemas/Category' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AssetListResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }

  /api/asset-info/{ticker}:
    get:
      tags: [Assets]
      summary: Cached metadata for an asset
      parameters:
        - in: path
          name: ticker
          required: true
          schema: { type: string }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AssetInfo' }
        '404': { $ref: '#/components/responses/NotFound' }

  /api/analysis/{ticker}:
    get:
      tags: [Analysis]
      summary: Run the full Dipsern engine for a ticker
      parameters:
        - in: path
          name: ticker
          required: true
          schema: { type: string }
        - in: query
          name: return_period
          schema: { type: integer, minimum: 7, maximum: 365, default: 90 }
        - in: query
          name: num_segments
          schema: { type: integer, minimum: 10, maximum: 50, default: 20 }
        - in: query
          name: decay_factor
          schema: { type: number, minimum: 0.80, maximum: 0.99, default: 0.95 }
        - in: query
          name: metric
          schema: { type: string, enum: [return, sharpe, sortino], default: return }
        - in: query
          name: start_date
          schema: { type: string, format: date }
        - in: query
          name: end_date
          schema: { type: string, format: date }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/AnalysisResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '404': { $ref: '#/components/responses/NotFound' }
        '429': { $ref: '#/components/responses/RateLimited' }

  /api/ranking:
    get:
      tags: [Ranking]
      summary: Ranked list of assets by selected metric
      parameters:
        - in: query
          name: category
          schema: { $ref: '#/components/schemas/Category' }
        - in: query
          name: categories
          description: Comma-separated list, overrides `category`
          schema: { type: string }
        - in: query
          name: sort_by
          schema:
            type: string
            enum: [median_return, expected_return, win_rate, prediction_error, current_drawdown, current_price]
            default: median_return
        - in: query
          name: sort
          schema: { type: string, enum: [asc, desc], default: desc }
        - in: query
          name: limit
          schema: { type: integer, minimum: 1, maximum: 500, default: 50 }
        - in: query
          name: return_period
          schema: { type: integer, enum: [7, 30, 90, 180, 365], default: 90 }
        - in: query
          name: metric
          schema: { type: string, enum: [return, sharpe, sortino], default: return }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RankingResponse' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }

  /api/backtest:
    post:
      tags: [Backtest]
      summary: Run a portfolio backtest (counts as 5 requests)
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/BacktestRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BacktestResponse' }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '429': { $ref: '#/components/responses/RateLimited' }
        '504':
          description: Computation timed out (reduce scope)

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: dipsern_<hex>

  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema: { type: integer }
          description: Seconds to wait
        X-RateLimit-Limit:
          schema: { type: integer }
        X-RateLimit-Remaining:
          schema: { type: integer }
        X-RateLimit-Reset:
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    BadRequest:
      description: Invalid input
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }

  schemas:
    Error:
      type: object
      properties:
        detail: { type: string }
      required: [detail]

    Category:
      type: string
      enum:
        - US Stocks
        - ETFs
        - BMV
        - Crypto
        - Commodities
        - Forex
        - Indices
        - UK Stocks
        - European Stocks
        - Japanese Stocks
        - Canadian Stocks
        - Brazilian Stocks
        - HK/China Stocks
        - Indian Stocks
        - Australian Stocks
        - Korean Stocks
        - CS2 Cases

    Asset:
      type: object
      properties:
        id: { type: integer }
        ticker: { type: string }
        name: { type: string }
        category: { $ref: '#/components/schemas/Category' }
        is_active: { type: boolean }
        is_premium: { type: boolean }
      required: [id, ticker, name, category]

    AssetListResponse:
      type: object
      properties:
        assets:
          type: array
          items: { $ref: '#/components/schemas/Asset' }
        total: { type: integer }

    AssetInfo:
      type: object
      properties:
        ticker: { type: string }
        name: { type: string }
        category: { $ref: '#/components/schemas/Category' }
        long_name: { type: string }
        sector: { type: string }
        industry: { type: string }
        summary: { type: string }
        logo_url: { type: string }
        market_cap: { type: integer }
        exchange: { type: string }
        currency: { type: string }
        website: { type: string }
        from_cache: { type: boolean }

    CurrentState:
      type: object
      properties:
        price: { type: number }
        drawdown: { type: number, description: "Fraction in [-1, 0]" }
        segment: { type: integer }
        expected_return: { type: number }
        prediction_error: { type: number }
        median_return: { type: number }
        median_error: { type: number }
        best_method: { type: string, enum: [ema, median] }
        best_error: { type: number }
        insufficient_data: { type: boolean }
        confirmed_count: { type: integer }

    Segment:
      type: object
      properties:
        segment_idx: { type: integer }
        lower_bound: { type: number }
        upper_bound: { type: number }
        count: { type: integer }
        expected_return: { type: number, nullable: true }
        avg_return: { type: number, nullable: true }
        median_return: { type: number, nullable: true }
        static_median_return: { type: number, nullable: true }
        max_loss: { type: number, nullable: true }
        win_rate: { type: number, nullable: true }

    SegmentPaths:
      type: object
      properties:
        paths:
          type: array
          items:
            type: array
            items: { type: number }
        p10: { type: array, items: { type: number } }
        p25: { type: array, items: { type: number } }
        p50: { type: array, items: { type: number } }
        p75: { type: array, items: { type: number } }
        p90: { type: array, items: { type: number } }
        num_paths_total: { type: integer }

    AnalysisResponse:
      type: object
      properties:
        ticker: { type: string }
        name: { type: string }
        category: { $ref: '#/components/schemas/Category' }
        current_state: { $ref: '#/components/schemas/CurrentState' }
        signals:
          type: object
          properties:
            expected_return: { type: number }
            prediction_error: { type: number }
            median_return: { type: number }
            median_error: { type: number }
            best_method: { type: string }
            best_error: { type: number }
        time_series:
          type: object
          properties:
            dates: { type: array, items: { type: string, format: date } }
            prices: { type: array, items: { type: number } }
            drawdowns: { type: array, items: { type: number } }
            expected_returns: { type: array, items: { type: number } }
            prediction_errors: { type: array, items: { type: number, nullable: true } }
            prediction_error_cumavg: { type: array, items: { type: number, nullable: true } }
            actual_returns: { type: array, items: { type: number, nullable: true } }
            median_returns: { type: array, items: { type: number } }
            median_errors: { type: array, items: { type: number, nullable: true } }
            median_error_cumavg: { type: array, items: { type: number, nullable: true } }
            projected_prices: { type: array, items: { type: number, nullable: true } }
        segments:
          type: array
          items: { $ref: '#/components/schemas/Segment' }
        return_distribution:
          type: object
          properties:
            bins: { type: array, items: { type: number } }
            counts: { type: array, items: { type: integer } }
        scatter_data:
          type: array
          items:
            type: object
            properties:
              drawdown: { type: number }
              future_return: { type: number }
              segment: { type: integer }
        segment_paths:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/SegmentPaths'
        metadata:
          type: object
          properties:
            return_period: { type: integer }
            num_segments: { type: integer }
            decay_factor: { type: number }
            calculated_at: { type: string, format: date-time }
            from_cache: { type: boolean }
            data_points: { type: integer }
            asset_id: { type: integer }

    RankedAsset:
      type: object
      properties:
        rank: { type: integer }
        ticker: { type: string }
        name: { type: string }
        category: { $ref: '#/components/schemas/Category' }
        expected_return: { type: number }
        prediction_error: { type: number }
        current_drawdown: { type: number }
        current_price: { type: number }
        win_rate: { type: number }
        median_return: { type: number }
        median_error: { type: number }

    RankingResponse:
      type: object
      properties:
        ranked_assets:
          type: array
          items: { $ref: '#/components/schemas/RankedAsset' }
        metadata:
          type: object
          properties:
            total_assets: { type: integer }
            filtered_assets: { type: integer }
            calculated_at: { type: string, format: date-time }

    BacktestRequest:
      type: object
      properties:
        strategy: { type: string, enum: [topn, zscore, longshort], default: topn }
        tickers:
          type: array
          items: { type: string }
        category: { $ref: '#/components/schemas/Category' }
        initial_capital: { type: number, default: 100000 }
        top_n: { type: integer, default: 10 }
        rebalance: { type: string, enum: [daily, weekly, monthly], default: monthly }
        return_period: { type: integer, default: 90 }
        decay_factor: { type: number, default: 0.95 }
        num_segments: { type: integer, default: 20 }
        metric: { type: string, enum: [return, sharpe, sortino], default: return }
        start_date: { type: string, format: date }
        end_date: { type: string, format: date }
        include_costs: { type: boolean, default: true }
        zscore_lookback: { type: integer }
        zscore_entry: { type: number }
        zscore_exit: { type: number }

    BacktestResponse:
      type: object
      properties:
        equity_curve:
          type: array
          items:
            type: object
            properties:
              date: { type: string, format: date }
              equity: { type: number }
        trades:
          type: array
          items:
            type: object
            properties:
              date: { type: string, format: date }
              ticker: { type: string }
              side: { type: string, enum: [BUY, SELL] }
              qty: { type: number }
              price: { type: number }
        metrics:
          type: object
          properties:
            total_return: { type: number }
            cagr: { type: number }
            sharpe_ratio: { type: number }
            sortino_ratio: { type: number }
            max_drawdown: { type: number }
            total_trades: { type: integer }
            final_equity: { type: number }
            years: { type: number }
            total_costs: { type: number }
            cost_drag_pct: { type: number }
        benchmarks:
          type: object
          additionalProperties:
            type: object
            properties:
              dates: { type: array, items: { type: string, format: date } }
              equity: { type: array, items: { type: number } }
        metadata:
          type: object
          properties:
            assets_count: { type: integer }
            data_points: { type: integer }
            rebalance_dates: { type: integer }
            calculated_at: { type: string, format: date-time }
