flashalpha-historical 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,225 @@
1
+ # Local tooling / secrets — never commit
2
+ .claude/
3
+ CLAUDE.md
4
+ .env
5
+ .env.*
6
+
7
+ # Sandbox / agent / harness caches that may land at repo root
8
+ .review-artifacts/
9
+ .agent-tools/
10
+
11
+ # Byte-compiled / optimized / DLL files
12
+ __pycache__/
13
+ *.py[codz]
14
+ *$py.class
15
+
16
+ # C extensions
17
+ *.so
18
+
19
+ # Distribution / packaging
20
+ .Python
21
+ build/
22
+ develop-eggs/
23
+ dist/
24
+ downloads/
25
+ eggs/
26
+ .eggs/
27
+ lib/
28
+ lib64/
29
+ parts/
30
+ sdist/
31
+ var/
32
+ wheels/
33
+ share/python-wheels/
34
+ *.egg-info/
35
+ .installed.cfg
36
+ *.egg
37
+ MANIFEST
38
+
39
+ # PyInstaller
40
+ # Usually these files are written by a python script from a template
41
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
42
+ *.manifest
43
+ *.spec
44
+
45
+ # Installer logs
46
+ pip-log.txt
47
+ pip-delete-this-directory.txt
48
+
49
+ # Unit test / coverage reports
50
+ htmlcov/
51
+ .tox/
52
+ .nox/
53
+ .coverage
54
+ .coverage.*
55
+ .cache
56
+ nosetests.xml
57
+ coverage.xml
58
+ *.cover
59
+ *.py.cover
60
+ .hypothesis/
61
+ .pytest_cache/
62
+ cover/
63
+
64
+ # Translations
65
+ *.mo
66
+ *.pot
67
+
68
+ # Django stuff:
69
+ *.log
70
+ local_settings.py
71
+ db.sqlite3
72
+ db.sqlite3-journal
73
+
74
+ # Flask stuff:
75
+ instance/
76
+ .webassets-cache
77
+
78
+ # Scrapy stuff:
79
+ .scrapy
80
+
81
+ # Sphinx documentation
82
+ docs/_build/
83
+
84
+ # PyBuilder
85
+ .pybuilder/
86
+ target/
87
+
88
+ # Jupyter Notebook
89
+ .ipynb_checkpoints
90
+
91
+ # IPython
92
+ profile_default/
93
+ ipython_config.py
94
+
95
+ # pyenv
96
+ # For a library or package, you might want to ignore these files since the code is
97
+ # intended to run in multiple environments; otherwise, check them in:
98
+ # .python-version
99
+
100
+ # pipenv
101
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
102
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
103
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
104
+ # install all needed dependencies.
105
+ #Pipfile.lock
106
+
107
+ # UV
108
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
109
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
110
+ # commonly ignored for libraries.
111
+ #uv.lock
112
+
113
+ # poetry
114
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
115
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
116
+ # commonly ignored for libraries.
117
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
118
+ #poetry.lock
119
+ #poetry.toml
120
+
121
+ # pdm
122
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
123
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
124
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
125
+ #pdm.lock
126
+ #pdm.toml
127
+ .pdm-python
128
+ .pdm-build/
129
+
130
+ # pixi
131
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
132
+ #pixi.lock
133
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
134
+ # in the .venv directory. It is recommended not to include this directory in version control.
135
+ .pixi
136
+
137
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
138
+ __pypackages__/
139
+
140
+ # Celery stuff
141
+ celerybeat-schedule
142
+ celerybeat.pid
143
+
144
+ # SageMath parsed files
145
+ *.sage.py
146
+
147
+ # Environments
148
+ .env
149
+ .envrc
150
+ .venv
151
+ env/
152
+ venv/
153
+ ENV/
154
+ env.bak/
155
+ venv.bak/
156
+
157
+ # Spyder project settings
158
+ .spyderproject
159
+ .spyproject
160
+
161
+ # Rope project settings
162
+ .ropeproject
163
+
164
+ # mkdocs documentation
165
+ /site
166
+
167
+ # mypy
168
+ .mypy_cache/
169
+ .dmypy.json
170
+ dmypy.json
171
+
172
+ # Pyre type checker
173
+ .pyre/
174
+
175
+ # pytype static type analyzer
176
+ .pytype/
177
+
178
+ # Cython debug symbols
179
+ cython_debug/
180
+
181
+ # PyCharm
182
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
183
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
184
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
185
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
186
+ #.idea/
187
+
188
+ # Abstra
189
+ # Abstra is an AI-powered process automation framework.
190
+ # Ignore directories containing user credentials, local state, and settings.
191
+ # Learn more at https://abstra.io/docs
192
+ .abstra/
193
+
194
+ # Visual Studio Code
195
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
196
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
197
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
198
+ # you could uncomment the following to ignore the entire vscode folder
199
+ # .vscode/
200
+
201
+ # Ruff stuff:
202
+ .ruff_cache/
203
+
204
+ # PyPI configuration file
205
+ .pypirc
206
+
207
+ # Cursor
208
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
209
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
210
+ # refer to https://docs.cursor.com/context/ignore-files
211
+ .cursorignore
212
+ .cursorindexingignore
213
+
214
+ # Marimo
215
+ marimo/_static/
216
+ marimo/_lsp/
217
+ __marimo__/
218
+
219
+ # Sandbox / agent / harness caches (cross-ecosystem — defensive)
220
+ .dotnet/
221
+ .gocache/
222
+ .gopath/
223
+ .m2/
224
+ .review-artifacts/
225
+ .agent-tools/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FlashAlpha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: flashalpha-historical
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the FlashAlpha Historical API — point-in-time replay of GEX, gamma flip, VRP, narrative, max pain, and the full stock summary at any minute back to 2018-04-16. Backtest options strategies against 6.7B+ option rows.
5
+ Project-URL: Homepage, https://historical.flashalpha.com
6
+ Project-URL: Documentation, https://flashalpha.com/docs/historical
7
+ Project-URL: Repository, https://github.com/FlashAlpha-lab/flashalpha-historical-python
8
+ Project-URL: Issues, https://github.com/FlashAlpha-lab/flashalpha-historical-python/issues
9
+ Author-email: FlashAlpha <tom@flashalpha.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: 0dte,GEX history,SVI,VRP,as of,charm,dealer positioning,delta exposure,finance,gamma exposure,gex,historical options,historical options data,implied volatility,max pain,options,options API,options analytics,options backtest,options backtesting,options replay,point-in-time,quantitative finance,trading,vanna,variance risk premium,variance swap,volatility surface,zero dte
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Financial and Insurance Industry
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Office/Business :: Financial
23
+ Classifier: Topic :: Office/Business :: Financial :: Investment
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.10
26
+ Requires-Dist: requests>=2.28
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest-cov; extra == 'dev'
29
+ Requires-Dist: pytest>=7.0; extra == 'dev'
30
+ Requires-Dist: responses>=0.23; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # flashalpha-historical
34
+
35
+ Python SDK for the **FlashAlpha Historical API** — point-in-time replay of
36
+ every live analytics endpoint. Ask what GEX, gamma flip, VRP, narrative, max
37
+ pain, or the full stock summary looked like at any **minute back to
38
+ 2018-04-16**, in the same response shape as the live API.
39
+
40
+ Coverage: SPY 2018-04-16 → today, with daily extensions; more symbols
41
+ added on demand.
42
+
43
+ ```bash
44
+ pip install flashalpha-historical
45
+ ```
46
+
47
+ Requires Python 3.10+. Same `X-Api-Key` you use for `api.flashalpha.com`.
48
+ **Alpha plan or higher** on every endpoint.
49
+
50
+ ## Quickstart
51
+
52
+ ```python
53
+ from flashalpha_historical import FlashAlphaHistorical
54
+
55
+ hx = FlashAlphaHistorical("YOUR_API_KEY")
56
+
57
+ # One snapshot — what dealer positioning looked like during the COVID crash
58
+ snap = hx.exposure_summary("SPY", at="2020-03-16T15:30:00")
59
+ print(snap["regime"], snap["exposures"]["net_gex"])
60
+ # → 'negative_gamma' -2633970601
61
+ ```
62
+
63
+ The `at=` parameter accepts strings (`"2026-03-05T15:30:00"` or
64
+ `"2026-03-05"` → defaults to 16:00 ET), `datetime` objects, or `date` objects.
65
+
66
+ ## Backtesting
67
+
68
+ The SDK ships with replay utilities that turn any endpoint into an iterator
69
+ over a date / minute range. Holiday calendar is built in (NYSE 2018-2026);
70
+ gap days are skipped silently by default.
71
+
72
+ ### Daily replay
73
+
74
+ ```python
75
+ from flashalpha_historical import FlashAlphaHistorical, Backtester, iter_days
76
+
77
+ hx = FlashAlphaHistorical("YOUR_API_KEY")
78
+
79
+ def strategy(at, snap):
80
+ """Short vol when VRP rich AND dealers long gamma."""
81
+ vrp = snap["volatility"]["vrp"]
82
+ regime = snap["exposure"]["regime"]
83
+ return {
84
+ "signal": "short_strangle" if vrp > 5 and regime == "positive_gamma" else None,
85
+ "vrp": vrp,
86
+ "regime": regime,
87
+ }
88
+
89
+ bt = Backtester(hx, method="stock_summary", symbol="SPY")
90
+ results = bt.run(iter_days("2024-01-02", "2024-03-29"), strategy)
91
+
92
+ # Convert to DataFrame
93
+ import pandas as pd
94
+ df = pd.DataFrame(bt.to_records(results))
95
+ ```
96
+
97
+ ### Minute-level replay
98
+
99
+ ```python
100
+ from flashalpha_historical import iter_minutes, replay
101
+
102
+ # Walk every 15 minutes through one trading day
103
+ for at, snap in replay(hx, "exposure_summary", "SPY",
104
+ iter_minutes("2025-01-15", "2025-01-15", step_minutes=15)):
105
+ print(at, snap["regime"], snap["gamma_flip"], snap["exposures"]["net_gex"])
106
+ ```
107
+
108
+ > **Quota note:** every call counts against your daily plan quota (shared
109
+ > with the live API). 1-minute replay = 390 calls per analytic per day —
110
+ > coarsen with `step_minutes=15` or `step_minutes=30` for development loops.
111
+
112
+ ## API
113
+
114
+ Every analytics method takes a required `at` keyword argument.
115
+
116
+ ### Coverage
117
+
118
+ | Method | Endpoint |
119
+ |---|---|
120
+ | `tickers()` | `GET /v1/tickers` |
121
+ | `tickers(symbol="SPY")` | `GET /v1/tickers?symbol=SPY` |
122
+
123
+ ### Market data
124
+
125
+ | Method | Endpoint |
126
+ |---|---|
127
+ | `stock_quote(ticker, at=...)` | `/v1/stockquote/{ticker}` |
128
+ | `option_quote(ticker, at=..., expiry=, strike=, type=)` | `/v1/optionquote/{ticker}` |
129
+ | `surface(symbol, at=...)` | `/v1/surface/{symbol}` |
130
+
131
+ ### Exposure analytics
132
+
133
+ | Method | Endpoint |
134
+ |---|---|
135
+ | `gex(symbol, at=..., expiration=, min_oi=)` | `/v1/exposure/gex/{symbol}` |
136
+ | `dex(symbol, at=..., expiration=)` | `/v1/exposure/dex/{symbol}` |
137
+ | `vex(symbol, at=..., expiration=)` | `/v1/exposure/vex/{symbol}` |
138
+ | `chex(symbol, at=..., expiration=)` | `/v1/exposure/chex/{symbol}` |
139
+ | `exposure_summary(symbol, at=...)` | `/v1/exposure/summary/{symbol}` |
140
+ | `exposure_levels(symbol, at=...)` | `/v1/exposure/levels/{symbol}` |
141
+ | `narrative(symbol, at=...)` | `/v1/exposure/narrative/{symbol}` |
142
+ | `zero_dte(symbol, at=..., strike_range=)` | `/v1/exposure/zero-dte/{symbol}` |
143
+
144
+ ### Composite & vol
145
+
146
+ | Method | Endpoint |
147
+ |---|---|
148
+ | `stock_summary(symbol, at=...)` | `/v1/stock/{symbol}/summary` |
149
+ | `volatility(symbol, at=...)` | `/v1/volatility/{symbol}` |
150
+ | `adv_volatility(symbol, at=...)` | `/v1/adv_volatility/{symbol}` |
151
+ | `vrp(symbol, at=...)` | `/v1/vrp/{symbol}` |
152
+ | `max_pain(symbol, at=..., expiration=)` | `/v1/maxpain/{symbol}` |
153
+
154
+ ## Errors
155
+
156
+ ```python
157
+ from flashalpha_historical import (
158
+ FlashAlphaHistoricalError, # base
159
+ AuthenticationError, # 401
160
+ TierRestrictedError, # 403 — needs Alpha plan
161
+ InvalidAtError, # 400 — bad `at` format
162
+ NoDataError, # 404 — outside coverage / inside gap
163
+ SymbolNotFoundError, # 404 — symbol not at this `at`
164
+ NoCoverageError, # 404 — symbol not in historical dataset
165
+ InsufficientDataError, # 404 — surface grid too sparse
166
+ RateLimitError, # 429
167
+ ServerError, # 5xx
168
+ )
169
+
170
+ try:
171
+ hx.exposure_summary("SPY", at="2017-01-01") # before coverage starts
172
+ except NoDataError as e:
173
+ print("gap:", e)
174
+ ```
175
+
176
+ ## Known gaps from live (intentional, documented)
177
+
178
+ - `optionquote.bidSize` / `askSize` — always `0` (minute table has no sizes)
179
+ - `optionquote.volume` / `gex.call_volume` / `put_volume` — always `0`
180
+ - `optionquote.svi_vol` — `null` with `svi_vol_gated: "backtest_mode"`
181
+ - `narrative.data.top_oi_changes` — empty array (no prior-day OI diff yet)
182
+ - `gex.call_oi_change` / `put_oi_change` — always `null`
183
+ - `stock_summary.macro.vix_futures` / `fear_and_greed` — `null`
184
+ - `vrp.macro.hy_spread` — hard-coded `3.5`
185
+ - 0DTE intraday greeks (delta/gamma/theta/iv) often `0` / `null` — chain
186
+ still listed for OI analysis
187
+
188
+ ## License
189
+
190
+ MIT
@@ -0,0 +1,158 @@
1
+ # flashalpha-historical
2
+
3
+ Python SDK for the **FlashAlpha Historical API** — point-in-time replay of
4
+ every live analytics endpoint. Ask what GEX, gamma flip, VRP, narrative, max
5
+ pain, or the full stock summary looked like at any **minute back to
6
+ 2018-04-16**, in the same response shape as the live API.
7
+
8
+ Coverage: SPY 2018-04-16 → today, with daily extensions; more symbols
9
+ added on demand.
10
+
11
+ ```bash
12
+ pip install flashalpha-historical
13
+ ```
14
+
15
+ Requires Python 3.10+. Same `X-Api-Key` you use for `api.flashalpha.com`.
16
+ **Alpha plan or higher** on every endpoint.
17
+
18
+ ## Quickstart
19
+
20
+ ```python
21
+ from flashalpha_historical import FlashAlphaHistorical
22
+
23
+ hx = FlashAlphaHistorical("YOUR_API_KEY")
24
+
25
+ # One snapshot — what dealer positioning looked like during the COVID crash
26
+ snap = hx.exposure_summary("SPY", at="2020-03-16T15:30:00")
27
+ print(snap["regime"], snap["exposures"]["net_gex"])
28
+ # → 'negative_gamma' -2633970601
29
+ ```
30
+
31
+ The `at=` parameter accepts strings (`"2026-03-05T15:30:00"` or
32
+ `"2026-03-05"` → defaults to 16:00 ET), `datetime` objects, or `date` objects.
33
+
34
+ ## Backtesting
35
+
36
+ The SDK ships with replay utilities that turn any endpoint into an iterator
37
+ over a date / minute range. Holiday calendar is built in (NYSE 2018-2026);
38
+ gap days are skipped silently by default.
39
+
40
+ ### Daily replay
41
+
42
+ ```python
43
+ from flashalpha_historical import FlashAlphaHistorical, Backtester, iter_days
44
+
45
+ hx = FlashAlphaHistorical("YOUR_API_KEY")
46
+
47
+ def strategy(at, snap):
48
+ """Short vol when VRP rich AND dealers long gamma."""
49
+ vrp = snap["volatility"]["vrp"]
50
+ regime = snap["exposure"]["regime"]
51
+ return {
52
+ "signal": "short_strangle" if vrp > 5 and regime == "positive_gamma" else None,
53
+ "vrp": vrp,
54
+ "regime": regime,
55
+ }
56
+
57
+ bt = Backtester(hx, method="stock_summary", symbol="SPY")
58
+ results = bt.run(iter_days("2024-01-02", "2024-03-29"), strategy)
59
+
60
+ # Convert to DataFrame
61
+ import pandas as pd
62
+ df = pd.DataFrame(bt.to_records(results))
63
+ ```
64
+
65
+ ### Minute-level replay
66
+
67
+ ```python
68
+ from flashalpha_historical import iter_minutes, replay
69
+
70
+ # Walk every 15 minutes through one trading day
71
+ for at, snap in replay(hx, "exposure_summary", "SPY",
72
+ iter_minutes("2025-01-15", "2025-01-15", step_minutes=15)):
73
+ print(at, snap["regime"], snap["gamma_flip"], snap["exposures"]["net_gex"])
74
+ ```
75
+
76
+ > **Quota note:** every call counts against your daily plan quota (shared
77
+ > with the live API). 1-minute replay = 390 calls per analytic per day —
78
+ > coarsen with `step_minutes=15` or `step_minutes=30` for development loops.
79
+
80
+ ## API
81
+
82
+ Every analytics method takes a required `at` keyword argument.
83
+
84
+ ### Coverage
85
+
86
+ | Method | Endpoint |
87
+ |---|---|
88
+ | `tickers()` | `GET /v1/tickers` |
89
+ | `tickers(symbol="SPY")` | `GET /v1/tickers?symbol=SPY` |
90
+
91
+ ### Market data
92
+
93
+ | Method | Endpoint |
94
+ |---|---|
95
+ | `stock_quote(ticker, at=...)` | `/v1/stockquote/{ticker}` |
96
+ | `option_quote(ticker, at=..., expiry=, strike=, type=)` | `/v1/optionquote/{ticker}` |
97
+ | `surface(symbol, at=...)` | `/v1/surface/{symbol}` |
98
+
99
+ ### Exposure analytics
100
+
101
+ | Method | Endpoint |
102
+ |---|---|
103
+ | `gex(symbol, at=..., expiration=, min_oi=)` | `/v1/exposure/gex/{symbol}` |
104
+ | `dex(symbol, at=..., expiration=)` | `/v1/exposure/dex/{symbol}` |
105
+ | `vex(symbol, at=..., expiration=)` | `/v1/exposure/vex/{symbol}` |
106
+ | `chex(symbol, at=..., expiration=)` | `/v1/exposure/chex/{symbol}` |
107
+ | `exposure_summary(symbol, at=...)` | `/v1/exposure/summary/{symbol}` |
108
+ | `exposure_levels(symbol, at=...)` | `/v1/exposure/levels/{symbol}` |
109
+ | `narrative(symbol, at=...)` | `/v1/exposure/narrative/{symbol}` |
110
+ | `zero_dte(symbol, at=..., strike_range=)` | `/v1/exposure/zero-dte/{symbol}` |
111
+
112
+ ### Composite & vol
113
+
114
+ | Method | Endpoint |
115
+ |---|---|
116
+ | `stock_summary(symbol, at=...)` | `/v1/stock/{symbol}/summary` |
117
+ | `volatility(symbol, at=...)` | `/v1/volatility/{symbol}` |
118
+ | `adv_volatility(symbol, at=...)` | `/v1/adv_volatility/{symbol}` |
119
+ | `vrp(symbol, at=...)` | `/v1/vrp/{symbol}` |
120
+ | `max_pain(symbol, at=..., expiration=)` | `/v1/maxpain/{symbol}` |
121
+
122
+ ## Errors
123
+
124
+ ```python
125
+ from flashalpha_historical import (
126
+ FlashAlphaHistoricalError, # base
127
+ AuthenticationError, # 401
128
+ TierRestrictedError, # 403 — needs Alpha plan
129
+ InvalidAtError, # 400 — bad `at` format
130
+ NoDataError, # 404 — outside coverage / inside gap
131
+ SymbolNotFoundError, # 404 — symbol not at this `at`
132
+ NoCoverageError, # 404 — symbol not in historical dataset
133
+ InsufficientDataError, # 404 — surface grid too sparse
134
+ RateLimitError, # 429
135
+ ServerError, # 5xx
136
+ )
137
+
138
+ try:
139
+ hx.exposure_summary("SPY", at="2017-01-01") # before coverage starts
140
+ except NoDataError as e:
141
+ print("gap:", e)
142
+ ```
143
+
144
+ ## Known gaps from live (intentional, documented)
145
+
146
+ - `optionquote.bidSize` / `askSize` — always `0` (minute table has no sizes)
147
+ - `optionquote.volume` / `gex.call_volume` / `put_volume` — always `0`
148
+ - `optionquote.svi_vol` — `null` with `svi_vol_gated: "backtest_mode"`
149
+ - `narrative.data.top_oi_changes` — empty array (no prior-day OI diff yet)
150
+ - `gex.call_oi_change` / `put_oi_change` — always `null`
151
+ - `stock_summary.macro.vix_futures` / `fear_and_greed` — `null`
152
+ - `vrp.macro.hy_spread` — hard-coded `3.5`
153
+ - 0DTE intraday greeks (delta/gamma/theta/iv) often `0` / `null` — chain
154
+ still listed for OI analysis
155
+
156
+ ## License
157
+
158
+ MIT
@@ -0,0 +1,94 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "flashalpha-historical"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the FlashAlpha Historical API — point-in-time replay of GEX, gamma flip, VRP, narrative, max pain, and the full stock summary at any minute back to 2018-04-16. Backtest options strategies against 6.7B+ option rows."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "FlashAlpha", email = "tom@flashalpha.com" }]
13
+ keywords = [
14
+ "options",
15
+ "options backtest",
16
+ "options backtesting",
17
+ "historical options",
18
+ "historical options data",
19
+ "gamma exposure",
20
+ "gex",
21
+ "GEX history",
22
+ "options replay",
23
+ "point-in-time",
24
+ "as of",
25
+ "options analytics",
26
+ "options API",
27
+ "delta exposure",
28
+ "vanna",
29
+ "charm",
30
+ "implied volatility",
31
+ "0dte",
32
+ "zero dte",
33
+ "volatility surface",
34
+ "dealer positioning",
35
+ "max pain",
36
+ "VRP",
37
+ "variance risk premium",
38
+ "SVI",
39
+ "variance swap",
40
+ "quantitative finance",
41
+ "trading",
42
+ "finance",
43
+ ]
44
+ classifiers = [
45
+ "Development Status :: 3 - Alpha",
46
+ "Intended Audience :: Developers",
47
+ "Intended Audience :: Financial and Insurance Industry",
48
+ "License :: OSI Approved :: MIT License",
49
+ "Programming Language :: Python :: 3",
50
+ "Programming Language :: Python :: 3.10",
51
+ "Programming Language :: Python :: 3.11",
52
+ "Programming Language :: Python :: 3.12",
53
+ "Programming Language :: Python :: 3.13",
54
+ "Topic :: Office/Business :: Financial",
55
+ "Topic :: Office/Business :: Financial :: Investment",
56
+ "Topic :: Software Development :: Libraries :: Python Modules",
57
+ ]
58
+ dependencies = ["requests>=2.28"]
59
+
60
+ [project.urls]
61
+ Homepage = "https://historical.flashalpha.com"
62
+ Documentation = "https://flashalpha.com/docs/historical"
63
+ Repository = "https://github.com/FlashAlpha-lab/flashalpha-historical-python"
64
+ Issues = "https://github.com/FlashAlpha-lab/flashalpha-historical-python/issues"
65
+
66
+ [project.optional-dependencies]
67
+ dev = ["pytest>=7.0", "pytest-cov", "responses>=0.23"]
68
+
69
+ [tool.hatch.build.targets.wheel]
70
+ packages = ["src/flashalpha_historical"]
71
+
72
+ [tool.hatch.build.targets.sdist]
73
+ support-legacy = false
74
+ include = [
75
+ "/src/flashalpha_historical",
76
+ "/README.md",
77
+ "/LICENSE",
78
+ "/pyproject.toml",
79
+ ]
80
+ exclude = [
81
+ ".claude",
82
+ "CLAUDE.md",
83
+ ".env",
84
+ ".env.*",
85
+ ".gitignore",
86
+ "**/.gitignore",
87
+ ".vscode",
88
+ ".idea",
89
+ "*.local",
90
+ ]
91
+
92
+ [tool.pytest.ini_options]
93
+ testpaths = ["tests"]
94
+ markers = ["integration: hits the live FlashAlpha Historical API (deselect with -m 'not integration')"]