xoomar 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.
- xoomar-0.1.0/LICENSE +21 -0
- xoomar-0.1.0/PKG-INFO +70 -0
- xoomar-0.1.0/README.md +47 -0
- xoomar-0.1.0/pyproject.toml +32 -0
- xoomar-0.1.0/setup.cfg +4 -0
- xoomar-0.1.0/src/xoomar/__init__.py +230 -0
- xoomar-0.1.0/src/xoomar.egg-info/PKG-INFO +70 -0
- xoomar-0.1.0/src/xoomar.egg-info/SOURCES.txt +9 -0
- xoomar-0.1.0/src/xoomar.egg-info/dependency_links.txt +1 -0
- xoomar-0.1.0/src/xoomar.egg-info/top_level.txt +1 -0
- xoomar-0.1.0/tests/test_client.py +47 -0
xoomar-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 XOOMAR
|
|
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.
|
xoomar-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xoomar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Client for the XOOMAR free market data API: short interest, insider trades, COT, funding rates, Fed liquidity, SEC filings and more.
|
|
5
|
+
Author-email: XOOMAR <info@xoomar.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://xoomar.com/markets/api
|
|
8
|
+
Project-URL: Documentation, https://xoomar.com/markets/api
|
|
9
|
+
Project-URL: Source, https://github.com/xoomar-llc/xoomar-python
|
|
10
|
+
Project-URL: Data, https://xoomar.com/markets
|
|
11
|
+
Keywords: market data,sec,finra,cot,short interest,insider trading,crypto,api
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# xoomar
|
|
25
|
+
|
|
26
|
+
Python client for the [XOOMAR](https://xoomar.com/markets) free market data API: 29 datasets from primary sources (SEC EDGAR and XBRL, FINRA, CFTC, the Federal Reserve, USAspending, exchange APIs) as clean JSON, no key needed to start.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install xoomar
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from xoomar import Xoomar
|
|
34
|
+
|
|
35
|
+
x = Xoomar() # 30 requests a minute; Xoomar(api_key="...") for 120 with a free key
|
|
36
|
+
|
|
37
|
+
x.short_interest("GME")[-1] # FINRA short interest, latest settlement
|
|
38
|
+
x.short_volume("GME", days=30) # FINRA daily short sale volume
|
|
39
|
+
x.fails_to_deliver("GME") # SEC fails to deliver
|
|
40
|
+
x.insiders("NVDA") # SEC Form 4 trades
|
|
41
|
+
x.large_holders("HIMS") # Schedule 13D and 13G holders
|
|
42
|
+
x.financials("AAPL")["quarterly"] # XBRL income statement by quarter
|
|
43
|
+
x.fund_holders("AMZN") # which tracked 13F managers hold it
|
|
44
|
+
x.cot("gold") # CFTC positioning history
|
|
45
|
+
x.fed_liquidity()[-1] # net liquidity, this week
|
|
46
|
+
x.funding_rates() # perpetual funding on three exchanges
|
|
47
|
+
x.bitcoin_treasuries() # bitcoin on public balance sheets
|
|
48
|
+
x.form_d(days=7) # private placements filed this week
|
|
49
|
+
x.federal_contracts(ticker="LMT") # federal contract actions
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Every method returns the `data` part of the response; `x.last_meta` holds `updatedAt`, `source`, `license` and `attribution` from the last call. `x.get("short-interest", symbol="TSLA")` calls any endpoint directly and `x.csv("short-interest/csv")` fetches a CSV download.
|
|
53
|
+
|
|
54
|
+
Full endpoint reference, fields and limits: https://xoomar.com/markets/api
|
|
55
|
+
|
|
56
|
+
## Datasets
|
|
57
|
+
|
|
58
|
+
Short interest, daily short volume, fails to deliver, insider trades (Form 4), planned sales (Form 144), large holders (13D/13G), 13F fund holdings, company financials and buybacks (XBRL), 8-K events, structured products, federal contracts, Form D private placements, the IPO pipeline, bitcoin treasuries, CFTC COT, funding rates, open interest, liquidations, options, whale positions, sentiment, signals, ETF flows, prediction markets, Fed liquidity, macro, policy rates, economic calendar.
|
|
59
|
+
|
|
60
|
+
## Rate limits and keys
|
|
61
|
+
|
|
62
|
+
30 requests a minute per IP without a key. A free account at https://xoomar.com/signup gives a key for 120 a minute; pass it as `Xoomar(api_key=...)`. A 429 raises `XoomarRateLimited` with `retry_after`.
|
|
63
|
+
|
|
64
|
+
## Attribution
|
|
65
|
+
|
|
66
|
+
The data is free to use, including commercially. When you republish it, on a site, in an app, in an article, in a dataset or a chart, credit XOOMAR with a visible link to the dataset page on xoomar.com. Terms: https://xoomar.com/terms
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT, XOOMAR.
|
xoomar-0.1.0/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# xoomar
|
|
2
|
+
|
|
3
|
+
Python client for the [XOOMAR](https://xoomar.com/markets) free market data API: 29 datasets from primary sources (SEC EDGAR and XBRL, FINRA, CFTC, the Federal Reserve, USAspending, exchange APIs) as clean JSON, no key needed to start.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install xoomar
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from xoomar import Xoomar
|
|
11
|
+
|
|
12
|
+
x = Xoomar() # 30 requests a minute; Xoomar(api_key="...") for 120 with a free key
|
|
13
|
+
|
|
14
|
+
x.short_interest("GME")[-1] # FINRA short interest, latest settlement
|
|
15
|
+
x.short_volume("GME", days=30) # FINRA daily short sale volume
|
|
16
|
+
x.fails_to_deliver("GME") # SEC fails to deliver
|
|
17
|
+
x.insiders("NVDA") # SEC Form 4 trades
|
|
18
|
+
x.large_holders("HIMS") # Schedule 13D and 13G holders
|
|
19
|
+
x.financials("AAPL")["quarterly"] # XBRL income statement by quarter
|
|
20
|
+
x.fund_holders("AMZN") # which tracked 13F managers hold it
|
|
21
|
+
x.cot("gold") # CFTC positioning history
|
|
22
|
+
x.fed_liquidity()[-1] # net liquidity, this week
|
|
23
|
+
x.funding_rates() # perpetual funding on three exchanges
|
|
24
|
+
x.bitcoin_treasuries() # bitcoin on public balance sheets
|
|
25
|
+
x.form_d(days=7) # private placements filed this week
|
|
26
|
+
x.federal_contracts(ticker="LMT") # federal contract actions
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Every method returns the `data` part of the response; `x.last_meta` holds `updatedAt`, `source`, `license` and `attribution` from the last call. `x.get("short-interest", symbol="TSLA")` calls any endpoint directly and `x.csv("short-interest/csv")` fetches a CSV download.
|
|
30
|
+
|
|
31
|
+
Full endpoint reference, fields and limits: https://xoomar.com/markets/api
|
|
32
|
+
|
|
33
|
+
## Datasets
|
|
34
|
+
|
|
35
|
+
Short interest, daily short volume, fails to deliver, insider trades (Form 4), planned sales (Form 144), large holders (13D/13G), 13F fund holdings, company financials and buybacks (XBRL), 8-K events, structured products, federal contracts, Form D private placements, the IPO pipeline, bitcoin treasuries, CFTC COT, funding rates, open interest, liquidations, options, whale positions, sentiment, signals, ETF flows, prediction markets, Fed liquidity, macro, policy rates, economic calendar.
|
|
36
|
+
|
|
37
|
+
## Rate limits and keys
|
|
38
|
+
|
|
39
|
+
30 requests a minute per IP without a key. A free account at https://xoomar.com/signup gives a key for 120 a minute; pass it as `Xoomar(api_key=...)`. A 429 raises `XoomarRateLimited` with `retry_after`.
|
|
40
|
+
|
|
41
|
+
## Attribution
|
|
42
|
+
|
|
43
|
+
The data is free to use, including commercially. When you republish it, on a site, in an app, in an article, in a dataset or a chart, credit XOOMAR with a visible link to the dataset page on xoomar.com. Terms: https://xoomar.com/terms
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT, XOOMAR.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "xoomar"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Client for the XOOMAR free market data API: short interest, insider trades, COT, funding rates, Fed liquidity, SEC filings and more."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "XOOMAR", email = "info@xoomar.com" }]
|
|
13
|
+
keywords = ["market data", "sec", "finra", "cot", "short interest", "insider trading", "crypto", "api"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Topic :: Office/Business :: Financial",
|
|
22
|
+
]
|
|
23
|
+
dependencies = []
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://xoomar.com/markets/api"
|
|
27
|
+
Documentation = "https://xoomar.com/markets/api"
|
|
28
|
+
Source = "https://github.com/xoomar-llc/xoomar-python"
|
|
29
|
+
Data = "https://xoomar.com/markets"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
xoomar-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""Client for the XOOMAR free market data API (https://xoomar.com/markets/api).
|
|
2
|
+
|
|
3
|
+
Every method returns the ``data`` part of the JSON response as plain Python
|
|
4
|
+
objects (lists or dicts). The full envelope of the last call, with
|
|
5
|
+
``updatedAt``, ``source``, ``license`` and ``attribution``, is on
|
|
6
|
+
``client.last_meta``.
|
|
7
|
+
|
|
8
|
+
The data is free with attribution: when you republish it, link to the
|
|
9
|
+
dataset page on xoomar.com. See https://xoomar.com/terms.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import urllib.error
|
|
16
|
+
import urllib.parse
|
|
17
|
+
import urllib.request
|
|
18
|
+
from typing import Any, Dict, Optional
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
|
21
|
+
__all__ = ["Xoomar", "XoomarError", "XoomarRateLimited"]
|
|
22
|
+
|
|
23
|
+
DEFAULT_BASE_URL = "https://xoomar.com"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class XoomarError(Exception):
|
|
27
|
+
"""An HTTP or API error. ``status`` is the HTTP status, ``body`` the response text."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, status: int, body: str, url: str):
|
|
30
|
+
super().__init__(f"HTTP {status} from {url}: {body[:200]}")
|
|
31
|
+
self.status = status
|
|
32
|
+
self.body = body
|
|
33
|
+
self.url = url
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class XoomarRateLimited(XoomarError):
|
|
37
|
+
"""429: 30 requests a minute without a key, 120 with a free key from https://xoomar.com/signup."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, status: int, body: str, url: str, retry_after: Optional[int]):
|
|
40
|
+
super().__init__(status, body, url)
|
|
41
|
+
self.retry_after = retry_after
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Xoomar:
|
|
45
|
+
"""
|
|
46
|
+
>>> from xoomar import Xoomar
|
|
47
|
+
>>> x = Xoomar() # or Xoomar(api_key="...") for 120 requests a minute
|
|
48
|
+
>>> x.short_interest("GME")[-1]
|
|
49
|
+
{'settlementDate': '2026-08-14', 'symbol': 'GME', 'shortQty': 54036583, ...}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, api_key: Optional[str] = None, base_url: str = DEFAULT_BASE_URL, timeout: float = 30.0, user_agent: Optional[str] = None):
|
|
53
|
+
self.api_key = api_key
|
|
54
|
+
self.base_url = base_url.rstrip("/")
|
|
55
|
+
self.timeout = timeout
|
|
56
|
+
self.user_agent = user_agent or f"xoomar-python/{__version__}"
|
|
57
|
+
self.last_meta: Dict[str, Any] = {}
|
|
58
|
+
|
|
59
|
+
# ── transport ──
|
|
60
|
+
|
|
61
|
+
def get(self, path: str, **params: Any) -> Any:
|
|
62
|
+
"""GET ``/api/markets/<path>`` with query parameters; returns the ``data`` field."""
|
|
63
|
+
query = {k: v for k, v in params.items() if v is not None}
|
|
64
|
+
url = f"{self.base_url}/api/markets/{path.lstrip('/')}"
|
|
65
|
+
if query:
|
|
66
|
+
url += "?" + urllib.parse.urlencode(query)
|
|
67
|
+
headers = {"Accept": "application/json", "User-Agent": self.user_agent}
|
|
68
|
+
if self.api_key:
|
|
69
|
+
headers["x-api-key"] = self.api_key
|
|
70
|
+
req = urllib.request.Request(url, headers=headers)
|
|
71
|
+
try:
|
|
72
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as res:
|
|
73
|
+
payload = json.loads(res.read().decode("utf-8"))
|
|
74
|
+
except urllib.error.HTTPError as e:
|
|
75
|
+
body = e.read().decode("utf-8", "replace") if e.fp else ""
|
|
76
|
+
if e.code == 429:
|
|
77
|
+
ra = e.headers.get("Retry-After") if e.headers else None
|
|
78
|
+
raise XoomarRateLimited(e.code, body, url, int(ra) if ra and ra.isdigit() else None) from None
|
|
79
|
+
raise XoomarError(e.code, body, url) from None
|
|
80
|
+
if isinstance(payload, dict) and "data" in payload:
|
|
81
|
+
self.last_meta = {k: v for k, v in payload.items() if k != "data"}
|
|
82
|
+
return payload["data"]
|
|
83
|
+
self.last_meta = {}
|
|
84
|
+
return payload
|
|
85
|
+
|
|
86
|
+
def csv(self, path: str, **params: Any) -> str:
|
|
87
|
+
"""The CSV download for a dataset, e.g. ``csv("short-interest/csv")``, as text."""
|
|
88
|
+
query = {k: v for k, v in params.items() if v is not None}
|
|
89
|
+
url = f"{self.base_url}/api/markets/{path.lstrip('/')}"
|
|
90
|
+
if query:
|
|
91
|
+
url += "?" + urllib.parse.urlencode(query)
|
|
92
|
+
headers = {"Accept": "text/csv", "User-Agent": self.user_agent}
|
|
93
|
+
if self.api_key:
|
|
94
|
+
headers["x-api-key"] = self.api_key
|
|
95
|
+
req = urllib.request.Request(url, headers=headers)
|
|
96
|
+
try:
|
|
97
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as res:
|
|
98
|
+
return res.read().decode("utf-8")
|
|
99
|
+
except urllib.error.HTTPError as e:
|
|
100
|
+
raise XoomarError(e.code, e.read().decode("utf-8", "replace") if e.fp else "", url) from None
|
|
101
|
+
|
|
102
|
+
# ── companies (SEC and FINRA) ──
|
|
103
|
+
|
|
104
|
+
def short_interest(self, symbol: Optional[str] = None) -> Any:
|
|
105
|
+
"""FINRA short interest: history for a symbol, or the latest settlement's highest days to cover."""
|
|
106
|
+
return self.get("short-interest", symbol=symbol)
|
|
107
|
+
|
|
108
|
+
def short_volume(self, symbol: Optional[str] = None, days: Optional[int] = None, sort: Optional[str] = None) -> Any:
|
|
109
|
+
"""FINRA daily short sale volume: history for a symbol, or the latest day (sort="shares" for largest volumes)."""
|
|
110
|
+
return self.get("short-volume", symbol=symbol, days=days, sort=sort)
|
|
111
|
+
|
|
112
|
+
def fails_to_deliver(self, symbol: Optional[str] = None) -> Any:
|
|
113
|
+
"""SEC fails to deliver: history for a symbol, or the latest settlement date's largest fails."""
|
|
114
|
+
return self.get("fails-to-deliver", symbol=symbol)
|
|
115
|
+
|
|
116
|
+
def insiders(self, ticker: Optional[str] = None, type: Optional[str] = None, window: Optional[str] = None) -> Any:
|
|
117
|
+
"""SEC Form 4 trades: a ticker's history, or the latest across companies (type="buys", window="7d")."""
|
|
118
|
+
if ticker:
|
|
119
|
+
return self.get(f"insiders/{ticker.lower()}")
|
|
120
|
+
return self.get("insiders", type=type, window=window)
|
|
121
|
+
|
|
122
|
+
def planned_sales(self, symbol: Optional[str] = None, days: Optional[int] = None) -> Any:
|
|
123
|
+
"""SEC Form 144 notices of proposed sale."""
|
|
124
|
+
return self.get("planned-sales", symbol=symbol, days=days)
|
|
125
|
+
|
|
126
|
+
def large_holders(self, symbol: Optional[str] = None, form: Optional[str] = None, days: Optional[int] = None, new: Optional[bool] = None, sort: Optional[str] = None) -> Any:
|
|
127
|
+
"""Schedule 13D and 13G cover pages (form="13D" or "13G")."""
|
|
128
|
+
return self.get("large-holders", symbol=symbol, form=form, days=days, new=1 if new else None, sort=sort)
|
|
129
|
+
|
|
130
|
+
def financials(self, symbol: str) -> Any:
|
|
131
|
+
"""XBRL quarterly income, annual statements and latest balance sheet for a ticker."""
|
|
132
|
+
return self.get("financials", symbol=symbol)
|
|
133
|
+
|
|
134
|
+
def buybacks(self) -> Any:
|
|
135
|
+
"""Largest share repurchases per company in its latest fiscal year."""
|
|
136
|
+
return self.get("buybacks")
|
|
137
|
+
|
|
138
|
+
def fund_holders(self, ticker: str) -> Any:
|
|
139
|
+
"""Tracked 13F managers holding a ticker at their latest filing."""
|
|
140
|
+
return self.get("funds", ticker=ticker)
|
|
141
|
+
|
|
142
|
+
def fund(self, slug: str) -> Any:
|
|
143
|
+
"""One tracked manager's latest 13F portfolio (e.g. "berkshire-hathaway")."""
|
|
144
|
+
return self.get(f"funds/{slug}")
|
|
145
|
+
|
|
146
|
+
def events(self, ticker: Optional[str] = None, item: Optional[str] = None, days: Optional[int] = None) -> Any:
|
|
147
|
+
"""SEC 8-K material events."""
|
|
148
|
+
return self.get("events", ticker=ticker, item=item, days=days)
|
|
149
|
+
|
|
150
|
+
def structured_products(self, **params: Any) -> Any:
|
|
151
|
+
"""Bank structured notes from 424B2 and FWP filings (issuer=, underlying=, noteType=, days=, cursor=)."""
|
|
152
|
+
return self.get("structured-products", **params)
|
|
153
|
+
|
|
154
|
+
def federal_contracts(self, ticker: Optional[str] = None, days: Optional[int] = None, by: Optional[str] = None, listed: Optional[bool] = None) -> Any:
|
|
155
|
+
"""Largest US federal contract actions (by="ticker" sums by listed parent)."""
|
|
156
|
+
return self.get("federal-contracts", ticker=ticker, days=days, by=by, listed=1 if listed else None)
|
|
157
|
+
|
|
158
|
+
# ── markets ──
|
|
159
|
+
|
|
160
|
+
def funding_rates(self, slug: Optional[str] = None) -> Any:
|
|
161
|
+
"""Perpetual futures funding on Binance, Bybit and OKX; a symbol slug (e.g. "btc") gives its history."""
|
|
162
|
+
return self.get(f"funding-rates/{slug}") if slug else self.get("funding-rates")
|
|
163
|
+
|
|
164
|
+
def open_interest(self, slug: str) -> Any:
|
|
165
|
+
"""Hourly open interest history for a symbol slug."""
|
|
166
|
+
return self.get(f"open-interest/{slug}")
|
|
167
|
+
|
|
168
|
+
def liquidations(self) -> Any:
|
|
169
|
+
"""Recent crypto liquidations across exchanges."""
|
|
170
|
+
return self.get("liquidations")
|
|
171
|
+
|
|
172
|
+
def options(self, currency: str = "BTC") -> Any:
|
|
173
|
+
"""Deribit options: put/call, max pain, DVOL for BTC or ETH."""
|
|
174
|
+
return self.get(f"options/{currency}")
|
|
175
|
+
|
|
176
|
+
def whales(self, coin: Optional[str] = None) -> Any:
|
|
177
|
+
"""Hyperliquid whale positions, all or for one coin."""
|
|
178
|
+
return self.get(f"whales/{coin}") if coin else self.get("whales")
|
|
179
|
+
|
|
180
|
+
def cot(self, market: Optional[str] = None) -> Any:
|
|
181
|
+
"""CFTC Commitments of Traders: the latest report across markets, or one market's history (e.g. "gold")."""
|
|
182
|
+
return self.get(f"cot/{market}") if market else self.get("cot")
|
|
183
|
+
|
|
184
|
+
def sentiment(self, asset: Optional[str] = None, kind: Optional[str] = None, window: Optional[str] = None) -> Any:
|
|
185
|
+
"""Composite sentiment scores, all assets or one asset slug."""
|
|
186
|
+
return self.get(f"sentiment/{asset}") if asset else self.get("sentiment", kind=kind, window=window)
|
|
187
|
+
|
|
188
|
+
def signals(self, asset: Optional[str] = None) -> Any:
|
|
189
|
+
"""Rules-based composite signals."""
|
|
190
|
+
return self.get(f"signals/{asset}") if asset else self.get("signals")
|
|
191
|
+
|
|
192
|
+
def etf_flows(self, asset: Optional[str] = None, days: Optional[int] = None) -> Any:
|
|
193
|
+
"""Spot bitcoin and ether ETF flows."""
|
|
194
|
+
return self.get("etf-flows", asset=asset, days=days)
|
|
195
|
+
|
|
196
|
+
def bitcoin_treasuries(self) -> Any:
|
|
197
|
+
"""Bitcoin held by public companies from their SEC filings."""
|
|
198
|
+
return self.get("bitcoin-treasuries")
|
|
199
|
+
|
|
200
|
+
def predictions(self, category: Optional[str] = None) -> Any:
|
|
201
|
+
"""Polymarket odds."""
|
|
202
|
+
return self.get("predictions", category=category)
|
|
203
|
+
|
|
204
|
+
# ── macro ──
|
|
205
|
+
|
|
206
|
+
def macro(self, series: Optional[str] = None, from_: Optional[str] = None, to: Optional[str] = None) -> Any:
|
|
207
|
+
"""US Treasury yield curve, spreads, stablecoin supply (series=, from=, to=)."""
|
|
208
|
+
return self.get("macro", series=series, **{"from": from_, "to": to})
|
|
209
|
+
|
|
210
|
+
def fed_liquidity(self, series: Optional[str] = None, limit: Optional[int] = None) -> Any:
|
|
211
|
+
"""Weekly net liquidity with components, or one FRED series (WALCL, WRESBAL, RRPONTSYD, WTREGEN, SOFR, EFFR, IORB, WSHOSHO)."""
|
|
212
|
+
return self.get("fed-liquidity", series=series, limit=limit)
|
|
213
|
+
|
|
214
|
+
def rates(self, country: Optional[str] = None) -> Any:
|
|
215
|
+
"""Central bank policy rates: all economies, or one country code's history (e.g. "us")."""
|
|
216
|
+
return self.get(f"rates/{country}") if country else self.get("rates")
|
|
217
|
+
|
|
218
|
+
def calendar(self, from_: Optional[str] = None, to: Optional[str] = None, importance: Optional[str] = None) -> Any:
|
|
219
|
+
"""US economic calendar with consensus and actuals."""
|
|
220
|
+
return self.get("calendar", importance=importance, **{"from": from_, "to": to})
|
|
221
|
+
|
|
222
|
+
# ── filings and offerings ──
|
|
223
|
+
|
|
224
|
+
def form_d(self, days: Optional[int] = None, funds: Optional[bool] = None, cik: Optional[str] = None, sort: Optional[str] = None, amendments: Optional[bool] = None) -> Any:
|
|
225
|
+
"""SEC Form D private placements: largest raises in a window, one issuer by CIK, or sort="recent"."""
|
|
226
|
+
return self.get("startup-funding", days=days, funds=1 if funds else None, cik=cik, sort=sort, amendments=1 if amendments else None)
|
|
227
|
+
|
|
228
|
+
def ipos(self, form: Optional[str] = None, days: Optional[int] = None, new: Optional[bool] = None) -> Any:
|
|
229
|
+
"""IPO pipeline filings (form="S-1,F-1", "424B4", "RW", "EFFECT"; new=True for filers not yet listed)."""
|
|
230
|
+
return self.get("ipos", form=form, days=days, new=1 if new else None)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: xoomar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Client for the XOOMAR free market data API: short interest, insider trades, COT, funding rates, Fed liquidity, SEC filings and more.
|
|
5
|
+
Author-email: XOOMAR <info@xoomar.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://xoomar.com/markets/api
|
|
8
|
+
Project-URL: Documentation, https://xoomar.com/markets/api
|
|
9
|
+
Project-URL: Source, https://github.com/xoomar-llc/xoomar-python
|
|
10
|
+
Project-URL: Data, https://xoomar.com/markets
|
|
11
|
+
Keywords: market data,sec,finra,cot,short interest,insider trading,crypto,api
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# xoomar
|
|
25
|
+
|
|
26
|
+
Python client for the [XOOMAR](https://xoomar.com/markets) free market data API: 29 datasets from primary sources (SEC EDGAR and XBRL, FINRA, CFTC, the Federal Reserve, USAspending, exchange APIs) as clean JSON, no key needed to start.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install xoomar
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from xoomar import Xoomar
|
|
34
|
+
|
|
35
|
+
x = Xoomar() # 30 requests a minute; Xoomar(api_key="...") for 120 with a free key
|
|
36
|
+
|
|
37
|
+
x.short_interest("GME")[-1] # FINRA short interest, latest settlement
|
|
38
|
+
x.short_volume("GME", days=30) # FINRA daily short sale volume
|
|
39
|
+
x.fails_to_deliver("GME") # SEC fails to deliver
|
|
40
|
+
x.insiders("NVDA") # SEC Form 4 trades
|
|
41
|
+
x.large_holders("HIMS") # Schedule 13D and 13G holders
|
|
42
|
+
x.financials("AAPL")["quarterly"] # XBRL income statement by quarter
|
|
43
|
+
x.fund_holders("AMZN") # which tracked 13F managers hold it
|
|
44
|
+
x.cot("gold") # CFTC positioning history
|
|
45
|
+
x.fed_liquidity()[-1] # net liquidity, this week
|
|
46
|
+
x.funding_rates() # perpetual funding on three exchanges
|
|
47
|
+
x.bitcoin_treasuries() # bitcoin on public balance sheets
|
|
48
|
+
x.form_d(days=7) # private placements filed this week
|
|
49
|
+
x.federal_contracts(ticker="LMT") # federal contract actions
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Every method returns the `data` part of the response; `x.last_meta` holds `updatedAt`, `source`, `license` and `attribution` from the last call. `x.get("short-interest", symbol="TSLA")` calls any endpoint directly and `x.csv("short-interest/csv")` fetches a CSV download.
|
|
53
|
+
|
|
54
|
+
Full endpoint reference, fields and limits: https://xoomar.com/markets/api
|
|
55
|
+
|
|
56
|
+
## Datasets
|
|
57
|
+
|
|
58
|
+
Short interest, daily short volume, fails to deliver, insider trades (Form 4), planned sales (Form 144), large holders (13D/13G), 13F fund holdings, company financials and buybacks (XBRL), 8-K events, structured products, federal contracts, Form D private placements, the IPO pipeline, bitcoin treasuries, CFTC COT, funding rates, open interest, liquidations, options, whale positions, sentiment, signals, ETF flows, prediction markets, Fed liquidity, macro, policy rates, economic calendar.
|
|
59
|
+
|
|
60
|
+
## Rate limits and keys
|
|
61
|
+
|
|
62
|
+
30 requests a minute per IP without a key. A free account at https://xoomar.com/signup gives a key for 120 a minute; pass it as `Xoomar(api_key=...)`. A 429 raises `XoomarRateLimited` with `retry_after`.
|
|
63
|
+
|
|
64
|
+
## Attribution
|
|
65
|
+
|
|
66
|
+
The data is free to use, including commercially. When you republish it, on a site, in an app, in an article, in a dataset or a chart, credit XOOMAR with a visible link to the dataset page on xoomar.com. Terms: https://xoomar.com/terms
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT, XOOMAR.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
xoomar
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import json
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest import mock
|
|
5
|
+
|
|
6
|
+
from xoomar import Xoomar, XoomarRateLimited
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FakeResponse(io.BytesIO):
|
|
10
|
+
def __enter__(self):
|
|
11
|
+
return self
|
|
12
|
+
|
|
13
|
+
def __exit__(self, *a):
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ClientTests(unittest.TestCase):
|
|
18
|
+
def test_get_returns_data_and_keeps_meta(self):
|
|
19
|
+
payload = {"data": [{"symbol": "GME"}], "updatedAt": "2026-09-13T00:00:00Z", "source": "xoomar.com", "attribution": "Free with attribution"}
|
|
20
|
+
with mock.patch("urllib.request.urlopen", return_value=FakeResponse(json.dumps(payload).encode())) as u:
|
|
21
|
+
x = Xoomar(api_key="k")
|
|
22
|
+
rows = x.short_interest("GME")
|
|
23
|
+
self.assertEqual(rows, [{"symbol": "GME"}])
|
|
24
|
+
self.assertEqual(x.last_meta["source"], "xoomar.com")
|
|
25
|
+
req = u.call_args[0][0]
|
|
26
|
+
self.assertEqual(req.full_url, "https://xoomar.com/api/markets/short-interest?symbol=GME")
|
|
27
|
+
self.assertEqual(req.get_header("X-api-key"), "k")
|
|
28
|
+
|
|
29
|
+
def test_path_methods(self):
|
|
30
|
+
with mock.patch("urllib.request.urlopen", return_value=FakeResponse(b'{"data": []}')) as u:
|
|
31
|
+
Xoomar().insiders("nvda")
|
|
32
|
+
self.assertEqual(u.call_args[0][0].full_url, "https://xoomar.com/api/markets/insiders/nvda")
|
|
33
|
+
with mock.patch("urllib.request.urlopen", return_value=FakeResponse(b'{"data": []}')) as u:
|
|
34
|
+
Xoomar().large_holders("HIMS", form="13D", new=True)
|
|
35
|
+
self.assertEqual(u.call_args[0][0].full_url, "https://xoomar.com/api/markets/large-holders?symbol=HIMS&form=13D&new=1")
|
|
36
|
+
|
|
37
|
+
def test_rate_limit(self):
|
|
38
|
+
import urllib.error
|
|
39
|
+
err = urllib.error.HTTPError("https://xoomar.com/api/markets/cot", 429, "Too Many Requests", {"Retry-After": "12"}, io.BytesIO(b"slow down"))
|
|
40
|
+
with mock.patch("urllib.request.urlopen", side_effect=err):
|
|
41
|
+
with self.assertRaises(XoomarRateLimited) as ctx:
|
|
42
|
+
Xoomar().cot()
|
|
43
|
+
self.assertEqual(ctx.exception.retry_after, 12)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
unittest.main()
|