itmatrix 1.0.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,15 @@
1
+ node_modules/
2
+ dist/
3
+ .venv/
4
+ __pycache__/
5
+ *.pyc
6
+ *.egg-info/
7
+ .idea/
8
+ .vscode/
9
+ .DS_Store
10
+
11
+ target/
12
+ .pytest_cache/
13
+ .ruff_cache/
14
+ .wheel-smoke/
15
+ *.tgz
itmatrix-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ITMatrixHQ contributors
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,228 @@
1
+ Metadata-Version: 2.5
2
+ Name: itmatrix
3
+ Version: 1.0.0
4
+ Summary: Human-friendly sync and async clients for the ITMatrixHQ API
5
+ Project-URL: Homepage, https://itmatrixhq.com
6
+ Project-URL: Source, https://github.com/ITMatrixHQ/itmatrix-sdks
7
+ Project-URL: Issues, https://github.com/ITMatrixHQ/itmatrix-sdks/issues
8
+ Project-URL: Changelog, https://github.com/ITMatrixHQ/itmatrix-sdks/blob/master/CHANGELOG.md
9
+ Author: ITMatrixHQ
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: api client,gamma exposure,gex,itmatrix,itmatrixhq,market data,options
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Financial and Insurance Industry
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Office/Business :: Financial :: Investment
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: anyio<5,>=4
29
+ Requires-Dist: httpx<1,>=0.27
30
+ Requires-Dist: protobuf<8,>=7.35.1
31
+ Requires-Dist: typing-extensions>=4.7
32
+ Requires-Dist: tzdata>=2024.1
33
+ Provides-Extra: stream
34
+ Requires-Dist: websockets<16,>=14; extra == 'stream'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # ITMatrixHQ Python SDK
38
+
39
+ ```sh
40
+ pip install itmatrix
41
+ # For WebSocket streams:
42
+ pip install "itmatrix[stream]"
43
+ ```
44
+
45
+ ```python
46
+ import os
47
+ import itmatrix as itm
48
+
49
+ client = itm.ITMClient(api_key=os.environ["ITM_API_KEY"])
50
+ result = client.get_gex("SPY", top=10)
51
+ print(result.data.spot, result.data.net_gex)
52
+ for row in result.data.strikes:
53
+ print(row.strike, row.gex, row.expiry)
54
+ ```
55
+
56
+ No `with` block is needed. The same client works as a context manager, which
57
+ closes it when the block ends:
58
+
59
+ ```python
60
+ with itm.ITMClient(api_key=os.environ["ITM_API_KEY"]) as client:
61
+ print(client.get_gex("SPY").data.net_gex)
62
+ ```
63
+
64
+ `itmatrix` is the distribution and import name. Python 3.10+ is supported. To build
65
+ from source instead, run `pip install ./python` from the repository root.
66
+
67
+ ## A small vocabulary
68
+
69
+ | Method | `result.data` |
70
+ | --- | --- |
71
+ | `get_gex(symbol, at=..., top=..., by_expiry=...)` | `GexGrid` and `GexStrike` values |
72
+ | `get_gex_reference(symbol, date=..., basis="open")` | Exact persisted reference book, or explicit unavailable values |
73
+ | `get_option_chain(symbol, expiry=..., strike_gte=...)` | `OptionChain` with contract/quote models |
74
+ | `list_expirations(symbol)` | ISO expiry date strings |
75
+ | `list_symbols(symbol_class="equity")` | `Symbol` values from the registry |
76
+ | `lookup_symbol("spy")` | `SymbolIdentity` with broad class and conservative instrument classification |
77
+ | `get_bars(symbol, from_=..., to=..., timeframe="1m")` | `Bars` with `bars`, `cursor`, and completeness |
78
+ | `get_replay(symbol, date="2026-09-04")` | `Replay` with `Tick` or `Bar` events |
79
+ | `get_gex_analysis(symbol)` | Server net GEX and zero gamma plus ranked **visible** positive/negative strike levels |
80
+ | `get_bars_for_period(symbol, "today")` | New York calendar-day intraday bars; longer named periods default to daily bars |
81
+
82
+ The convenience methods are local compositions; they add no API route.
83
+ `get_bars_for_period(symbol, "calendar_month", month="2024-02")` requests the
84
+ whole named month with daily bars; inspect the returned cursor for another page.
85
+ `resolve_bar_window` can be used without a
86
+ client. See [convenience semantics](../docs/convenience.md) before treating
87
+ visible GEX levels as full-chain levels or a month as a completed session set.
88
+
89
+ `Result` retains `meta`, HTTP `status`, `request_id`, and `etag`. Unknown metadata
90
+ is preserved. Nullable measurements stay `None`; zero is a real value. Timestamps
91
+ are epoch milliseconds, dates are ISO strings, bar/quote prices are dollars, and
92
+ strike filters use integer thousandths of dollars. `row.strike` is a convenient
93
+ dollar value; `row.strike_thousandths` preserves the exact integer. GEX values
94
+ (`gex`, `net_gex`, `max_abs_gex`, `delta_adj`, `gex_0dte`) are dollars of dealer
95
+ hedging per $1 move. Each has a served `*_shares` sibling (`gex_shares`,
96
+ `net_gex_shares`, `max_abs_gex_shares`, `delta_adj_shares`, `gex_0dte_shares`) in
97
+ shares of the underlying per $1 move, on the grid, history and reference book;
98
+ `None` means the server does not know it.
99
+
100
+ The public protobuf representation omits an option contract's OSI root. Its
101
+ `underlying` is therefore `None` on that transport, rather than a guessed value.
102
+ The normalized model otherwise uses the same field names and units for JSON and
103
+ public protobuf. Set `transport="protobuf"` to negotiate public protobuf on the
104
+ bulk market-data and off-exchange endpoints. Streaming always uses the public protobuf protocol.
105
+
106
+ Account, journal, watchlists, economy, offexchange, market, reference, screener, flow,
107
+ fundamentals, vol, protocol and service-info operations are grouped on both
108
+ clients. Together with the client's own methods they model **every non-internal
109
+ `/v2` operation the contract declares**; the pre-`/v2` aliases are deliberately
110
+ not modelled.
111
+
112
+ `client.offexchange` is the typed synthetic surface: `activity`, `concentration`,
113
+ `profile`, and `composition`. `client.darkpool` is a compatibility alias to the
114
+ same safe resource. There is no generic dataset method or raw-evidence route.
115
+
116
+ ```python
117
+ with itm.ITMClient(api_key=os.environ["ITM_API_KEY"]) as client:
118
+ rates = client.economy.treasury_yields()
119
+ trades = client.journal.list_trades()
120
+ news = client.fundamentals.digest(kind="brief", scope="market")
121
+
122
+ # A whole session's GEX captures. `to` is exclusive, so 16:00 drops the close.
123
+ day = client.get_gex_history("SPY", date="2026-09-04", from_="09:25", to="16:05", top=120)
124
+
125
+ # Exact immutable book; captured_at=None means unavailable, not zero.
126
+ reference = client.get_gex_reference("SPY", date="2026-09-25", basis="prev_close")
127
+
128
+ # First-party app session only: Pro + CBOE attestation, explicitly partial.
129
+ premium = client.flow.premium("SPY", sessions=2, dte="zero", min_premium_usd=100_000)
130
+
131
+ # Our own implied vol, out of the greeks engine.
132
+ term = client.vol.term("SPY")
133
+
134
+ # Is the API up, and what does this deployment serve? No credential needed.
135
+ ready = client.info.ready()
136
+ spec = client.info.openapi()
137
+ ```
138
+
139
+ `info.health()` and `info.ready()` are the only calls that leave `/v2/`: the
140
+ client keeps a two-entry allowlist for them and rejects every other path, so
141
+ `request()` cannot wander off the documented surface. `ready()` is the deploy
142
+ contract rather than a synonym for `health()` — it is non-2xx while the process
143
+ is still warming up.
144
+
145
+ Grouped resources and `request("/v2/...")` expose raw JSON data for less common
146
+ operations. Endpoint availability still depends on server entitlements; installing
147
+ an SDK does not grant app-only products such as bars or chain data.
148
+
149
+ ## Async and streaming
150
+
151
+ ```python
152
+ import asyncio
153
+
154
+ async def main():
155
+ async with itm.AsyncITMClient(api_key=os.environ["ITM_API_KEY"]) as client:
156
+ spy, qqq = await asyncio.gather(
157
+ client.get_gex("SPY"), client.get_gex("QQQ"))
158
+ print(spy.data.net_gex, qqq.data.net_gex)
159
+
160
+ asyncio.run(main())
161
+ ```
162
+
163
+ Subscriptions live on `AsyncITMClient.stream`; they are bounded async iterators of
164
+ public protobuf frames. See the repository streaming documentation and the
165
+ `stream.gex(symbol)` / `stream.spot(symbol)` helpers. Close subscriptions with
166
+ `async with` or `aclose()`, and close the client with `async with` or `aclose()`. Falling behind the configured
167
+ queue limit raises an error rather than silently discarding market updates.
168
+
169
+ ## Client lifecycle
170
+
171
+ The blocking `ITMClient` owns one persistent background event loop and HTTP pool;
172
+ it never reuses async connections across short-lived loops. Create it once and
173
+ reuse it. `close()` is optional:
174
+
175
+ - Call `close()` (or use `with`) when you want the loop thread and connections
176
+ released at a known point, such as a long-running process that makes a client
177
+ per task. It is safe to call more than once; a request after it raises
178
+ `RuntimeError`, and `client.closed` reports it.
179
+ - A client you never close is closed when it is garbage-collected or when the
180
+ interpreter exits. Its loop runs on a daemon thread, so it never keeps a script
181
+ from exiting.
182
+
183
+ Blocking requests are serialized. Use `AsyncITMClient` for concurrent work; an async
184
+ client belongs to the loop where it is used. Without `async with`, call
185
+ `await client.aclose()` when you are done: async cleanup cannot run from a
186
+ finalizer, so an async client that owns its pool and is collected unclosed only
187
+ emits `ResourceWarning`. Injected async HTTP clients remain owned by the caller.
188
+
189
+ ## Errors, retries, and configuration
190
+
191
+ ```python
192
+ client = itm.ITMClient(api_key=os.environ["ITM_API_KEY"], timeout=15, retries=2)
193
+ try:
194
+ grid = client.get_gex("SPY")
195
+ except itm.ITMError as error:
196
+ print(error.code, error.status, error.request_id)
197
+ ```
198
+
199
+ Only GET responses with status 429, 502, 503, or 504 are retried; mutations and
200
+ transport exceptions are not replayed. Retry waits are bounded at 30 seconds,
201
+ with exponential fallback. `timeout` is HTTPX's per-operation timeout, not an
202
+ end-to-end deadline including retries. HTTPX transport exceptions propagate.
203
+ Redirects are disabled even for injected clients. The `request` escape hatch
204
+ accepts only `/v2/` paths; query parameters go in `query={...}`. Credentials are
205
+ resolved for each attempt and never attached to arbitrary absolute URLs.
206
+
207
+ `base_url` must be an HTTP(S) origin and defaults to `itm.DEFAULT_BASE_URL`, the
208
+ one place the package defines it. `api_key` and `token` are mutually exclusive;
209
+ each accepts a string or refresh callback (sync or async). No credentials are
210
+ inferred from environment variables. Tests can inject `http_transport`.
211
+
212
+ ## Studies and indicators
213
+
214
+ There is no native evaluator in Python. The ITMScript runtime is TypeScript, unpublished,
215
+ and has no Python port planned; this package hands you normalized bars and stops there.
216
+ A study over `get_bars(...).data` is either computed in your own code or run through the
217
+ TypeScript runtime out of process. The rules a host has to resolve either way — a 304 is
218
+ revalidation and not an empty history, pagination is the caller's loop, entitlement
219
+ belongs to the credential — are in [docs/itmscript.md](https://github.com/ITMatrixHQ/itmatrix-sdks/blob/master/docs/itmscript.md).
220
+
221
+ ## Contributing
222
+
223
+ From `python/`, install with `pip install -e .`, run
224
+ `python -m unittest discover -s tests`, and build with `python -m build`.
225
+ The repository contract-coverage test also requires the sibling `spec/` directory.
226
+ Only the protobuf serializers in `itmatrix/_core/_wire` are generated. Public clients,
227
+ models, options, resources, and tests are hand maintained. Wheels and source
228
+ archives contain the single `itmatrix` import package and its `py.typed` marker.
@@ -0,0 +1,192 @@
1
+ # ITMatrixHQ Python SDK
2
+
3
+ ```sh
4
+ pip install itmatrix
5
+ # For WebSocket streams:
6
+ pip install "itmatrix[stream]"
7
+ ```
8
+
9
+ ```python
10
+ import os
11
+ import itmatrix as itm
12
+
13
+ client = itm.ITMClient(api_key=os.environ["ITM_API_KEY"])
14
+ result = client.get_gex("SPY", top=10)
15
+ print(result.data.spot, result.data.net_gex)
16
+ for row in result.data.strikes:
17
+ print(row.strike, row.gex, row.expiry)
18
+ ```
19
+
20
+ No `with` block is needed. The same client works as a context manager, which
21
+ closes it when the block ends:
22
+
23
+ ```python
24
+ with itm.ITMClient(api_key=os.environ["ITM_API_KEY"]) as client:
25
+ print(client.get_gex("SPY").data.net_gex)
26
+ ```
27
+
28
+ `itmatrix` is the distribution and import name. Python 3.10+ is supported. To build
29
+ from source instead, run `pip install ./python` from the repository root.
30
+
31
+ ## A small vocabulary
32
+
33
+ | Method | `result.data` |
34
+ | --- | --- |
35
+ | `get_gex(symbol, at=..., top=..., by_expiry=...)` | `GexGrid` and `GexStrike` values |
36
+ | `get_gex_reference(symbol, date=..., basis="open")` | Exact persisted reference book, or explicit unavailable values |
37
+ | `get_option_chain(symbol, expiry=..., strike_gte=...)` | `OptionChain` with contract/quote models |
38
+ | `list_expirations(symbol)` | ISO expiry date strings |
39
+ | `list_symbols(symbol_class="equity")` | `Symbol` values from the registry |
40
+ | `lookup_symbol("spy")` | `SymbolIdentity` with broad class and conservative instrument classification |
41
+ | `get_bars(symbol, from_=..., to=..., timeframe="1m")` | `Bars` with `bars`, `cursor`, and completeness |
42
+ | `get_replay(symbol, date="2026-09-04")` | `Replay` with `Tick` or `Bar` events |
43
+ | `get_gex_analysis(symbol)` | Server net GEX and zero gamma plus ranked **visible** positive/negative strike levels |
44
+ | `get_bars_for_period(symbol, "today")` | New York calendar-day intraday bars; longer named periods default to daily bars |
45
+
46
+ The convenience methods are local compositions; they add no API route.
47
+ `get_bars_for_period(symbol, "calendar_month", month="2024-02")` requests the
48
+ whole named month with daily bars; inspect the returned cursor for another page.
49
+ `resolve_bar_window` can be used without a
50
+ client. See [convenience semantics](../docs/convenience.md) before treating
51
+ visible GEX levels as full-chain levels or a month as a completed session set.
52
+
53
+ `Result` retains `meta`, HTTP `status`, `request_id`, and `etag`. Unknown metadata
54
+ is preserved. Nullable measurements stay `None`; zero is a real value. Timestamps
55
+ are epoch milliseconds, dates are ISO strings, bar/quote prices are dollars, and
56
+ strike filters use integer thousandths of dollars. `row.strike` is a convenient
57
+ dollar value; `row.strike_thousandths` preserves the exact integer. GEX values
58
+ (`gex`, `net_gex`, `max_abs_gex`, `delta_adj`, `gex_0dte`) are dollars of dealer
59
+ hedging per $1 move. Each has a served `*_shares` sibling (`gex_shares`,
60
+ `net_gex_shares`, `max_abs_gex_shares`, `delta_adj_shares`, `gex_0dte_shares`) in
61
+ shares of the underlying per $1 move, on the grid, history and reference book;
62
+ `None` means the server does not know it.
63
+
64
+ The public protobuf representation omits an option contract's OSI root. Its
65
+ `underlying` is therefore `None` on that transport, rather than a guessed value.
66
+ The normalized model otherwise uses the same field names and units for JSON and
67
+ public protobuf. Set `transport="protobuf"` to negotiate public protobuf on the
68
+ bulk market-data and off-exchange endpoints. Streaming always uses the public protobuf protocol.
69
+
70
+ Account, journal, watchlists, economy, offexchange, market, reference, screener, flow,
71
+ fundamentals, vol, protocol and service-info operations are grouped on both
72
+ clients. Together with the client's own methods they model **every non-internal
73
+ `/v2` operation the contract declares**; the pre-`/v2` aliases are deliberately
74
+ not modelled.
75
+
76
+ `client.offexchange` is the typed synthetic surface: `activity`, `concentration`,
77
+ `profile`, and `composition`. `client.darkpool` is a compatibility alias to the
78
+ same safe resource. There is no generic dataset method or raw-evidence route.
79
+
80
+ ```python
81
+ with itm.ITMClient(api_key=os.environ["ITM_API_KEY"]) as client:
82
+ rates = client.economy.treasury_yields()
83
+ trades = client.journal.list_trades()
84
+ news = client.fundamentals.digest(kind="brief", scope="market")
85
+
86
+ # A whole session's GEX captures. `to` is exclusive, so 16:00 drops the close.
87
+ day = client.get_gex_history("SPY", date="2026-09-04", from_="09:25", to="16:05", top=120)
88
+
89
+ # Exact immutable book; captured_at=None means unavailable, not zero.
90
+ reference = client.get_gex_reference("SPY", date="2026-09-25", basis="prev_close")
91
+
92
+ # First-party app session only: Pro + CBOE attestation, explicitly partial.
93
+ premium = client.flow.premium("SPY", sessions=2, dte="zero", min_premium_usd=100_000)
94
+
95
+ # Our own implied vol, out of the greeks engine.
96
+ term = client.vol.term("SPY")
97
+
98
+ # Is the API up, and what does this deployment serve? No credential needed.
99
+ ready = client.info.ready()
100
+ spec = client.info.openapi()
101
+ ```
102
+
103
+ `info.health()` and `info.ready()` are the only calls that leave `/v2/`: the
104
+ client keeps a two-entry allowlist for them and rejects every other path, so
105
+ `request()` cannot wander off the documented surface. `ready()` is the deploy
106
+ contract rather than a synonym for `health()` — it is non-2xx while the process
107
+ is still warming up.
108
+
109
+ Grouped resources and `request("/v2/...")` expose raw JSON data for less common
110
+ operations. Endpoint availability still depends on server entitlements; installing
111
+ an SDK does not grant app-only products such as bars or chain data.
112
+
113
+ ## Async and streaming
114
+
115
+ ```python
116
+ import asyncio
117
+
118
+ async def main():
119
+ async with itm.AsyncITMClient(api_key=os.environ["ITM_API_KEY"]) as client:
120
+ spy, qqq = await asyncio.gather(
121
+ client.get_gex("SPY"), client.get_gex("QQQ"))
122
+ print(spy.data.net_gex, qqq.data.net_gex)
123
+
124
+ asyncio.run(main())
125
+ ```
126
+
127
+ Subscriptions live on `AsyncITMClient.stream`; they are bounded async iterators of
128
+ public protobuf frames. See the repository streaming documentation and the
129
+ `stream.gex(symbol)` / `stream.spot(symbol)` helpers. Close subscriptions with
130
+ `async with` or `aclose()`, and close the client with `async with` or `aclose()`. Falling behind the configured
131
+ queue limit raises an error rather than silently discarding market updates.
132
+
133
+ ## Client lifecycle
134
+
135
+ The blocking `ITMClient` owns one persistent background event loop and HTTP pool;
136
+ it never reuses async connections across short-lived loops. Create it once and
137
+ reuse it. `close()` is optional:
138
+
139
+ - Call `close()` (or use `with`) when you want the loop thread and connections
140
+ released at a known point, such as a long-running process that makes a client
141
+ per task. It is safe to call more than once; a request after it raises
142
+ `RuntimeError`, and `client.closed` reports it.
143
+ - A client you never close is closed when it is garbage-collected or when the
144
+ interpreter exits. Its loop runs on a daemon thread, so it never keeps a script
145
+ from exiting.
146
+
147
+ Blocking requests are serialized. Use `AsyncITMClient` for concurrent work; an async
148
+ client belongs to the loop where it is used. Without `async with`, call
149
+ `await client.aclose()` when you are done: async cleanup cannot run from a
150
+ finalizer, so an async client that owns its pool and is collected unclosed only
151
+ emits `ResourceWarning`. Injected async HTTP clients remain owned by the caller.
152
+
153
+ ## Errors, retries, and configuration
154
+
155
+ ```python
156
+ client = itm.ITMClient(api_key=os.environ["ITM_API_KEY"], timeout=15, retries=2)
157
+ try:
158
+ grid = client.get_gex("SPY")
159
+ except itm.ITMError as error:
160
+ print(error.code, error.status, error.request_id)
161
+ ```
162
+
163
+ Only GET responses with status 429, 502, 503, or 504 are retried; mutations and
164
+ transport exceptions are not replayed. Retry waits are bounded at 30 seconds,
165
+ with exponential fallback. `timeout` is HTTPX's per-operation timeout, not an
166
+ end-to-end deadline including retries. HTTPX transport exceptions propagate.
167
+ Redirects are disabled even for injected clients. The `request` escape hatch
168
+ accepts only `/v2/` paths; query parameters go in `query={...}`. Credentials are
169
+ resolved for each attempt and never attached to arbitrary absolute URLs.
170
+
171
+ `base_url` must be an HTTP(S) origin and defaults to `itm.DEFAULT_BASE_URL`, the
172
+ one place the package defines it. `api_key` and `token` are mutually exclusive;
173
+ each accepts a string or refresh callback (sync or async). No credentials are
174
+ inferred from environment variables. Tests can inject `http_transport`.
175
+
176
+ ## Studies and indicators
177
+
178
+ There is no native evaluator in Python. The ITMScript runtime is TypeScript, unpublished,
179
+ and has no Python port planned; this package hands you normalized bars and stops there.
180
+ A study over `get_bars(...).data` is either computed in your own code or run through the
181
+ TypeScript runtime out of process. The rules a host has to resolve either way — a 304 is
182
+ revalidation and not an empty history, pagination is the caller's loop, entitlement
183
+ belongs to the credential — are in [docs/itmscript.md](https://github.com/ITMatrixHQ/itmatrix-sdks/blob/master/docs/itmscript.md).
184
+
185
+ ## Contributing
186
+
187
+ From `python/`, install with `pip install -e .`, run
188
+ `python -m unittest discover -s tests`, and build with `python -m build`.
189
+ The repository contract-coverage test also requires the sibling `spec/` directory.
190
+ Only the protobuf serializers in `itmatrix/_core/_wire` are generated. Public clients,
191
+ models, options, resources, and tests are hand maintained. Wheels and source
192
+ archives contain the single `itmatrix` import package and its `py.typed` marker.
@@ -0,0 +1,62 @@
1
+ [project]
2
+ name = "itmatrix"
3
+ version = "1.0.0"
4
+ description = "Human-friendly sync and async clients for the ITMatrixHQ API"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{ name = "ITMatrixHQ" }]
10
+ keywords = ["itmatrixhq", "itmatrix", "options", "gamma exposure", "gex", "market data", "api client"]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Intended Audience :: Developers",
14
+ "Intended Audience :: Financial and Insurance Industry",
15
+ "Operating System :: OS Independent",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ "Topic :: Office/Business :: Financial :: Investment",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = [
28
+ "httpx>=0.27,<1",
29
+ "anyio>=4,<5",
30
+ "protobuf>=7.35.1,<8",
31
+ "typing-extensions>=4.7",
32
+ "tzdata>=2024.1",
33
+ ]
34
+
35
+ [project.optional-dependencies]
36
+ stream = ["websockets>=14,<16"]
37
+
38
+ [project.urls]
39
+ Homepage = "https://itmatrixhq.com"
40
+ Source = "https://github.com/ITMatrixHQ/itmatrix-sdks"
41
+ Issues = "https://github.com/ITMatrixHQ/itmatrix-sdks/issues"
42
+ Changelog = "https://github.com/ITMatrixHQ/itmatrix-sdks/blob/master/CHANGELOG.md"
43
+
44
+ [build-system]
45
+ requires = ["hatchling>=1.27"]
46
+ build-backend = "hatchling.build"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/itmatrix"]
50
+
51
+ [tool.hatch.build]
52
+ include = [
53
+ "src/itmatrix/**/*.py",
54
+ "src/itmatrix/py.typed",
55
+ "README.md",
56
+ "LICENSE",
57
+ "pyproject.toml",
58
+ "tests/**/*.py",
59
+ ]
60
+
61
+ [tool.ruff.lint]
62
+ ignore = ["E501"]
@@ -0,0 +1,53 @@
1
+ """The ITMatrixHQ API, for people and agents."""
2
+
3
+ from ._core.config import DEFAULT_BASE_URL
4
+ from ._core.errors import ITMError
5
+ from ._core.models import Transport
6
+ from ._core.stream import Stream, Subscription
7
+ from .analytics import GexAnalysis, GexLevel, analyze_gex
8
+ from .client import AsyncClient, AsyncITMClient, Client, ITMClient
9
+ from .models import (
10
+ Bar,
11
+ Bars,
12
+ GexGrid,
13
+ GexStrike,
14
+ OptionChain,
15
+ OptionContract,
16
+ OptionQuote,
17
+ Replay,
18
+ Result,
19
+ Symbol,
20
+ SymbolIdentity,
21
+ Tick,
22
+ )
23
+ from .periods import BarPeriod, BarWindow, resolve_bar_window
24
+
25
+ __all__ = [
26
+ "DEFAULT_BASE_URL",
27
+ "AsyncClient",
28
+ "AsyncITMClient",
29
+ "Bar",
30
+ "BarPeriod",
31
+ "BarWindow",
32
+ "Bars",
33
+ "Client",
34
+ "GexAnalysis",
35
+ "GexGrid",
36
+ "GexLevel",
37
+ "GexStrike",
38
+ "ITMClient",
39
+ "ITMError",
40
+ "OptionChain",
41
+ "OptionContract",
42
+ "OptionQuote",
43
+ "Replay",
44
+ "Result",
45
+ "Stream",
46
+ "Subscription",
47
+ "Symbol",
48
+ "SymbolIdentity",
49
+ "Tick",
50
+ "Transport",
51
+ "analyze_gex",
52
+ "resolve_bar_window",
53
+ ]
@@ -0,0 +1,9 @@
1
+ """Thin client for the ITMatrixHQ /v2 API."""
2
+
3
+ from .client import ITMClient
4
+ from .config import DEFAULT_BASE_URL
5
+ from .errors import ITMError
6
+ from .models import Result, Transport
7
+ from .stream import Stream, Subscription
8
+
9
+ __all__ = ["DEFAULT_BASE_URL", "ITMClient", "ITMError", "Result", "Stream", "Subscription", "Transport"]
@@ -0,0 +1 @@
1
+ """Generated public protobuf bindings. Prefer the high-level client."""
@@ -0,0 +1,65 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: itmatrixhq/rest/v1/wire.proto
5
+ # Protobuf Python Version: 7.35.1
6
+ """Generated protocol buffer code."""
7
+
8
+ from google.protobuf import descriptor as _descriptor
9
+ from google.protobuf import descriptor_pool as _descriptor_pool
10
+ from google.protobuf import runtime_version as _runtime_version
11
+ from google.protobuf import symbol_database as _symbol_database
12
+ from google.protobuf.internal import builder as _builder
13
+
14
+ _runtime_version.ValidateProtobufRuntimeVersion(
15
+ _runtime_version.Domain.PUBLIC, 7, 35, 1, "", "itmatrixhq/rest/v1/wire.proto"
16
+ )
17
+ # @@protoc_insertion_point(imports)
18
+
19
+ _sym_db = _symbol_database.Default()
20
+
21
+
22
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
23
+ b'\n\x1ditmatrixhq/rest/v1/wire.proto\x12\x12itmatrixhq.rest.v1"\xcc\x01\n\x0c\x42\x61rsResponse\x12\x13\n\x0bprice_scale\x18\x01 \x01(\r\x12 \n\x13\x63omplete_through_ms\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1f\n\x12prior_close_scaled\x18\x03 \x01(\x11H\x01\x88\x01\x01\x12%\n\x04\x62\x61rs\x18\x04 \x03(\x0b\x32\x17.itmatrixhq.rest.v1.Bar\x12\x0e\n\x06\x63ursor\x18\x05 \x01(\tB\x16\n\x14_complete_through_msB\x15\n\x13_prior_close_scaled"\x88\x01\n\x03\x42\x61r\x12\r\n\x05ts_ms\x18\x01 \x01(\x04\x12\t\n\x01o\x18\x02 \x01(\x11\x12\t\n\x01h\x18\x03 \x01(\x11\x12\t\n\x01l\x18\x04 \x01(\x11\x12\t\n\x01\x63\x18\x05 \x01(\x11\x12\x0e\n\x01v\x18\x06 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04vwap\x18\x07 \x01(\x11H\x01\x88\x01\x01\x12\x0e\n\x01n\x18\x08 \x01(\rH\x02\x88\x01\x01\x42\x04\n\x02_vB\x07\n\x05_vwapB\x04\n\x02_n"\xaa\x01\n\x0eReplayResponse\x12\x13\n\x0bprice_scale\x18\x01 \x01(\r\x12\x10\n\x08\x66idelity\x18\x02 \x01(\r\x12\x10\n\x08\x63omplete\x18\x03 \x01(\x08\x12\x0f\n\x07sources\x18\x04 \x03(\t\x12\'\n\x05ticks\x18\x05 \x03(\x0b\x32\x18.itmatrixhq.rest.v1.Tick\x12%\n\x04\x62\x61rs\x18\x06 \x03(\x0b\x32\x17.itmatrixhq.rest.v1.Bar"K\n\x04Tick\x12\r\n\x05ts_ms\x18\x01 \x01(\x04\x12\x14\n\x0cprice_scaled\x18\x02 \x01(\x11\x12\x11\n\tsource_id\x18\x03 \x01(\r\x12\x0b\n\x03seq\x18\x04 \x01(\x04"\x84\x03\n\x0fGexGridResponse\x12\x1b\n\x0e\x63\x61ptured_at_ms\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\x04spot\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x1d\n\x10prior_close_spot\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x0f\n\x07net_gex\x18\x04 \x01(\x01\x12\x1b\n\x0enet_gex_shares\x18\t \x01(\x01H\x03\x88\x01\x01\x12\x17\n\nflip_point\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x13\n\x0bmax_abs_gex\x18\x06 \x01(\x01\x12\x1f\n\x12max_abs_gex_shares\x18\n \x01(\x01H\x05\x88\x01\x01\x12\r\n\x05\x61\x64hoc\x18\x07 \x01(\x08\x12,\n\x04rows\x18\x08 \x03(\x0b\x32\x1e.itmatrixhq.rest.v1.GexGridRowB\x11\n\x0f_captured_at_msB\x07\n\x05_spotB\x13\n\x11_prior_close_spotB\x11\n\x0f_net_gex_sharesB\r\n\x0b_flip_pointB\x15\n\x13_max_abs_gex_shares"\x83\x03\n\nGexGridRow\x12\x1a\n\x12strike_thousandths\x18\x01 \x01(\x03\x12\x0b\n\x03gex\x18\x02 \x01(\x01\x12\x17\n\ngex_shares\x18\x08 \x01(\x01H\x00\x88\x01\x01\x12\x15\n\x08gex_0dte\x18\x03 \x01(\x01H\x01\x88\x01\x01\x12\x1c\n\x0fgex_0dte_shares\x18\t \x01(\x01H\x02\x88\x01\x01\x12\x14\n\x07\x63\x61ll_oi\x18\x04 \x01(\x05H\x03\x88\x01\x01\x12\x13\n\x06put_oi\x18\x05 \x01(\x05H\x04\x88\x01\x01\x12\x16\n\tdelta_adj\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12\x1d\n\x10\x64\x65lta_adj_shares\x18\n \x01(\x01H\x06\x88\x01\x01\x12\x1d\n\x10\x65xpiry_epoch_day\x18\x07 \x01(\x05H\x07\x88\x01\x01\x42\r\n\x0b_gex_sharesB\x0b\n\t_gex_0dteB\x12\n\x10_gex_0dte_sharesB\n\n\x08_call_oiB\t\n\x07_put_oiB\x0c\n\n_delta_adjB\x13\n\x11_delta_adj_sharesB\x13\n\x11_expiry_epoch_day"\x98\x01\n\rChainResponse\x12\x1b\n\x0e\x63\x61ptured_at_ms\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\x04spot\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x0f\n\x07partial\x18\x03 \x01(\x08\x12*\n\x04rows\x18\x04 \x03(\x0b\x32\x1c.itmatrixhq.rest.v1.ChainRowB\x11\n\x0f_captured_at_msB\x07\n\x05_spot"\x9c\x03\n\x08\x43hainRow\x12\x18\n\x10\x65xpiry_epoch_day\x18\x01 \x01(\x05\x12\r\n\x05right\x18\x02 \x01(\r\x12\x1a\n\x12strike_thousandths\x18\x03 \x01(\x03\x12\x0f\n\x02oi\x18\x04 \x01(\x03H\x00\x88\x01\x01\x12\x13\n\x06volume\x18\x05 \x01(\x03H\x01\x88\x01\x01\x12\x10\n\x03\x62id\x18\x06 \x01(\x01H\x02\x88\x01\x01\x12\x10\n\x03\x61sk\x18\x07 \x01(\x01H\x03\x88\x01\x01\x12\x11\n\x04last\x18\x08 \x01(\x01H\x04\x88\x01\x01\x12\x10\n\x03\x66mv\x18\t \x01(\x01H\x05\x88\x01\x01\x12\x0f\n\x02iv\x18\n \x01(\x01H\x06\x88\x01\x01\x12\x12\n\x05\x64\x65lta\x18\x0b \x01(\x01H\x07\x88\x01\x01\x12\x12\n\x05gamma\x18\x0c \x01(\x01H\x08\x88\x01\x01\x12\x12\n\x05theta\x18\r \x01(\x01H\t\x88\x01\x01\x12\x11\n\x04vega\x18\x0e \x01(\x01H\n\x88\x01\x01\x12\x1b\n\x13shares_per_contract\x18\x0f \x01(\rB\x05\n\x03_oiB\t\n\x07_volumeB\x06\n\x04_bidB\x06\n\x04_askB\x07\n\x05_lastB\x06\n\x04_fmvB\x05\n\x03_ivB\x08\n\x06_deltaB\x08\n\x06_gammaB\x08\n\x06_thetaB\x07\n\x05_vega"W\n\x1bOffExchangeActivityResponse\x12\x38\n\x04rows\x18\x01 \x03(\x0b\x32*.itmatrixhq.rest.v1.OffExchangeActivityRow"\xbe\x02\n\x16OffExchangeActivityRow\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\t\x12\x1b\n\x13off_exchange_volume\x18\x02 \x01(\x01\x12\x10\n\x08notional\x18\x03 \x01(\x01\x12\x13\n\x0btrade_count\x18\x04 \x01(\x04\x12\x14\n\x0c\x61verage_size\x18\x05 \x01(\x01\x12\x0c\n\x04vwap\x18\x06 \x01(\x01\x12\x1f\n\x12off_exchange_share\x18\x07 \x01(\x01H\x00\x88\x01\x01\x12 \n\x13\x63onsolidated_volume\x18\x08 \x01(\x01H\x01\x88\x01\x01\x12"\n\x15\x63onsolidated_notional\x18\t \x01(\x01H\x02\x88\x01\x01\x42\x15\n\x13_off_exchange_shareB\x16\n\x14_consolidated_volumeB\x18\n\x16_consolidated_notional"a\n OffExchangeConcentrationResponse\x12=\n\x04rows\x18\x01 \x03(\x0b\x32/.itmatrixhq.rest.v1.OffExchangeConcentrationRow"\xab\x02\n\x1bOffExchangeConcentrationRow\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\t\x12\x17\n\x0f\x62ucket_midpoint\x18\x02 \x01(\x01\x12\x14\n\x0c\x62ucket_width\x18\x03 \x01(\x01\x12\x0e\n\x06volume\x18\x04 \x01(\x01\x12\x10\n\x08notional\x18\x05 \x01(\x01\x12\x13\n\x0btrade_count\x18\x06 \x01(\x04\x12\x19\n\x11\x64istinct_sessions\x18\x07 \x01(\r\x12\x12\n\nrank_score\x18\x08 \x01(\x01\x12\x0c\n\x04rank\x18\t \x01(\r\x12\x14\n\x0c\x62ucket_count\x18\n \x01(\r\x12\x19\n\x0c\x64istance_bps\x18\x0b \x01(\x01H\x00\x88\x01\x01\x12\x19\n\x11lookback_sessions\x18\x0c \x01(\rB\x0f\n\r_distance_bps"U\n\x1aOffExchangeProfileResponse\x12\x37\n\x04rows\x18\x01 \x03(\x0b\x32).itmatrixhq.rest.v1.OffExchangeProfileRow"w\n\x15OffExchangeProfileRow\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\t\x12\x19\n\x11price_thousandths\x18\x02 \x01(\x03\x12\x0e\n\x06volume\x18\x03 \x01(\x01\x12\x10\n\x08notional\x18\x04 \x01(\x01\x12\x13\n\x0btrade_count\x18\x05 \x01(\x04"]\n\x1eOffExchangeCompositionResponse\x12;\n\x04rows\x18\x01 \x03(\x0b\x32-.itmatrixhq.rest.v1.OffExchangeCompositionRow"\x9a\x01\n\x19OffExchangeCompositionRow\x12\x0c\n\x04\x64\x61te\x18\x01 \x01(\t\x12\x10\n\x08\x63\x61tegory\x18\x02 \x01(\t\x12\x0e\n\x06volume\x18\x03 \x01(\x01\x12\x10\n\x08notional\x18\x04 \x01(\x01\x12\x13\n\x0btrade_count\x18\x05 \x01(\x04\x12&\n\x1eshare_of_off_exchange_notional\x18\x06 \x01(\x01\x62\x06proto3'
24
+ )
25
+
26
+ _globals = globals()
27
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
28
+ _builder.BuildTopDescriptorsAndMessages(
29
+ DESCRIPTOR, "itmatrixhq.rest.v1.wire_pb2", _globals
30
+ )
31
+ if not _descriptor._USE_C_DESCRIPTORS:
32
+ DESCRIPTOR._loaded_options = None
33
+ _globals["_BARSRESPONSE"]._serialized_start = 54
34
+ _globals["_BARSRESPONSE"]._serialized_end = 258
35
+ _globals["_BAR"]._serialized_start = 261
36
+ _globals["_BAR"]._serialized_end = 397
37
+ _globals["_REPLAYRESPONSE"]._serialized_start = 400
38
+ _globals["_REPLAYRESPONSE"]._serialized_end = 570
39
+ _globals["_TICK"]._serialized_start = 572
40
+ _globals["_TICK"]._serialized_end = 647
41
+ _globals["_GEXGRIDRESPONSE"]._serialized_start = 650
42
+ _globals["_GEXGRIDRESPONSE"]._serialized_end = 1038
43
+ _globals["_GEXGRIDROW"]._serialized_start = 1041
44
+ _globals["_GEXGRIDROW"]._serialized_end = 1428
45
+ _globals["_CHAINRESPONSE"]._serialized_start = 1431
46
+ _globals["_CHAINRESPONSE"]._serialized_end = 1583
47
+ _globals["_CHAINROW"]._serialized_start = 1586
48
+ _globals["_CHAINROW"]._serialized_end = 1998
49
+ _globals["_OFFEXCHANGEACTIVITYRESPONSE"]._serialized_start = 2000
50
+ _globals["_OFFEXCHANGEACTIVITYRESPONSE"]._serialized_end = 2087
51
+ _globals["_OFFEXCHANGEACTIVITYROW"]._serialized_start = 2090
52
+ _globals["_OFFEXCHANGEACTIVITYROW"]._serialized_end = 2408
53
+ _globals["_OFFEXCHANGECONCENTRATIONRESPONSE"]._serialized_start = 2410
54
+ _globals["_OFFEXCHANGECONCENTRATIONRESPONSE"]._serialized_end = 2507
55
+ _globals["_OFFEXCHANGECONCENTRATIONROW"]._serialized_start = 2510
56
+ _globals["_OFFEXCHANGECONCENTRATIONROW"]._serialized_end = 2809
57
+ _globals["_OFFEXCHANGEPROFILERESPONSE"]._serialized_start = 2811
58
+ _globals["_OFFEXCHANGEPROFILERESPONSE"]._serialized_end = 2896
59
+ _globals["_OFFEXCHANGEPROFILEROW"]._serialized_start = 2898
60
+ _globals["_OFFEXCHANGEPROFILEROW"]._serialized_end = 3017
61
+ _globals["_OFFEXCHANGECOMPOSITIONRESPONSE"]._serialized_start = 3019
62
+ _globals["_OFFEXCHANGECOMPOSITIONRESPONSE"]._serialized_end = 3112
63
+ _globals["_OFFEXCHANGECOMPOSITIONROW"]._serialized_start = 3115
64
+ _globals["_OFFEXCHANGECOMPOSITIONROW"]._serialized_end = 3269
65
+ # @@protoc_insertion_point(module_scope)