---
title: "Opportunity Scan"
method: GET
path: "/research/opportunities"
tags: ["research"]
---

# Opportunity Scan

`GET /research/opportunities`

## Request

- Base URL: `https://mcp.f1ow.com/v1`
- URL: `https://mcp.f1ow.com/v1/research/opportunities`
- Auth: one of:
  - HTTP bearer
  - none

Cross-sectional opportunity scan across every major and medium-cap coin at once — the screener the per-coin tools cannot be looped into safely. Two whole-universe feeds (the merged market snapshot + venue perp contexts) build a liquidity-filtered universe and rank it, then candles are fetched for a bounded shortlist only; ZERO per-symbol calls are made to the rate-limited derivatives provider, which is what lets this cover a universe without silently degrading. Up to six signals per candidate: crowding (annualized funding vs the venue-standard anchor AND its percentile across the screened universe right now — a cross-sectional read, explicitly NOT the own-history percentile research_positioning reports), stress (24h liquidations as a share of open interest — whether forced sellers already cleared), leverage (open interest as a share of market cap — the gap-through-your-stop condition), compression (4h Bollinger bandwidth ranked against the coin's own trailing window — the per-coin form of the reading research_regime computes for BTC alone; says energy is loading, never direction), trend (ADX with the +DI/-DI separation on 4h, confirmed against 1d — the vote is withheld unless the slow timeframe agrees), and location (distance from the 4h EMA20 in ATR units — an extended coin scores DOWN, because a chase is not a setup). Each signal reports its state, the numbers, and a +1/0/-1 setup-quality vote; the setup name and score come from a disclosed rule shipped with every scan. Candidates carry ATR-and-structure geometry — a resting-limit entry at the EMA20 (never at market), a stop a full 2 ATR beyond it, and a target shaded inside the first structural level — priced so it feeds research_trade_plan directly. 'tier' filters major/medium/all; 'limit' caps the ranked list; 'symbols' narrows the universe to specific coins, and every symbol named there is accounted for individually in the response — selected, excluded by your own tier filter, below the liquidity floors (never screened, so no band was ever applied to it), below the tier floor, or not quoted at all — because mere absence from the ranked list conflates four different answers. Reports a board, not a forecast: the bands are disclosed constants chosen for plausibility with no backtest behind them, and 'NO CANDIDATES' is a real and frequent answer. Use it to pick what to look at, then research_positioning and ta_technicals on the two or three that stand out.

## Query parameters

- `tier` 'major' | 'medium' | 'all': Size tier to screen: 'major' (market cap >= $10B), 'medium' ($300M-$10B), or 'all' (default).
- `limit` integer: Ranked candidates to return. Default 10, max 25.
- `symbols` string[]: Optional: narrow the universe to these coin symbols, e.g. ["SOL","AVAX"]. Omit to screen everything that clears the tier and liquidity floors.
- `intervals` string[]: The timeframe pair to analyse, FINEST FIRST: [fast] or [fast, slow]. Default ["4h","1d"]. This is the most consequential argument: entry, stop, target, ATR and every AT STRUCTURE verdict are measured on the fast timeframe, so changing it produces a DIFFERENT trade rather than a refinement of the same one — on one live BTC read the 1h pair put the entry 0.2% above spot while the 1d pair put it 10.7% below, with stops of 0.77% and 3.31%. Lookbacks are counted in candles, so the swing windows scale too (30 candles is 5 days at 4h, 7 hours at 15m). The second interval only CONFIRMS the first and must be slower; pass one interval to skip confirmation, which halves the candle cost and makes the trend vote abstain.

## Response `200`

The result.

- object
  - `data` object
    - `coverage` object: What the scan actually looked at — read this before treating an empty candidate list as a quiet market
      - `quoted` integer: Coins the merged market feed returned with a usable price
      - `perpCovered` integer: Coins with a venue perp context, the source of the 24h volume floor
      - `screened` integer: Every coin passing the liquidity floors — the universe every cross-sectional percentile is computed against, whatever tier or symbols were requested
      - `selected` integer: Coins left after the tier/symbol narrowing — the set the shortlist is drawn from. Equal to screened when nothing was filtered.
      - `major` integer
      - `medium` integer
      - `analyzed` integer: Shortlisted coins whose candles answered
      - `technicalsFailed` integer: Shortlisted coins whose candles did not answer; their candidates carry Stage A signals only
      - `priceDisagreements` object[]: Shortlisted coins whose candle series was DISCARDED because its newest close disagreed with the merged market price beyond the divergence band — a stale or mismatched series (e.g. a delisted pair a venue still serves). A wrong answer, not a missing one. These candidates carry Stage A signals only and are never priced.
        - `symbol` string
        - `candleCloseUsd` number
        - `mergePriceUsd` number
      - `sources` string[]
      - `fastInterval` string: The timeframe every level in this report is measured on
      - `slowInterval` string: The confirmation timeframe; absent when only one interval was requested, in which case the trend vote abstains
    - `requestedSymbols` object[]: One entry per symbol named in the 'symbols' argument, so a symbol missing from 'candidates' can be explained. Empty when no symbols were named. A status other than 'selected' means no band was applied to that coin, so its absence says nothing about its setup.
      - `symbol` string
      - `status` string: selected | tier_excluded | below_liquidity_floors | below_tier_floor | not_quoted
      - `detail` string: The numbers behind the status, with the floor each was measured against
    - `candidates` object[]
      - `symbol` string
      - `tier` string: major or medium
      - `tierFromVolume` boolean: True when no market-cap source answered and the tier was inferred from 24h volume instead — a weaker claim than a size tier, and those rows carry no leverage signal
      - `setup` string: TRENDING PULLBACK | COILED | SQUEEZE FUEL | EXTENDED | NO SETUP. EXTENDED is reported so it is NOT taken as an entry.
      - `lean` string: long, short, or empty. A lean, never a call; COILED deliberately carries none.
      - `score` integer: Plain sum of signal votes
      - `priceUsd` number
      - `marketCapUsd` number
      - `change24hPct` number
      - `dayNotionalUsd` number: 24h perp notional volume, USD
      - `timeframes` string[]: Intervals whose candles answered; empty means Stage A signals only
      - `signals` object[]
        - `name` string
        - `state` string
        - `detail` string: The numbers behind the state, as prose
        - `metrics` object: The same numbers keyed by metric name, e.g. fundingUniversePercentile, bandwidthOwnPercentile, distanceFromEma20Atr
        - `vote` integer: The signal's own reading: +1 supports a defined entry, -1 argues against one, 0 informational. Not summable with research_regime or research_positioning votes.
        - `contribution` integer: What the signal contributed to the score once the setup's side was known. Equals vote except where the signal leans against that side, when the sign flips — a reading favouring the opposite side is evidence the entry would be joining the cohort that pays.
        - `lean` string
      - `geometry` object: ATR-and-structure prices, ready for research_trade_plan. Absent for a setup with no priceable side (COILED) and for EXTENDED, where refusing to price a chase is the point.
        - `entryUsd` number: Resting limit at the 4h EMA20 — never the current price
        - `stopUsd` number
        - `targetUsd` number
        - `rewardRisk` number: Reported as computed, never adjusted to clear the 2:1 bar
        - `stopDistancePct` number: Stop distance as a percentage of entry
        - `stopDistanceAtr` number: Stop distance in ATR(14) units
        - `targetDistanceAtr` number: Target distance in ATR(14) units. Read alongside rewardRisk: a tight stop with a distant target prices well and still asks price to travel a long way. R:R is geometry, never expectancy.
        - `targetBasis` string: The structural level the target was shaded inside, named
        - `livePriceUsd` number: The live merged market price the entry was checked against
        - `entryGapAtr` number: How far live price sits from the entry in ATR units, signed in the setup's favour: positive means the limit has not been reached yet, negative that price has passed it
        - `freshness` string: PENDING (limit not reached) | AT ENTRY (filling now) | THROUGH (passed the limit, stop still holds). Geometry is absent entirely when INVALIDATED — price is already at or beyond the stop.
        - `rule` string
      - `signalsComputed` integer
      - `signalsTotal` integer
    - `rule` string: The exact rule that produced setup and score
    - `caveats` string[]: What the scan cannot see, stated rather than implied
  - `text` string, required: Human-readable rendering of the result.
  - `meta` ResponseMeta
    - `generatedAt` string, date-time: When the data was produced (source timestamp where the provider has one), ISO 8601 UTC

## Other responses

- `400`: An argument is missing or invalid.
- `401`: No credential was sent.
- `402`: x402 route: a priced call without a valid payment (challenge in the base64 PAYMENT-REQUIRED header). Metered routes: the account balance or access period is spent.
- `403`: The credential was rejected.
- `500`: Unexpected server error.
- `502`: The data source could not answer.

---

[API](https://skmtc.dev/f1ow/apis/f1ow-api.md) · [All operations](https://skmtc.dev/f1ow/apis/f1ow-api/llms.txt) · [OpenAPI document](https://skmtc.dev/f1ow/apis/f1ow-api/revisions/6974f25b6067?raw)
