# apps2 · ants

Retail-participation lens for zero-day options.

The app deliberately separates three different things that are often collapsed into
one misleading "retail" number:

1. **OCC customer-side 0DTE volume** — real clearing data, exact for the selected
   expiry and underlyings, but "customer" includes professional and institutional
   customer accounts. It is an upper bound/context series, not retail-only volume.
2. **Tiny-ticket 0DTE flow** — a tape-derived retail proxy. The default policy keeps
   non-complex tickets of 1–5 contracts and at most $1,000 premium. Small tickets can
   still be sliced institutional orders, and retail can trade larger tickets.
3. **WSB activity** — comments, unique pseudonymous authors, and 0DTE mentions in
   rolling 12-hour windows. This is an attention proxy, not traded volume.
4. **Robinhood app acquisition** — optional daily third-party download estimates.
   Exact downloads are visible only to Robinhood in App Store Connect / Play Console;
   competitor-intelligence values must remain labeled estimates.

## What the P&L chart means

Public OPRA prints contain contracts, prices, and timestamps, not brokerage account
IDs or complete positions. Therefore it is not possible to calculate "the percent of
unique retail traders in profit" from public tape.

The chart instead shows the outcome distribution of **estimated tiny buyer-initiated
0DTE tickets**. Each accepted ticket is marked to the executable bid at 15 minutes,
1 hour, the close, or expiry. This answers "what fraction of small tickets would have
been profitable at this horizon?" It does not claim that tickets are unique traders.

The frontend never silently substitutes demo values. Add `?demo=1` to the page URL
only when testing the chart; a large DEMO DATA banner is shown.

## Files and data

- `ants.php` reads the three JSONL banks and exposes one response to the browser.
- `local-only/backfill_occ_0dte.py` downloads free OCC Volume Query data and banks
  one daily aggregate row.
- `local-only/build_tiny_0dte.py` converts normalized OPRA/Massive tiny-ticket rows
  into the chart's daily policy and P&L schema.
- `local-only/record_wsb.py` uses Reddit OAuth, stores no comment text or usernames,
  and banks rolling 12-hour counts.
- `local-only/import_robinhood_downloads.py` imports a daily Sensor Tower or
  Similarweb CSV export without storing vendor credentials in the web app.

Data directories:

```text
data/
  occ_0dte/YYYY-MM.jsonl
  tiny_0dte/YYYY-MM.jsonl
  wsb/YYYY-MM.jsonl
  app_downloads/YYYY-MM.jsonl
```

## Run the free OCC backfill

From this directory:

```powershell
python local-only/backfill_occ_0dte.py --days 45
```

The default universe is `SPY,QQQ,IWM,SPX`. The script requests all OCC account
types in one response per symbol/date and skips weekends, holidays, and rows already
banked.

## Build the tiny-ticket bank

First normalize trade-and-mark observations to JSONL:

```json
{"ts":"2026-07-23T14:31:04Z","symbol":"SPY260723C00640000","underlying":"SPY","expiry":"2026-07-23","right":"C","price":0.42,"size":2,"bid":0.41,"ask":0.42,"complex":false,"marks":{"15m_bid":0.48,"1h_bid":0.36,"close_bid":0.15,"expiry_value":0.0}}
```

Then:

```powershell
python local-only/build_tiny_0dte.py normalized-tickets.jsonl
```

The normalizer should use OPRA TCBBO (trade plus pre-trade NBBO) or Massive trades
plus quotes. OPRA does not publish the aggressor side, so the script infers buy/sell
from the trade price versus the NBBO midpoint and excludes midpoint/unknown trades
from P&L.

## Record WSB rolling activity

Create a Reddit "script" or installed application and set:

```text
REDDIT_CLIENT_ID
REDDIT_CLIENT_SECRET
REDDIT_USER_AGENT
```

Run every 2–5 minutes:

```powershell
python local-only/record_wsb.py
```

The local SQLite dedupe bank stores comment IDs, timestamps, hashed authors, and
classification flags only. It does not retain bodies or usernames. Confirm that the
use complies with Reddit's current Data API terms, especially for commercial use.

## Robinhood downloads per day

For an outside observer these are estimates, not an official count. Sensor Tower
Store Intelligence and Similarweb App Intelligence both offer daily download series.
Use both platforms separately when possible:

```text
iOS app ID:      938003185
Android package: com.robinhood.android
```

Normalize an export to either wide form:

```csv
date,ios_downloads,android_downloads
2026-07-23,1200,1900
```

or long form:

```csv
date,platform,downloads
2026-07-23,ios,1200
2026-07-23,android,1900
```

Then bank it:

```powershell
python local-only/import_robinhood_downloads.py export.csv --source similarweb
```

Do not use the public Google Play `10M+` install bucket as a daily series, and do not
infer a numeric download count from App Store rank without a separately calibrated
rank-to-download model. Rank is still useful as a free directional proxy.

## Sources

- OCC Volume Query batch parameters and account-type fields:
  https://www.theocc.com/market-data/market-data-reports/other-market-data-info/batch-processing/volume-query-batch-processing
- Databento OPRA and TCBBO:
  https://databento.com/docs/venues-and-datasets/opra-pillar
- Massive options trades:
  https://massive.com/docs/rest/options/trades-quotes/trades
- Cboe Open-Close (paid origin-coded benchmark):
  https://datashop.cboe.com/cboe-options-open-close-volume-summary
- Reddit Data API terms:
  https://redditinc.com/policies/data-api-terms
- Similarweb App Intelligence downloads:
  https://support.similarweb.com/hc/en-us/articles/25657774735133-Using-Store-Downloads
- Sensor Tower daily download estimates:
  https://sensortower.com/blog/faster-better-app-store-intelligence

