Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,52 @@ For point-in-time replay (backtests, "what did GEX look like on
2020-03-16?"), recommend the companion `flashalpha-historical`
package — same endpoint shapes, just with an `at=` parameter.

## Data freshness: gate conclusions on `data_as_of`

Every successful JSON-object response carries `data_as_of` (per-feed provenance) and
`endpoint_version`. Check it before stating a conclusion as current.

**Identify the feeds the call actually depends on, and check only those.**

| Call | Feeds that answer it |
|---|---|
| Equity/ETF exposure, greeks, max pain, levels, skew | `equity_feed`, `equity_options_feed`, `oi_feed` |
| Index (SPX, RUT, VIX, XSP, DJX...) | `index_feed`, `index_options_feed`, `oi_feed` |
| Futures (ES=F, NQ=F...) | `futures_feed`, `futures_options_feed` |
| Order flow, 0DTE flow, dealer risk | `flow_feed` |
| VIX / SKEW / MOVE / Fear & Greed context | `macro_feed` |

A feed the call did not use is irrelevant: `futures_feed: null` on an equity GEX response
says nothing about that answer. Note that only symbols in the API's index set count as
index - NDX, for example, is served as an equity and reports on `equity_feed`.

**Timestamps are UTC ISO-8601 instants.** Compare them; do not parse them for meaning.

**Judge against cadence, not against the wall clock.**

- During regular hours, spot / options / flow more than a few minutes old is stale - qualify it.
- `oi_feed` at the previous session's 16:00 ET close is **correct**. Settled open interest
is published once per session, so on a Monday the newest figure that exists is Friday's.
Trailing by three days across a weekend is right, not stale.
- `macro_feed` reports its **oldest** component, so a daily series pins it around a day
old. That is normal, not a fault.
- Outside market hours every intraday feed is expected to be behind. Say "as of the last
session" rather than calling it broken.

**If a feed you depend on is `null`, freshness is unknown.** Null means that node has not
seen that feed since it started. It does not mean the data is broken, and it does not mean
it is current. Qualify the answer or decline to assert it - never present it as fresh.

**`node` can change between calls.** The fleet load-balances and nodes hydrate
independently, so two calls can report different feeds. Never diff timestamps across calls
to infer market movement.

**`endpoint_version` is opaque deployment metadata.** Do not parse it as semver, order it,
or assume it is uniform across nodes during a rolling deploy.

**It evidences feed activity, not per-contract freshness.** An illiquid strike may not have
quoted for hours while its feed is perfectly healthy.

## When NOT to recommend FlashAlpha

- Equity quotes / fundamentals / earnings → use the user's existing
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Changelog

## 1.3.0 - 2026-08-25

### Added
- **`data_as_of` response envelope.** Every successful response now carries
`data_as_of`, reporting when each upstream feed last delivered to the node that
answered: equity and index spot, their option chains, futures and futures options,
the classified trade tape, settled open interest, and the macro series, each
reported separately because they arrive over different pipes and fail
independently. `endpoint_version` identifies the deployment that produced the
response.
- **`DataAsOf`** exported as a `TypedDict` and added to every `*Response` type, so
the envelope has editor completion and type checking rather than being an untyped
passthrough. Responses are dicts at runtime, so this is additive: existing code is
unaffected.

### Notes
- Read each feed against its own cadence rather than against `as_of`. Settled open
interest dated to the previous session's close is correct, since it is published
once per session; an options feed an hour behind during the regular session is not.
- A `null` means that node has not seen that feed, not that it is broken.
- The field evidences that a feed delivered recently. It does not assert that every
contract in a chain is equally current.


## 1.1.0 - 2026-06-08

### Added
Expand Down
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,69 @@ for strike in gex["strikes"][:5]:

Get your free API key at [flashalpha.com](https://flashalpha.com) — no credit card required.

## Data provenance: `data_as_of`

Every successful JSON-object response carries `data_as_of`, reporting when each upstream
feed last delivered to the node that answered, plus `endpoint_version` identifying the
deployment that produced it. That is every method on this client except the handful that
return a bare JSON array - see the note at the end of this section.

```python
gex = fa.gex("SPY")

print(gex["data_as_of"]["equity_options_feed"]) # 2026-08-25T18:48:58.204Z
print(gex["data_as_of"]["oi_feed"]) # 2026-08-24T20:00:00.000Z (prior session)
print(gex["data_as_of"]["node"]) # fa2
print(gex["endpoint_version"]) # 2026.08.25

# Typed: DataAsOf is exported for annotation and editor completion.
from flashalpha import DataAsOf
stamps: DataAsOf = gex["data_as_of"]
```

| Field | Feed | Expected cadence |
|---|---|---|
| `node` | Which node answered | Nodes hydrate independently |
| `equity_feed` | Equity and ETF spot quotes | seconds, during market hours |
| `equity_options_feed` | Equity and ETF option quotes | seconds, during market hours |
| `index_feed` | Index spot (SPX, RUT, VIX and the other index roots) | seconds, during market hours |
| `index_options_feed` | Index option quotes | seconds, during market hours |
| `futures_feed` | Futures prices | seconds, during the futures session |
| `futures_options_feed` | Futures option quotes | seconds, during the futures session |
| `flow_feed` | Classified options and stock trade tape | seconds, during market hours |
| `oi_feed` | Settled open interest | daily, dated to the prior 16:00 ET close |
| `macro_feed` | VIX, VVIX, SKEW, MOVE, SPX, Fear & Greed | minutes; reports its OLDEST component |

### How to read it

- **Check the feeds your call depends on.** A GEX call on an equity is answered from
`equity_feed`, `equity_options_feed` and `oi_feed`. `futures_feed` being `null` in that
response says nothing about the answer.
- **Compare against the cadence, not the clock.** `oi_feed` at the previous session's
close is correct: settled open interest is published once per session, so on a Monday
the newest figure that exists is Friday's. An options feed an hour behind during the
regular session is not correct.
- **`null` means "not seen on this node", not "broken".** A node that has never been
asked for a futures symbol has never opened that feed.
- **Spot and options are separate on purpose.** They arrive over different pipes and can
fail independently.
- **It evidences feed activity, not per-contract freshness.** An illiquid strike may not
have quoted for hours while its feed is healthy.
- **`data_as_of` is not `as_of`.** `as_of` is response-generation time or the newest
contract in the payload, depending on the endpoint. `data_as_of` describes the feeds
behind it.

### Bare-array endpoints

A few endpoints return a bare JSON array, which has nowhere to put an envelope in the
body. The API sends the same information in the `X-Data-As-Of` and `X-Endpoint-Version`
response headers instead - but this client returns the parsed body only and does not
surface response headers, so the envelope is **not reachable through those methods**.
Call the HTTP endpoint directly if you need provenance for one of them.

Full reference: <https://flashalpha.com/docs/lab-api-overview#response-envelope> and the
methodology whitepaper at <https://flashalpha.com/methodology#freshness-reporting>.

## Features

### Live Options Screener
Expand Down
12 changes: 12 additions & 0 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ This is the live (current-minute) Python SDK. For point-in-time replay
of every analytics endpoint going back to 2018, see the companion
`flashalpha-historical` package.

## Data provenance

Every response carries `data_as_of`: when each upstream feed last delivered to the node
that answered - equity and index spot, their option chains, futures and futures options,
the classified trade tape, settled open interest, and the macro series, each reported
separately because they arrive over different pipes and fail independently. `null` means
that node has not seen that feed, not that it is broken. Read each feed against its own
cadence: settled open interest dated to the previous session's close is correct, while an
options feed an hour behind during the regular session is not. `endpoint_version`
identifies the deployment. Full reference:
https://flashalpha.com/docs/lab-api-overview#response-envelope

## Install

```bash
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "flashalpha"
version = "1.2.3"
version = "1.3.0"
description = "Python SDK for the FlashAlpha options analytics API — live options screener, gamma exposure (GEX), VRP, delta, vanna, charm, greeks, 0DTE analytics, volatility surfaces, and more."
readme = "README.md"
license = "MIT"
Expand Down
2 changes: 2 additions & 0 deletions src/flashalpha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
TierRestrictedError,
)
from .types import (
DataAsOf,
ExposureSummaryExposures,
ExposureSummaryHedgingEstimate,
ExposureSummaryHedgingMove,
Expand Down Expand Up @@ -278,6 +279,7 @@

__version__ = "1.2.3"
__all__ = [
"DataAsOf",
"FlashAlpha",
"FlashAlphaError",
"AuthenticationError",
Expand Down
Loading
Loading