# Unusual Options Activity Scanner (`michael_b/stock-unusual-options-activity`) Actor

Scans US options markets (stocks, ETFs, indices) for unusual activity (extreme Vol/OI smart-money signal) or most-active contracts. Returns strike, expiration, volume, open interest, Vol/OI, bid/ask, IV, delta, underlying price, optional Greeks. Built for AI trading agents and options flow analysis.

- **URL**: https://apify.com/michael_b/stock-unusual-options-activity.md
- **Developed by:** [Michal Búci](https://apify.com/michael_b) (community)
- **Categories:** Developer tools, Agents, AI
- **Features:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Unusual Options Activity Scanner

Daily feed of unusual options activity and most-active contracts across US stocks, ETFs, and indices. Data sourced from Barchart, delayed approximately 15-30 minutes.

### What it does

Returns option contracts that are trading at unusually high volume relative to their Open Interest (OI) — the total number of contracts that already existed from prior days. When today's volume is far above OI, it suggests new large positions are being opened, which is often called a "smart money" signal. Also returns the highest-volume contracts regardless of OI. Each row includes the strike price, expiration date, volume, OI, the Volume/OI ratio, pricing, Implied Volatility (IV), delta, and the stock's current price.

### Scan Modes

- **unusual_activity** — Contracts where today's trading volume is much higher than the prior day's Open Interest (OI), specifically where the ratio exceeds the threshold (default: 1.25). This matches Barchart's own definition of "unusual" activity.
- **most_active** — Contracts with the highest raw trading volume today, regardless of the Vol/OI ratio.
- **ticker_flow_summary** — One summary row per stock ticker, combining all its unusual contracts into call/put dollar totals, a put/call ratio, net directional exposure, and contract count. The compact mode for AI agents asking "which stocks have notable options activity today" — then drill into specific tickers using the contract modes.

### Filters

| Input | Default | Notes |
|---|---|---|
| tickers | (all) | Comma-separated ticker symbols. Leave blank to scan the whole market. |
| optionType | both | calls, puts, or both |
| minVolume | 500 | Minimum number of contracts traded today |
| minVolumeOIRatio | 1.25 | Minimum Vol/OI ratio to qualify as "unusual" |
| minPremium | 0 | Minimum estimated dollars spent (e.g. 250000 to focus on institutional-scale flow) |
| excludeIndexETFs | false | Drop broad market indices and ETFs (SPX, VIX, SPY, QQQ, etc.). Individual stock ETFs are kept. |
| daysToExpirationMin | 0 | Minimum days until expiration. Set to 1 to exclude same-day (0 DTE) contracts. |
| daysToExpirationMax | 365 | Maximum days until expiration. Default caps at one year (long-dated LEAPS territory). |
| sortBy | volumeOIRatio | Sort by: volume, volatility, days to expiration, or expiration date |
| limit | 100 | Max 1000. In ticker_flow_summary mode, limits the number of tickers returned. |
| includeGreeks | false | Add gamma, theta, vega, and rho from CBOE (contract modes only) |

### Output

Every row has a `rowType` field: either `contract` or `tickerSummary`. Fields that don't apply to the row type will be null.

Contract rows (`unusual_activity`, `most_active`):

**The bet itself:**

- `ticker`, `optionType`, `strike`, `expirationDate` — e.g. an AAPL call with strike 200 expiring 2026-07-17 reads as "a bet that AAPL will be above $200 by July 17" (call = bullish, put = bearish)
- `daysToExpiration` — how many days the bet has left before it expires
- `moneyness` — ITM (In The Money — the bet is already winning), ATM (At The Money — right at the strike price), OTM (Out of The Money — the stock still needs to move)
- `contractSymbol` — unique identifier (e.g. `AAPL|20260516|200.00C`), use this to track the same contract across multiple runs

**The unusual-activity signal:**

- `volume` — contracts traded today; `openInterest` — contracts that were already open before today
- `volumeOIRatio` — the key signal. Around 0.5 is normal. 10+ means today's trading far outnumbers everything previously open — someone is opening a big new position.
- `estimatedPremium` — approximate dollars spent (volume × midpoint × 100). Under ~$100k is retail scale; $1M+ is institutional scale.
- `sideHint` — a rough guess at who drove the last trade: `ask` (likely a buyer), `bid` (likely a seller), `mid` (unclear). Captured at scan time, not at trade time — treat as a weak indicator only.
- `spreadSuspect` — true when this looks like one leg of a two-leg spread trade (matched volume on the same ticker, expiration, and option type). Spread trades limit risk in both directions and are not pure directional bets — discount as a signal.
- `spansEarnings`, `nextEarningsDate` — whether this contract's life covers the stock's next earnings report. Options that span earnings are often bought as insurance against a big move, not as a directional bet.

**Prices:**

- `underlyingPrice`, `percentChange` — the stock price and its move today (decimal: 0.0291 = +2.91%)
- `lastPrice`, `bid`, `ask`, `midpoint` — option prices per share (multiply by 100 for total contract cost)

**Volatility and Greeks** (safe to skip as a beginner — delta is the one worth learning first):

- `impliedVolatility` (IV) — how large a move the market expects, annualized (0.42 = 42% per year); higher = more expected volatility
- `delta` — how much the option price moves per $1 move in the stock; also a rough probability the bet wins (0.85 ≈ 85% chance)
- `gamma`, `theta`, `vega`, `rho` — advanced sensitivity metrics: how fast delta changes with price, time decay per day, sensitivity to volatility changes, sensitivity to interest rates. Only populated when `includeGreeks` is enabled.

**Where it came from:** `tradeTime` (UTC), `source`, `dataDelayMinutes`, `scannedAt`

Ticker summary rows (`ticker_flow_summary`), sorted by `totalPremium` highest first:

- `ticker`, `contractCount` — the stock and how many unusual contracts it had
- `totalPremium`, `callPremium`, `putPremium` — estimated dollars spent per side, **per scan, not per full day**
- `putCallPremiumRatio` — below 1 = more money in calls (bullish lean), above 1 = more money in puts (bearish lean)
- `netDeltaShares` — approximate net directional exposure in share-equivalents (positive = net bullish pressure, negative = net bearish)
- `callVolume`, `putVolume`, `maxVolumeOIRatio`
- `topContractSymbol` — the single contract with the largest estimated premium, the best place to start digging in
- `spreadSuspectCount`, `spansEarningsCount` — how many of the contracts look like spread trades or earnings hedging rather than directional bets
- `nextEarningsDate`, `underlyingPrice`, `percentChange`

### Verifying the data

- Cross-check any row on [barchart.com/options/unusual-activity](https://www.barchart.com/options/unusual-activity/stocks) — same source, numbers should match within the delay window.
- Independent check: Yahoo Finance → ticker → Options → pick the expiration → find the strike. Volume, open interest, and last price should agree (both delayed).
- Derived fields are recomputable: `volumeOIRatio = volume / openInterest`, `estimatedPremium = volume × midpoint × 100`, `moneyness` from strike vs `underlyingPrice`, `spansEarnings` from the two dates.

### Data disclaimer

All Barchart data is delayed approximately 15-30 minutes. This actor is intended for end-of-day analysis, pre-market screening, and AI agent tooling — not live day-trading execution.

### Typical queries for AI agents via MCP

- "Which stocks have the most unusual options flow today?" (ticker_flow_summary, excludeIndexETFs, minPremium 250000)
- "Show top 20 unusual call flow across the market today with 7-60 days to expiration and premium > $100k"
- "Scan my portfolio tickers AAPL, NVDA, TSLA, MSFT for any unusual put activity"
- "List most active same-day (0 DTE) contracts on SPY, QQQ, IWM"
- "Find unusual call buying that does NOT span an earnings report" (filter spansEarnings=false rows)

### Companion actors

- `finviz-ticker-news` — real-time ticker news with full article text
- `stock-earnings-calendar` — upcoming and recent earnings dates with estimates
- `stock-analyst-ratings` — daily feed of all analyst upgrades/downgrades

# Actor input Schema

## `mode` (type: `string`):

unusual_activity returns contracts with high Vol/OI ratio (smart-money signal). most_active returns contracts with the highest absolute volume regardless of OI. ticker_flow_summary aggregates unusual activity into one row per underlying ticker (call/put premium totals, put/call ratio, net delta exposure) — the context-efficient choice for AI agents asking 'which tickers have notable flow'.

## `tickers` (type: `string`):

Comma-separated list of underlying tickers to filter by (e.g. 'AAPL,NVDA,SPY'). Leave blank to scan the whole market.

## `optionType` (type: `string`):

Filter by call, put, or return both.

## `minVolume` (type: `integer`):

Contracts below this day-volume are excluded. Barchart's own unusual-activity default is 500.

## `minVolumeOIRatio` (type: `number`):

Contracts with today-volume / prior-day-OI below this ratio are excluded. Barchart's unusual-activity default is 1.25. Ignored when mode is most_active.

## `minPremium` (type: `number`):

Exclude contracts whose estimated traded premium (volume x midpoint x 100) is below this dollar amount. Useful to focus on institutional-scale flow, e.g. 250000. Contracts without a computable premium (missing midpoint) are also excluded when this is set. 0 disables the filter.

## `excludeIndexETFs` (type: `boolean`):

When enabled, drops index options ($SPX, $VIX, ...) and broad index/sector/bond/commodity/volatility ETFs (SPY, QQQ, XLF, TLT, GLD, VXX, ...) whose flow is dominated by hedging. Single-stock ETFs are kept. Off by default so whole-market scans include SPY and QQQ as expected.

## `daysToExpirationMin` (type: `integer`):

Exclude contracts expiring within fewer than this many calendar days. Set to 1 to skip same-day (0DTE) expirations.

## `daysToExpirationMax` (type: `integer`):

Exclude contracts expiring more than this many calendar days out. Default 365 includes LEAPS up to one year.

## `sortBy` (type: `string`):

Which metric to rank results by before applying the row limit.

## `sortDirection` (type: `string`):

Direction to apply to the Sort By metric. Descending puts the most unusual or most active rows first.

## `limit` (type: `integer`):

Maximum number of rows to return. Barchart caps at 1000 per request. In ticker_flow_summary mode this limits the number of tickers (the underlying contract fetch always uses the full 1000).

## `includeGreeks` (type: `boolean`):

When enabled, adds gamma, theta, vega, and rho to each row by fetching the CBOE option chain for every underlying in the result. Adds a few hundred KB of transfer per unique underlying.

## Actor input object example

```json
{
  "mode": "unusual_activity",
  "optionType": "both",
  "minVolume": 500,
  "minVolumeOIRatio": 1.25,
  "minPremium": 0,
  "excludeIndexETFs": false,
  "daysToExpirationMin": 0,
  "daysToExpirationMax": 365,
  "sortBy": "volumeOIRatio",
  "sortDirection": "desc",
  "limit": 100,
  "includeGreeks": false
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset of contracts matching the scan mode and filters. Data is delayed approximately 15-30 minutes.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("michael_b/stock-unusual-options-activity").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("michael_b/stock-unusual-options-activity").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call michael_b/stock-unusual-options-activity --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,michael_b/stock-unusual-options-activity"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/gsA828eDewbJE55jn/builds/EoxDtb4PxdXMJh8m5/openapi.json
