> ## Documentation Index
> Fetch the complete documentation index at: https://docs.compasslabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Model impermanent loss

> What this position would do if the price moved — and how likely that is.

A fee APR on a memecoin pair means nothing without the loss it is paid
against, which is what this measures.

Each scenario takes **two** independent moves, and keeping them apart is the
point. `ratio_move_pct` is how far the pair's ratio travels, and that is
what causes impermanent loss. `quote_usd_move_pct` is how far the stock
moves in dollars, which scales what the position is worth without touching
the ratio at all — a stock-only move produces an impermanent loss of exactly
zero. "NVDA drops 10%" and "the memecoin drops 10% against NVDA" are
different questions, and one number cannot ask both.

The two most useful columns are `breakeven_fee_apr_pct` — what this position
would have to earn for the loss to be worth taking, which you can compare
against the pool's actual fee APR — and `days_of_fees_to_recover_il`, which
is null when the pool pays liquidity providers nothing, because no amount of
time fixes that.

`probabilistic` runs the pair's own measured volatility through a driftless
simulation. Driftless deliberately: nobody knows which way a memecoin goes,
and a model that assumed one would be predicting returns rather than
measuring risk. It is absent when the pair has no measured volatility —
which a pool with a few hours of history does not — because a distribution
built on zero would report that nothing can happen.



## OpenAPI

````yaml /v2/combined_spec.json post /v2/risk_yield/risk/scenarios
openapi: 3.1.0
info:
  title: Compass API
  description: Compass Labs DeFi API
  version: 0.0.1
servers:
  - url: https://api.compasslabs.ai
    description: Production server
security:
  - ApiKeyAuth: []
paths:
  /v2/risk_yield/risk/scenarios:
    post:
      tags:
        - Risk Yield
      summary: Model impermanent loss
      description: >-
        What this position would do if the price moved — and how likely that is.


        A fee APR on a memecoin pair means nothing without the loss it is paid

        against, which is what this measures.


        Each scenario takes **two** independent moves, and keeping them apart is
        the

        point. `ratio_move_pct` is how far the pair's ratio travels, and that is

        what causes impermanent loss. `quote_usd_move_pct` is how far the stock

        moves in dollars, which scales what the position is worth without
        touching

        the ratio at all — a stock-only move produces an impermanent loss of
        exactly

        zero. "NVDA drops 10%" and "the memecoin drops 10% against NVDA" are

        different questions, and one number cannot ask both.


        The two most useful columns are `breakeven_fee_apr_pct` — what this
        position

        would have to earn for the loss to be worth taking, which you can
        compare

        against the pool's actual fee APR — and `days_of_fees_to_recover_il`,
        which

        is null when the pool pays liquidity providers nothing, because no
        amount of

        time fixes that.


        `probabilistic` runs the pair's own measured volatility through a
        driftless

        simulation. Driftless deliberately: nobody knows which way a memecoin
        goes,

        and a model that assumed one would be predicting returns rather than

        measuring risk. It is absent when the pair has no measured volatility —

        which a pool with a few hours of history does not — because a
        distribution

        built on zero would report that nothing can happen.
      operationId: v2_risk_yield_risk_scenarios
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RiskScenariosRequest'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RiskScenariosResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      x-codeSamples:
        - lang: python
          label: Python (SDK)
          source: |-
            from compass_api_sdk import CompassAPI, models


            with CompassAPI(
                api_key_auth="<YOUR_API_KEY_HERE>",
            ) as compass_api:

                res = compass_api.risk_yield.risk_yield_risk_scenarios(pool={
                    "pool_id": 1,
                }, range={
                    "type": "symmetric_pct",
                    "width_pct": "30",
                }, deposit={
                    "type": "usd",
                    "value_usd": "1000",
                }, chain=models.RiskScenariosRequestChain.ROBINHOOD, horizon_days=30, scenarios=[
                    {
                        "name": "meme halves",
                        "ratio_move_pct": "-50",
                    },
                    {
                        "name": "meme doubles",
                        "ratio_move_pct": "100",
                    },
                    {
                        "name": "stock falls 10%",
                        "quote_usd_move_pct": "-10",
                    },
                ], monte_carlo=True, n_paths=2000, seed=0)

                # Handle response
                print(res)
        - lang: typescript
          label: Typescript (SDK)
          source: |-
            import { CompassApiSDK } from "@compass-labs/api-sdk";

            const compassApiSDK = new CompassApiSDK({
              apiKeyAuth: "<YOUR_API_KEY_HERE>",
            });

            async function run() {
              const result = await compassApiSDK.riskYield.riskYieldRiskScenarios({
                chain: "robinhood",
                pool: {
                  poolId: 1,
                },
                range: {
                  type: "symmetric_pct",
                  widthPct: "30",
                },
                deposit: {
                  type: "usd",
                  valueUsd: "1000",
                },
                horizonDays: 30,
                scenarios: [
                  {
                    name: "meme halves",
                    ratioMovePct: "-50",
                  },
                  {
                    name: "meme doubles",
                    ratioMovePct: "100",
                  },
                  {
                    name: "stock falls 10%",
                    quoteUsdMovePct: "-10",
                  },
                ],
              });

              console.log(result);
            }

            run();
components:
  schemas:
    RiskScenariosRequest:
      properties:
        chain:
          type: string
          enum:
            - robinhood
          title: Chain
          default: robinhood
          examples:
            - robinhood
        pool:
          $ref: '#/components/schemas/PoolSelector'
          default:
            pool_id: 1
          examples:
            - pool_id: 1
        range:
          oneOf:
            - $ref: '#/components/schemas/SymmetricRange'
            - $ref: '#/components/schemas/TickRange'
            - $ref: '#/components/schemas/PriceRange'
            - $ref: '#/components/schemas/FullRange'
          title: Range
          discriminator:
            propertyName: type
            mapping:
              full:
                $ref: '#/components/schemas/FullRange'
              prices:
                $ref: '#/components/schemas/PriceRange'
              symmetric_pct:
                $ref: '#/components/schemas/SymmetricRange'
              ticks:
                $ref: '#/components/schemas/TickRange'
          default:
            type: symmetric_pct
            width_pct: '30'
          examples:
            - type: symmetric_pct
              width_pct: '30'
        deposit:
          oneOf:
            - $ref: '#/components/schemas/BothSidesDeposit'
            - $ref: '#/components/schemas/SingleSidedDeposit'
            - $ref: '#/components/schemas/UsdDeposit'
          title: Deposit
          discriminator:
            propertyName: type
            mapping:
              both:
                $ref: '#/components/schemas/BothSidesDeposit'
              single:
                $ref: '#/components/schemas/SingleSidedDeposit'
              usd:
                $ref: '#/components/schemas/UsdDeposit'
          default:
            type: usd
            value_usd: '1000'
          examples:
            - type: usd
              value_usd: '1000'
        horizon_days:
          type: integer
          maximum: 365
          minimum: 1
          title: Horizon Days
          default: 30
          examples:
            - 30
        scenarios:
          items:
            $ref: '#/components/schemas/ScenarioInput'
          type: array
          maxItems: 50
          title: Scenarios
          description: Leave empty for a default spread of moves.
          default:
            - name: meme halves
              ratio_move_pct: '-50'
            - name: meme doubles
              ratio_move_pct: '100'
            - name: stock falls 10%
              quote_usd_move_pct: '-10'
          examples:
            - - name: meme halves
                ratio_move_pct: '-50'
              - name: meme doubles
                ratio_move_pct: '100'
              - name: stock falls 10%
                quote_usd_move_pct: '-10'
        fee_apr_pct_override:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
            - type: 'null'
          title: Fee Apr Pct Override
          description: Assume this fee rate instead of the pool's measured one.
        vol_override_pct:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
            - type: 'null'
          title: Vol Override Pct
          description: >-
            Assume this annualized volatility instead of the pair's measured
            one. Needed for a pool too new to have measured any.
        monte_carlo:
          type: boolean
          title: Monte Carlo
          description: Also estimate how likely each outcome is, not just its size.
          default: true
        n_paths:
          type: integer
          maximum: 10000
          minimum: 100
          title: N Paths
          default: 2000
        seed:
          type: integer
          title: Seed
          description: Fixed so the same request gives the same answer.
          default: 0
      type: object
      required:
        - pool
        - range
        - deposit
      title: RiskScenariosRequest
      description: Ask what a position would do under a set of hypotheticals.
      default:
        chain: robinhood
        pool:
          pool_id: 1
        range:
          type: symmetric_pct
          width_pct: '30'
        deposit:
          type: usd
          value_usd: '1000'
        horizon_days: 30
        scenarios:
          - name: meme halves
            ratio_move_pct: '-50'
          - name: meme doubles
            ratio_move_pct: '100'
          - name: stock falls 10%
            quote_usd_move_pct: '-10'
    RiskScenariosResponse:
      properties:
        pool_id:
          anyOf:
            - type: integer
            - type: 'null'
          title: Pool Id
        range:
          $ref: '#/components/schemas/ResolvedRange'
        assumptions:
          $ref: '#/components/schemas/RiskAssumptions'
        scenarios:
          items:
            $ref: '#/components/schemas/ScenarioResult'
          type: array
          title: Scenarios
        probabilistic:
          anyOf:
            - $ref: '#/components/schemas/ProbabilisticOutlook'
            - type: 'null'
          description: >-
            Absent when the pair's volatility cannot be measured — a pool with a
            few hours of history has no measurable volatility, and a
            distribution built on zero would report that nothing can happen.
        warnings:
          items:
            type: string
          type: array
          title: Warnings
      type: object
      required:
        - range
        - assumptions
        - scenarios
      title: RiskScenariosResponse
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    PoolSelector:
      properties:
        pool_id:
          anyOf:
            - type: integer
            - type: 'null'
          title: Pool Id
          description: The pool's id, as returned by `GET /pools`.
        token0:
          anyOf:
            - type: string
            - type: 'null'
          title: Token0
          description: Lower-sorting token address.
        token1:
          anyOf:
            - type: string
            - type: 'null'
          title: Token1
          description: Higher-sorting token address.
        fee_ppm:
          anyOf:
            - type: integer
              maximum: 1000000
              minimum: 0
            - type: 'null'
          title: Fee Ppm
          description: Fee tier in hundredths of a bip (3000 = 0.30%).
        tick_spacing:
          anyOf:
            - type: integer
            - type: 'null'
          title: Tick Spacing
          description: Required for v4, where it is part of the pool key.
        hooks:
          anyOf:
            - type: string
            - type: 'null'
          title: Hooks
          description: v4 hook address; part of the pool key.
        dex_version:
          $ref: '#/components/schemas/DexVersion'
          default: V3
      type: object
      title: PoolSelector
      description: >-
        Which pool, by id or by its key.


        A pool id is stable and is what the list endpoints return. The long form

        exists so a caller who knows the pair can address a pool without a
        lookup —

        on a chain minting twenty thousand pools a day, a client should not have
        to

        search for one it just created.
    SymmetricRange:
      properties:
        type:
          type: string
          const: symmetric_pct
          title: Type
          default: symmetric_pct
        width_pct:
          anyOf:
            - type: number
              maximum: 1000
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Width Pct
          description: Half-width either side of the current price, in percent.
          examples:
            - '30'
      type: object
      required:
        - width_pct
      title: SymmetricRange
      description: >-
        A range centred on the current price, given as a percentage either side.


        The usual way to ask for a position: narrow earns more fees per dollar
        while

        the price stays inside it, and stops earning entirely when it leaves.
    TickRange:
      properties:
        type:
          type: string
          const: ticks
          title: Type
          default: ticks
        tick_lower:
          type: integer
          title: Tick Lower
        tick_upper:
          type: integer
          title: Tick Upper
      type: object
      required:
        - tick_lower
        - tick_upper
      title: TickRange
      description: Exact tick bounds, for a caller who has already done the maths.
    PriceRange:
      properties:
        type:
          type: string
          const: prices
          title: Type
          default: prices
        price_lower:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Price Lower
        price_upper:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Price Upper
      type: object
      required:
        - price_lower
        - price_upper
      title: PriceRange
      description: Bounds as prices (token1 per token0, decimals-adjusted).
    FullRange:
      properties:
        type:
          type: string
          const: full
          title: Type
          default: full
      type: object
      title: FullRange
      description: 'The whole price range: never out of range, and never concentrated.'
    BothSidesDeposit:
      properties:
        type:
          type: string
          const: both
          title: Type
          default: both
        amount0:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Amount0
        amount1:
          anyOf:
            - type: number
              minimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Amount1
      type: object
      required:
        - amount0
        - amount1
      title: BothSidesDeposit
      description: Explicit amounts of each token.
    SingleSidedDeposit:
      properties:
        type:
          type: string
          const: single
          title: Type
          default: single
        token:
          type: string
          title: Token
        amount:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Amount
      type: object
      required:
        - token
        - amount
      title: SingleSidedDeposit
      description: >-
        One token, split to fit the range.


        A range needs both assets in a ratio the range itself determines, so
        this

        asks the API to solve for that ratio and swap the difference.
    UsdDeposit:
      properties:
        type:
          type: string
          const: usd
          title: Type
          default: usd
        value_usd:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Value Usd
      type: object
      required:
        - value_usd
      title: UsdDeposit
      description: |-
        A dollar figure, for analytics only.

        Enough to answer "what would this look like", not enough to build a
        transaction — the API will not choose which of your tokens to spend.
    ScenarioInput:
      properties:
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: What to call this row.
        ratio_move_pct:
          anyOf:
            - type: number
              exclusiveMinimum: -100
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
            - type: 'null'
          title: Ratio Move Pct
          description: How far the pair's ratio moves. This is what drives loss.
        base_usd_move_pct:
          anyOf:
            - type: number
              exclusiveMinimum: -100
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
            - type: 'null'
          title: Base Usd Move Pct
          description: >-
            How far token0 moves in dollars, independently. Mutually exclusive
            with `ratio_move_pct`.
        quote_usd_move_pct:
          anyOf:
            - type: number
              exclusiveMinimum: -100
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
          title: Quote Usd Move Pct
          description: >-
            How far token1 — the stock, on a meme x stock pair — moves in
            dollars. On its own this changes what the position is worth without
            causing any impermanent loss at all.
          default: '0'
      type: object
      title: ScenarioInput
      description: >-
        One hypothetical.


        The two moves are deliberately separate. On a meme-against-stock pair
        the

        ratio between them is what causes impermanent loss, while the stock's
        own

        dollar move scales the whole position without touching the ratio — so
        "NVDA

        drops 10%" and "the memecoin drops 10% against NVDA" are different

        questions with different answers, and one number cannot ask both.
    ResolvedRange:
      properties:
        tick_lower:
          type: integer
          title: Tick Lower
        tick_upper:
          type: integer
          title: Tick Upper
        price_lower:
          type: string
          title: Price Lower
        price_upper:
          type: string
          title: Price Upper
        width_lower_pct:
          type: string
          title: Width Lower Pct
          description: How far below the current price the lower bound sits.
        width_upper_pct:
          type: string
          title: Width Upper Pct
        is_full_range:
          type: boolean
          title: Is Full Range
      type: object
      required:
        - tick_lower
        - tick_upper
        - price_lower
        - price_upper
        - width_lower_pct
        - width_upper_pct
        - is_full_range
      title: ResolvedRange
      description: >-
        A range after the API has snapped it to the pool's tick spacing.


        Returned because the range you asked for is rarely the range you get:
        ticks

        are discrete, and a 30% band becomes whichever usable ticks bracket it.
    RiskAssumptions:
      properties:
        entry_price:
          type: string
          title: Entry Price
        price0_usd:
          anyOf:
            - type: string
            - type: 'null'
          title: Price0 Usd
        price1_usd:
          anyOf:
            - type: string
            - type: 'null'
          title: Price1 Usd
        price_source0:
          anyOf:
            - type: string
            - type: 'null'
          title: Price Source0
        price_source1:
          anyOf:
            - type: string
            - type: 'null'
          title: Price Source1
        prices_updated_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Prices Updated At
        deposit_value_usd:
          type: string
          title: Deposit Value Usd
        horizon_days:
          type: integer
          title: Horizon Days
        annual_vol_pct:
          anyOf:
            - type: string
            - type: 'null'
          title: Annual Vol Pct
        vol_source:
          type: string
          title: Vol Source
          description: '`measured`, `override`, or `unavailable`.'
          default: measured
        fee_apr_pct:
          anyOf:
            - type: string
            - type: 'null'
          title: Fee Apr Pct
        fee_source:
          type: string
          title: Fee Source
          default: measured
        fees_per_day_usd_pool:
          anyOf:
            - type: string
            - type: 'null'
          title: Fees Per Day Usd Pool
        share_of_active_liquidity_pct:
          anyOf:
            - type: string
            - type: 'null'
          title: Share Of Active Liquidity Pct
        lp_earns_fees:
          type: boolean
          title: Lp Earns Fees
          default: true
      type: object
      required:
        - entry_price
        - deposit_value_usd
        - horizon_days
      title: RiskAssumptions
      description: >-
        What the answers rest on.


        Returned in full because every number below is only as good as these,
        and a

        caller who disagrees with one can override it and ask again.
    ScenarioResult:
      properties:
        name:
          type: string
          title: Name
        price_ratio:
          type: string
          title: Price Ratio
        end_price:
          type: string
          title: End Price
        in_range_at_end:
          type: boolean
          title: In Range At End
        position_value_usd:
          type: string
          title: Position Value Usd
        hold_value_usd:
          type: string
          title: Hold Value Usd
        il_usd:
          type: string
          title: Il Usd
        il_pct:
          type: string
          title: Il Pct
        hold_pnl_usd:
          type: string
          title: Hold Pnl Usd
        expected_fees_usd:
          type: string
          title: Expected Fees Usd
        net_pnl_usd:
          type: string
          title: Net Pnl Usd
          description: Hold profit, plus the impermanent loss, plus the fees.
        breakeven_fee_apr_pct:
          anyOf:
            - type: string
            - type: 'null'
          title: Breakeven Fee Apr Pct
          description: >-
            What this position would have to earn, annualized, for the loss to
            be worth taking. The number to compare against the pool's actual fee
            APR.
        days_of_fees_to_recover_il:
          anyOf:
            - type: string
            - type: 'null'
          title: Days Of Fees To Recover Il
          description: >-
            How long the pool's current fee rate would take to earn the loss
            back. Null when the pool pays liquidity providers nothing, which no
            amount of time fixes.
      type: object
      required:
        - name
        - price_ratio
        - end_price
        - in_range_at_end
        - position_value_usd
        - hold_value_usd
        - il_usd
        - il_pct
        - hold_pnl_usd
        - expected_fees_usd
        - net_pnl_usd
      title: ScenarioResult
    ProbabilisticOutlook:
      properties:
        method:
          type: string
          title: Method
        paths:
          type: integer
          title: Paths
        p_exit_within_horizon_pct:
          type: string
          title: P Exit Within Horizon Pct
          description: >-
            The chance the price *touches* a bound at any point, not just ends
            outside it. A position that left the range and came back still
            stopped earning while it was out.
        p_out_at_horizon_pct:
          type: string
          title: P Out At Horizon Pct
        expected_time_in_range_pct:
          type: string
          title: Expected Time In Range Pct
        expected_il_usd:
          type: string
          title: Expected Il Usd
        expected_net_pnl_usd:
          type: string
          title: Expected Net Pnl Usd
        prob_net_loss_pct:
          type: string
          title: Prob Net Loss Pct
        il_pct_percentiles:
          additionalProperties:
            type: string
          type: object
          title: Il Pct Percentiles
        il_usd_percentiles:
          additionalProperties:
            type: string
          type: object
          title: Il Usd Percentiles
        net_pnl_usd_percentiles:
          additionalProperties:
            type: string
          type: object
          title: Net Pnl Usd Percentiles
      type: object
      required:
        - method
        - paths
        - p_exit_within_horizon_pct
        - p_out_at_horizon_pct
        - expected_time_in_range_pct
        - expected_il_usd
        - expected_net_pnl_usd
        - prob_net_loss_pct
        - il_pct_percentiles
        - il_usd_percentiles
        - net_pnl_usd_percentiles
      title: ProbabilisticOutlook
      description: How likely the outcomes are, not just how large.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    DexVersion:
      type: string
      enum:
        - V3
        - V4
      title: DexVersion
      description: >-
        Which Uniswap deployment a pool belongs to.


        v3 pools hold their own tokens and pay LPs the fee tier. v4 pools live
        in a

        shared PoolManager and may route the fee through a hook, which is where
        the

        launchpad pools' zero LP yield comes from.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Your Compass API Key. Get your key
        [here](https://www.compasslabs.ai/dashboard).

````