tradytics 0.1.0__py3-none-any.whl

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.
tradytics/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Tradytics: options flow, dark pool prints, dealer exposure and market data. Docs: https://tradytics.com/docs"""
2
+ from ._base import TradyticsError
3
+ from ._version import __version__
4
+ from .client import BASE_URL, Client
5
+
6
+ __all__ = ["Client", "TradyticsError", "BASE_URL", "__version__"]
tradytics/_base.py ADDED
@@ -0,0 +1,69 @@
1
+ """The HTTP layer: one GET with the key in the header, retries on rate limits and blips, and one error type that
2
+ carries the API's code, message and request id."""
3
+ import json
4
+ import time
5
+ import urllib.error
6
+ import urllib.parse
7
+ import urllib.request
8
+ from typing import Any, Dict, Optional
9
+
10
+ RETRY = {429, 500, 502, 503, 504}
11
+
12
+
13
+ class TradyticsError(Exception):
14
+ """A failed call: `status`, `code` (e.g. `missing_scope`), `message` and `request_id` (quote it to support)."""
15
+
16
+ def __init__(self, status: int, code: str, message: str, request_id: Optional[str] = None):
17
+ super().__init__(f"{status} {code}: {message}" + (f" (request {request_id})" if request_id else ""))
18
+ self.status, self.code, self.message, self.request_id = status, code, message, request_id
19
+
20
+
21
+ def _q(v: Any) -> str:
22
+ return urllib.parse.quote(str(v), safe="")
23
+
24
+
25
+ def _value(v: Any) -> str:
26
+ return ("true" if v else "false") if isinstance(v, bool) else str(v)
27
+
28
+
29
+ class Transport:
30
+ def __init__(self, api_key: str, base_url: str, timeout: float, max_retries: int, user_agent: str):
31
+ self.api_key, self.base_url, self.timeout, self.max_retries, self.user_agent = api_key, base_url.rstrip("/"), timeout, max_retries, user_agent
32
+
33
+ def get(self, path: str, params: Dict[str, Any]) -> Dict[str, Any]:
34
+ query = urllib.parse.urlencode({k: _value(v) for k, v in params.items() if v is not None})
35
+ url = self.base_url + path + (f"?{query}" if query else "")
36
+ req = urllib.request.Request(url, headers={"Authorization": f"Bearer {self.api_key}", "User-Agent": self.user_agent, "Accept": "application/json"})
37
+ for attempt in range(self.max_retries + 1):
38
+ try:
39
+ with urllib.request.urlopen(req, timeout=self.timeout) as r:
40
+ return json.loads(r.read())
41
+ except urllib.error.HTTPError as e:
42
+ wait = float(e.headers.get("Retry-After") or 2 ** attempt)
43
+ if e.code in RETRY and attempt < self.max_retries and wait <= 60:
44
+ time.sleep(wait)
45
+ continue
46
+ raise _error(e) from None
47
+ except (urllib.error.URLError, TimeoutError) as e:
48
+ if attempt < self.max_retries:
49
+ time.sleep(2 ** attempt)
50
+ continue
51
+ raise TradyticsError(0, "network_error", str(e)) from None
52
+ raise TradyticsError(0, "network_error", "no response")
53
+
54
+
55
+ def _error(e: urllib.error.HTTPError) -> TradyticsError:
56
+ try:
57
+ body = json.loads(e.read())
58
+ err = body.get("error", {})
59
+ return TradyticsError(e.code, err.get("code", "error"), err.get("message", ""), body.get("request_id"))
60
+ except ValueError:
61
+ return TradyticsError(e.code, "error", e.reason or "")
62
+
63
+
64
+ class Resource:
65
+ def __init__(self, transport: Transport):
66
+ self._t = transport
67
+
68
+ def _get(self, path: str, params: Dict[str, Any], extra: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
69
+ return self._t.get(path, {**params, **(extra or {})})
@@ -0,0 +1,805 @@
1
+ # Generated by web/scripts/sdk.mjs from the docs content. Do not edit.
2
+ from typing import Any, Dict, Optional
3
+
4
+ from ._base import Resource, _q
5
+
6
+
7
+ class Symbols(Resource):
8
+ """Symbols, quotes, bars and each symbol's news."""
9
+
10
+ def list(self, *, q: Optional[str] = None, kind: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
11
+ """The ~6,000 symbols Tradytics tracks, most traded first. Search by ticker or name with q.
12
+
13
+ GET /symbols (a list: rows in data, paged by next_cursor)
14
+
15
+ Args:
16
+ q: Matches the start of a ticker or any part of a name.
17
+ kind: stock, etf or adr.
18
+ limit: Rows per page, up to 1000. Default 100.
19
+ cursor: next_cursor from the previous page.
20
+ """
21
+ return self._get("/symbols", {"q": q, "kind": kind, "limit": limit, "cursor": cursor})
22
+
23
+ def get(self, symbol: str) -> Dict[str, Any]:
24
+ """One symbol: its name, kind, sector, industry, market cap and live quote.
25
+
26
+ GET /symbols/{symbol}
27
+
28
+ Args:
29
+ symbol: Ticker symbol, e.g. AAPL. Required.
30
+ """
31
+ return self._get(f"/symbols/{_q(symbol)}", {})
32
+
33
+ def quotes(self, *, symbols: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
34
+ """Live quotes for up to 100 symbols, or for every tracked symbol (most traded first) when symbols is left out. Refreshed every 30 seconds.
35
+
36
+ GET /quotes (a list: rows in data, paged by next_cursor)
37
+
38
+ Args:
39
+ symbols: Comma-separated tickers, up to 100.
40
+ limit: Rows per page, up to 1000. Default 100.
41
+ cursor: next_cursor from the previous page.
42
+ """
43
+ return self._get("/quotes", {"symbols": symbols, "limit": limit, "cursor": cursor})
44
+
45
+ def bars(self, symbol: str, *, timeframe: Optional[str] = None, before: Optional[int] = None, limit: Optional[int] = None) -> Dict[str, Any]:
46
+ """OHLCV bars, oldest first, with today's forming bar. Daily, weekly and monthly bars go back to 2004 for every US stock; intraday bars cover two years of the ~3,000 most traded symbols, 1-second bars the last 7 days. Intraday buckets are aligned to New York time and include extended hours.
47
+
48
+ GET /symbols/{symbol}/bars (a list: rows in data, paged by next_cursor)
49
+
50
+ Args:
51
+ symbol: Ticker symbol, e.g. AAPL. Required.
52
+ timeframe: 1s, 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w or 1M. Default 1d.
53
+ before: Only bars starting before this time, epoch seconds. Pass next_cursor here to page back.
54
+ limit: Rows per page, up to 5000. Default 500.
55
+ """
56
+ return self._get(f"/symbols/{_q(symbol)}/bars", {"timeframe": timeframe, "before": before, "limit": limit})
57
+
58
+ def news(self, symbol: str) -> Dict[str, Any]:
59
+ """A symbol's latest 8 stories from the last 60 days. data is a list.
60
+
61
+ GET /symbols/{symbol}/news
62
+
63
+ Args:
64
+ symbol: Ticker symbol, e.g. AAPL. Required.
65
+ """
66
+ return self._get(f"/symbols/{_q(symbol)}/news", {})
67
+
68
+
69
+ class Market(Resource):
70
+ """The market overview, news and calendars."""
71
+
72
+ def assets(self) -> Dict[str, Any]:
73
+ """Fifteen benchmarks in a fixed order: S&P 500, Nasdaq 100, Russell 2000, Dow, VIX, 20Y and 10Y Treasuries, high yield, gold, silver, oil, natural gas, the dollar, Bitcoin and Ethereum (through their ETFs).
74
+
75
+ GET /market/assets
76
+ """
77
+ return self._get("/market/assets", {})
78
+
79
+ def breadth(self) -> Dict[str, Any]:
80
+ """How many liquid names ($20M+ traded today) are up and down, and how today's moves are spread.
81
+
82
+ GET /market/breadth
83
+ """
84
+ return self._get("/market/breadth", {})
85
+
86
+ def sectors(self) -> Dict[str, Any]:
87
+ """The 11 sector ETFs with returns over six periods, their best and worst large caps today and how many members are up.
88
+
89
+ GET /market/sectors
90
+ """
91
+ return self._get("/market/sectors", {})
92
+
93
+ def heatmap(self) -> Dict[str, Any]:
94
+ """The 300 largest US stocks by sector and industry with their size and move.
95
+
96
+ GET /market/heatmap
97
+ """
98
+ return self._get("/market/heatmap", {})
99
+
100
+ def themes(self) -> Dict[str, Any]:
101
+ """Two dozen investing themes (AI chips, nuclear, crypto miners …) as equal-weight baskets.
102
+
103
+ GET /market/themes
104
+ """
105
+ return self._get("/market/themes", {})
106
+
107
+ def movers(self) -> Dict[str, Any]:
108
+ """Today's top 10 gainers, losers, most active and most unusual volume among stocks worth $2B+ trading $20M+.
109
+
110
+ GET /market/movers
111
+ """
112
+ return self._get("/market/movers", {})
113
+
114
+ def crypto(self) -> Dict[str, Any]:
115
+ """The crypto market and its top 100 coins, with the day's biggest gainers and losers.
116
+
117
+ GET /market/crypto
118
+ """
119
+ return self._get("/market/crypto", {})
120
+
121
+ def crypto_sectors(self) -> Dict[str, Any]:
122
+ """Up to 16 crypto categories by market cap. data is a list.
123
+
124
+ GET /market/crypto/sectors
125
+ """
126
+ return self._get("/market/crypto/sectors", {})
127
+
128
+ def digest(self) -> Dict[str, Any]:
129
+ """An AI-written read of the market: what moved and why, and the week's events ahead. Rewritten every 30 minutes in the session.
130
+
131
+ GET /market/digest
132
+ """
133
+ return self._get("/market/digest", {})
134
+
135
+ def news(self) -> Dict[str, Any]:
136
+ """The day's most important stories, newest first. data is a list.
137
+
138
+ GET /news
139
+ """
140
+ return self._get("/news", {})
141
+
142
+ def earnings_calendar(self) -> Dict[str, Any]:
143
+ """Companies worth $10B+ reporting in the next 7 days, by date then size, with today's reported EPS as it comes in.
144
+
145
+ GET /calendar/earnings
146
+ """
147
+ return self._get("/calendar/earnings", {})
148
+
149
+ def insiders(self) -> Dict[str, Any]:
150
+ """The latest large open-market insider buys and sells (Form 4), and 20 days of insider money flow.
151
+
152
+ GET /insiders
153
+ """
154
+ return self._get("/insiders", {})
155
+
156
+ def congress(self) -> Dict[str, Any]:
157
+ """The latest stock trades disclosed by members of Congress, newest disclosure first.
158
+
159
+ GET /congress
160
+ """
161
+ return self._get("/congress", {})
162
+
163
+
164
+ class Options(Resource):
165
+ """Options flow, the options market and each symbol's options."""
166
+
167
+ def flow(self, *, symbol: Optional[str] = None, date: Optional[str] = None, type: Optional[str] = None, kind: Optional[str] = None, side: Optional[str] = None, min_premium: Optional[int] = None, expiry: Optional[str] = None, opening: Optional[bool] = None, since: Optional[int] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
168
+ """The latest session's prints as they happen, newest first, and any of the last 7 sessions with date. Pass since to poll for new prints. The response also lists the sessions held in dates.
169
+
170
+ GET /options/flow (a list: rows in data, paged by next_cursor)
171
+
172
+ Args:
173
+ symbol: One underlying.
174
+ date: A session in the last 7, YYYY-MM-DD. Default the latest.
175
+ type: call or put.
176
+ kind: sweep, block, split or single.
177
+ side: bought (at or above the ask side) or sold.
178
+ min_premium: Only prints of at least this premium, $. Default 0.
179
+ expiry: One expiration, YYYY-MM-DD.
180
+ opening: Only prints larger than the open interest: new positions. Default false.
181
+ since: Only prints after this id, oldest first (polling).
182
+ limit: Rows per page, up to 1000. Default 100.
183
+ cursor: next_cursor from the previous page.
184
+ """
185
+ return self._get("/options/flow", {"symbol": symbol, "date": date, "type": type, "kind": kind, "side": side, "min_premium": min_premium, "expiry": expiry, "opening": opening, "since": since, "limit": limit, "cursor": cursor})
186
+
187
+ def flow_history(self, *, symbol: Optional[str] = None, from_: Optional[str] = None, to: Optional[str] = None, type: Optional[str] = None, kind: Optional[str] = None, side: Optional[str] = None, min_premium: Optional[int] = None, expiry: Optional[str] = None, opening: Optional[bool] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
188
+ """One symbol's prints from the archive, every session since 2025-03-31, newest first.
189
+
190
+ GET /options/flow/history (a list: rows in data, paged by next_cursor)
191
+
192
+ Args:
193
+ symbol: Underlying. Required.
194
+ from_: First day, YYYY-MM-DD. Default 30 days before to.
195
+ to: Last day. Default today.
196
+ type: call or put.
197
+ kind: sweep, block, split or single.
198
+ side: bought (at or above the ask side) or sold.
199
+ min_premium: Only prints of at least this premium, $. Default 0.
200
+ expiry: One expiration, YYYY-MM-DD.
201
+ opening: Only prints larger than the open interest: new positions. Default false.
202
+ limit: Rows per page, up to 1000. Default 100.
203
+ cursor: next_cursor from the previous page.
204
+ """
205
+ return self._get("/options/flow/history", {"symbol": symbol, "from": from_, "to": to, "type": type, "kind": kind, "side": side, "min_premium": min_premium, "expiry": expiry, "opening": opening, "limit": limit, "cursor": cursor})
206
+
207
+ def sentiment(self, *, scope: Optional[str] = None) -> Dict[str, Any]:
208
+ """Net premium through the day: calls bought minus sold against puts bought minus sold. market is single stocks (indices left out) over 1, 3 or 5 sessions; or one of SPY, QQQ, IWM.
209
+
210
+ GET /market/options/sentiment
211
+
212
+ Args:
213
+ scope: market, market-3d, market-5d, SPY, QQQ or IWM. Default market.
214
+ """
215
+ return self._get("/market/options/sentiment", {"scope": scope})
216
+
217
+ def pressure(self) -> Dict[str, Any]:
218
+ """Each session's net delta of options flow in stock-equivalent dollars, over 60 sessions, with SPY.
219
+
220
+ GET /market/options/pressure
221
+ """
222
+ return self._get("/market/options/pressure", {})
223
+
224
+ def index_gamma(self, *, symbol: Optional[str] = None) -> Dict[str, Any]:
225
+ """Dealer gamma exposure by strike near spot, with the call wall, put wall and gamma flip.
226
+
227
+ GET /market/options/gamma
228
+
229
+ Args:
230
+ symbol: SPX, SPY or QQQ. Default SPX.
231
+ """
232
+ return self._get("/market/options/gamma", {"symbol": symbol})
233
+
234
+ def put_call(self) -> Dict[str, Any]:
235
+ """Today's call and put premium, split between index products and single stocks.
236
+
237
+ GET /market/options/put-call
238
+ """
239
+ return self._get("/market/options/put-call", {})
240
+
241
+ def leaders(self) -> Dict[str, Any]:
242
+ """The 10 single stocks with the most bullish and the most bearish net premium today.
243
+
244
+ GET /market/options/leaders
245
+ """
246
+ return self._get("/market/options/leaders", {})
247
+
248
+ def sectors(self) -> Dict[str, Any]:
249
+ """Bullish and bearish premium today by sector, most bullish first.
250
+
251
+ GET /market/options/sectors
252
+ """
253
+ return self._get("/market/options/sectors", {})
254
+
255
+ def top_prints(self) -> Dict[str, Any]:
256
+ """Today's 12 largest prints by premium.
257
+
258
+ GET /market/options/top-prints
259
+ """
260
+ return self._get("/market/options/top-prints", {})
261
+
262
+ def contracts(self) -> Dict[str, Any]:
263
+ """The 15 contracts with the most volume today.
264
+
265
+ GET /market/options/contracts
266
+ """
267
+ return self._get("/market/options/contracts", {})
268
+
269
+ def iv(self) -> Dict[str, Any]:
270
+ """Liquid stocks with the highest implied volatility and the biggest IV rises and drops since the last session.
271
+
272
+ GET /market/options/iv
273
+ """
274
+ return self._get("/market/options/iv", {})
275
+
276
+ def zero_dte(self, *, symbol: Optional[str] = None) -> Dict[str, Any]:
277
+ """Same-day expiring premium through the day, bought minus sold.
278
+
279
+ GET /market/options/zero-dte
280
+
281
+ Args:
282
+ symbol: SPY, QQQ, SPX or IWM. Default SPY.
283
+ """
284
+ return self._get("/market/options/zero-dte", {"symbol": symbol})
285
+
286
+ def zero_dte_gamma(self, *, symbol: Optional[str] = None) -> Dict[str, Any]:
287
+ """Dealer gamma from today's same-day expiring prints by strike, updated every 30 seconds.
288
+
289
+ GET /market/options/zero-dte/gamma
290
+
291
+ Args:
292
+ symbol: SPX, SPY or QQQ. Default SPX.
293
+ """
294
+ return self._get("/market/options/zero-dte/gamma", {"symbol": symbol})
295
+
296
+ def read(self) -> Dict[str, Any]:
297
+ """The Options page's read of the day: the lean against the last 20 sessions at the same minute, notes, SPY's expected move, dealer gamma and hedging.
298
+
299
+ GET /market/options/read
300
+ """
301
+ return self._get("/market/options/read", {})
302
+
303
+ def bets(self) -> Dict[str, Any]:
304
+ """Single stocks with today's largest net premium against their own usual day, and the same by sector.
305
+
306
+ GET /market/options/bets
307
+ """
308
+ return self._get("/market/options/bets", {})
309
+
310
+ def unusual(self) -> Dict[str, Any]:
311
+ """Today's biggest single-stock prints that opened new positions ($250K+, size above open interest, no spreads).
312
+
313
+ GET /market/options/unusual
314
+ """
315
+ return self._get("/market/options/unusual", {})
316
+
317
+ def chains(self) -> Dict[str, Any]:
318
+ """Today's contracts in four lists: most active, cheap (under $1), LEAPs (a year+) and far out of the money.
319
+
320
+ GET /market/options/chains
321
+ """
322
+ return self._get("/market/options/chains", {})
323
+
324
+ def evidence(self) -> Dict[str, Any]:
325
+ """What the flow lab proved, computed live every 5 minutes: likely ranges for SPY and QQQ, the gamma regime, IV against realized, the 0DTE share and big-move odds.
326
+
327
+ GET /market/options/evidence
328
+ """
329
+ return self._get("/market/options/evidence", {})
330
+
331
+ def overview(self, symbol: str) -> Dict[str, Any]:
332
+ """Today at a glance from prints of $25K+.
333
+
334
+ GET /symbols/{symbol}/options
335
+
336
+ Args:
337
+ symbol: Ticker symbol, e.g. AAPL. Required.
338
+ """
339
+ return self._get(f"/symbols/{_q(symbol)}/options", {})
340
+
341
+ def net_flow(self, symbol: str) -> Dict[str, Any]:
342
+ """Bullish and bearish premium through today against the price.
343
+
344
+ GET /symbols/{symbol}/options/flow
345
+
346
+ Args:
347
+ symbol: Ticker symbol, e.g. AAPL. Required.
348
+ """
349
+ return self._get(f"/symbols/{_q(symbol)}/options/flow", {})
350
+
351
+ def gamma(self, symbol: str) -> Dict[str, Any]:
352
+ """Dealer gamma by strike near spot with the call wall, put wall and gamma flip. 404 for symbols without dealer data.
353
+
354
+ GET /symbols/{symbol}/options/gamma
355
+
356
+ Args:
357
+ symbol: Ticker symbol, e.g. AAPL. Required.
358
+ """
359
+ return self._get(f"/symbols/{_q(symbol)}/options/gamma", {})
360
+
361
+ def volatility(self, symbol: str) -> Dict[str, Any]:
362
+ """The at-the-money term structure and the front expiry's smile.
363
+
364
+ GET /symbols/{symbol}/options/volatility
365
+
366
+ Args:
367
+ symbol: Ticker symbol, e.g. AAPL. Required.
368
+ """
369
+ return self._get(f"/symbols/{_q(symbol)}/options/volatility", {})
370
+
371
+ def open_interest(self, symbol: str, *, expiry: Optional[str] = None) -> Dict[str, Any]:
372
+ """Call and put open interest by strike near spot, for all expiries or one.
373
+
374
+ GET /symbols/{symbol}/options/open-interest
375
+
376
+ Args:
377
+ symbol: Ticker symbol, e.g. AAPL. Required.
378
+ expiry: One expiry, YYYY-MM-DD (from expiries). Default all.
379
+ """
380
+ return self._get(f"/symbols/{_q(symbol)}/options/open-interest", {"expiry": expiry})
381
+
382
+ def expiries(self, symbol: str) -> Dict[str, Any]:
383
+ """Net premium on the 6 busiest strikes by the 6 busiest expiries, today and over the last 5 sessions.
384
+
385
+ GET /symbols/{symbol}/options/expiries
386
+
387
+ Args:
388
+ symbol: Ticker symbol, e.g. AAPL. Required.
389
+ """
390
+ return self._get(f"/symbols/{_q(symbol)}/options/expiries", {})
391
+
392
+ def orders(self, symbol: str) -> Dict[str, Any]:
393
+ """Today's 12 largest orders and 12 most unusual contracts (volume far above open interest).
394
+
395
+ GET /symbols/{symbol}/options/orders
396
+
397
+ Args:
398
+ symbol: Ticker symbol, e.g. AAPL. Required.
399
+ """
400
+ return self._get(f"/symbols/{_q(symbol)}/options/orders", {})
401
+
402
+ def history(self, symbol: str) -> Dict[str, Any]:
403
+ """The last 30 sessions of premium, oldest first.
404
+
405
+ GET /symbols/{symbol}/options/history
406
+
407
+ Args:
408
+ symbol: Ticker symbol, e.g. AAPL. Required.
409
+ """
410
+ return self._get(f"/symbols/{_q(symbol)}/options/history", {})
411
+
412
+ def exposure(self, symbol: str, *, greek: Optional[str] = None, expiry: Optional[str] = None) -> Dict[str, Any]:
413
+ """Dealer gamma, delta, vanna or charm by strike for about 2,500 stocks and ETFs, recomputed every five minutes, with each strike's change since the session before, a week and two weeks back.
414
+
415
+ GET /symbols/{symbol}/options/exposure
416
+
417
+ Args:
418
+ symbol: Ticker symbol, e.g. AAPL. Required.
419
+ greek: gamma, delta, vanna or charm. Default gamma.
420
+ expiry: One expiry, YYYY-MM-DD. Default all.
421
+ """
422
+ return self._get(f"/symbols/{_q(symbol)}/options/exposure", {"greek": greek, "expiry": expiry})
423
+
424
+ def exposure_history(self, symbol: str, *, from_: Optional[str] = None, to: Optional[str] = None) -> Dict[str, Any]:
425
+ """About 18 months of daily dealer net delta and net gamma against the close, oldest first.
426
+
427
+ GET /symbols/{symbol}/options/exposure/history
428
+
429
+ Args:
430
+ symbol: Ticker symbol, e.g. AAPL. Required.
431
+ from_: First day, YYYY-MM-DD.
432
+ to: Last day, YYYY-MM-DD.
433
+ """
434
+ return self._get(f"/symbols/{_q(symbol)}/options/exposure/history", {"from": from_, "to": to})
435
+
436
+
437
+ class Darkpool(Resource):
438
+ """Dark pool prints, history, levels and ranks."""
439
+
440
+ def prints(self, *, symbol: Optional[str] = None, date: Optional[str] = None, venue: Optional[str] = None, min_value: Optional[int] = None, since: Optional[int] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
441
+ """The latest session's prints as they happen, newest first, and any of the last 10 sessions with date. Pass since to poll.
442
+
443
+ GET /darkpool/prints (a list: rows in data, paged by next_cursor)
444
+
445
+ Args:
446
+ symbol: One ticker.
447
+ date: A session in the last 10, YYYY-MM-DD.
448
+ venue: dark (off-exchange) or lit (exchange blocks).
449
+ min_value: Only prints worth at least this, $. Default 0.
450
+ since: Only prints after this id, oldest first.
451
+ limit: Rows per page, up to 1000. Default 100.
452
+ cursor: next_cursor from the previous page.
453
+ """
454
+ return self._get("/darkpool/prints", {"symbol": symbol, "date": date, "venue": venue, "min_value": min_value, "since": since, "limit": limit, "cursor": cursor})
455
+
456
+ def history(self, *, symbol: Optional[str] = None, from_: Optional[str] = None, to: Optional[str] = None, venue: Optional[str] = None, min_value: Optional[int] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
457
+ """One symbol's prints from the archive, every session since 2025-06-09, newest first.
458
+
459
+ GET /darkpool/history (a list: rows in data, paged by next_cursor)
460
+
461
+ Args:
462
+ symbol: Ticker. Required.
463
+ from_: First day. Default 30 days before to.
464
+ to: Last day. Default today.
465
+ venue: dark or lit.
466
+ min_value: Minimum value, $. Default 0.
467
+ limit: Rows per page, up to 1000. Default 100.
468
+ cursor: next_cursor from the previous page.
469
+ """
470
+ return self._get("/darkpool/history", {"symbol": symbol, "from": from_, "to": to, "venue": venue, "min_value": min_value, "limit": limit, "cursor": cursor})
471
+
472
+ def levels(self, symbol: str) -> Dict[str, Any]:
473
+ """The price levels with the most dark pool money over the last 10 sessions, and 30 sessions of daily totals.
474
+
475
+ GET /symbols/{symbol}/darkpool
476
+
477
+ Args:
478
+ symbol: Ticker symbol, e.g. AAPL. Required.
479
+ """
480
+ return self._get(f"/symbols/{_q(symbol)}/darkpool", {})
481
+
482
+ def ranked(self, symbol: str, *, kind: Optional[str] = None, sort: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
483
+ """A year of one stock's prints, each ranked against every print of the same kind in its last 12 months (1 is the largest in a year). Prints are reported after they happen.
484
+
485
+ GET /symbols/{symbol}/darkpool/ranked (a list: rows in data, paged by next_cursor)
486
+
487
+ Args:
488
+ symbol: Ticker symbol, e.g. AAPL. Required.
489
+ kind: trade (block trades), close or open (the auctions). Default trade.
490
+ sort: big (largest first) or new. Default big.
491
+ limit: Rows per page, up to 200. Default 50.
492
+ cursor: next_cursor from the previous page.
493
+ """
494
+ return self._get(f"/symbols/{_q(symbol)}/darkpool/ranked", {"kind": kind, "sort": sort, "limit": limit, "cursor": cursor})
495
+
496
+
497
+ class Research(Resource):
498
+ """Each symbol's fundamentals, analysts and technicals."""
499
+
500
+ def summary(self, symbol: str) -> Dict[str, Any]:
501
+ """Quote, returns, valuation, margins, short interest and the company profile.
502
+
503
+ GET /symbols/{symbol}/summary
504
+
505
+ Args:
506
+ symbol: Ticker symbol, e.g. AAPL. Required.
507
+ """
508
+ return self._get(f"/symbols/{_q(symbol)}/summary", {})
509
+
510
+ def analysts(self, symbol: str) -> Dict[str, Any]:
511
+ """Ratings from each firm's latest call in the last year, price targets from the last six months, and recent actions.
512
+
513
+ GET /symbols/{symbol}/analysts
514
+
515
+ Args:
516
+ symbol: Ticker symbol, e.g. AAPL. Required.
517
+ """
518
+ return self._get(f"/symbols/{_q(symbol)}/analysts", {})
519
+
520
+ def earnings(self, symbol: str) -> Dict[str, Any]:
521
+ """The last 8 quarters against estimates, the beat record and the next report.
522
+
523
+ GET /symbols/{symbol}/earnings
524
+
525
+ Args:
526
+ symbol: Ticker symbol, e.g. AAPL. Required.
527
+ """
528
+ return self._get(f"/symbols/{_q(symbol)}/earnings", {})
529
+
530
+ def financials(self, symbol: str) -> Dict[str, Any]:
531
+ """Twelve quarters and six fiscal years of the income statement, with cash flow.
532
+
533
+ GET /symbols/{symbol}/financials
534
+
535
+ Args:
536
+ symbol: Ticker symbol, e.g. AAPL. Required.
537
+ """
538
+ return self._get(f"/symbols/{_q(symbol)}/financials", {})
539
+
540
+ def technicals(self, symbol: str) -> Dict[str, Any]:
541
+ """An overall technical rating with the signals, moving averages and oscillators behind it, and monthly seasonality.
542
+
543
+ GET /symbols/{symbol}/technicals
544
+
545
+ Args:
546
+ symbol: Ticker symbol, e.g. AAPL. Required.
547
+ """
548
+ return self._get(f"/symbols/{_q(symbol)}/technicals", {})
549
+
550
+ def ownership(self, symbol: str) -> Dict[str, Any]:
551
+ """Recent insider buys and sells, short interest history, float and institutional ownership.
552
+
553
+ GET /symbols/{symbol}/ownership
554
+
555
+ Args:
556
+ symbol: Ticker symbol, e.g. AAPL. Required.
557
+ """
558
+ return self._get(f"/symbols/{_q(symbol)}/ownership", {})
559
+
560
+ def holdings(self, symbol: str) -> Dict[str, Any]:
561
+ """An ETF's sector weights, top 10 holdings and facts.
562
+
563
+ GET /symbols/{symbol}/holdings
564
+
565
+ Args:
566
+ symbol: Ticker symbol, e.g. AAPL. Required.
567
+ """
568
+ return self._get(f"/symbols/{_q(symbol)}/holdings", {})
569
+
570
+
571
+ class Scanner(Resource):
572
+ """The full-market scanner."""
573
+
574
+ def scan(self, *, fields: Optional[str] = None, labels: Optional[str] = None, sector: Optional[str] = None, kind: Optional[str] = None, sort: Optional[str] = None, order: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None, **extra: Any) -> Dict[str, Any]:
575
+ """Filter on any numeric field with min_<field> and max_<field> (e.g. min_rvol=2&max_rsi=30), require labels, and sort by any field.
576
+
577
+ GET /scanner (a list: rows in data, paged by next_cursor)
578
+
579
+ Args:
580
+ fields: Comma-separated fields to return. Default a compact set.
581
+ labels: Comma-separated label ids; a row must carry all of them.
582
+ sector: One sector, e.g. Technology.
583
+ kind: stock, etf or adr.
584
+ sort: Field to sort by. Default dvol.
585
+ order: asc or desc. Default desc.
586
+ limit: Rows per page, up to 1000. Default 100.
587
+ cursor: next_cursor from the previous page.
588
+ """
589
+ return self._get("/scanner", {"fields": fields, "labels": labels, "sector": sector, "kind": kind, "sort": sort, "order": order, "limit": limit, "cursor": cursor}, extra)
590
+
591
+ def fields(self) -> Dict[str, Any]:
592
+ """Every field name, and every label with its group and lean.
593
+
594
+ GET /scanner/fields
595
+ """
596
+ return self._get("/scanner/fields", {})
597
+
598
+ def symbol(self, symbol: str) -> Dict[str, Any]:
599
+ """One symbol's full scanner row.
600
+
601
+ GET /symbols/{symbol}/scan
602
+
603
+ Args:
604
+ symbol: Ticker symbol, e.g. AAPL. Required.
605
+ """
606
+ return self._get(f"/symbols/{_q(symbol)}/scan", {})
607
+
608
+
609
+ class Signals(Resource):
610
+ """Live orders matching tested flow patterns."""
611
+
612
+ def list(self, *, source: Optional[str] = None, symbol: Optional[str] = None, status: Optional[str] = None, date: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
613
+ """Signals newest first, today's live.
614
+
615
+ GET /signals (a list: rows in data, paged by next_cursor)
616
+
617
+ Args:
618
+ source: One pattern: repeat-flow, opening-calls, heavy-calls, etf-repeat-calls, itm-puts, burst-calls, index-tide-calls.
619
+ symbol: One ticker.
620
+ status: open or settled.
621
+ date: Fired on this day, YYYY-MM-DD.
622
+ limit: Rows per page, up to 100. Default 20.
623
+ cursor: next_cursor from the previous page.
624
+ """
625
+ return self._get("/signals", {"source": source, "symbol": symbol, "status": status, "date": date, "limit": limit, "cursor": cursor})
626
+
627
+ def get(self, id: int) -> Dict[str, Any]:
628
+ """One signal with the prints behind it and every mark.
629
+
630
+ GET /signals/{id}
631
+
632
+ Args:
633
+ id: Signal id. Required.
634
+ """
635
+ return self._get(f"/signals/{_q(id)}", {})
636
+
637
+
638
+ class Probabilities(Resource):
639
+ """Kalshi and Polymarket finance questions."""
640
+
641
+ def list(self, *, group: Optional[str] = None, symbol: Optional[str] = None) -> Dict[str, Any]:
642
+ """Every live question the board shows (books no wider than 10 cents, active in the last day), with its leading outcomes.
643
+
644
+ GET /probabilities (a list: rows in data, paged by next_cursor)
645
+
646
+ Args:
647
+ group: close, range, hit or earnings.
648
+ symbol: One underlying, e.g. SPX, NDX, NVDA.
649
+ """
650
+ return self._get("/probabilities", {"group": group, "symbol": symbol})
651
+
652
+ def get(self, id: str) -> Dict[str, Any]:
653
+ """One question with every outcome, and for earnings questions the stock's past report-day moves.
654
+
655
+ GET /probabilities/{id}
656
+
657
+ Args:
658
+ id: A question's id. Required.
659
+ """
660
+ return self._get(f"/probabilities/{_q(id)}", {})
661
+
662
+ def history(self, market: str, *, interval: Optional[str] = None, from_: Optional[str] = None, to: Optional[str] = None) -> Dict[str, Any]:
663
+ """One outcome's chance through time, live or settled: the last trade's price in each hour or day.
664
+
665
+ GET /probabilities/markets/{market}/history
666
+
667
+ Args:
668
+ market: A Kalshi ticker or Polymarket condition id: an outcome's market or a settled row's id. Required.
669
+ interval: hour or day. Default hour.
670
+ from_: First day, YYYY-MM-DD.
671
+ to: Last day, YYYY-MM-DD.
672
+ """
673
+ return self._get(f"/probabilities/markets/{_q(market)}/history", {"interval": interval, "from": from_, "to": to})
674
+
675
+ def outlook(self) -> Dict[str, Any]:
676
+ """Where Kalshi and Polymarket put the S&P 500 at each date they price, from the next close to year end.
677
+
678
+ GET /probabilities/outlook
679
+ """
680
+ return self._get("/probabilities/outlook", {})
681
+
682
+ def track(self) -> Dict[str, Any]:
683
+ """The S&P 500's and Nasdaq-100's last 30 sessions: where the crowd put the close at 10 AM against where it closed.
684
+
685
+ GET /probabilities/track
686
+ """
687
+ return self._get("/probabilities/track", {})
688
+
689
+ def settled(self, *, source: Optional[str] = None, series: Optional[str] = None, category: Optional[str] = None, from_: Optional[str] = None, to: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
690
+ """Every finance market Kalshi has settled since June 2021, and Polymarket's, the latest to close first.
691
+
692
+ GET /probabilities/settled (a list: rows in data, paged by next_cursor)
693
+
694
+ Args:
695
+ source: kalshi or polymarket. Default kalshi.
696
+ series: Kalshi series (e.g. KXINXU, the S&P 500 close) or Polymarket event id.
697
+ category: Kalshi category (Financials, Economics, Crypto) or Polymarket tag (stocks, economy, fed).
698
+ from_: Closed on or after, YYYY-MM-DD. Default 30 days back unless series is set.
699
+ to: Closed on or before, YYYY-MM-DD.
700
+ limit: Rows per page, up to 1000. Default 100.
701
+ cursor: next_cursor from the previous page.
702
+ """
703
+ return self._get("/probabilities/settled", {"source": source, "series": series, "category": category, "from": from_, "to": to, "limit": limit, "cursor": cursor})
704
+
705
+
706
+ class Fund(Resource):
707
+ """The Tradytics AI Fund."""
708
+
709
+ def summary(self) -> Dict[str, Any]:
710
+ """Exposure, open positions and the latest posts.
711
+
712
+ GET /fund
713
+ """
714
+ return self._get("/fund", {})
715
+
716
+ def trades(self, *, status: Optional[str] = None, symbol: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
717
+ """Every trade, open and closed, newest first.
718
+
719
+ GET /fund/trades (a list: rows in data, paged by next_cursor)
720
+
721
+ Args:
722
+ status: open or closed.
723
+ symbol: One ticker.
724
+ limit: Rows per page, up to 200. Default 50.
725
+ cursor: next_cursor from the previous page.
726
+ """
727
+ return self._get("/fund/trades", {"status": status, "symbol": symbol, "limit": limit, "cursor": cursor})
728
+
729
+ def posts(self, *, kind: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None) -> Dict[str, Any]:
730
+ """The portfolio manager's posts (one per trade, add, trim or close) and weekly letters, newest first.
731
+
732
+ GET /fund/posts (a list: rows in data, paged by next_cursor)
733
+
734
+ Args:
735
+ kind: post or letter.
736
+ limit: Rows per page, up to 100. Default 20.
737
+ cursor: next_cursor from the previous page.
738
+ """
739
+ return self._get("/fund/posts", {"kind": kind, "limit": limit, "cursor": cursor})
740
+
741
+
742
+ class Account(Resource):
743
+ """Your own backtests, portfolio and dashboards."""
744
+
745
+ def backtests(self, *, q: Optional[str] = None, limit: Optional[int] = None) -> Dict[str, Any]:
746
+ """Your backtests, the latest first.
747
+
748
+ GET /backtests (a list: rows in data, paged by next_cursor)
749
+
750
+ Args:
751
+ q: Only backtests whose title or messages mention this.
752
+ limit: Rows per page, up to 200. Default 50.
753
+ """
754
+ return self._get("/backtests", {"q": q, "limit": limit})
755
+
756
+ def backtest(self, id: int) -> Dict[str, Any]:
757
+ """One backtest: the conversation and every run it made.
758
+
759
+ GET /backtests/{id}
760
+
761
+ Args:
762
+ id: A backtest's id. Required.
763
+ """
764
+ return self._get(f"/backtests/{_q(id)}", {})
765
+
766
+ def portfolio(self) -> Dict[str, Any]:
767
+ """Your linked brokerage accounts, read-only: holdings, options and recent orders.
768
+
769
+ GET /portfolio
770
+ """
771
+ return self._get("/portfolio", {})
772
+
773
+ def portfolio_trades(self) -> Dict[str, Any]:
774
+ """A year of closed round trips, newest first, with dividends and fees.
775
+
776
+ GET /portfolio/trades
777
+ """
778
+ return self._get("/portfolio/trades", {})
779
+
780
+ def portfolio_factors(self) -> Dict[str, Any]:
781
+ """Risk by source, beta, value at risk and tracking error, and returns split into market, sectors, styles and stock picking. 404 not_connected before a brokerage is linked.
782
+
783
+ GET /portfolio/factors
784
+ """
785
+ return self._get("/portfolio/factors", {})
786
+
787
+ def dashboards(self) -> Dict[str, Any]:
788
+ """Your dashboards.
789
+
790
+ GET /dashboards (a list: rows in data, paged by next_cursor)
791
+ """
792
+ return self._get("/dashboards", {})
793
+
794
+ def dashboard(self, id: int) -> Dict[str, Any]:
795
+ """One dashboard's layout.
796
+
797
+ GET /dashboards/{id}
798
+
799
+ Args:
800
+ id: A dashboard's id. Required.
801
+ """
802
+ return self._get(f"/dashboards/{_q(id)}", {})
803
+
804
+
805
+ RESOURCES = {"symbols": Symbols, "market": Market, "options": Options, "darkpool": Darkpool, "research": Research, "scanner": Scanner, "signals": Signals, "probabilities": Probabilities, "fund": Fund, "account": Account}
tradytics/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
tradytics/client.py ADDED
@@ -0,0 +1,59 @@
1
+ """The client: one attribute per resource (client.options.flow(...), client.symbols.bars("SPY")), and helpers to walk
2
+ every page of a list and to stream new prints."""
3
+ import os
4
+ import time
5
+ from typing import Any, Callable, Dict, Iterator, Optional
6
+
7
+ from ._base import Transport, TradyticsError
8
+ from ._resources import RESOURCES, Account, Darkpool, Fund, Market, Options, Probabilities, Research, Scanner, Signals, Symbols
9
+ from ._version import __version__
10
+
11
+ BASE_URL = "https://tradytics.com/api/public/v1"
12
+
13
+
14
+ class Client:
15
+ symbols: Symbols
16
+ market: Market
17
+ options: Options
18
+ darkpool: Darkpool
19
+ research: Research
20
+ scanner: Scanner
21
+ signals: Signals
22
+ probabilities: Probabilities
23
+ fund: Fund
24
+ account: Account
25
+
26
+ def __init__(self, api_key: Optional[str] = None, *, base_url: str = BASE_URL, timeout: float = 30, max_retries: int = 3):
27
+ """`api_key` defaults to the TRADYTICS_API_KEY environment variable. Make one at https://tradytics.com/developer/keys."""
28
+ key = api_key or os.environ.get("TRADYTICS_API_KEY")
29
+ if not key:
30
+ raise TradyticsError(401, "missing_key", "Pass api_key or set TRADYTICS_API_KEY. Make a key at https://tradytics.com/developer/keys.")
31
+ self._t = Transport(key, base_url, timeout, max_retries, f"tradytics-python/{__version__}")
32
+ for name, cls in RESOURCES.items():
33
+ setattr(self, name, cls(self._t))
34
+
35
+ def get(self, path: str, **params: Any) -> Dict[str, Any]:
36
+ """Any endpoint by its path, e.g. client.get("/options/flow", symbol="NVDA")."""
37
+ return self._t.get(path, params)
38
+
39
+ def paginate(self, method: Callable[..., Dict[str, Any]], *args: Any, **params: Any) -> Iterator[Dict[str, Any]]:
40
+ """Every row of a list endpoint, page after page: for row in client.paginate(client.options.flow, symbol="SPY")."""
41
+ cursor = "before" if getattr(method, "__name__", "") == "bars" else "cursor"
42
+ while True:
43
+ page = method(*args, **params)
44
+ yield from page.get("data") or []
45
+ nxt = page.get("next_cursor")
46
+ if not nxt:
47
+ return
48
+ params[cursor] = nxt
49
+
50
+ def stream(self, method: Callable[..., Dict[str, Any]], *, interval: float = 2.0, **filters: Any) -> Iterator[Dict[str, Any]]:
51
+ """New prints as they happen, each once, oldest first, from client.options.flow or client.darkpool.prints:
52
+ for p in client.stream(client.options.flow, min_premium=250_000)."""
53
+ latest = method(limit=1, **filters).get("data") or []
54
+ last = latest[0]["id"] if latest else 0
55
+ while True:
56
+ for row in method(since=last, **filters).get("data") or []:
57
+ last = max(last, row["id"])
58
+ yield row
59
+ time.sleep(interval)
tradytics/py.typed ADDED
File without changes
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: tradytics
3
+ Version: 0.1.0
4
+ Summary: Python client for the Tradytics API: options flow, dark pool prints, dealer exposure and market data.
5
+ Author-email: Tradytics <hello@tradytics.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://tradytics.com/docs
8
+ Project-URL: Documentation, https://tradytics.com/docs
9
+ Project-URL: API keys, https://tradytics.com/developer/keys
10
+ Keywords: options flow,dark pool,gamma exposure,market data,stocks,trading,api
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Office/Business :: Financial :: Investment
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Dynamic: license-file
20
+
21
+ # Tradytics for Python
22
+
23
+ The official Python client for the [Tradytics API](https://tradytics.com/docs): live and historical options flow, dark pool prints ranked against a year, dealer gamma, delta, vanna and charm, the options market, research on every symbol, the scanner, Signals, Kalshi and Polymarket, and the AI Fund. One method per endpoint, no dependencies.
24
+
25
+ ```bash
26
+ pip install tradytics
27
+ ```
28
+
29
+ The API is part of Tradytics Pro. Make a key at [tradytics.com/developer/keys](https://tradytics.com/developer/keys) and export it:
30
+
31
+ ```bash
32
+ export TRADYTICS_API_KEY="tdy_..."
33
+ ```
34
+
35
+ ## Quickstart
36
+
37
+ ```python
38
+ from tradytics import Client
39
+
40
+ client = Client() # reads TRADYTICS_API_KEY
41
+
42
+ flow = client.options.flow(symbol="NVDA", min_premium=1_000_000, opening=True)
43
+ for p in flow["data"]:
44
+ print(p["time"], p["symbol"], p["type"], p["strike"], p["expiry"], p["premium"])
45
+
46
+ bars = client.symbols.bars("SPY", timeframe="5m", limit=500)
47
+ gamma = client.options.exposure("SPY", greek="gamma")
48
+ prints = client.darkpool.prints(symbol="AAPL", min_value=5_000_000)
49
+ ```
50
+
51
+ Objects come back as `{"data": ..., "as_of": ...}`, lists as `{"data": [...], "count": n, "next_cursor": ...}`, the same JSON the API returns.
52
+
53
+ ## Resources
54
+
55
+ | Attribute | Covers |
56
+ | --- | --- |
57
+ | `client.symbols` | Symbols, live quotes, bars from 1 second to 1 month, each symbol's news |
58
+ | `client.market` | Major assets, breadth, sectors, heatmap, themes, movers, crypto, news, earnings, insiders, Congress |
59
+ | `client.options` | Options flow and its history, the options market, each symbol's gamma, volatility, open interest and dealer exposure |
60
+ | `client.darkpool` | Dark pool prints, history, levels and ranked prints |
61
+ | `client.research` | Summary, analysts, earnings, financials, technicals, ownership, fund holdings |
62
+ | `client.scanner` | The full-market scanner: any field with `min_<field>` / `max_<field>` |
63
+ | `client.signals` | Signals: live orders matching tested flow patterns |
64
+ | `client.probabilities` | Kalshi and Polymarket finance questions, history and the settled archive |
65
+ | `client.fund` | The Tradytics AI Fund's book, trades and posts |
66
+ | `client.account` | Your own backtests, portfolio and dashboards |
67
+
68
+ Every method's docstring has its parameters; [the docs](https://tradytics.com/docs) have every field.
69
+
70
+ ## Every page
71
+
72
+ ```python
73
+ for p in client.paginate(client.options.flow_history, symbol="TSLA", min_premium=500_000):
74
+ ...
75
+ ```
76
+
77
+ ## New prints as they happen
78
+
79
+ ```python
80
+ for p in client.stream(client.options.flow, min_premium=250_000):
81
+ print(p["symbol"], p["type"], p["strike"], p["premium"])
82
+ ```
83
+
84
+ `client.stream` works with `client.options.flow` and `client.darkpool.prints`, and yields each print once.
85
+
86
+ ## Errors and limits
87
+
88
+ Calls that fail raise `tradytics.TradyticsError` with `status`, `code` (e.g. `missing_scope`, `invalid_parameter`), `message` and `request_id`. Rate limits (300 calls a minute, 50,000 a day per key) and brief outages are retried for you, honoring `Retry-After`. `Client(api_key=..., timeout=30, max_retries=3)` changes the defaults.
89
+
90
+ Docs: [tradytics.com/docs](https://tradytics.com/docs). MCP server for Claude, ChatGPT and Cursor: [tradytics.com/docs/mcp](https://tradytics.com/docs/mcp).
@@ -0,0 +1,11 @@
1
+ tradytics/__init__.py,sha256=mEqwci7sE7Z_-kIZcSAohwFvhtDZIteRH_0as81WEZI,287
2
+ tradytics/_base.py,sha256=ACm-DuoRsUQvbkwEUieznewrvgvl0ZWv4NjI9cYswvA,3059
3
+ tradytics/_resources.py,sha256=G2vPh4nQDzunkcr8FyeZbVnVqMgF23BpJdVxQshqBiA,33733
4
+ tradytics/_version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
5
+ tradytics/client.py,sha256=vUuai7UgnEbixt3PW14y68q1bj0YSzq3ut9e3aohN-U,2799
6
+ tradytics/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ tradytics-0.1.0.dist-info/licenses/LICENSE,sha256=8pcafi7gmWYScDTJOqQfGXd53d8fPM9B7z9N6NItkVg,1066
8
+ tradytics-0.1.0.dist-info/METADATA,sha256=xYfk6PhdMJvFuyYLzELxriHkS8_eNT4uXTGDrCgzWNw,4033
9
+ tradytics-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
10
+ tradytics-0.1.0.dist-info/top_level.txt,sha256=2opQBI8pi5qcxWxLuOF90JzvpErdAQlSZMGvimG2sUE,10
11
+ tradytics-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,16 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tradytics
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
+ persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
+ Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ tradytics