openapi: 3.0.3
info:
  title: BlockQuant B2B Gateway
  version: 0.1.0
  description: >-
    Lossless Uniswap V3 WETH/USDC (0.05%) tick data archives and sandboxed
    parametric backtesting compute on Base Mainnet, sold to humans and
    autonomous agents and settled in USDC over the native x402 payment protocol.
    Each paid endpoint answers an unpaid request with HTTP 402 and the payment
    requirements (price, USDC asset, Base network, pay-to address); replaying the
    identical request with a valid `X-Payment` header is verified and settled
    on-chain via the Coinbase CDP facilitator before the product is served.
  license:
    name: UNLICENSED
servers:
  - url: https://api.blockquant.tech
    description: Production (nginx TLS reverse proxy → gateway on 127.0.0.1:7000)

tags:
  - name: data
    description: Raw tick data products
  - name: compute
    description: Sandboxed backtest compute
  - name: free
    description: Free, non-billed service + discovery endpoints

components:
  securitySchemes:
    x402Payment:
      type: apiKey
      in: header
      name: X-Payment
      description: >-
        Base64-encoded x402 payment payload (EIP-3009 USDC transfer authorization
        on Base). Obtain the price/pay-to requirements from the 402 challenge,
        sign, then replay the identical request with this header. Standard x402
        client tooling (x402-fetch / x402-axios) automates the challenge,
        signing, and replay. A settlement receipt is returned in the
        `X-Payment-Response` response header.
  headers:
    XPaymentResponse:
      description: Base64-encoded x402 settlement receipt for the completed payment.
      schema:
        type: string
  responses:
    PaymentRequired:
      description: >-
        Payment Required. Returned when no valid `X-Payment` header is present.
        The body carries the x402 payment requirements: price, asset (USDC),
        network (`base`), and the pay-to address. Replay the same request with a
        valid `X-Payment` header to be served.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PaymentRequirements'
  schemas:
    PaymentRequirements:
      type: object
      description: x402 payment requirements returned with HTTP 402.
      properties:
        x402Version:
          type: integer
          example: 1
        accepts:
          type: array
          items:
            type: object
            properties:
              scheme: { type: string, example: exact }
              network: { type: string, example: base }
              maxAmountRequired: { type: string, example: "1000000" }
              asset: { type: string, example: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" }
              payTo: { type: string, example: "0x...", description: "Live pay-to address" }
              resource: { type: string }
              description: { type: string }
              mimeType: { type: string }
    Error:
      type: object
      properties:
        error: { type: string }
        detail: { type: string }
        code: { type: string }
    StreamRowRaw:
      type: object
      properties:
        timestamp: { type: string }
        blockNumber: { type: integer, nullable: true }
        tick: { type: integer }
        sqrtPriceX96:
          type: string
          description: Un-truncated uint160 as a string (lossless).
    StreamRowHuman:
      type: object
      properties:
        timestamp: { type: string }
        blockNumber: { type: integer, nullable: true }
        tick: { type: integer }
        usdcPricePerEth: { type: number, description: Base-10 USDC/ETH computed from sqrtPriceX96. }
        usdcPriceCsv: { type: number, description: Price as stored in the source CSV. }
    StreamResponse:
      type: object
      properties:
        status: { type: string, example: success }
        date: { type: string, format: date }
        format: { type: string, enum: [raw, human] }
        fromBlock: { type: integer, nullable: true }
        limit: { type: integer }
        matchedTotal: { type: integer }
        count: { type: integer }
        rows:
          type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/StreamRowRaw'
              - $ref: '#/components/schemas/StreamRowHuman'
    BacktestConfig:
      type: object
      description: Strictly allowlisted Base-tier engine config. Unknown keys are rejected.
      additionalProperties: false
      properties:
        candleMinutes:
          type: integer
          enum: [1, 3, 5, 10]
          default: 1
        matrix:
          type: object
          additionalProperties: false
          properties:
            profitTargets:
              type: array
              minItems: 1
              maxItems: 12
              items: { type: number, minimum: 0.0001, maximum: 0.5 }
              default: [0.001, 0.002, 0.005]
            stopLosses:
              type: array
              minItems: 1
              maxItems: 12
              items: { type: number, minimum: 0.0001, maximum: 0.5 }
              default: [0.003, 0.005, 0.01]
            bidLayerCounts:
              type: array
              minItems: 1
              maxItems: 12
              items: { type: integer, minimum: 1, maximum: 50 }
              default: [1, 5, 10]
          description: Product of the three arrays must be <= 1000 cells.
        indicators:
          type: array
          minItems: 1
          maxItems: 6
          description: Built-in kernels only, no duplicates.
          items:
            type: object
            additionalProperties: false
            properties:
              name: { type: string, enum: [rsi, bbands, macd] }
              params: { type: object }
        feeBps: { type: number, minimum: 0, maximum: 100, default: 5 }
        notionalPerLayer: { type: number, minimum: 1, maximum: 1000000, default: 1000 }
        topN: { type: integer, minimum: 1, maximum: 100, default: 25 }
        rankBy: { type: string, enum: [netPnLPct, fillRate, trades], default: netPnLPct }
    BacktestRequest:
      type: object
      required: [dates]
      properties:
        dates:
          type: array
          minItems: 1
          items: { type: string, format: date }
          description: Closed UTC days. Unknown days are reported in missingDates.
        config:
          $ref: '#/components/schemas/BacktestConfig'
    BacktestResponse:
      type: object
      properties:
        status: { type: string, example: success }
        schema: { type: string }
        engineVersion: { type: string }
        generatedAt: { type: string }
        requestedDates: { type: array, items: { type: string } }
        presentDates: { type: array, items: { type: string } }
        missingDates: { type: array, items: { type: string } }
        matrixSize: { type: integer }
        rankBy: { type: string }
        winner: { type: object }
        leaderboard: { type: array, items: { type: object } }

paths:
  /api/v1/data/archive/{date}:
    get:
      tags: [data]
      summary: Full-day raw tick archive (zipped CSV) — $5.00
      description: >-
        Returns a streamed, memory-safe ZIP of base_price_ticks_{date}.csv for
        one closed UTC day. Requires payment via the `X-Payment` header.
      operationId: getDataArchive
      security:
        - x402Payment: []
      parameters:
        - name: date
          in: path
          required: true
          schema: { type: string, format: date }
          description: YYYY-MM-DD, closed (past) UTC day only.
      responses:
        '200':
          description: Zipped full-day CSV archive.
          headers:
            X-Payment-Response:
              $ref: '#/components/headers/XPaymentResponse'
          content:
            application/zip:
              schema: { type: string, format: binary }
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          description: Date not in catalog / not a closed day.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '400':
          description: Invalid date.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /api/v1/data/stream:
    get:
      tags: [data]
      summary: Targeted block-range tick query — $1.00
      description: >-
        Targeted historical block-range query over one closed UTC day. Requires
        payment via the `X-Payment` header.
      operationId: getDataStream
      security:
        - x402Payment: []
      parameters:
        - name: date
          in: query
          required: true
          schema: { type: string, format: date }
          description: YYYY-MM-DD, closed (past) UTC day only.
        - name: format
          in: query
          required: false
          schema: { type: string, enum: [raw, human], default: raw }
          description: raw = lossless sqrtPriceX96 string; human = base-10 USDC/ETH.
        - name: limit
          in: query
          required: false
          schema: { type: integer, minimum: 1, maximum: 5000, default: 500 }
          description: Last N matching rows, returned in chronological order.
        - name: fromBlock
          in: query
          required: false
          schema: { type: integer, minimum: 0 }
          description: Keep only rows with blockNumber >= fromBlock.
      responses:
        '200':
          description: Matching tick rows.
          headers:
            X-Payment-Response:
              $ref: '#/components/headers/XPaymentResponse'
          content:
            application/json:
              schema: { $ref: '#/components/schemas/StreamResponse' }
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '400':
          description: Invalid parameters.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /api/v1/matrix/backtest:
    post:
      tags: [compute]
      summary: Sandboxed parametric backtest sweep — $25.00
      description: >-
        Runs a sandboxed parametric backtest sweep over one or more closed days.
        Config is strictly allowlisted (Base tier). Requires payment via the
        `X-Payment` header.
      operationId: postMatrixBacktest
      security:
        - x402Payment: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/BacktestRequest' }
      responses:
        '200':
          description: Leaderboard for the requested sweep.
          headers:
            X-Payment-Response:
              $ref: '#/components/headers/XPaymentResponse'
          content:
            application/json:
              schema: { $ref: '#/components/schemas/BacktestResponse' }
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '400':
          description: Invalid config or dates.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '404':
          description: None of the requested days are available.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '503':
          description: Backtest sandbox unavailable.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '504':
          description: Backtest timed out (wall-clock limit).
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /healthz:
    get:
      tags: [free]
      summary: Liveness probe (free)
      operationId: getHealthz
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean, example: true }

  /api/v1/status:
    get:
      tags: [free]
      summary: Service info, payments config, pricing, catalog range (free)
      operationId: getStatus
      responses:
        '200':
          description: Service status.
          content:
            application/json:
              schema: { type: object }

  /api/v1/catalog:
    get:
      tags: [free]
      summary: Dataset asset, CSV schema, available closed days (free)
      operationId: getCatalog
      responses:
        '200':
          description: Catalog listing.
          content:
            application/json:
              schema:
                type: object
                properties:
                  asset: { type: string, example: "WETH/USDC 0.05% (Base Mainnet)" }
                  schema: { type: string, example: "timestamp,blockNumber,tick,sqrtPriceX96,usdcPrice" }
                  days: { type: array, items: { type: string, format: date } }
