# AGENTS.md — ImpliedOptions

Instructions for coding agents integrating US equity options data. Every capability is a typed tool callable over REST or MCP; the same names appear in the SDKs.

## Setup

```bash
npm i @impliedoptions/sdk        # TypeScript / Node
pip install impliedoptions       # Python
export IMPLIEDOPTIONS_API_KEY=io_live_…      # create at https://impliedoptions.com/account
```

- OpenAPI 3.1: https://impliedoptions.com/api/v1/openapi.json (use it for codegen or ChatGPT Actions)
- MCP (Streamable HTTP): https://impliedoptions.com/mcp — add it to Claude, Cursor or any MCP host; OAuth 2.1 with PKCE is discovered automatically, or append `?api_key=io_live_…`
- Tool registry JSON: https://impliedoptions.com/api/v1/tools
- Skill file: https://impliedoptions.com/SKILL.md · Index: https://impliedoptions.com/llms.txt

## Conventions

- Tool names are `namespace.verb` (`flow.search`, `pnl.model`). Symbols are uppercase; dates are `YYYY-MM-DD`; timestamps are ISO-8601; money is a USD number.
- `POST /api/v1/tools/{name}` takes the arguments as the JSON body (or `{ "args": { … } }`). Read tools also accept `GET` with flat query params; arrays as CSV, nested objects as JSON strings.
- Always read `meta.freshness` (`realtime` | `delayed`) and `meta.truncated`/`meta.total` before summarizing results. Follow `meta.next_cursor` for more rows.
- Write tools (positions, watchlist, alerts): send `Idempotency-Key` so retries are safe; send `X-Dry-Run: true` to preview a change without persisting it.
- Handle `application/problem+json` errors by `type`; back off on `quota_exceeded` for `retry_after` seconds and on `upstream_unavailable`.

## Recipes

### Scan options flow — `flow.search`

Unusual options prints (blocks, sweeps, complex, strategies) filtered by ticker, type, side, premium and DTE. Free callers get 15-minute delayed prints capped at 50 rows.

```bash
curl -X POST https://impliedoptions.com/api/v1/tools/flow.search \
  -H "Authorization: Bearer $IMPLIEDOPTIONS_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"tickers":["SPY","QQQ"],"min_premium":1000000,"limit":20}'
```

SDK:

```ts
import { ImpliedOptions } from '@impliedoptions/sdk';
const io = new ImpliedOptions({ apiKey: process.env.IMPLIEDOPTIONS_API_KEY });
const { data, meta } = await io.call('flow.search', {"tickers":["SPY","QQQ"],"min_premium":1000000,"limit":20});
```

```python
import os
from impliedoptions import ImpliedOptions
io = ImpliedOptions(api_key=os.environ["IMPLIEDOPTIONS_API_KEY"])
result = io.call("flow.search", {"tickers": ["SPY", "QQQ"], "min_premium": 1000000, "limit": 20})
```

### Pull an option chain — `chain.get`

Option chain for one expiration with bid/ask, volume, open interest, IV and greeks per contract. Defaults to the nearest expiration; free callers are 15-minute delayed and capped at 50 contracts.

```bash
curl -X POST https://impliedoptions.com/api/v1/tools/chain.get \
  -H "Authorization: Bearer $IMPLIEDOPTIONS_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"symbol":"SPY","strikes_around":10}'
```

SDK:

```ts
import { ImpliedOptions } from '@impliedoptions/sdk';
const io = new ImpliedOptions({ apiKey: process.env.IMPLIEDOPTIONS_API_KEY });
const { data, meta } = await io.call('chain.get', {"symbol":"SPY","strikes_around":10});
```

```python
import os
from impliedoptions import ImpliedOptions
io = ImpliedOptions(api_key=os.environ["IMPLIEDOPTIONS_API_KEY"])
result = io.call("chain.get", {"symbol": "SPY", "strikes_around": 10})
```

### Model a multi-leg P&L — `pnl.model`

Black-Scholes P&L for a 1–8 leg option position: P&L grid now and at expiration, per-leg and net Greeks, break-evens, max profit/loss and probability of profit. Missing prices, IVs and spot are marked from the live chain.

```bash
curl -X POST https://impliedoptions.com/api/v1/tools/pnl.model \
  -H "Authorization: Bearer $IMPLIEDOPTIONS_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"legs":[{"occ":"SPY260918C00620000","qty":1},{"occ":"SPY260918C00640000","qty":-1}]}'
```

SDK:

```ts
import { ImpliedOptions } from '@impliedoptions/sdk';
const io = new ImpliedOptions({ apiKey: process.env.IMPLIEDOPTIONS_API_KEY });
const { data, meta } = await io.call('pnl.model', {"legs":[{"occ":"SPY260918C00620000","qty":1},{"occ":"SPY260918C00640000","qty":-1}]});
```

```python
import os
from impliedoptions import ImpliedOptions
io = ImpliedOptions(api_key=os.environ["IMPLIEDOPTIONS_API_KEY"])
result = io.call("pnl.model", {"legs": [{"occ": "SPY260918C00620000", "qty": 1}, {"occ": "SPY260918C00640000", "qty": -1}]})
```

### Add symbols to the watchlist — `watchlist.add`

Adds symbols to the watchlist; symbols already present are ignored.

```bash
curl -X POST https://impliedoptions.com/api/v1/tools/watchlist.add \
  -H "Authorization: Bearer $IMPLIEDOPTIONS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d '{"symbols":["SPY","NVDA"]}'
```

SDK:

```ts
import { ImpliedOptions } from '@impliedoptions/sdk';
const io = new ImpliedOptions({ apiKey: process.env.IMPLIEDOPTIONS_API_KEY });
const { data, meta } = await io.call('watchlist.add', {"symbols":["SPY","NVDA"]});
```

```python
import os
from impliedoptions import ImpliedOptions
io = ImpliedOptions(api_key=os.environ["IMPLIEDOPTIONS_API_KEY"])
result = io.call("watchlist.add", {"symbols": ["SPY", "NVDA"]})
```

### Create a flow alert — `alerts.create`

Creates a flow alert that fires when new options flow matches the filters. Free accounts may hold one alert.

```bash
curl -X POST https://impliedoptions.com/api/v1/tools/alerts.create \
  -H "Authorization: Bearer $IMPLIEDOPTIONS_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d '{"name":"NVDA big call sweeps","filters":{"tickers":["NVDA"],"rights":["C"],"min_premium":500000}}'
```

SDK:

```ts
import { ImpliedOptions } from '@impliedoptions/sdk';
const io = new ImpliedOptions({ apiKey: process.env.IMPLIEDOPTIONS_API_KEY });
const { data, meta } = await io.call('alerts.create', {"name":"NVDA big call sweeps","filters":{"tickers":["NVDA"],"rights":["C"],"min_premium":500000}});
```

```python
import os
from impliedoptions import ImpliedOptions
io = ImpliedOptions(api_key=os.environ["IMPLIEDOPTIONS_API_KEY"])
result = io.call("alerts.create", {"name": "NVDA big call sweeps", "filters": {"tickers": ["NVDA"], "rights": ["C"], "min_premium": 500000}})
```

## Errors

| type | status | meaning |
|---|---|---|
| unauthorized | 401 | Missing or invalid credentials |
| forbidden | 403 | Credential lacks the required scope |
| tier_required | 403 | Tool needs the Paid plan |
| quota_exceeded | 429 | Daily call limit hit; `retry_after` is seconds until reset |
| invalid_args | 400 | Arguments failed validation; see `errors[]` (`path`, `message`) |
| symbol_unknown | 404 | Ticker not found |
| not_found | 404 | Tool or record not found |
| conflict | 409 | Write conflicts with existing state |
| upstream_unavailable | 502 | Data source unreachable; retry with backoff |
| internal | 500 | Unexpected failure |

## Limits

- Free: 15-minute delayed data, 50 rows per response, 500 calls/day. Anonymous runs share a smaller per-IP quota.
- Paid: realtime data, up to 2000 rows per response, unlimited calls. Responses report meta.truncated and meta.total when capped.
