openapi: 3.0.3
info:
  title: Vantg Bot Trading API
  description: |
    Build trading bots and AI agents that trade on Vantg prediction markets.

    ## Authentication
    All endpoints require Bearer token authentication via the `Authorization` header.

    ## Getting Started
    1. Register your bot at POST /register
    2. Deposit USDC to start trading
  version: 1.0.0
  contact:
    email: support@vantg.io
  license:
    name: Proprietary

servers:
  - url: https://vantg.io/api/v1/bot
    description: Production

security:
  - BearerAuth: []

tags:
  - name: Account
    description: Bot account management
  - name: Markets
    description: Browse and query prediction markets
  - name: Trading
    description: Execute trades via AMM
  - name: Order Book
    description: P2P order book for selling shares
  - name: Positions
    description: Portfolio and position management
  - name: Derivatives
    description: Derivative bets on price movements

paths:
  /register:
    post:
      tags: [Account]
      summary: Register a new trading bot
      description: |
        Register a new trading bot. Creates bot immediately with USDC deposit instructions.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, email, country]
              properties:
                name:
                  type: string
                  minLength: 2
                  example: "My Trading Bot"
                email:
                  type: string
                  format: email
                  example: "bot@example.com"
                country:
                  type: string
                  pattern: "^[A-Z]{2}$"
                  example: "SG"
                  description: ISO 3166-1 alpha-2 country code
                description:
                  type: string
                  example: "Arbitrage bot for AI markets"
      responses:
        '201':
          description: Bot created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BotRegistrationResponse'

  /account:
    get:
      tags: [Account]
      summary: Get bot account info
      responses:
        '200':
          description: Account information
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /account/location:
    patch:
      tags: [Account]
      summary: Update bot location
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [country]
              properties:
                country:
                  type: string
                  pattern: "^[A-Z]{2}$"
                  example: "SG"
      responses:
        '200':
          description: Location updated
        '401':
          $ref: '#/components/responses/Unauthorized'

  /markets:
    get:
      tags: [Markets]
      summary: List available markets
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [ACTIVE, RESOLVED, all]
            default: ACTIVE
        - name: category
          in: query
          schema:
            type: string
          example: "AI"
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of markets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketsListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /markets/{id}:
    get:
      tags: [Markets]
      summary: Get market details
      parameters:
        - $ref: '#/components/parameters/MarketId'
      responses:
        '200':
          description: Market details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Market'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /markets/{id}/quote:
    post:
      tags: [Trading]
      summary: Get trade quote
      description: Get a quote for a trade without executing it
      parameters:
        - $ref: '#/components/parameters/MarketId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TradeRequest'
      responses:
        '200':
          description: Trade quote
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuoteResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /markets/{id}/history:
    get:
      tags: [Markets]
      summary: Get market trade history
      description: Get historical trade data, price history, and volume stats for strategy development
      parameters:
        - $ref: '#/components/parameters/MarketId'
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
          description: Number of trades to return
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
        - name: type
          in: query
          schema:
            type: string
            enum: [all, buys, sells]
            default: all
          description: Filter by trade type
      responses:
        '200':
          description: Market history data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketHistoryResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /markets/{id}/trade:
    post:
      tags: [Trading]
      summary: Execute trade
      description: Execute a trade on a primary market
      parameters:
        - $ref: '#/components/parameters/MarketId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/TradeRequest'
                - type: object
                  properties:
                    maxPriceImpact:
                      type: number
                      description: Maximum allowed price impact in percentage points
                      example: 5.0
      responses:
        '200':
          description: Trade executed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TradeResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /positions:
    get:
      tags: [Positions]
      summary: Get open positions
      parameters:
        - name: type
          in: query
          schema:
            type: string
            enum: [PRIMARY, SECONDARY, DERIVATIVE, all]
            default: all
        - name: status
          in: query
          schema:
            type: string
            enum: [open, all]
            default: open
      responses:
        '200':
          description: Portfolio positions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PositionsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /transactions:
    get:
      tags: [Positions]
      summary: Get transaction history
      parameters:
        - name: type
          in: query
          schema:
            type: string
          description: Filter by transaction type
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Transaction history
        '401':
          $ref: '#/components/responses/Unauthorized'

  /derivatives/quote:
    post:
      tags: [Derivatives]
      summary: Get derivative bet quote
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DerivativeRequest'
      responses:
        '200':
          description: Derivative quote
        '401':
          $ref: '#/components/responses/Unauthorized'

  /derivatives/bet:
    post:
      tags: [Derivatives]
      summary: Place derivative bet
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DerivativeRequest'
      responses:
        '200':
          description: Bet placed
        '401':
          $ref: '#/components/responses/Unauthorized'

  /derivatives/{id}/exit:
    post:
      tags: [Derivatives]
      summary: Exit derivative bet early
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Bet exited
        '401':
          $ref: '#/components/responses/Unauthorized'

  /secondary/{id}/trade:
    post:
      tags: [Trading]
      summary: Trade secondary market
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TradeRequest'
      responses:
        '200':
          description: Trade executed
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders:
    get:
      tags: [Order Book]
      summary: List open orders
      description: Get the order book for a market
      parameters:
        - name: marketId
          in: query
          required: true
          schema:
            type: string
          description: Primary market ID
        - name: side
          in: query
          schema:
            type: string
            enum: [YES, NO]
        - name: mine
          in: query
          schema:
            type: string
            enum: ['true']
          description: Only show your orders
      responses:
        '200':
          description: Order book
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderBookResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags: [Order Book]
      summary: Create sell order
      description: List your shares for sale at a specific price
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
      responses:
        '201':
          description: Order created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/{id}:
    get:
      tags: [Order Book]
      summary: Get order details
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Order details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderDetailResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags: [Order Book]
      summary: Cancel order
      description: Cancel your own order
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Order cancelled
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Not your order
        '404':
          $ref: '#/components/responses/NotFound'

  /orders/{id}/fill:
    post:
      tags: [Order Book]
      summary: Fill an order
      description: Buy shares from someone's sell order
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                shares:
                  type: number
                  description: Shares to buy (defaults to all remaining)
      responses:
        '200':
          description: Order filled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderFillResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: API key obtained during bot registration

  parameters:
    MarketId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Market ID

  responses:
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
          enum:
            - UNAUTHORIZED
            - INVALID_SIDE
            - INVALID_AMOUNT
            - AMOUNT_TOO_SMALL
            - INSUFFICIENT_FUNDS
            - INSUFFICIENT_SHARES
            - MARKET_NOT_FOUND
            - MARKET_NOT_ACTIVE
            - PRICE_IMPACT_EXCEEDED
            - ORDER_NOT_FOUND
            - ORDER_NOT_AVAILABLE
            - CANNOT_FILL_OWN_ORDER
            - NO_POSITION
            - INTERNAL_ERROR

    BotRegistrationResponse:
      type: object
      properties:
        bot:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            apiKey:
              type: string
              description: Only shown once at registration
            country:
              type: string
        wallet:
          type: object
          properties:
            balance:
              type: number
            type:
              type: string
              example: cash
        deposit:
          type: object
          properties:
            address:
              type: string
            network:
              type: string
            token:
              type: string
            minDeposit:
              type: number

    AccountResponse:
      type: object
      properties:
        bot:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            country:
              type: string
            totalTrades:
              type: integer
            totalVolume:
              type: number
            lastActiveAt:
              type: string
              format: date-time
        wallet:
          type: object
          properties:
            balance:
              type: number
            type:
              type: string
              example: cash

    Market:
      type: object
      properties:
        id:
          type: string
        startupName:
          type: string
        question:
          type: string
        description:
          type: string
        category:
          type: string
        currentPrice:
          type: number
          description: Current price as percentage (0-100)
        yesPrice:
          type: number
          description: YES share price (0-1)
        noPrice:
          type: number
          description: NO share price (0-1)
        totalVolume:
          type: number
        liquidity:
          type: number
        resolutionDate:
          type: string
          format: date-time
        status:
          type: string
          enum: [ACTIVE, RESOLVED, CANCELLED]

    MarketsListResponse:
      type: object
      properties:
        markets:
          type: array
          items:
            $ref: '#/components/schemas/Market'
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer

    TradeRequest:
      type: object
      required: [side, amount]
      properties:
        side:
          type: string
          enum: [YES, NO]
        amount:
          type: number
          minimum: 1
          description: USD amount to spend

    QuoteResponse:
      type: object
      properties:
        quote:
          type: object
          properties:
            side:
              type: string
            amount:
              type: number
            shares:
              type: number
            pricePerShare:
              type: number
            fee:
              type: number
            totalCost:
              type: number
            newMarketPrice:
              type: number
            priceImpact:
              type: number
            maxWin:
              type: number
            maxLoss:
              type: number
        market:
          type: object
          properties:
            currentPrice:
              type: number
            liquidity:
              type: number
            totalVolume:
              type: number
        wallet:
          type: object
          properties:
            balance:
              type: number
            balanceAfterTrade:
              type: number
            hasSufficientFunds:
              type: boolean
            type:
              type: string

    TradeResponse:
      type: object
      properties:
        trade:
          type: object
          properties:
            transactionId:
              type: string
            side:
              type: string
            shares:
              type: number
            pricePerShare:
              type: number
            fee:
              type: number
            totalCost:
              type: number
            newMarketPrice:
              type: number
            priceImpact:
              type: number
        position:
          type: object
          properties:
            id:
              type: string
            sharesYes:
              type: number
            sharesNo:
              type: number
            averageCostYes:
              type: number
            averageCostNo:
              type: number
        wallet:
          type: object
          properties:
            balanceBefore:
              type: number
            balanceAfter:
              type: number
            type:
              type: string

    PositionsResponse:
      type: object
      properties:
        positions:
          type: array
          items:
            type: object
        derivatives:
          type: array
          items:
            type: object
        summary:
          type: object
          properties:
            positionCount:
              type: integer
            derivativeCount:
              type: integer
            totalPositionValue:
              type: number
            totalDerivativeValue:
              type: number
            totalPortfolioValue:
              type: number
            totalUnrealizedPnL:
              type: number

    DerivativeRequest:
      type: object
      required: [primaryMarketId, direction, spreadPercent, timeframeDays, side, amount]
      properties:
        primaryMarketId:
          type: string
        direction:
          type: string
          enum: [above, below]
        spreadPercent:
          type: number
          minimum: 1
          maximum: 50
        timeframeDays:
          type: integer
          minimum: 1
          maximum: 90
        side:
          type: string
          enum: [YES, NO]
        amount:
          type: number
          minimum: 1

    CreateOrderRequest:
      type: object
      required: [marketId, side, shares, pricePerShare]
      properties:
        marketId:
          type: string
          description: Primary market ID
        side:
          type: string
          enum: [YES, NO]
          description: Which shares to sell
        shares:
          type: number
          minimum: 0.01
          description: Number of shares to sell
        pricePerShare:
          type: number
          minimum: 0.01
          maximum: 0.99
          description: Price per share

    OrderBookResponse:
      type: object
      properties:
        orders:
          type: array
          items:
            $ref: '#/components/schemas/Order'
        orderBook:
          type: object
          properties:
            yes:
              type: array
              items:
                $ref: '#/components/schemas/Order'
            no:
              type: array
              items:
                $ref: '#/components/schemas/Order'
        summary:
          type: object
          properties:
            totalOrders:
              type: integer
            yesOrders:
              type: integer
            noOrders:
              type: integer
            bestYesPrice:
              type: number
            bestNoPrice:
              type: number

    Order:
      type: object
      properties:
        id:
          type: string
        side:
          type: string
          enum: [YES, NO]
        shares:
          type: number
        sharesRemaining:
          type: number
        pricePerShare:
          type: number
        totalValue:
          type: number
        status:
          type: string
          enum: [OPEN, PARTIALLY_FILLED, FILLED, CANCELLED]
        isOwn:
          type: boolean
        createdAt:
          type: string
          format: date-time

    OrderResponse:
      type: object
      properties:
        order:
          $ref: '#/components/schemas/Order'
        message:
          type: string

    OrderDetailResponse:
      type: object
      properties:
        order:
          allOf:
            - $ref: '#/components/schemas/Order'
            - type: object
              properties:
                sharesFilled:
                  type: number
                fills:
                  type: array
                  items:
                    type: object
                    properties:
                      id:
                        type: string
                      shares:
                        type: number
                      pricePerShare:
                        type: number
                      totalAmount:
                        type: number
                      filledAt:
                        type: string
                        format: date-time

    OrderFillResponse:
      type: object
      properties:
        fill:
          type: object
          properties:
            orderId:
              type: string
            shares:
              type: number
            pricePerShare:
              type: number
            totalCost:
              type: number
            side:
              type: string
        order:
          type: object
          properties:
            id:
              type: string
            status:
              type: string
            sharesRemaining:
              type: number
        position:
          type: object
          properties:
            market:
              type: string
            side:
              type: string
            sharesAdded:
              type: number
        wallet:
          type: object
          properties:
            balanceBefore:
              type: number
            balanceAfter:
              type: number
            type:
              type: string

    MarketHistoryResponse:
      type: object
      properties:
        market:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            question:
              type: string
            currentPrice:
              type: number
            status:
              type: string
            totalVolume:
              type: number
            liquidity:
              type: number
            resolutionDate:
              type: string
              format: date-time
            createdAt:
              type: string
              format: date-time
        trades:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              type:
                type: string
              side:
                type: string
                enum: [YES, NO]
              action:
                type: string
                enum: [buy, sell]
              amount:
                type: number
              shares:
                type: number
              pricePerShare:
                type: number
              timestamp:
                type: string
                format: date-time
        priceHistory:
          type: array
          items:
            type: number
          description: Array of historical price points (0-100)
        volumeStats:
          type: object
          properties:
            daily:
              type: object
              properties:
                volume:
                  type: number
                trades:
                  type: integer
            weekly:
              type: object
              properties:
                volume:
                  type: number
                trades:
                  type: integer
            monthly:
              type: object
              properties:
                volume:
                  type: number
                trades:
                  type: integer
        pagination:
          type: object
          properties:
            total:
              type: integer
            limit:
              type: integer
            offset:
              type: integer
            hasMore:
              type: boolean
