# US Stock Screener - Backtested Opportunity Score (`assured_vertigo/us-stock-opportunity-scanner`) Actor

Scores US stocks 0-100 on trend, momentum, fundamentals and news sentiment with a backtest-calibrated model, and returns a ranked verdict per ticker with the reasons behind it. Optional SEC EDGAR insider-buying and 13F fund-ownership layer. No API key.

- **URL**: https://apify.com/assured\_vertigo/us-stock-opportunity-scanner.md
- **Developed by:** [Mustafa Abut](https://apify.com/assured_vertigo) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## US Stock Screener — Backtested Opportunity Score

Most finance scrapers hand you fields. This one hands you a **ranked shortlist with reasons**.

Every US ticker you feed it gets a **0–100 opportunity score** built from trend, 6-month momentum, fundamentals and news sentiment, a **verdict** (`STRONG_CANDIDATE` / `CANDIDATE` / `WATCHLIST` / `WEAK`), and a plain-English list of **why** it scored that way. The weights are not guesses — they come from a 5-year walk-forward backtest (method and numbers below).

Optional second layer, straight from **SEC EDGAR**: open-market insider buying and selling (Form 4) and which of 19 well-known managers hold the stock (13F).

No API key. No login. No data-vendor contract.

***

### What you get per stock

```json
{
  "symbol": "NVDA",
  "companyName": "NVIDIA Corporation",
  "sector": "Technology",
  "price": 225.07,
  "score": 90.6,
  "verdict": "STRONG_CANDIDATE",
  "technicalScore": 82.0,
  "fundamentalScore": 100.0,
  "newsScore": 85.0,
  "momentum6mPct": 34.66,
  "distanceFrom52wHighPct": -4.31,
  "rsi14": 55.2,
  "goldenCross": true,
  "forwardPE": 14.35,
  "peg": 0.48,
  "roePct": 117.21,
  "debtToEquity": 0.17,
  "analystUpsidePct": 45.6,
  "analystRating": 1.3,
  "signalCodes": ["ABOVE_SMA200", "GOLDEN_CROSS", "MOMENTUM_STRONG", "ATTRACTIVE_PEG"],
  "signals": [
    "Price is above the 200-day moving average (long-term trend is positive)",
    "Golden cross active (SMA50 > SMA200)",
    "Strong 6-month momentum (35%)",
    "Attractive PEG (0.48) - cheap relative to growth",
    "Analyst consensus: STRONG BUY"
  ]
}
```

`signalCodes` is the stable machine-readable contract — branch on it in your pipeline or agent. `signals` is the same thing in words, for humans and for LLM context.

Full field list: scores and verdict · price, market cap, sector, industry · SMA50/SMA200, RSI14, MACD histogram, 6-month momentum, distance from the 52-week high, annualised volatility, golden cross · revenue and earnings growth, net margin, ROE, debt/equity, forward P/E, PEG, free cash flow, analyst target upside and consensus rating · news counts and the top headlines with sentiment and links · recent candlestick patterns · optional insider and 13F block.

***

### Inputs

| Field | What it does |
|---|---|
| `universe` | `curated` (~80 liquid large caps, fast), `custom` (your own list), `sp500`, `nasdaq100` |
| `symbols` | Your tickers when `universe` is `custom` — Yahoo notation, share classes with a dash (`BRK-B`) |
| `sector` | Scan one curated sector only (`Technology`, `Financials`, `Healthcare`, `Energy`, …) |
| `maxSymbols` | Hard cap per run — keeps cost and runtime predictable |
| `minScore` | Return only stocks at or above this score |
| `verdicts` | Return only these verdicts, e.g. just `STRONG_CANDIDATE` |
| `includeNews` | 14-day headline sentiment (15% of the score) — off makes runs faster |
| `includeInsiderData` | SEC Form 4 insider buys/sells |
| `insiderWindowDays` | Form 4 lookback, default 90 days |
| `includeFundHoldings` | 13F check against 19 tracked managers |
| `concurrency` | Parallel fetches, 1–8 |
| `proxyConfiguration` | Recommended for runs over ~50 tickers |

**How you are charged.** One event per stock successfully scored, plus one event per stock when the SEC insider layer runs. Tickers with too little data are never charged. `minScore` and `verdicts` shape the output for free — they do not change the price, because the scoring work has already happened. Cap your spend per run with `maxSymbols`.

***

### How the score is built

| Component | Weight | Contents |
|---|---|---|
| Technical | 40% | 6-month momentum (40 pts), trend filter — above SMA200 + golden cross (25 pts), distance from the 52-week high / pullback inside an uptrend (20 pts), RSI (10 pts), MACD (5 pts) |
| Fundamental | 45% | Revenue and earnings growth, net margin, ROE, debt/equity, forward P/E, PEG, free cash flow, analyst target and consensus |
| News | 15% | Positive/negative keyword sentiment over the last 14 days of headlines |

Verdict thresholds: **≥70 and both technical and fundamental ≥55 → `STRONG_CANDIDATE`** · ≥60 → `CANDIDATE` · ≥48 → `WATCHLIST` · below → `WEAK`.

The bias is explicitly medium-to-long-term, not day trading: overbought extremes (RSI > 78) are penalised, and a 5–25% pullback inside an intact uptrend is rewarded as an entry setup rather than treated as weakness.

#### The backtest behind the weights

Over 5 years, 84 tickers and 3,523 monthly samples, the technical score was recomputed at each historical month **using only data available on that date** (no look-ahead), then compared with the following 6- and 12-month return versus SPY.

- Stocks scoring 70+ beat SPY by **+8.4% on average over 6 months** (win rate 56%); the in-sample 12-month figure was +17.3%, but see the caveat below — the 12-month edge did **not** survive out-of-sample testing. Treat this as a 6-month-horizon signal.
- Before calibration the model could not separate winners from losers at all (information coefficient ≈ 0.03). Momentum was the only component with real predictive power, so it carries the most weight; RSI and MACD were cut to 10 and 5 points because their standalone IC was near zero.
- The "close to the 52-week high" bonus was shrunk after it showed *negative* correlation with forward excess return, while pullbacks inside an uptrend showed positive.
- Out-of-sample check: on the 2017–2022 period the calibration never saw, the calibrated model still beat the pre-calibration version (IC 0.036 vs 0.006; top-quintile excess return +5.8% vs +3.9%).
- Year-by-year: IC positive in 7 of 9 years. The weak years were 2018 and 2021 — signals issued ahead of a bear market, the well-known failure mode of momentum. No catastrophic year (worst top-quintile excess return −1.0%).
- Parameter sensitivity: moving the momentum and 52-week-high thresholds ±30% keeps IC in a 0.038–0.050 band, so the result is not a knife-edge fit.
- Candlestick patterns are detected and reported but **deliberately excluded from the score** — in a 10-year test only bullish engulfing beat the baseline meaningfully (+4.1% / 63 days vs +2.1%), and the rest carried no information.

**What the backtest does not cover.** Only the *technical* component can be tested historically — Yahoo publishes no archive of past fundamentals or headlines, so the fundamental (45%) and news (15%) weights are reasoned, not backtested. The test universe is today's constituent list, so it carries survivorship bias that free data cannot fully remove (no point-in-time universe). Transaction costs and T+1 execution lag are not modelled. And the edge is concentrated in the 6-month horizon; the 12-month out-of-sample IC was ≈ 0.

So this validates **relative ranking over ~6 months**, not absolute return. It is evidence that the ordering carries information — not a promise about any future period.

***

### Typical uses

- **Weekly shortlist.** Scan the S\&P 500 with `minScore: 70`, get back only what clears the bar, ranked.
- **Watchlist triage.** Feed your own 30 tickers and see which ones the model still likes and, more usefully, *which signals broke*.
- **Agent tool.** Small input schema, stable `signalCodes`, one verdict field — cheap for an LLM agent to call and reason over without parsing 80 raw metrics.
- **Insider confirmation.** Run the shortlist again with `includeInsiderData` to see whether insiders were buying alongside the score.
- **Dashboards and sheets.** Export to JSON, CSV or Excel, or pull the dataset over the Apify API.

### Limitations — read these

- Data comes from Yahoo Finance and SEC EDGAR. Yahoo's summary fundamentals are occasionally stale or missing; a missing metric lowers its weight instead of penalising the company, and a company with no fundamental data at all scores a neutral 50.
- Needs about a year of price history (220 sessions). Recent IPOs are skipped and reported in the run summary.
- News sentiment is keyword-based, not a language model. The Yahoo feed mixes in loosely related market stories, which is exactly why news is only 15% of the score.
- Insider (Form 4) data lags ~2 days; 13F data lags up to 45 days and only covers the 19 tracked managers, not every holder.
- The insider layer is reported **next to** the score, never folded into it, because the backtest that set the weights did not include SEC data.
- Yahoo rate-limits datacenter IP ranges. Use the proxy option and keep `concurrency` moderate on large universes.

### Disclaimer

This Actor is an automated **screening** tool. It is not investment advice, not a recommendation to buy or sell any security, and not a guarantee of any outcome. Backtested results do not predict future performance. Do your own research and consider your own circumstances before making any investment decision.

### Notes

- Data sources: Yahoo Finance (prices, fundamentals, headlines), SEC EDGAR (Form 4, 13F-HR), Wikipedia (index constituent lists).
- The SEC requires a contact address in the User-Agent of every request. Set the `SEC_CONTACT_EMAIL` environment variable if you run this Actor as your own copy.
- Found a bug or want another market or metric? Open an issue on the Actor's Issues tab.

# Actor input Schema

## `universe` (type: `string`):

Which set of tickers to score. Use 'custom' together with the Symbols field to scan your own list.

## `symbols` (type: `array`):

Tickers to scan when Universe is 'custom', e.g. NVDA, MSFT, LLY. Yahoo Finance notation: share classes use a dash (BRK-B).

## `sector` (type: `string`):

Scan a single curated sector, e.g. Technology, Financials, Healthcare, Energy. Partial names are accepted. Leave empty for all sectors.

## `maxSymbols` (type: `integer`):

Hard cap on how many tickers are analysed in this run. Keeps cost and runtime predictable on the large universes.

## `minScore` (type: `integer`):

Only return stocks scoring at or above this value (0-100). You are charged only for the stocks actually returned.

## `verdicts` (type: `array`):

Keep only these verdicts. Leave empty to return every scanned stock.

## `includeNews` (type: `boolean`):

Score the last 14 days of headlines (15% of the total score) and return the top headlines. Turn off for a faster, price-and-fundamentals-only run.

## `includeInsiderData` (type: `boolean`):

Add open-market insider buying and selling from SEC EDGAR. Reported alongside the score, never folded into it. Adds several seconds per ticker.

## `insiderWindowDays` (type: `integer`):

How far back to read Form 4 filings.

## `includeFundHoldings` (type: `boolean`):

Check whether 19 well-known managers (Berkshire, Scion, Pershing Square, ARK and others) hold the stock, per their latest 13F filing. Requires the insider layer.

## `concurrency` (type: `integer`):

How many tickers to fetch in parallel. Lower this if Yahoo Finance starts rate-limiting your run.

## `proxyConfiguration` (type: `object`):

Yahoo Finance rate-limits datacenter IP ranges. Using a proxy is recommended for runs over ~50 tickers.

## Actor input object example

```json
{
  "universe": "curated",
  "symbols": [
    "NVDA",
    "MSFT",
    "LLY",
    "JPM",
    "XOM"
  ],
  "maxSymbols": 40,
  "minScore": 0,
  "includeNews": true,
  "includeInsiderData": false,
  "insiderWindowDays": 90,
  "includeFundHoldings": false,
  "concurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `opportunities` (type: `string`):

One item per scored stock: opportunity score, verdict, component scores, technical and fundamental metrics, news sentiment, signal codes and the plain-English reasons behind the score. Sorted by score, highest first.

## `summary` (type: `string`):

Counts of stocks scanned, returned and skipped, verdict breakdown, the top symbols of the run and the screening disclaimer.

# 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 = {
    "symbols": [
        "NVDA",
        "MSFT",
        "LLY",
        "JPM",
        "XOM"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("assured_vertigo/us-stock-opportunity-scanner").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 = { "symbols": [
        "NVDA",
        "MSFT",
        "LLY",
        "JPM",
        "XOM",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("assured_vertigo/us-stock-opportunity-scanner").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 '{
  "symbols": [
    "NVDA",
    "MSFT",
    "LLY",
    "JPM",
    "XOM"
  ]
}' |
apify call assured_vertigo/us-stock-opportunity-scanner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,assured_vertigo/us-stock-opportunity-scanner"
        }
    }
}
```

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/9ZUle2vkPdm2yQ3xn/builds/4wj2h0uIAbHz5z6eQ/openapi.json
