{
  "openapi": "3.1.1",
  "info": {
    "title": "AVM Trade Reporter API",
    "description": "# AVM Trade Reporter – Developer Guide\n\n## Authentication (ARC-0014)\n\nMost endpoints in this API require **[ARC-0014](https://github.com/algorandfoundation/ARCs/issues/42)**\nauthentication (\"Algorand transaction signature as authentication\"). Instead of a username/password or\nlong-lived API key, the client signs a zero-fee, unbroadcast `NoOp`/payment-shaped Algorand transaction\nwhose `Note` field encodes a realm and expiry, then sends the signed transaction as the `Authorization`\nheader. The server verifies the signature and transaction validity window against Algorand node\ntransaction params — no signed transaction is ever submitted to the chain, it is purely a proof of key\nownership.\n\n- Realm for this API: `BiatecScan#ARC14`\n- Header: `Authorization: SigTx <base64-encoded-signed-transaction>` (produced by the libraries below)\n- The signed transaction's validity window (`firstValid`/`lastValid`) doubles as the token's expiry —\n  there is no separate refresh step; the client just signs a new transaction with a fresh validity\n  window when the old one expires\n- Endpoints marked **\"no authentication required\"** below are intentionally public and accept\n  unauthenticated requests\n\n### Recommended client libraries\n\n- **JavaScript/TypeScript**: [`arc14`](https://www.npmjs.com/package/arc14) (builds and signs the\n  ARC-14 transaction, produces the `Authorization` header value) together with\n  [`arc76`](https://www.npmjs.com/package/arc76) (deterministic Algorand keypair derivation, if the\n  client needs a stable identity without a wallet) and [`algosdk`](https://www.npmjs.com/package/algosdk)\n  for transaction params/signing primitives. Example:\n  ```ts\n  import algosdk from \"algosdk\";\n  import { makeArc14AuthHeader, makeArc14TxWithSuggestedParams } from \"arc14\";\n\n  const tx = await makeArc14TxWithSuggestedParams(\"BiatecScan#ARC14\", account.addr.toString(), suggestedParams);\n  const signed = tx.signTxn(account.sk);\n  const authorizationHeader = makeArc14AuthHeader(signed);\n  // send as: Authorization: <authorizationHeader>\n  ```\n- **.NET**: server-side verification here uses the\n  [`AlgorandAuthentication`](https://www.nuget.org/packages/AlgorandAuthentication) NuGet package\n  (see `AlgorandAuthenticationHandlerV2` in `Program.cs`); .NET clients can build the equivalent\n  ARC-14 signed transaction directly with the [`Algorand4`](https://www.nuget.org/packages/Algorand4)\n  SDK (construct a transaction with `Note` set to the realm, sign it with the account's private key,\n  base64-encode it into the `Authorization` header) — there is no separate client-only package needed\n  since the same SDK used to sign is already an `AVMTradeReporter` dependency.\n\nShort overview\n- ASP.NET Core (.NET 10) Web API + SignalR service for streaming Algorand DEX activity (trades, liquidity, pools, blocks)\n- Ingests data from Algod/Gossip, enriches via protocol-specific processors (Pact, Tiny, Biatec)\n- Indexes documents in Elasticsearch and broadcasts live updates to clients via SignalR\n- Optional Redis cache for assets and pools, plus background services for continuous operation\n\nKey capabilities\n- Live SignalR hub at /biatecScanHub with recent updates and subscription-based filtering\n- REST API for pool discovery and stats\n- Background block processing and optional periodic pool refresh\n- Swagger at /swagger using this document as the top-level description\n\nArchitecture\n- Web/API: Program.cs configures controllers, Swagger, CORS, authentication, SignalR\n- Hubs: Hubs/BiatecScanHub.cs exposes methods, streams events, and manages client subscriptions\n- Background services\n  - TradeReporterBackgroundService: processes blocks, extracts trades and liquidity, updates Elasticsearch, publishes to hub\n  - GossipBackgroundService: handles gossip websocket feed (see configuration)\n  - PoolRefreshBackgroundService: optional periodic re-enrichment of pools\n- Repositories\n  - TradeRepository: Elasticsearch storage for trades, hub broadcast\n  - LiquidityRepository: storage and hub broadcast for liquidity updates\n  - PoolRepository and AggregatedPoolRepository: pool storage, enrichment, Redis integration, statistics\n  - AssetRepository: asset metadata via Algod with optional Redis cache\n  - BlockRepository: hub publishing for new blocks\n  - IndexerRepository: progress tracking (round, genesis)\n  - StatsRepository: read-only Elasticsearch sum aggregations for DefiLlama DEX stats export\n- Processors (protocol-specific pool logic): PactPoolProcessor, TinyPoolProcessor, BiatecPoolProcessor\n- Models: see AVMTradeReporter/Model/* (Trade, Liquidity, Pool, AggregatedPool, Block, configuration types)\n\nData flow (high level)\n1) TradeReporterBackgroundService initializes pool cache, loads indexer progress\n2) Iterates blocks from Algod (with optional fallbacks), processes transactions\n3) Detects trades/liquidity/pool updates, enriches pools using protocol processors when needed\n4) Stores documents in Elasticsearch (data stream/index per type)\n5) Publishes recent updates to SignalR hub for clients\n\nConfiguration\nThe app reads AppConfiguration and related sections from appsettings.json (or environment-specific variants).\n\nAppConfiguration\n- IndexerId: string unique identifier for this indexer instance\n- DelayMs: optional processing delay in milliseconds\n- Direction: \"+\" or \"-\" to move forward or backward through rounds\n- StartRound, MinRound, MaxRound: optional round bounds\n- Algod, Algod2, Algod3: AlgodConfiguration endpoints (Host, ApiKey, Header)\n- Elastic: ElasticConfiguration (Host, ApiKey)\n- GossipWebsocketClientConfigurations: list of websocket peers (if used by gossip service). If the first entry's Host is empty (e.g. left unset via `AppConfiguration__GossipWebsocketClientConfigurations__0__Host`), the gossip service instead discovers all relays via DNS SRV, connects to all of them, and keeps only the fastest ones connected per GossipDiscovery.\n- GossipDiscovery: dynamic relay discovery/ranking used when no gossip relay is statically configured\n  - MaxActiveRelayConnections: how many relays to stay connected to once ranked (default 10)\n  - WarmUpSeconds: how long to race all discovered relays before pruning down (default 120)\n  - ReEvaluationIntervalSeconds: how often to check active relays' health once pruned (default 300)\n  - StaleRelaySeconds: a connected relay silent for longer than this is replaced with a fresh candidate (default 600)\n- Redis: RedisConfiguration\n  - ConnectionString: e.g., localhost:6379\n  - KeyPrefix: default avmtrade:pools:\n  - Enabled: true/false\n  - DatabaseId: integer DB id\n- PoolRefresh: PoolRefreshConfiguration\n  - Enabled: enable background refresh\n  - IntervalHours: cadence of full refresh\n  - DelayBetweenPoolsSeconds: throttle between pool refreshes\n  - InitialDelayMinutes: startup delay before first cycle\n\nCORS\n- Configure allowed origins under the Cors section; the app will fail startup if not provided\n\nAuthentication\n- Uses AlgorandAuthenticationV2 per Program.cs; see the \"Authentication (ARC-0014)\" section at the top of this document for how to generate a token as a client\n- Swagger defines an API key scheme named arc14 (ARC-0014 Algorand authentication transaction transmitted in Authorization header)\n- The SignalR pipeline moves access_token from query string to Authorization header for compatibility\n- All REST endpoints require authentication ([Authorize]) except the following, which are intentionally public:\n  - GET /api/asset (asset list/lookup; the TradingView charting widget resolves asset id -> ticker via this endpoint with a plain unauthenticated fetch and has no ARC-14 signing capability - see AVMTradeReporterTests/Controllers/AssetControllerAuthorizationTests.cs)\n  - GET /api/asset/image/{assetId} (asset image, embedded directly as an <img> src)\n  - GET /api/Gossip/status (relay connectivity health check)\n  - GET /api/Stats/dex (DefiLlama DEX stats adapter integration)\n  - GET /api/OHLC/* (TradingView UDF charting datafeed: config, time, symbols, symbol_info, search, marks, timescale_marks, quotes, history)\n  - GET /api/coingecko/* (GeckoTerminal / CoinGecko DEX integration: latest-block, asset, pair, events - polled by CoinGecko's indexer, which cannot sign ARC-14 tokens)\n\nElasticsearch\n- Client configured with ApiKey authentication and default mappings\n- TradeRepository creates a template for trades-* as a data stream\n- Mappings configured in Program.cs for:\n  - Trade -> index \"trades\"\n  - Liquidity -> index \"liquidity\"\n  - AggregatedPool -> index \"liquidity\" (aggregates)\n  - Pool -> index \"pools\"\n  - Indexer -> index \"indexers\"\n\nSignalR hub (/biatecScanHub)\nClient methods to call\n- TestConnection(): returns connection/auth context\n- Subscribe(filter): authorized; sets a user-specific filter string\n- Unsubscribe(): authorized; clears subscription\n\nServer events you can receive\n- Block: latest block metadata\n- FilteredTradeUpdated: trade that passed your subscription filter\n- FilteredLiquidityUpdated: liquidity event that passed your filter\n- PoolUpdated: pool metadata update\n- AggregatedPoolUpdated: aggregated pool update\n- TestMessage: from test controller broadcast endpoint\n- Subscribed / Unsubscribed: subscription acknowledgements\n\nFilter format examples\n- protocol:Biatec\n- asset:31566704\n- trader:ALGORAND_ACCOUNT\n- pool:123456\n- pooladdress:ALGORAND_ADDRESS\n- state:TxPool or Confirmed\n\nTypical JS client setup (pseudo)\n- new signalR.HubConnectionBuilder().withUrl(\"/biatecScanHub?access_token=ALG_AUTH_TOKEN\").build()\n- await connection.start()\n- await connection.invoke(\"Subscribe\", \"protocol:Biatec\")\n- connection.on(\"FilteredTradeUpdated\", payload => { /* handle */ })\n\nREST API\n- GET /api/pool\n  - Query: assetIdA, assetIdB, protocol (Pact|Tiny|Biatec), size (default 100)\n  - Returns list of pools (optionally filtered)\n- GET /api/pool/{poolAddress}\n  - Returns details for a specific pool address\n- GET /api/pool/stats\n  - Optional: assetIdA, assetIdB\n- GET /api/stats/dex (no authentication required, designed for DefiLlama adapters)\n  - Query: dex (Biatec|Pact|Tiny), timestamp (DateTimeOffset, inclusive window start), to (DateTimeOffset, optional, exclusive window end)\n  - Returns aggregated statistics for the given DEX protocol over [timestamp, to)\n  - When `to` is omitted, defaults to a full day: [timestamp, timestamp+1day) - preserves the original daily-aggregate behaviour for DefiLlama's v1 adapter\n  - An explicit `to` allows arbitrary (e.g. hourly) windows, for DefiLlama's v2 adapter model (`pullHourly: true`); 400 if `to` is not after `timestamp`\n  - Only confirmed trades are included in the aggregation\n  - Response fields: protocol, from, to, volumeUSD, feesUSD, feesLPUSD, feesProtocolUSD\n  - Powered by Elasticsearch sum aggregations on valueUSD, feesUSD, feesUSDProvider, feesUSDProtocol\n  - Example (daily): GET /api/stats/dex?dex=Biatec&timestamp=2024-01-15T00:00:00Z\n  - Example (hourly): GET /api/stats/dex?dex=Biatec&timestamp=2024-01-15T00:00:00Z&to=2024-01-15T01:00:00Z\n- GeckoTerminal / CoinGecko integration (no authentication required), implements \"GeckoTerminal Integration API Standards v0.1\" for the Biatec DEX\n  - GET /api/coingecko/latest-block -> `{block:{blockNumber, blockTimestamp}}`: the newest block for which every swap / join / exit is stored and searchable (watermark of fully processed blocks held back `CoinGecko.LatestBlockVisibilityDelaySeconds` for the Elasticsearch refresh; mirrored to Redis so every replica answers the same). 503 until known.\n  - GET /api/coingecko/asset?id=<assetId> -> `{asset:{id,name,symbol,decimals,totalSupply,metadata}}` (ALGO is id 0, supply decimalized). 404 for unknown/destroyed assets.\n  - GET /api/coingecko/pair?id=<poolAppId> -> `{pair:{id,dexKey,asset0Id,asset1Id,feeBps}}`; asset0 = pool asset A, asset1 = pool asset B (on-chain order, never changes). 404 for unknown / scam pools.\n  - GET /api/coingecko/events?fromBlock=&toBlock= -> `{events:[...]}`, both bounds inclusive, sorted by block, txnIndex, eventIndex. 400 when toBlock < fromBlock or the range exceeds `CoinGecko.MaxBlockSpan` (1000) / `MaxEventsPerRequest`; 503 (retryable) when toBlock is beyond this replica's latest-block.\n    - swap: `asset0In+asset1Out` or `asset1In+asset0Out` (amounts as the trader paid / received), `priceNative` = asset1 per asset0 of the executed swap, `reserves` after the swap, `metadata.fees{0|1}In` = input amount x pool LP fee.\n    - liquidity deposit -> `join`, withdrawal -> `exit` (`amount0`, `amount1`, `reserves`).\n    - amounts are decimalized strings (amount / 10^decimals; Biatec reserves are stored in a 1e9 scale), `txnId` is the top-level transaction id, `maker` the sender, `blockTimestamp` unix seconds.\n    - `txnIndex` / `eventIndex` are persisted by the block processor (`Trade.TxnIndex/EventIndex`); documents indexed before that get a deterministic synthetic position after the real ones of their block.\n    - events that cannot be expressed safely (unknown pool, zero amount, asset mismatch) are skipped and logged: GeckoTerminal halts indexing on an invalid event.\n  - Rate limit: `/api/coingecko/*` has its own bucket per client IP (`CoinGecko.RateLimitPerMinute`, default 1200/min) instead of the 60/min anonymous budget - the indexer polls every ~2 s.\n  - Failures are retryable: storage / lookup trouble answers 503 (`Retry-After: 2`), never a partial event list, and nothing partial is cached. `/asset` only answers for assets of published pools (404 only for a destroyed asset, 503 when it cannot be read right now).\n  - Efficiency: asset/pair come from the in-memory caches; events cost two blockId-filtered Elasticsearch queries (range bisection instead of deep pagination) and are then cached in memory + Redis (`CoinGecko.EventsCacheSeconds`) with concurrent identical requests collapsed. Only the protocols in `CoinGecko.Protocols` (default Biatec) are published.\n- Test utilities (development)\n  - GET /api/signalr/auth-test\n  - GET /api/signalr/auth-test-authorized (requires auth)\n  - POST /api/signalr/test-broadcast (requires auth)\n  - POST /api/signalr/test-trade (requires auth)\n\nPool enrichment and refresh\n- Pools updated from trades or liquidity may miss metadata (e.g., approval program hash)\n- PoolRepository can auto-enrich using protocol processors, with fallback between Tiny/Pact where appropriate\n- Optional PoolRefreshBackgroundService periodically refreshes all pools\n- See POOL_AUTO_ENRICHMENT.md and POOL_REFRESH_CONFIGURATION.md for deeper details\n\nRunning locally\nPrerequisites\n- .NET 10 SDK\n- Elasticsearch endpoint and API key\n- Algod endpoint(s) and API key(s)\n- Redis (optional but recommended)\n\nSteps\n- Configure appsettings.Development.json with AppConfiguration, Cors, AlgorandAuthentication\n- dotnet run in AVMTradeReporter\n- Open /swagger to explore the API; this document appears as the description\n\nNotes\n- GenerateDocumentationFile is enabled in AVMTradeReporter.csproj and Program.cs wires the generated\n  AVMTradeReporter.xml into Swagger via IncludeXmlComments, so /// XML doc comments on controllers and\n  actions are surfaced as operation summaries/descriptions in Swagger UI\n- Swagger is served as OpenAPI 3.1 (app.UseSwagger sets OpenApiVersion = OpenApi3_1 in Program.cs)\n- DockerDefaultTargetOS is Linux; containerization supported via standard ASP.NET Core tooling\n- Tests live in AVMTradeReporterTests (NUnit)\n\nTroubleshooting\n- If SignalR auth fails, ensure access_token query string contains the Algorand auth value and that CORS allows your origin\n- If startup fails due to CORS, add origins under Cors in configuration\n- If Elasticsearch calls fail, verify Host/ApiKey and network access\n- If Algod calls fail, verify Algod Host/ApiKey/Header and optional fallbacks Algod2/Algod3\n- Logs include detailed diagnostics for SignalR, authentication, and background processing\n",
    "version": "v1"
  },
  "paths": {
    "/api/aggregated-pool": {
      "get": {
        "tags": [
          "AggregatedPool"
        ],
        "summary": "Get all aggregated pools or filter by asset ids. An asset id filter matches the asset on either side of the pair.",
        "parameters": [
          {
            "name": "assetIdA",
            "in": "query",
            "description": "Optional asset filter; matches pairs containing this asset on either side",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdB",
            "in": "query",
            "description": "Optional asset filter; matches pairs containing this asset on either side",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of pools to skip (default: 0)",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Number of pools to return (default: 100)",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Server-side ordering (default: TVL) so that size/offset return the top items. Options: TVL, Volume1H, Volume24H, Volume7D, LastUpdated, PoolCount",
            "schema": {
              "default": 0,
              "$ref": "#/components/schemas/PoolOrderBy"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "Sort direction (default: Desc)",
            "schema": {
              "default": 0,
              "$ref": "#/components/schemas/SortDirection"
            }
          },
          {
            "name": "light",
            "in": "query",
            "description": "When true, omits the level1Pools/level2Pools collections from the response to reduce payload size",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AggregatedPool"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AggregatedPool"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AggregatedPool"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/aggregated-pool/reload": {
      "get": {
        "tags": [
          "AggregatedPool"
        ],
        "summary": "Update and retrieve aggregated pool",
        "parameters": [
          {
            "name": "assetIdA",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdB",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AggregatedPool"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AggregatedPool"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AggregatedPool"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/asset": {
      "get": {
        "tags": [
          "Asset"
        ],
        "summary": "List assets from the in-memory cache (prefilled from Redis, with Elasticsearch fallback) or filter by IDs / search term.\nIntentionally public (no authentication required): the TradingView charting widget\n(biatec-charting-widget, a separate unauthenticated browser client with no ARC-14 signing\ncapability) resolves an asset id to its ticker/symbol via this endpoint with a plain\nfetch. Same public/read-only trust level as the asset image endpoint and the OHLC\ncontroller below, which stayed public for the same reason.",
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "description": "Comma separated list of asset IDs to include. Missing IDs will be fetched on-demand.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Case-insensitive substring filter applied to asset name or unit name. Special case: utility returns utility tokens. Special case: stable returns the assets with stabilityIndex > 0.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of records to skip for pagination (default: 0).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Maximum number of results to return (default 100, max 500).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BiatecAsset"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BiatecAsset"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BiatecAsset"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/asset/image/{assetId}": {
      "get": {
        "tags": [
          "Asset"
        ],
        "summary": "Returns the cached PNG image for the given asset id. Intentionally public (no authentication\nrequired) so it can be embedded directly as an <img> src in browsers/clients without an\nARC-14 signed transaction.",
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "description": "Algorand asset id to fetch the image for.",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/api/asset-stat": {
      "get": {
        "tags": [
          "AssetStat"
        ],
        "summary": "Get backend-computed per-asset TVL/volume/fees/APR stats, optionally filtered by protocol.",
        "parameters": [
          {
            "name": "protocol",
            "in": "query",
            "description": "Optional protocol filter (Pact, Tiny, Biatec). When omitted, both the\n            combined (\"all protocols\") rows and the per-protocol rows are returned.",
            "schema": {
              "$ref": "#/components/schemas/DEXProtocol"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "description": "Optional sort field: TVLUSD, Volume24hUSD, Volume7dUSD, Apr24h, Apr7d. Defaults to TVLUSD.",
            "schema": {
              "type": "string",
              "default": "TVLUSD"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "Sort direction (default: Desc)",
            "schema": {
              "default": 0,
              "$ref": "#/components/schemas/SortDirection"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AssetStat"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AssetStat"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AssetStat"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/asset-stat/{assetId}": {
      "get": {
        "tags": [
          "AssetStat"
        ],
        "summary": "Get the stat row for a single asset, optionally scoped to a protocol.",
        "parameters": [
          {
            "name": "assetId",
            "in": "path",
            "description": "Asset id to look up",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "protocol",
            "in": "query",
            "description": "Optional protocol filter; omit for the combined (\"all protocols\") row.",
            "schema": {
              "$ref": "#/components/schemas/DEXProtocol"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AssetStat"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetStat"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetStat"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/asset/timeseries/7d": {
      "get": {
        "tags": [
          "AssetTimeseries"
        ],
        "summary": "Get the 7 day hourly timeseries (USD price OHLC and real TVL OHLC candles) for up to 100\nassets at once, for the sparkline/candle columns on the asset and pool list pages. Series\nare recomputed on a 1 hour cadence and served from the Redis cache in between, so this\nendpoint is cheap to call for a whole page of assets.",
        "parameters": [
          {
            "name": "assetIds",
            "in": "query",
            "description": "Comma separated list of asset IDs (0 = ALGO). Max 100 per request.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "description": "7 day hourly history for one asset: USD price OHLC candles (from trade OHLC aggregates) and\nreal TVL OHLC candles (from the hourly Redis TVL snapshots). Cached in Redis and refreshed on\na 1 hour cadence, so consumers may treat it as immutable within the hour.",
                    "$ref": "#/components/schemas/AssetTimeseries7D"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "description": "7 day hourly history for one asset: USD price OHLC candles (from trade OHLC aggregates) and\nreal TVL OHLC candles (from the hourly Redis TVL snapshots). Cached in Redis and refreshed on\na 1 hour cadence, so consumers may treat it as immutable within the hour.",
                    "$ref": "#/components/schemas/AssetTimeseries7D"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "description": "7 day hourly history for one asset: USD price OHLC candles (from trade OHLC aggregates) and\nreal TVL OHLC candles (from the hourly Redis TVL snapshots). Cached in Redis and refreshed on\na 1 hour cadence, so consumers may treat it as immutable within the hour.",
                    "$ref": "#/components/schemas/AssetTimeseries7D"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/coingecko/latest-block": {
      "get": {
        "tags": [
          "CoinGecko"
        ],
        "summary": "Latest block for which every event is available through `/events` (never ahead of the data).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoLatestBlockResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoLatestBlockResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoLatestBlockResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/coingecko/asset": {
      "get": {
        "tags": [
          "CoinGecko"
        ],
        "summary": "Asset (ASA) details with decimalized total supply.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "Asset id, ALGO is 0.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoAssetResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoAssetResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoAssetResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/coingecko/pair": {
      "get": {
        "tags": [
          "CoinGecko"
        ],
        "summary": "Pair (pool) details. asset0 / asset1 follow the on-chain asset A / asset B order and never change.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "Pool application id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoPairResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoPairResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoPairResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/coingecko/events": {
      "get": {
        "tags": [
          "CoinGecko"
        ],
        "summary": "Swap / join / exit events of the inclusive block range, sorted by transaction then event index.",
        "parameters": [
          {
            "name": "fromBlock",
            "in": "query",
            "description": "First block (inclusive).",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "toBlock",
            "in": "query",
            "description": "Last block (inclusive); must not exceed `latest-block` and the range is limited to `MaxBlockSpan` blocks.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoEventsResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoEventsResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CoinGeckoEventsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/Gossip/status": {
      "get": {
        "tags": [
          "Gossip"
        ],
        "summary": "Returns the set of gossip relays currently connected (or being connected to), along with how many\nmempool messages each has delivered and when it last delivered one.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GossipRelayStatus"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GossipRelayStatus"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GossipRelayStatus"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/indexer/status": {
      "get": {
        "tags": [
          "Indexer"
        ],
        "summary": "Gets the current indexer status",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Indexer"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Indexer"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Indexer"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/liquidity": {
      "get": {
        "tags": [
          "Liquidity"
        ],
        "summary": "Get liquidity updates with optional filtering and pagination.",
        "parameters": [
          {
            "name": "assetIdA",
            "in": "query",
            "description": "Filter by asset A ID. When used alone, matches liquidity where this asset is either A or B.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdB",
            "in": "query",
            "description": "Filter by asset B ID. When used with assetIdA, requires exact asset pair match.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "txId",
            "in": "query",
            "description": "Filter by transaction ID. Takes precedence over asset filters.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "poolAddress",
            "in": "query",
            "description": "Filter by pool escrow address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "poolAppId",
            "in": "query",
            "description": "Filter by pool application ID.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of records to skip for pagination (default: 0).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Maximum number of records to return (default: 100, max: 500).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Liquidity"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Liquidity"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Liquidity"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/OHLC/config": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Returns the TradingView UDF datafeed configuration (supported resolutions, exchanges, symbol types).",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/time": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Returns the current server time as a Unix timestamp, used by TradingView for datafeed sync.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/symbols": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Resolves a single symbol (e.g. \"31566704_0\") to its TradingView symbol info.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "description": "Underscore-separated asset id pair, e.g. \"assetA_assetB\".",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/symbol_info": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Resolves a comma separated group of symbols to their TradingView symbol info in bulk.",
        "parameters": [
          {
            "name": "group",
            "in": "query",
            "description": "Comma separated list of underscore-separated asset id pairs.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/search": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Searches for tradable symbols (asset pairs) matching a free-text query, for the TradingView symbol search box.",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "description": "Free-text search term (asset name/unit name).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Optional symbol type filter.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of results to return (default 30).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/marks": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Returns chart marks (annotations) for the TradingView datafeed. Currently always empty.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/timescale_marks": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Returns timescale marks for the TradingView datafeed. Currently always empty.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/quotes": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Returns last-quote snapshots for the given symbols, for the TradingView quotes API.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "description": "Comma separated list of underscore-separated asset id pairs.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/OHLC/history": {
      "get": {
        "tags": [
          "OHLC"
        ],
        "summary": "Returns OHLCV bars for an asset pair over a time range, for the TradingView history API.",
        "parameters": [
          {
            "name": "assetA",
            "in": "query",
            "description": "First asset id of the pair.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetB",
            "in": "query",
            "description": "Second asset id of the pair.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "resolution",
            "in": "query",
            "description": "TradingView resolution string (e.g. \"1\", \"60\", \"1D\").",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Range start, Unix timestamp (seconds).",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Range end, Unix timestamp (seconds).",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/pool": {
      "get": {
        "tags": [
          "Pool"
        ],
        "summary": "Get all pools or filter by protocol",
        "parameters": [
          {
            "name": "assetIdA",
            "in": "query",
            "description": "Filter by asset A ID. When used alone, matches pools where this asset is either A or B.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdB",
            "in": "query",
            "description": "Filter by asset B ID. When used with assetIdA, requires exact asset pair match.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "address",
            "in": "query",
            "description": "Filter by pool escrow address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "protocol",
            "in": "query",
            "description": "Optional protocol filter (Pact, Tiny, Biatec)",
            "schema": {
              "$ref": "#/components/schemas/DEXProtocol"
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Number of pools to return (default: 100)",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100
            }
          },
          {
            "name": "orderBy",
            "in": "query",
            "description": "Optional server-side ordering (TVL, Volume1H, Volume24H, Volume7D, LastUpdated). Defaults to LastUpdated (timestamp descending)",
            "schema": {
              "$ref": "#/components/schemas/PoolOrderBy"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "Sort direction (default: Desc)",
            "schema": {
              "default": 0,
              "$ref": "#/components/schemas/SortDirection"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Pool"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Pool"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Pool"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/pool/stats": {
      "get": {
        "tags": [
          "Pool"
        ],
        "summary": "Get pool statistics",
        "parameters": [
          {
            "name": "assetIdA",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdB",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/pool/reload": {
      "get": {
        "tags": [
          "Pool"
        ],
        "summary": "Get pool statistics",
        "parameters": [
          {
            "name": "protocol",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/DEXProtocol"
            }
          },
          {
            "name": "poolId",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "poolAddress",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/search": {
      "get": {
        "tags": [
          "Search"
        ],
        "summary": "Searches assets and pools matching the given query term.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search term (asset name, unit name, or pool address).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/signalr/auth-test": {
      "get": {
        "tags": [
          "SignalRTest"
        ],
        "summary": "Returns the caller's current authentication state and claims, for debugging ARC-14 auth.",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/signalr/auth-test-authorized": {
      "get": {
        "tags": [
          "SignalRTest"
        ],
        "summary": "Same as M:AVMTradeReporter.Controllers.SignalRTestController.AuthTest but forces authorization at the action level (redundant given the class-level [Authorize], kept explicit for clarity/testing).",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/signalr/test-broadcast": {
      "post": {
        "tags": [
          "SignalRTest"
        ],
        "summary": "Broadcasts a test info message to all connected SignalR clients.",
        "requestBody": {
          "description": "Free-text message to broadcast.",
          "content": {
            "application/json": {
              "schema": {
                "type": "string"
              }
            },
            "text/json": {
              "schema": {
                "type": "string"
              }
            },
            "application/*+json": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/signalr/test-trade": {
      "post": {
        "tags": [
          "SignalRTest"
        ],
        "summary": "Broadcasts a synthetic test trade to all connected SignalR clients, for verifying the trade event pipeline end to end.",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/signalr/connections": {
      "get": {
        "tags": [
          "SignalRTest"
        ],
        "summary": "Returns the current count and list of active SignalR hub subscriptions.",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/Stats/dex": {
      "get": {
        "tags": [
          "Stats"
        ],
        "summary": "Returns aggregated DEX statistics (volume, fees) for the given protocol over the window\n[timestamp, to). When to is omitted,\nthe window defaults to a full day: [timestamp, timestamp + 1 day), preserving the original\ndaily-aggregate behaviour. An explicit to allows arbitrary windows (e.g. 1\nhour) for consumers that pull data more granularly, such as DefiLlama's v2 adapter model\n(`pullHourly: true`).\nOnly confirmed trades are included. Suitable for DefiLlama adapter consumption.",
        "parameters": [
          {
            "name": "dex",
            "in": "query",
            "description": "DEX protocol identifier: `Biatec`, `Pact`, or `Tiny`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "timestamp",
            "in": "query",
            "description": "Inclusive start of the statistics window.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Exclusive end of the statistics window. Defaults to timestamp + 1 day when omitted.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "description": "Aggregated DEX trading statistics for an arbitrary window, formatted for DefiLlama export.\nThe window covers [AVMTradeReporter.Model.DTO.DexStatsResponse.From, AVMTradeReporter.Model.DTO.DexStatsResponse.To). Defaults to a full day\n(To = From + 1 day) when the caller does not request a specific end; an explicit end lets\ncallers pull sub-day (e.g. hourly) windows, as needed by DefiLlama's v2 adapter model.\nOnly confirmed trades are included in the aggregation.",
                  "$ref": "#/components/schemas/DexStatsResponse"
                }
              },
              "application/json": {
                "schema": {
                  "description": "Aggregated DEX trading statistics for an arbitrary window, formatted for DefiLlama export.\nThe window covers [AVMTradeReporter.Model.DTO.DexStatsResponse.From, AVMTradeReporter.Model.DTO.DexStatsResponse.To). Defaults to a full day\n(To = From + 1 day) when the caller does not request a specific end; an explicit end lets\ncallers pull sub-day (e.g. hourly) windows, as needed by DefiLlama's v2 adapter model.\nOnly confirmed trades are included in the aggregation.",
                  "$ref": "#/components/schemas/DexStatsResponse"
                }
              },
              "text/json": {
                "schema": {
                  "description": "Aggregated DEX trading statistics for an arbitrary window, formatted for DefiLlama export.\nThe window covers [AVMTradeReporter.Model.DTO.DexStatsResponse.From, AVMTradeReporter.Model.DTO.DexStatsResponse.To). Defaults to a full day\n(To = From + 1 day) when the caller does not request a specific end; an explicit end lets\ncallers pull sub-day (e.g. hourly) windows, as needed by DefiLlama's v2 adapter model.\nOnly confirmed trades are included in the aggregation.",
                  "$ref": "#/components/schemas/DexStatsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error"
          }
        }
      }
    },
    "/api/asset/top": {
      "get": {
        "tags": [
          "TopAssets"
        ],
        "summary": "Get the \"top assets\" highlight lists for the scan homepage header: Popular (24h volume),\nTrending (1h volume), Top gainers/losers (24h price change in percent) and Top liquidity\ngainers/losers (24h real TVL change in percent). Candidates are the top 150 assets by real TVL excluding stable assets;\neach list holds up to 3 entries. The response is recomputed every 5 minutes and served from\nthe Redis cache in between.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "description": "Backend-computed \"top assets\" highlight lists shown at the top of the Biatec Scan UI.\nThe candidate universe for every list is the top 150 assets ordered by real TVL (BiatecAsset.TVL_USD).\nStable/base reference assets (StabilityIndex > 0, e.g. ALGO and USDC) are excluded from the\nvolume-ranked lists (Popular, Trending) but compete normally in the price and liquidity\ngainers/losers lists. Each list contains up to 3 entries. The response is recomputed and cached in Redis every 5 minutes\n(see TopAssetsBackgroundService), so it is served mostly from cache.",
                  "$ref": "#/components/schemas/TopAssetsResponse"
                }
              },
              "application/json": {
                "schema": {
                  "description": "Backend-computed \"top assets\" highlight lists shown at the top of the Biatec Scan UI.\nThe candidate universe for every list is the top 150 assets ordered by real TVL (BiatecAsset.TVL_USD).\nStable/base reference assets (StabilityIndex > 0, e.g. ALGO and USDC) are excluded from the\nvolume-ranked lists (Popular, Trending) but compete normally in the price and liquidity\ngainers/losers lists. Each list contains up to 3 entries. The response is recomputed and cached in Redis every 5 minutes\n(see TopAssetsBackgroundService), so it is served mostly from cache.",
                  "$ref": "#/components/schemas/TopAssetsResponse"
                }
              },
              "text/json": {
                "schema": {
                  "description": "Backend-computed \"top assets\" highlight lists shown at the top of the Biatec Scan UI.\nThe candidate universe for every list is the top 150 assets ordered by real TVL (BiatecAsset.TVL_USD).\nStable/base reference assets (StabilityIndex > 0, e.g. ALGO and USDC) are excluded from the\nvolume-ranked lists (Popular, Trending) but compete normally in the price and liquidity\ngainers/losers lists. Each list contains up to 3 entries. The response is recomputed and cached in Redis every 5 minutes\n(see TopAssetsBackgroundService), so it is served mostly from cache.",
                  "$ref": "#/components/schemas/TopAssetsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/trade": {
      "get": {
        "tags": [
          "Trade"
        ],
        "summary": "Get trades with optional filtering and pagination.",
        "parameters": [
          {
            "name": "assetIdIn",
            "in": "query",
            "description": "Filter by input asset ID. When used alone, matches trades where this asset is either input or output.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdOut",
            "in": "query",
            "description": "Filter by output asset ID. When used with assetIdIn, requires exact asset pair match.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetId",
            "in": "query",
            "description": "Filter by asset ID on either side of the trade.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdA",
            "in": "query",
            "description": "Filter unordered pair asset A.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "assetIdB",
            "in": "query",
            "description": "Filter unordered pair asset B.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "txId",
            "in": "query",
            "description": "Filter by transaction ID. Takes precedence over asset filters.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "trader",
            "in": "query",
            "description": "Filter by trader address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "poolAddress",
            "in": "query",
            "description": "Filter by pool address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "poolAppId",
            "in": "query",
            "description": "Filter by pool application ID.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "protocol",
            "in": "query",
            "description": "Filter by DEX protocol.",
            "schema": {
              "$ref": "#/components/schemas/DEXProtocol"
            }
          },
          {
            "name": "tradeState",
            "in": "query",
            "description": "Filter by trade state.",
            "schema": {
              "$ref": "#/components/schemas/TxState"
            }
          },
          {
            "name": "blockFrom",
            "in": "query",
            "description": "Filter by minimum block.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "blockTo",
            "in": "query",
            "description": "Filter by maximum block.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "timestampFrom",
            "in": "query",
            "description": "Filter by minimum timestamp.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "timestampTo",
            "in": "query",
            "description": "Filter by maximum timestamp.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "minValueUSD",
            "in": "query",
            "description": "Filter by minimum USD value.",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "maxValueUSD",
            "in": "query",
            "description": "Filter by maximum USD value.",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "minFeesUSD",
            "in": "query",
            "description": "Filter by minimum USD fees.",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "maxFeesUSD",
            "in": "query",
            "description": "Filter by maximum USD fees.",
            "schema": {
              "type": "number",
              "format": "double"
            }
          },
          {
            "name": "minAmountIn",
            "in": "query",
            "description": "Filter by minimum input amount.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "maxAmountIn",
            "in": "query",
            "description": "Filter by maximum input amount.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "minAmountOut",
            "in": "query",
            "description": "Filter by minimum output amount.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "maxAmountOut",
            "in": "query",
            "description": "Filter by maximum output amount.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "sortBy",
            "in": "query",
            "description": "Sort by timestamp, valueUSD, feesUSD, assetAmountIn, or assetAmountOut.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sortDirection",
            "in": "query",
            "description": "Sort direction: asc or desc.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of records to skip for pagination (default: 0).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Maximum number of records to return (default: 100, max: 500).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TradePagedResult"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TradePagedResult"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TradePagedResult"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          }
        },
        "security": [
          {
            "arc14": [ ]
          }
        ]
      }
    },
    "/api/TVL/history": {
      "get": {
        "tags": [
          "TVL"
        ],
        "summary": "Returns hourly TVL (USD) bars for one asset over the trailing window ending now.",
        "parameters": [
          {
            "name": "assetId",
            "in": "query",
            "description": "Asset id (0 = ALGO).",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "hoursBack",
            "in": "query",
            "description": "How many trailing hours of bars to return (default 168 = 7 days).",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 168
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AMMType": {
        "enum": [
          "OldAMM",
          "StableSwap",
          "ConcentratedLiquidityAMM",
          "WeightedAMM",
          "TickBasedCLAMM"
        ],
        "type": "string"
      },
      "Address": {
        "type": "object",
        "properties": {
          "bytes": {
            "type": [
              "null",
              "string"
            ],
            "format": "byte",
            "readOnly": true
          }
        },
        "additionalProperties": false
      },
      "AggregatedPool": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "null",
              "string"
            ],
            "readOnly": true
          },
          "assetIdA": {
            "type": "integer",
            "format": "int64"
          },
          "assetIdB": {
            "type": "integer",
            "format": "int64"
          },
          "virtualSumALevel1": {
            "type": "number",
            "format": "double"
          },
          "virtualSumBLevel1": {
            "type": "number",
            "format": "double"
          },
          "virtualSumALevel1ForPrice": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "virtualSumBLevel1ForPrice": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "virtualSumALevel2": {
            "type": "number",
            "format": "double"
          },
          "virtualSumBLevel2": {
            "type": "number",
            "format": "double"
          },
          "tvL_A": {
            "type": "number",
            "format": "double"
          },
          "tvL_B": {
            "type": "number",
            "format": "double"
          },
          "tvL_A_ForPrice": {
            "type": "number",
            "format": "double"
          },
          "tvL_B_ForPrice": {
            "type": "number",
            "format": "double"
          },
          "poolCount": {
            "type": "integer",
            "format": "int32"
          },
          "level1Pools": {
            "uniqueItems": true,
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "level2Pools": {
            "uniqueItems": true,
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "lastUpdated": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "virtualSumA": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "virtualSumB": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "totalTVLAssetAInUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "totalTVLAssetBInUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAUSD1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAUSD24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAUSD7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceBUSD1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceBUSD24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceBUSD7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "AssetParams": {
        "type": "object",
        "properties": {
          "clawback": {
            "$ref": "#/components/schemas/Address"
          },
          "creator": {
            "$ref": "#/components/schemas/Address"
          },
          "decimals": {
            "maximum": 19,
            "minimum": 0,
            "type": "integer",
            "format": "int64"
          },
          "defaultFrozen": {
            "type": [
              "null",
              "boolean"
            ],
            "default": false
          },
          "freeze": {
            "$ref": "#/components/schemas/Address"
          },
          "manager": {
            "$ref": "#/components/schemas/Address"
          },
          "metadataHash": {
            "type": [
              "null",
              "string"
            ],
            "format": "byte"
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "nameB64": {
            "type": [
              "null",
              "string"
            ],
            "format": "byte"
          },
          "reserve": {
            "$ref": "#/components/schemas/Address"
          },
          "total": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64",
            "default": 0
          },
          "unitName": {
            "type": [
              "null",
              "string"
            ]
          },
          "unitNameB64": {
            "type": [
              "null",
              "string"
            ],
            "format": "byte"
          },
          "url": {
            "type": [
              "null",
              "string"
            ]
          },
          "urlB64": {
            "type": [
              "null",
              "string"
            ],
            "format": "byte"
          }
        },
        "additionalProperties": false
      },
      "AssetStat": {
        "type": "object",
        "properties": {
          "assetId": {
            "type": "integer",
            "format": "int64"
          },
          "protocol": {
            "$ref": "#/components/schemas/DEXProtocol"
          },
          "id": {
            "type": [
              "null",
              "string"
            ],
            "readOnly": true
          },
          "assetName": {
            "type": [
              "null",
              "string"
            ]
          },
          "unitName": {
            "type": [
              "null",
              "string"
            ]
          },
          "decimals": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "imageUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "priceUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "tvlusd": {
            "type": "number",
            "format": "double"
          },
          "tvlOtherUSD": {
            "type": "number",
            "format": "double"
          },
          "volume24hUSD": {
            "type": "number",
            "format": "double"
          },
          "volume7dUSD": {
            "type": "number",
            "format": "double"
          },
          "fees24hUSD": {
            "type": "number",
            "format": "double"
          },
          "fees7dUSD": {
            "type": "number",
            "format": "double"
          },
          "apr24h": {
            "type": "number",
            "format": "double"
          },
          "apr7d": {
            "type": "number",
            "format": "double"
          },
          "poolCount": {
            "type": "integer",
            "format": "int32"
          },
          "lastUpdated": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "AssetTimeseries7D": {
        "type": "object",
        "properties": {
          "assetId": {
            "type": "integer",
            "description": "ASA id (0 = ALGO).",
            "format": "int64"
          },
          "price": {
            "description": "Hourly USD price OHLC candles for the last 7 days. Empty when the asset had no priced trades.",
            "$ref": "#/components/schemas/TimeseriesCandles"
          },
          "tvl": {
            "description": "Hourly real TVL (USD) OHLC candles for the last 7 days. Grows as snapshots accumulate.",
            "$ref": "#/components/schemas/TimeseriesCandles"
          },
          "generatedAt": {
            "type": "string",
            "description": "When this series was computed.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "7 day hourly history for one asset: USD price OHLC candles (from trade OHLC aggregates) and\nreal TVL OHLC candles (from the hourly Redis TVL snapshots). Cached in Redis and refreshed on\na 1 hour cadence, so consumers may treat it as immutable within the hour."
      },
      "AssetType": {
        "enum": [
          "ASA",
          "ARC200"
        ],
        "type": "string"
      },
      "BiatecAsset": {
        "required": [
          "index",
          "params"
        ],
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "format": "int64"
          },
          "params": {
            "$ref": "#/components/schemas/AssetParams"
          },
          "type": {
            "$ref": "#/components/schemas/AssetType"
          },
          "priceUSD": {
            "type": "number",
            "format": "double"
          },
          "stabilityIndex": {
            "type": "integer",
            "format": "int32"
          },
          "tvL_USD": {
            "type": "number",
            "format": "double"
          },
          "timestamp": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "totalTVLAssetInUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceUSD1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceUSD24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceUSD7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "poolsCount": {
            "type": "integer",
            "format": "int32"
          },
          "deleted": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoAsset": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "null",
              "string"
            ]
          },
          "name": {
            "type": [
              "null",
              "string"
            ]
          },
          "symbol": {
            "type": [
              "null",
              "string"
            ]
          },
          "decimals": {
            "type": "integer",
            "format": "int32"
          },
          "totalSupply": {
            "type": [
              "null",
              "string"
            ],
            "description": "Decimalized total supply (`total / 10^decimals`)."
          },
          "metadata": {
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoAssetResponse": {
        "type": "object",
        "properties": {
          "asset": {
            "$ref": "#/components/schemas/CoinGeckoAsset"
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoBlock": {
        "type": "object",
        "properties": {
          "blockNumber": {
            "type": "integer",
            "format": "int64"
          },
          "blockTimestamp": {
            "type": "integer",
            "description": "UNIX timestamp in seconds (no milliseconds).",
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoEvent": {
        "type": "object",
        "properties": {
          "block": {
            "$ref": "#/components/schemas/CoinGeckoBlock"
          },
          "eventType": {
            "type": [
              "null",
              "string"
            ]
          },
          "txnId": {
            "type": [
              "null",
              "string"
            ]
          },
          "txnIndex": {
            "type": "integer",
            "format": "int64"
          },
          "eventIndex": {
            "type": "integer",
            "format": "int32"
          },
          "maker": {
            "type": [
              "null",
              "string"
            ]
          },
          "pairId": {
            "type": [
              "null",
              "string"
            ]
          },
          "reserves": {
            "$ref": "#/components/schemas/CoinGeckoReserves"
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoEventsResponse": {
        "type": "object",
        "properties": {
          "events": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/CoinGeckoEvent"
            }
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoLatestBlockResponse": {
        "type": "object",
        "properties": {
          "block": {
            "$ref": "#/components/schemas/CoinGeckoBlock"
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoPair": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "null",
              "string"
            ],
            "description": "Pool application id."
          },
          "dexKey": {
            "type": [
              "null",
              "string"
            ]
          },
          "asset0Id": {
            "type": [
              "null",
              "string"
            ]
          },
          "asset1Id": {
            "type": [
              "null",
              "string"
            ]
          },
          "feeBps": {
            "type": [
              "null",
              "number"
            ],
            "description": "Swap fee in basis points (1 % = 100).",
            "format": "double"
          },
          "metadata": {
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoPairResponse": {
        "type": "object",
        "properties": {
          "pair": {
            "$ref": "#/components/schemas/CoinGeckoPair"
          }
        },
        "additionalProperties": false
      },
      "CoinGeckoReserves": {
        "type": "object",
        "properties": {
          "asset0": {
            "type": [
              "null",
              "string"
            ]
          },
          "asset1": {
            "type": [
              "null",
              "string"
            ]
          }
        },
        "additionalProperties": false
      },
      "DEXProtocol": {
        "enum": [
          "Pact",
          "Tiny",
          "Biatec",
          "Scam"
        ],
        "type": "string"
      },
      "DexStatsResponse": {
        "type": "object",
        "properties": {
          "protocol": {
            "type": [
              "null",
              "string"
            ],
            "description": "DEX protocol identifier (Biatec, Pact, or Tiny)."
          },
          "from": {
            "type": "string",
            "description": "Start of the statistics window (inclusive).",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "description": "End of the statistics window (exclusive; equals From + 1 day unless an explicit end was requested).",
            "format": "date-time"
          },
          "volumeUSD": {
            "type": "number",
            "description": "Total USD volume traded during the window (sum of valueUSD across all confirmed trades).",
            "format": "double"
          },
          "feesUSD": {
            "type": "number",
            "description": "Total fees collected from all trades in USD (sum of feesUSD).",
            "format": "double"
          },
          "feesLPUSD": {
            "type": "number",
            "description": "Total fees collected by liquidity providers in USD (sum of feesUSDProvider).",
            "format": "double"
          },
          "feesProtocolUSD": {
            "type": "number",
            "description": "Total fees collected by the protocol in USD (sum of feesUSDProtocol).",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "Aggregated DEX trading statistics for an arbitrary window, formatted for DefiLlama export.\nThe window covers [AVMTradeReporter.Model.DTO.DexStatsResponse.From, AVMTradeReporter.Model.DTO.DexStatsResponse.To). Defaults to a full day\n(To = From + 1 day) when the caller does not request a specific end; an explicit end lets\ncallers pull sub-day (e.g. hourly) windows, as needed by DefiLlama's v2 adapter model.\nOnly confirmed trades are included in the aggregation."
      },
      "GossipRelayStatus": {
        "type": "object",
        "properties": {
          "host": {
            "type": [
              "null",
              "string"
            ]
          },
          "connectedAtUtc": {
            "type": "string",
            "format": "date-time"
          },
          "messageCount": {
            "type": "integer",
            "format": "int64"
          },
          "winCount": {
            "type": "integer",
            "format": "int64"
          },
          "lastMessageUtc": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "Indexer": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "null",
              "string"
            ]
          },
          "round": {
            "type": "integer",
            "format": "int64"
          },
          "genesisId": {
            "type": [
              "null",
              "string"
            ]
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          },
          "storedThrough": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          }
        },
        "additionalProperties": false
      },
      "Liquidity": {
        "type": "object",
        "properties": {
          "direction": {
            "$ref": "#/components/schemas/LiquidityDirection"
          },
          "assetIdA": {
            "type": "integer",
            "format": "int64"
          },
          "assetIdB": {
            "type": "integer",
            "format": "int64"
          },
          "assetIdLP": {
            "type": "integer",
            "format": "int64"
          },
          "assetAmountA": {
            "type": "integer",
            "format": "int64"
          },
          "assetAmountB": {
            "type": "integer",
            "format": "int64"
          },
          "assetAmountLP": {
            "type": "integer",
            "format": "int64"
          },
          "a": {
            "type": "integer",
            "format": "int64"
          },
          "b": {
            "type": "integer",
            "format": "int64"
          },
          "af": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "bf": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "l": {
            "type": "integer",
            "format": "int64"
          },
          "txId": {
            "type": [
              "null",
              "string"
            ]
          },
          "blockId": {
            "type": "integer",
            "format": "int64"
          },
          "txGroup": {
            "type": [
              "null",
              "string"
            ]
          },
          "timestamp": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "protocol": {
            "$ref": "#/components/schemas/DEXProtocol"
          },
          "liquidityProvider": {
            "type": [
              "null",
              "string"
            ]
          },
          "poolAddress": {
            "type": [
              "null",
              "string"
            ]
          },
          "poolAppId": {
            "type": "integer",
            "format": "int64"
          },
          "topTxId": {
            "type": [
              "null",
              "string"
            ]
          },
          "txState": {
            "$ref": "#/components/schemas/TxState"
          },
          "valueUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "txnIndex": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "eventIndex": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          }
        },
        "additionalProperties": false
      },
      "LiquidityDirection": {
        "enum": [
          "DepositLiquidity",
          "WithdrawLiquidity"
        ],
        "type": "string"
      },
      "Pool": {
        "type": "object",
        "properties": {
          "poolAddress": {
            "type": [
              "null",
              "string"
            ]
          },
          "poolAppId": {
            "type": "integer",
            "format": "int64"
          },
          "assetIdA": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "assetADecimals": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "assetIdB": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "assetBDecimals": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "assetIdLP": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "a": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "b": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "stableA": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "stableB": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "amplifier": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "weightA": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "weightB": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "currentPrice": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "currentTick": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "tickSpacing": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "af": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "bf": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "l": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "pMin": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "pMax": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "verificationClass": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "protocol": {
            "$ref": "#/components/schemas/DEXProtocol"
          },
          "timestamp": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "ammType": {
            "$ref": "#/components/schemas/AMMType"
          },
          "approvalProgramHash": {
            "type": [
              "null",
              "string"
            ]
          },
          "scamRating": {
            "type": "integer",
            "format": "int32"
          },
          "lpFee": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "protocolFeePortion": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "totalTVLAssetAInUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "totalTVLAssetBInUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "volume7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAUSD1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAUSD24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAUSD7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceBUSD1H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceBUSD24H": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceBUSD7D": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "virtualAmountA": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "virtualAmountAForPrice": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "realAmountA": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "virtualAmountB": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "virtualAmountBForPrice": {
            "type": "number",
            "format": "double",
            "readOnly": true
          },
          "hasZeroWidthPriceRange": {
            "type": "boolean",
            "readOnly": true
          },
          "isDepletedSingleSided": {
            "type": "boolean",
            "readOnly": true
          },
          "realAmountB": {
            "type": "number",
            "format": "double",
            "readOnly": true
          }
        },
        "additionalProperties": false
      },
      "PoolOrderBy": {
        "enum": [
          0,
          1,
          2,
          3,
          4,
          5
        ],
        "type": "integer",
        "format": "int32"
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": [
              "null",
              "string"
            ]
          },
          "title": {
            "type": [
              "null",
              "string"
            ]
          },
          "status": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "detail": {
            "type": [
              "null",
              "string"
            ]
          },
          "instance": {
            "type": [
              "null",
              "string"
            ]
          }
        },
        "additionalProperties": { }
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "assets": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/BiatecAsset"
            }
          },
          "pools": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/Pool"
            }
          },
          "aggregatedPools": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/AggregatedPool"
            }
          },
          "addresses": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            }
          },
          "blocks": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "integer",
              "format": "int64"
            }
          },
          "trades": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/Trade"
            }
          }
        },
        "additionalProperties": false
      },
      "SortDirection": {
        "enum": [
          0,
          1
        ],
        "type": "integer",
        "format": "int32"
      },
      "TimeseriesCandles": {
        "type": "object",
        "properties": {
          "t": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "integer",
              "format": "int64"
            },
            "description": "Bucket start times (unix seconds, UTC, ascending)."
          },
          "o": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "number",
              "format": "double"
            },
            "description": "Open values."
          },
          "h": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "number",
              "format": "double"
            },
            "description": "High values."
          },
          "l": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "number",
              "format": "double"
            },
            "description": "Low values."
          },
          "c": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "number",
              "format": "double"
            },
            "description": "Close values."
          }
        },
        "additionalProperties": false,
        "description": "Compact OHLC candle series in TradingView-style parallel arrays (all arrays share the same\nlength and index i describes one candle). Chosen over an array of candle objects to keep the\nJSON payload small — the 7d asset timeseries endpoint returns up to ~168 hourly candles per\nseries for up to 100 assets per request."
      },
      "TopAssetItem": {
        "type": "object",
        "properties": {
          "assetId": {
            "type": "integer",
            "description": "Algorand asset id.",
            "format": "int64"
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "Asset display name."
          },
          "unitName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Asset unit name (ticker)."
          },
          "decimals": {
            "type": [
              "null",
              "integer"
            ],
            "description": "Number of decimals of the asset.",
            "format": "int64"
          },
          "priceUSD": {
            "type": "number",
            "description": "Current USD price of the asset.",
            "format": "double"
          },
          "priceUSD24H": {
            "type": [
              "null",
              "number"
            ],
            "description": "USD price 24 hours ago (null when no history is available yet).",
            "format": "double"
          },
          "priceChange24HPercent": {
            "type": [
              "null",
              "number"
            ],
            "description": "Price change over the past 24 hours in percent (null when no price history).",
            "format": "double"
          },
          "volume1HUSD": {
            "type": "number",
            "description": "Trading volume in USD over the past hour.",
            "format": "double"
          },
          "volume1HUSDPrev": {
            "type": [
              "null",
              "number"
            ],
            "description": "Trading volume in USD over the previous hour window (2h ago .. 1h ago). Null when trade history was unavailable during computation.",
            "format": "double"
          },
          "volume1HChangePercent": {
            "type": [
              "null",
              "number"
            ],
            "description": "Volume change of the past-hour window vs the previous-hour window in percent (null when the previous window had no volume).",
            "format": "double"
          },
          "volume24HUSD": {
            "type": "number",
            "description": "Trading volume in USD over the past 24 hours.",
            "format": "double"
          },
          "volume24HUSDPrev": {
            "type": [
              "null",
              "number"
            ],
            "description": "Trading volume in USD over the previous 24h window (48h ago .. 24h ago). Null when trade history was unavailable during computation.",
            "format": "double"
          },
          "volume24HChangePercent": {
            "type": [
              "null",
              "number"
            ],
            "description": "Volume change of the past-24h window vs the previous-24h window in percent (null when the previous window had no volume).",
            "format": "double"
          },
          "realTVLUSD": {
            "type": "number",
            "description": "Current real TVL in USD (trusted-token side only, see BiatecAsset.TVL_USD).",
            "format": "double"
          },
          "realTVLUSD24H": {
            "type": [
              "null",
              "number"
            ],
            "description": "Real TVL in USD ~24 hours ago (null when no snapshot history is available yet).",
            "format": "double"
          },
          "tvlChange24HUSD": {
            "type": [
              "null",
              "number"
            ],
            "description": "Absolute real TVL change over the past 24 hours in USD (null when no snapshot history).",
            "format": "double"
          },
          "tvlChange24HPercent": {
            "type": [
              "null",
              "number"
            ],
            "description": "Real TVL change over the past 24 hours in percent (null when no snapshot history).",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "One asset entry inside AVMTradeReporter.Model.DTO.TopAssetsResponse lists."
      },
      "TopAssetsResponse": {
        "type": "object",
        "properties": {
          "popular": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One asset entry inside AVMTradeReporter.Model.DTO.TopAssetsResponse lists.",
              "$ref": "#/components/schemas/TopAssetItem"
            },
            "description": "Top assets by 24h trading volume (\"Popular assets\")."
          },
          "trending": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One asset entry inside AVMTradeReporter.Model.DTO.TopAssetsResponse lists.",
              "$ref": "#/components/schemas/TopAssetItem"
            },
            "description": "Top assets by 1h trading volume (\"Trending assets\")."
          },
          "topGainers": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One asset entry inside AVMTradeReporter.Model.DTO.TopAssetsResponse lists.",
              "$ref": "#/components/schemas/TopAssetItem"
            },
            "description": "Top assets by 24h price growth in percent (\"Top gainers\"). Only positive changes qualify."
          },
          "topLosers": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One asset entry inside AVMTradeReporter.Model.DTO.TopAssetsResponse lists.",
              "$ref": "#/components/schemas/TopAssetItem"
            },
            "description": "Top assets by 24h price loss in percent (\"Top losers\"). Only negative changes qualify."
          },
          "topValueGainers": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One asset entry inside AVMTradeReporter.Model.DTO.TopAssetsResponse lists.",
              "$ref": "#/components/schemas/TopAssetItem"
            },
            "description": "Top assets by relative real TVL growth in percent over 24h (\"Top liquidity gainers\"). Only positive, finite changes qualify.\n            Newly funded assets (no TVL 24h ago) have an undefined percent change from a zero baseline and are excluded."
          },
          "topValueLosers": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One asset entry inside AVMTradeReporter.Model.DTO.TopAssetsResponse lists.",
              "$ref": "#/components/schemas/TopAssetItem"
            },
            "description": "Top assets by relative real TVL loss in percent over 24h (\"Top liquidity losers\"). Only negative changes qualify."
          },
          "generatedAt": {
            "type": "string",
            "description": "Timestamp this response was computed.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "Backend-computed \"top assets\" highlight lists shown at the top of the Biatec Scan UI.\nThe candidate universe for every list is the top 150 assets ordered by real TVL (BiatecAsset.TVL_USD).\nStable/base reference assets (StabilityIndex > 0, e.g. ALGO and USDC) are excluded from the\nvolume-ranked lists (Popular, Trending) but compete normally in the price and liquidity\ngainers/losers lists. Each list contains up to 3 entries. The response is recomputed and cached in Redis every 5 minutes\n(see TopAssetsBackgroundService), so it is served mostly from cache."
      },
      "Trade": {
        "type": "object",
        "properties": {
          "assetIdIn": {
            "type": "integer",
            "format": "int64"
          },
          "assetIdOut": {
            "type": "integer",
            "format": "int64"
          },
          "assetAmountIn": {
            "type": "integer",
            "format": "int64"
          },
          "assetAmountOut": {
            "type": "integer",
            "format": "int64"
          },
          "txId": {
            "type": [
              "null",
              "string"
            ]
          },
          "blockId": {
            "type": "integer",
            "format": "int64"
          },
          "txGroup": {
            "type": [
              "null",
              "string"
            ]
          },
          "timestamp": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "protocol": {
            "$ref": "#/components/schemas/DEXProtocol"
          },
          "trader": {
            "type": [
              "null",
              "string"
            ]
          },
          "poolAddress": {
            "type": [
              "null",
              "string"
            ]
          },
          "poolAppId": {
            "type": "integer",
            "format": "int64"
          },
          "topTxId": {
            "type": [
              "null",
              "string"
            ]
          },
          "tradeState": {
            "$ref": "#/components/schemas/TxState"
          },
          "a": {
            "type": "integer",
            "format": "int64"
          },
          "b": {
            "type": "integer",
            "format": "int64"
          },
          "l": {
            "type": "integer",
            "format": "int64"
          },
          "poolSqrtPriceX64": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "poolTick": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "txnIndex": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "eventIndex": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int32"
          },
          "af": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "bf": {
            "type": [
              "null",
              "integer"
            ],
            "format": "int64"
          },
          "valueUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAssetInUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "priceAssetOutUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "feesUSD": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "feesUSDProvider": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          },
          "feesUSDProtocol": {
            "type": [
              "null",
              "number"
            ],
            "format": "double"
          }
        },
        "additionalProperties": false
      },
      "TradePagedResult": {
        "type": "object",
        "properties": {
          "items": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/Trade"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64"
          },
          "offset": {
            "type": "integer",
            "format": "int32"
          },
          "size": {
            "type": "integer",
            "format": "int32"
          },
          "hasMore": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "TxState": {
        "enum": [
          "TxPool",
          "Confirmed"
        ],
        "type": "string"
      }
    },
    "securitySchemes": {
      "arc14": {
        "type": "apiKey",
        "description": "ARC-0014 Algorand authentication: a signed transaction, base64-encoded and sent as the full Authorization header value, e.g. \"SigTx <base64-signed-tx>\". Paste the whole value including the \"SigTx \" prefix. See the top-level API description for how to generate this token.",
        "name": "Authorization",
        "in": "header"
      }
    }
  },
  "tags": [
    {
      "name": "AggregatedPool"
    },
    {
      "name": "Asset",
      "description": "Asset metadata lookup and asset image retrieval."
    },
    {
      "name": "AssetStat"
    },
    {
      "name": "AssetTimeseries"
    },
    {
      "name": "CoinGecko",
      "description": "GeckoTerminal / CoinGecko non-EVM DEX integration (spec: \"GeckoTerminal Integration API Standards v0.1\").\nPublicly accessible (no authentication): the integration is polled by CoinGecko's indexer, which cannot sign\nARC-14 tokens, and serves public market data of the Biatec DEX only. Pair id = pool application id,\nasset id = ASA id (ALGO = 0); all amounts are decimalized strings."
    },
    {
      "name": "Gossip",
      "description": "Exposes the live connection health of the mempool gossip listener. Publicly accessible (no\nauthentication required) so relay connectivity can be verified without cluster/SSH access."
    },
    {
      "name": "Indexer"
    },
    {
      "name": "Liquidity"
    },
    {
      "name": "OHLC",
      "description": "TradingView UDF-compatible OHLC/charting datafeed (config, symbol resolution, search, bars,\nmarks, quotes). Publicly accessible (no authentication required) so the charting widget can be\nembedded and queried directly from the browser without an ARC-14 signed transaction."
    },
    {
      "name": "Pool"
    },
    {
      "name": "Search",
      "description": "Free-text search across assets and pools. Requires ARC-14 authentication."
    },
    {
      "name": "SignalRTest",
      "description": "Diagnostic/test utilities for verifying ARC-14 authentication and the SignalR hub broadcast\npipeline. Requires ARC-14 authentication. Intended for development/debugging, not production\nclient use."
    },
    {
      "name": "Stats",
      "description": "Provides DEX statistics endpoints for DefiLlama integration.\nAll endpoints are publicly accessible (no authentication required)."
    },
    {
      "name": "TopAssets"
    },
    {
      "name": "Trade"
    },
    {
      "name": "TVL",
      "description": "Durable TVL (real total value locked, USD) OHLC history for a single asset, backed by the\n\"tvlohlc\" Elasticsearch index (see AVMTradeReporter.Repository.TvlOhlcRepository) - the TVL counterpart of\nAVMTradeReporter.Controllers.OHLCController's price history. Publicly accessible (no authentication\nrequired), same as OHLCController: this is public market data.\n            \nDeliberately not a full TradingView UDF datafeed (no symbol resolution/config/search\nendpoints) - this is a plain bars endpoint for a future liquidity chart consumer to build on,\nnot a drop-in TradingView data source."
    }
  ]
}