finbrain-python 0.2.5__tar.gz → 0.2.7__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.
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/.github/workflows/ci.yml +4 -1
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/.github/workflows/release.yml +22 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/CHANGELOG.md +26 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/PKG-INFO +35 -1
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/README.md +34 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/pyproject.toml +19 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/house_trades.py +13 -1
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/screener.py +14 -2
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/senate_trades.py +13 -1
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/house_trades.py +24 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/screener.py +14 -2
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/senate_trades.py +24 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain_python.egg-info/PKG-INFO +35 -1
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain_python.egg-info/SOURCES.txt +2 -0
- finbrain_python-0.2.7/src/finbrain_python.egg-info/scm_file_list.json +85 -0
- finbrain_python-0.2.7/src/finbrain_python.egg-info/scm_version.json +8 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_house_trades.py +14 -1
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_integration.py +16 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_screener.py +48 -8
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_senate_trades.py +14 -1
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/.gitattributes +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/.gitignore +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/LICENSE +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/RELEASE.md +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/examples/async_example.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/examples/transactions_plotting_example.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/setup.cfg +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/__init__.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/__init__.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/client.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/__init__.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/_utils.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/analyst_ratings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/app_ratings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/available.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/corporate_lobbying.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/government_contracts.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/insider_transactions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/linkedin_data.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/news.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/options.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/patent_filings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/predictions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/recent.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/reddit_mentions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/sentiments.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/client.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/__init__.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/_utils.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/analyst_ratings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/app_ratings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/available.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/corporate_lobbying.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/government_contracts.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/insider_transactions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/linkedin_data.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/news.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/options.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/patent_filings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/predictions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/recent.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/reddit_mentions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/sentiments.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/exceptions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/plotting.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/py.typed +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain_python.egg-info/dependency_links.txt +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain_python.egg-info/requires.txt +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain_python.egg-info/top_level.txt +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/__init__.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/conftest.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_analyst_ratings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_app_ratings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_async_client.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_available.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_client.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_corporate_lobbying.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_envelope.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_government_contracts.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_insider_transactions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_linkedin_data.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_news.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_options.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_patent_filings.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_plotting.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_predictions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_recent.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_reddit_mentions.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_sentiments.py +0 -0
- {finbrain_python-0.2.5 → finbrain_python-0.2.7}/tests/test_utils.py +0 -0
|
@@ -5,7 +5,29 @@ on:
|
|
|
5
5
|
tags: ['v[0-9]*'] # e.g. v0.1.0, v1.2.3
|
|
6
6
|
|
|
7
7
|
jobs:
|
|
8
|
+
# Gate the upload on the tagged commit actually passing. The publish job
|
|
9
|
+
# previously ran straight off the tag push with no dependency on CI, so a
|
|
10
|
+
# commit with failing checks could still reach PyPI -- and PyPI versions are
|
|
11
|
+
# immutable, so a bad upload cannot be replaced, only superseded.
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python }}
|
|
24
|
+
- run: pip install -e .[dev]
|
|
25
|
+
- run: ruff check .
|
|
26
|
+
- run: mypy
|
|
27
|
+
- run: pytest -q
|
|
28
|
+
|
|
8
29
|
build:
|
|
30
|
+
needs: test
|
|
9
31
|
runs-on: ubuntu-latest
|
|
10
32
|
|
|
11
33
|
steps:
|
|
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.7] - 2026-07-24
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Tooling**: pinned the ruff rule set to `E4`/`E7`/`E9`/`F` in `[tool.ruff.lint]`. `ruff check .` previously ran with no ruff configuration, so it followed whatever ruff's current defaults were; 0.16.0 widened that selection and turned CI red with 455 findings in unchanged code. These four groups are ruff's long-standing defaults, so enforcement is unchanged — it just no longer moves when ruff releases
|
|
13
|
+
- **Tooling**: mypy no longer follows numpy's stubs. numpy ships inline stubs using PEP 695 `type` statements, which mypy only parses when both its target `python_version` and the interpreter it runs under are 3.12+, so a numpy release aborted the entire run on the lower matrix jobs. Raising `python_version` alone does not fix this — it clears the target check but not the interpreter one. `python_version` stays at `3.10`, so the 3.9 target is still exercised, and no public API surfaces numpy types
|
|
14
|
+
|
|
15
|
+
## [0.2.6] - 2026-07-24
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Congressional disclosure date**: House and Senate trade rows now carry `disclosureDate` alongside the transaction `date` — the date the trade was publicly disclosed in the member's periodic transaction report. The gap between the two is the reporting lag. Available on `fb.house_trades.ticker()`, `fb.senate_trades.ticker()`, their async equivalents, and the `fb.screener.congress_house()` / `fb.screener.congress_senate()` rows
|
|
20
|
+
- **Congressional trade date documentation**: README section explaining the two dates, and expanded docstrings on the sync/async ticker and screener methods
|
|
21
|
+
- **Tests**: Unit tests assert `disclosureDate` passes through in both the raw-dict and DataFrame branches, including the null case for historical rows; integration tests assert the key is present on live responses
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **DataFrame shape**: `fb.house_trades.ticker(..., as_dataframe=True)`, `fb.senate_trades.ticker(..., as_dataframe=True)` and the congress screener frames gain a `disclosureDate` column. Code that asserts an exact column set (`set(df.columns) == {...}`) or compares frames for equality needs updating
|
|
26
|
+
|
|
27
|
+
### Notes
|
|
28
|
+
|
|
29
|
+
- `disclosureDate` is `null` for rows collected before the upstream pipeline captured the field. In a DataFrame the missing value reads as `None` or `NaN` depending on the pandas version, so test it with `pandas.isna()` rather than `is None`
|
|
30
|
+
- **`.dropna()` caveat**: because historical rows have no disclosure date, calling `.dropna()` on a congressional trades DataFrame now drops those rows entirely. Use `.dropna(subset=[...])` if that is not what you want
|
|
31
|
+
- `date_from` / `date_to` bound the transaction date, not the disclosure date
|
|
32
|
+
- The raw-dict branch is purely additive — no call-site changes are required there to receive the new field
|
|
33
|
+
|
|
8
34
|
## [0.2.5] - 2026-06-18
|
|
9
35
|
|
|
10
36
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finbrain-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Official Python client for the FinBrain API
|
|
5
5
|
Author-email: Ahmet Salim Bilgin <ahmet@finbrain.tech>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -84,6 +84,7 @@ fb.analyst_ratings.ticker("AMZN",
|
|
|
84
84
|
as_dataframe=True)
|
|
85
85
|
|
|
86
86
|
# ---------- house trades ----------
|
|
87
|
+
# Rows include `disclosureDate` alongside the transaction `date`.
|
|
87
88
|
fb.house_trades.ticker("AMZN",
|
|
88
89
|
date_from="2025-01-01",
|
|
89
90
|
date_to="2025-06-30",
|
|
@@ -155,12 +156,45 @@ fb.screener.insider_trading(limit=50)
|
|
|
155
156
|
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
156
157
|
fb.screener.government_contracts(limit=100, as_dataframe=True)
|
|
157
158
|
fb.screener.patent_filings(limit=100, as_dataframe=True)
|
|
159
|
+
fb.screener.congress_house(limit=50) # rows carry `disclosureDate`
|
|
160
|
+
fb.screener.congress_senate(limit=50)
|
|
158
161
|
|
|
159
162
|
# ---------- recent data ----------
|
|
160
163
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
161
164
|
fb.recent.analyst_ratings(limit=50)
|
|
162
165
|
```
|
|
163
166
|
|
|
167
|
+
### Congressional trade dates
|
|
168
|
+
|
|
169
|
+
House and Senate trade rows carry **two** dates, and the gap between them is
|
|
170
|
+
the reporting lag — often weeks:
|
|
171
|
+
|
|
172
|
+
| Field | Meaning |
|
|
173
|
+
| ---------------- | ---------------------------------------------------------------- |
|
|
174
|
+
| `date` | Transaction date — when the member actually bought or sold |
|
|
175
|
+
| `disclosureDate` | Public disclosure date — when the periodic transaction report ran |
|
|
176
|
+
|
|
177
|
+
`disclosureDate` is `null` on historical rows collected before the field was
|
|
178
|
+
captured upstream. With `as_dataframe=True`, `date` is the **index** and
|
|
179
|
+
`disclosureDate` is a column whose missing values read as `None` or `NaN`
|
|
180
|
+
depending on your pandas version — test them with `pandas.isna()` rather than
|
|
181
|
+
`is None`. Note that `.dropna()` on such a frame will now discard every
|
|
182
|
+
historical row. The same field is present on `fb.screener.congress_house()`
|
|
183
|
+
and `fb.screener.congress_senate()` rows.
|
|
184
|
+
|
|
185
|
+
`date_from` / `date_to` bound the **transaction** date, not the disclosure
|
|
186
|
+
date — a trade executed inside the window is returned even if it was disclosed
|
|
187
|
+
after `date_to`.
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
trades = fb.house_trades.ticker("AMZN")["trades"]
|
|
191
|
+
lag_days = [
|
|
192
|
+
(pd.Timestamp(t["disclosureDate"]) - pd.Timestamp(t["date"])).days
|
|
193
|
+
for t in trades
|
|
194
|
+
if t["disclosureDate"]
|
|
195
|
+
]
|
|
196
|
+
```
|
|
197
|
+
|
|
164
198
|
## ⚡ Async Usage
|
|
165
199
|
|
|
166
200
|
For async/await support, install with the `async` extra:
|
|
@@ -56,6 +56,7 @@ fb.analyst_ratings.ticker("AMZN",
|
|
|
56
56
|
as_dataframe=True)
|
|
57
57
|
|
|
58
58
|
# ---------- house trades ----------
|
|
59
|
+
# Rows include `disclosureDate` alongside the transaction `date`.
|
|
59
60
|
fb.house_trades.ticker("AMZN",
|
|
60
61
|
date_from="2025-01-01",
|
|
61
62
|
date_to="2025-06-30",
|
|
@@ -127,12 +128,45 @@ fb.screener.insider_trading(limit=50)
|
|
|
127
128
|
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
128
129
|
fb.screener.government_contracts(limit=100, as_dataframe=True)
|
|
129
130
|
fb.screener.patent_filings(limit=100, as_dataframe=True)
|
|
131
|
+
fb.screener.congress_house(limit=50) # rows carry `disclosureDate`
|
|
132
|
+
fb.screener.congress_senate(limit=50)
|
|
130
133
|
|
|
131
134
|
# ---------- recent data ----------
|
|
132
135
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
133
136
|
fb.recent.analyst_ratings(limit=50)
|
|
134
137
|
```
|
|
135
138
|
|
|
139
|
+
### Congressional trade dates
|
|
140
|
+
|
|
141
|
+
House and Senate trade rows carry **two** dates, and the gap between them is
|
|
142
|
+
the reporting lag — often weeks:
|
|
143
|
+
|
|
144
|
+
| Field | Meaning |
|
|
145
|
+
| ---------------- | ---------------------------------------------------------------- |
|
|
146
|
+
| `date` | Transaction date — when the member actually bought or sold |
|
|
147
|
+
| `disclosureDate` | Public disclosure date — when the periodic transaction report ran |
|
|
148
|
+
|
|
149
|
+
`disclosureDate` is `null` on historical rows collected before the field was
|
|
150
|
+
captured upstream. With `as_dataframe=True`, `date` is the **index** and
|
|
151
|
+
`disclosureDate` is a column whose missing values read as `None` or `NaN`
|
|
152
|
+
depending on your pandas version — test them with `pandas.isna()` rather than
|
|
153
|
+
`is None`. Note that `.dropna()` on such a frame will now discard every
|
|
154
|
+
historical row. The same field is present on `fb.screener.congress_house()`
|
|
155
|
+
and `fb.screener.congress_senate()` rows.
|
|
156
|
+
|
|
157
|
+
`date_from` / `date_to` bound the **transaction** date, not the disclosure
|
|
158
|
+
date — a trade executed inside the window is returned even if it was disclosed
|
|
159
|
+
after `date_to`.
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
trades = fb.house_trades.ticker("AMZN")["trades"]
|
|
163
|
+
lag_days = [
|
|
164
|
+
(pd.Timestamp(t["disclosureDate"]) - pd.Timestamp(t["date"])).days
|
|
165
|
+
for t in trades
|
|
166
|
+
if t["disclosureDate"]
|
|
167
|
+
]
|
|
168
|
+
```
|
|
169
|
+
|
|
136
170
|
## ⚡ Async Usage
|
|
137
171
|
|
|
138
172
|
For async/await support, install with the `async` extra:
|
|
@@ -60,3 +60,22 @@ markers = [
|
|
|
60
60
|
python_version = "3.10"
|
|
61
61
|
files = ["src/finbrain"]
|
|
62
62
|
ignore_missing_imports = true # third-party libs (pandas, plotly, …) ship no stubs here
|
|
63
|
+
|
|
64
|
+
# numpy ships inline stubs that use PEP 695 `type` statements. mypy refuses to
|
|
65
|
+
# parse those unless BOTH its target `python_version` and the interpreter it is
|
|
66
|
+
# running under are 3.12+, so a numpy release can abort the whole run ("Type
|
|
67
|
+
# statement is only supported in Python 3.12 and greater") on any matrix job
|
|
68
|
+
# below 3.12 -- with no change to this codebase. Nothing in the public API
|
|
69
|
+
# surfaces numpy types, so skip its stubs rather than chasing numpy's baseline.
|
|
70
|
+
[[tool.mypy.overrides]]
|
|
71
|
+
module = ["numpy.*"]
|
|
72
|
+
follow_imports = "skip"
|
|
73
|
+
follow_imports_for_stubs = true
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint]
|
|
76
|
+
# Pin the rule set explicitly. `ruff check .` otherwise follows whatever ruff's
|
|
77
|
+
# current defaults happen to be, so a new ruff release can turn CI red with no
|
|
78
|
+
# code change (0.16.0 widened the defaults and surfaced hundreds of findings in
|
|
79
|
+
# code that had been passing). These four are ruff's long-standing defaults, so
|
|
80
|
+
# this preserves existing behaviour rather than changing it.
|
|
81
|
+
select = ["E4", "E7", "E9", "F"]
|
|
@@ -24,7 +24,19 @@ class AsyncHouseTradesAPI:
|
|
|
24
24
|
limit: int | None = None,
|
|
25
25
|
as_dataframe: bool = False,
|
|
26
26
|
) -> Dict[str, Any] | pd.DataFrame:
|
|
27
|
-
"""
|
|
27
|
+
"""
|
|
28
|
+
Fetch House-member trades for a symbol (async).
|
|
29
|
+
|
|
30
|
+
Each row in ``trades`` carries ``date`` (the transaction date),
|
|
31
|
+
``politician``, ``transactionType``, ``amount`` and
|
|
32
|
+
``disclosureDate`` — the date the trade was publicly disclosed in the
|
|
33
|
+
member's periodic transaction report. ``disclosureDate`` is ``None``
|
|
34
|
+
for historical rows collected before the field was captured (``NaN``
|
|
35
|
+
in the DataFrame branch on newer pandas — use ``pandas.isna``).
|
|
36
|
+
|
|
37
|
+
``date_from``/``date_to`` bound the transaction date, not the
|
|
38
|
+
disclosure date.
|
|
39
|
+
"""
|
|
28
40
|
params: Dict[str, str] = {}
|
|
29
41
|
if date_from:
|
|
30
42
|
params["startDate"] = to_datestr(date_from)
|
|
@@ -107,7 +107,13 @@ class AsyncScreenerAPI:
|
|
|
107
107
|
limit: int | None = None,
|
|
108
108
|
as_dataframe: bool = False,
|
|
109
109
|
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
110
|
-
"""
|
|
110
|
+
"""
|
|
111
|
+
Screen House trades across all tickers.
|
|
112
|
+
|
|
113
|
+
Rows carry ``symbol``, ``name``, ``date``, ``politician``,
|
|
114
|
+
``transactionType``, ``amount`` and ``disclosureDate`` (the public
|
|
115
|
+
disclosure date, ``None`` on historical rows).
|
|
116
|
+
"""
|
|
111
117
|
params = self._build_params(limit=limit)
|
|
112
118
|
return await self._get("screener/congress/house", params, as_dataframe)
|
|
113
119
|
|
|
@@ -119,7 +125,13 @@ class AsyncScreenerAPI:
|
|
|
119
125
|
limit: int | None = None,
|
|
120
126
|
as_dataframe: bool = False,
|
|
121
127
|
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
122
|
-
"""
|
|
128
|
+
"""
|
|
129
|
+
Screen Senate trades across all tickers.
|
|
130
|
+
|
|
131
|
+
Rows carry ``symbol``, ``name``, ``date``, ``politician``,
|
|
132
|
+
``transactionType``, ``amount`` and ``disclosureDate`` (the public
|
|
133
|
+
disclosure date, ``None`` on historical rows).
|
|
134
|
+
"""
|
|
123
135
|
params = self._build_params(limit=limit)
|
|
124
136
|
return await self._get("screener/congress/senate", params, as_dataframe)
|
|
125
137
|
|
|
@@ -24,7 +24,19 @@ class AsyncSenateTradesAPI:
|
|
|
24
24
|
limit: int | None = None,
|
|
25
25
|
as_dataframe: bool = False,
|
|
26
26
|
) -> Dict[str, Any] | pd.DataFrame:
|
|
27
|
-
"""
|
|
27
|
+
"""
|
|
28
|
+
Fetch Senate-member trades for a symbol (async).
|
|
29
|
+
|
|
30
|
+
Each row in ``trades`` carries ``date`` (the transaction date),
|
|
31
|
+
``politician``, ``transactionType``, ``amount`` and
|
|
32
|
+
``disclosureDate`` — the date the trade was publicly disclosed in the
|
|
33
|
+
member's periodic transaction report. ``disclosureDate`` is ``None``
|
|
34
|
+
for historical rows collected before the field was captured (``NaN``
|
|
35
|
+
in the DataFrame branch on newer pandas — use ``pandas.isna``).
|
|
36
|
+
|
|
37
|
+
``date_from``/``date_to`` bound the transaction date, not the
|
|
38
|
+
disclosure date.
|
|
39
|
+
"""
|
|
28
40
|
params: Dict[str, str] = {}
|
|
29
41
|
if date_from:
|
|
30
42
|
params["startDate"] = to_datestr(date_from)
|
|
@@ -49,6 +49,30 @@ class HouseTradesAPI:
|
|
|
49
49
|
Returns
|
|
50
50
|
-------
|
|
51
51
|
dict | pandas.DataFrame
|
|
52
|
+
The raw dict has a ``trades`` list whose rows carry ``date``
|
|
53
|
+
(the transaction date), ``politician``, ``transactionType``,
|
|
54
|
+
``amount`` and ``disclosureDate`` — the date the trade was
|
|
55
|
+
publicly disclosed in the member's periodic transaction report.
|
|
56
|
+
The gap between the two dates is the reporting lag.
|
|
57
|
+
|
|
58
|
+
``disclosureDate`` is ``None`` for historical rows collected
|
|
59
|
+
before the field was captured upstream. In the DataFrame branch
|
|
60
|
+
``date`` becomes the index and ``disclosureDate`` is a column
|
|
61
|
+
whose missing values read as ``None`` or ``NaN`` depending on the
|
|
62
|
+
pandas version — test them with :func:`pandas.isna`.
|
|
63
|
+
|
|
64
|
+
Notes
|
|
65
|
+
-----
|
|
66
|
+
``date_from`` and ``date_to`` bound the **transaction** date, not the
|
|
67
|
+
disclosure date. A trade executed inside the window is returned even
|
|
68
|
+
if it was disclosed after ``date_to``.
|
|
69
|
+
|
|
70
|
+
Example row::
|
|
71
|
+
|
|
72
|
+
{"date": "2026-06-15", "politician": "Jane Doe",
|
|
73
|
+
"transactionType": "Purchase", "amount": "$1,001 - $15,000",
|
|
74
|
+
"disclosureDate": "2026-07-01"}
|
|
75
|
+
|
|
52
76
|
"""
|
|
53
77
|
params: Dict[str, str] = {}
|
|
54
78
|
if date_from:
|
|
@@ -112,7 +112,13 @@ class ScreenerAPI:
|
|
|
112
112
|
limit: int | None = None,
|
|
113
113
|
as_dataframe: bool = False,
|
|
114
114
|
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
115
|
-
"""
|
|
115
|
+
"""
|
|
116
|
+
Screen House trades across all tickers.
|
|
117
|
+
|
|
118
|
+
Rows carry ``symbol``, ``name``, ``date``, ``politician``,
|
|
119
|
+
``transactionType``, ``amount`` and ``disclosureDate`` (the public
|
|
120
|
+
disclosure date, ``None`` on historical rows).
|
|
121
|
+
"""
|
|
116
122
|
params = self._build_params(limit=limit)
|
|
117
123
|
return self._get("screener/congress/house", params, as_dataframe)
|
|
118
124
|
|
|
@@ -124,7 +130,13 @@ class ScreenerAPI:
|
|
|
124
130
|
limit: int | None = None,
|
|
125
131
|
as_dataframe: bool = False,
|
|
126
132
|
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
127
|
-
"""
|
|
133
|
+
"""
|
|
134
|
+
Screen Senate trades across all tickers.
|
|
135
|
+
|
|
136
|
+
Rows carry ``symbol``, ``name``, ``date``, ``politician``,
|
|
137
|
+
``transactionType``, ``amount`` and ``disclosureDate`` (the public
|
|
138
|
+
disclosure date, ``None`` on historical rows).
|
|
139
|
+
"""
|
|
128
140
|
params = self._build_params(limit=limit)
|
|
129
141
|
return self._get("screener/congress/senate", params, as_dataframe)
|
|
130
142
|
|
|
@@ -49,6 +49,30 @@ class SenateTradesAPI:
|
|
|
49
49
|
Returns
|
|
50
50
|
-------
|
|
51
51
|
dict | pandas.DataFrame
|
|
52
|
+
The raw dict has a ``trades`` list whose rows carry ``date``
|
|
53
|
+
(the transaction date), ``politician``, ``transactionType``,
|
|
54
|
+
``amount`` and ``disclosureDate`` — the date the trade was
|
|
55
|
+
publicly disclosed in the member's periodic transaction report.
|
|
56
|
+
The gap between the two dates is the reporting lag.
|
|
57
|
+
|
|
58
|
+
``disclosureDate`` is ``None`` for historical rows collected
|
|
59
|
+
before the field was captured upstream. In the DataFrame branch
|
|
60
|
+
``date`` becomes the index and ``disclosureDate`` is a column
|
|
61
|
+
whose missing values read as ``None`` or ``NaN`` depending on the
|
|
62
|
+
pandas version — test them with :func:`pandas.isna`.
|
|
63
|
+
|
|
64
|
+
Notes
|
|
65
|
+
-----
|
|
66
|
+
``date_from`` and ``date_to`` bound the **transaction** date, not the
|
|
67
|
+
disclosure date. A trade executed inside the window is returned even
|
|
68
|
+
if it was disclosed after ``date_to``.
|
|
69
|
+
|
|
70
|
+
Example row::
|
|
71
|
+
|
|
72
|
+
{"date": "2026-06-10", "politician": "Jane Doe",
|
|
73
|
+
"transactionType": "Purchase", "amount": "$1,001 - $15,000",
|
|
74
|
+
"disclosureDate": "2026-06-25"}
|
|
75
|
+
|
|
52
76
|
"""
|
|
53
77
|
params: Dict[str, str] = {}
|
|
54
78
|
if date_from:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finbrain-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Official Python client for the FinBrain API
|
|
5
5
|
Author-email: Ahmet Salim Bilgin <ahmet@finbrain.tech>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -84,6 +84,7 @@ fb.analyst_ratings.ticker("AMZN",
|
|
|
84
84
|
as_dataframe=True)
|
|
85
85
|
|
|
86
86
|
# ---------- house trades ----------
|
|
87
|
+
# Rows include `disclosureDate` alongside the transaction `date`.
|
|
87
88
|
fb.house_trades.ticker("AMZN",
|
|
88
89
|
date_from="2025-01-01",
|
|
89
90
|
date_to="2025-06-30",
|
|
@@ -155,12 +156,45 @@ fb.screener.insider_trading(limit=50)
|
|
|
155
156
|
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
156
157
|
fb.screener.government_contracts(limit=100, as_dataframe=True)
|
|
157
158
|
fb.screener.patent_filings(limit=100, as_dataframe=True)
|
|
159
|
+
fb.screener.congress_house(limit=50) # rows carry `disclosureDate`
|
|
160
|
+
fb.screener.congress_senate(limit=50)
|
|
158
161
|
|
|
159
162
|
# ---------- recent data ----------
|
|
160
163
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
161
164
|
fb.recent.analyst_ratings(limit=50)
|
|
162
165
|
```
|
|
163
166
|
|
|
167
|
+
### Congressional trade dates
|
|
168
|
+
|
|
169
|
+
House and Senate trade rows carry **two** dates, and the gap between them is
|
|
170
|
+
the reporting lag — often weeks:
|
|
171
|
+
|
|
172
|
+
| Field | Meaning |
|
|
173
|
+
| ---------------- | ---------------------------------------------------------------- |
|
|
174
|
+
| `date` | Transaction date — when the member actually bought or sold |
|
|
175
|
+
| `disclosureDate` | Public disclosure date — when the periodic transaction report ran |
|
|
176
|
+
|
|
177
|
+
`disclosureDate` is `null` on historical rows collected before the field was
|
|
178
|
+
captured upstream. With `as_dataframe=True`, `date` is the **index** and
|
|
179
|
+
`disclosureDate` is a column whose missing values read as `None` or `NaN`
|
|
180
|
+
depending on your pandas version — test them with `pandas.isna()` rather than
|
|
181
|
+
`is None`. Note that `.dropna()` on such a frame will now discard every
|
|
182
|
+
historical row. The same field is present on `fb.screener.congress_house()`
|
|
183
|
+
and `fb.screener.congress_senate()` rows.
|
|
184
|
+
|
|
185
|
+
`date_from` / `date_to` bound the **transaction** date, not the disclosure
|
|
186
|
+
date — a trade executed inside the window is returned even if it was disclosed
|
|
187
|
+
after `date_to`.
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
trades = fb.house_trades.ticker("AMZN")["trades"]
|
|
191
|
+
lag_days = [
|
|
192
|
+
(pd.Timestamp(t["disclosureDate"]) - pd.Timestamp(t["date"])).days
|
|
193
|
+
for t in trades
|
|
194
|
+
if t["disclosureDate"]
|
|
195
|
+
]
|
|
196
|
+
```
|
|
197
|
+
|
|
164
198
|
## ⚡ Async Usage
|
|
165
199
|
|
|
166
200
|
For async/await support, install with the `async` extra:
|
|
@@ -58,6 +58,8 @@ src/finbrain_python.egg-info/PKG-INFO
|
|
|
58
58
|
src/finbrain_python.egg-info/SOURCES.txt
|
|
59
59
|
src/finbrain_python.egg-info/dependency_links.txt
|
|
60
60
|
src/finbrain_python.egg-info/requires.txt
|
|
61
|
+
src/finbrain_python.egg-info/scm_file_list.json
|
|
62
|
+
src/finbrain_python.egg-info/scm_version.json
|
|
61
63
|
src/finbrain_python.egg-info/top_level.txt
|
|
62
64
|
tests/__init__.py
|
|
63
65
|
tests/conftest.py
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
"CHANGELOG.md",
|
|
4
|
+
".gitattributes",
|
|
5
|
+
"pyproject.toml",
|
|
6
|
+
"LICENSE",
|
|
7
|
+
"README.md",
|
|
8
|
+
"RELEASE.md",
|
|
9
|
+
".gitignore",
|
|
10
|
+
".github/workflows/ci.yml",
|
|
11
|
+
".github/workflows/release.yml",
|
|
12
|
+
"examples/transactions_plotting_example.py",
|
|
13
|
+
"examples/async_example.py",
|
|
14
|
+
"tests/test_available.py",
|
|
15
|
+
"tests/test_async_client.py",
|
|
16
|
+
"tests/test_insider_transactions.py",
|
|
17
|
+
"tests/test_reddit_mentions.py",
|
|
18
|
+
"tests/test_plotting.py",
|
|
19
|
+
"tests/test_house_trades.py",
|
|
20
|
+
"tests/test_sentiments.py",
|
|
21
|
+
"tests/test_corporate_lobbying.py",
|
|
22
|
+
"tests/test_options.py",
|
|
23
|
+
"tests/test_envelope.py",
|
|
24
|
+
"tests/__init__.py",
|
|
25
|
+
"tests/test_patent_filings.py",
|
|
26
|
+
"tests/test_integration.py",
|
|
27
|
+
"tests/test_linkedin_data.py",
|
|
28
|
+
"tests/test_client.py",
|
|
29
|
+
"tests/test_app_ratings.py",
|
|
30
|
+
"tests/test_recent.py",
|
|
31
|
+
"tests/test_screener.py",
|
|
32
|
+
"tests/test_news.py",
|
|
33
|
+
"tests/conftest.py",
|
|
34
|
+
"tests/test_utils.py",
|
|
35
|
+
"tests/test_analyst_ratings.py",
|
|
36
|
+
"tests/test_predictions.py",
|
|
37
|
+
"tests/test_senate_trades.py",
|
|
38
|
+
"tests/test_government_contracts.py",
|
|
39
|
+
"src/finbrain/py.typed",
|
|
40
|
+
"src/finbrain/__init__.py",
|
|
41
|
+
"src/finbrain/client.py",
|
|
42
|
+
"src/finbrain/exceptions.py",
|
|
43
|
+
"src/finbrain/plotting.py",
|
|
44
|
+
"src/finbrain/aio/__init__.py",
|
|
45
|
+
"src/finbrain/aio/client.py",
|
|
46
|
+
"src/finbrain/aio/endpoints/app_ratings.py",
|
|
47
|
+
"src/finbrain/aio/endpoints/available.py",
|
|
48
|
+
"src/finbrain/aio/endpoints/predictions.py",
|
|
49
|
+
"src/finbrain/aio/endpoints/corporate_lobbying.py",
|
|
50
|
+
"src/finbrain/aio/endpoints/reddit_mentions.py",
|
|
51
|
+
"src/finbrain/aio/endpoints/__init__.py",
|
|
52
|
+
"src/finbrain/aio/endpoints/patent_filings.py",
|
|
53
|
+
"src/finbrain/aio/endpoints/options.py",
|
|
54
|
+
"src/finbrain/aio/endpoints/linkedin_data.py",
|
|
55
|
+
"src/finbrain/aio/endpoints/_utils.py",
|
|
56
|
+
"src/finbrain/aio/endpoints/senate_trades.py",
|
|
57
|
+
"src/finbrain/aio/endpoints/insider_transactions.py",
|
|
58
|
+
"src/finbrain/aio/endpoints/analyst_ratings.py",
|
|
59
|
+
"src/finbrain/aio/endpoints/house_trades.py",
|
|
60
|
+
"src/finbrain/aio/endpoints/news.py",
|
|
61
|
+
"src/finbrain/aio/endpoints/sentiments.py",
|
|
62
|
+
"src/finbrain/aio/endpoints/recent.py",
|
|
63
|
+
"src/finbrain/aio/endpoints/screener.py",
|
|
64
|
+
"src/finbrain/aio/endpoints/government_contracts.py",
|
|
65
|
+
"src/finbrain/endpoints/app_ratings.py",
|
|
66
|
+
"src/finbrain/endpoints/available.py",
|
|
67
|
+
"src/finbrain/endpoints/predictions.py",
|
|
68
|
+
"src/finbrain/endpoints/corporate_lobbying.py",
|
|
69
|
+
"src/finbrain/endpoints/reddit_mentions.py",
|
|
70
|
+
"src/finbrain/endpoints/__init__.py",
|
|
71
|
+
"src/finbrain/endpoints/patent_filings.py",
|
|
72
|
+
"src/finbrain/endpoints/options.py",
|
|
73
|
+
"src/finbrain/endpoints/linkedin_data.py",
|
|
74
|
+
"src/finbrain/endpoints/_utils.py",
|
|
75
|
+
"src/finbrain/endpoints/senate_trades.py",
|
|
76
|
+
"src/finbrain/endpoints/insider_transactions.py",
|
|
77
|
+
"src/finbrain/endpoints/analyst_ratings.py",
|
|
78
|
+
"src/finbrain/endpoints/house_trades.py",
|
|
79
|
+
"src/finbrain/endpoints/news.py",
|
|
80
|
+
"src/finbrain/endpoints/sentiments.py",
|
|
81
|
+
"src/finbrain/endpoints/recent.py",
|
|
82
|
+
"src/finbrain/endpoints/screener.py",
|
|
83
|
+
"src/finbrain/endpoints/government_contracts.py"
|
|
84
|
+
]
|
|
85
|
+
}
|
|
@@ -21,6 +21,7 @@ def test_house_trades_raw_ok(client, _activate_responses):
|
|
|
21
21
|
"politician": "Nancy Pelosi",
|
|
22
22
|
"transactionType": "Purchase",
|
|
23
23
|
"amount": "$15,001 - $50,000",
|
|
24
|
+
"disclosureDate": "2024-02-05",
|
|
24
25
|
}
|
|
25
26
|
],
|
|
26
27
|
})
|
|
@@ -31,6 +32,7 @@ def test_house_trades_raw_ok(client, _activate_responses):
|
|
|
31
32
|
assert data["symbol"] == TICKER
|
|
32
33
|
assert isinstance(data["trades"], list)
|
|
33
34
|
assert data["trades"][0]["politician"] == "Nancy Pelosi"
|
|
35
|
+
assert data["trades"][0]["disclosureDate"] == "2024-02-05"
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
# ─────────── DataFrame branch ───────────────────────────────────────────
|
|
@@ -46,12 +48,14 @@ def test_house_trades_dataframe_ok(client, _activate_responses):
|
|
|
46
48
|
"politician": "Nancy Pelosi",
|
|
47
49
|
"transactionType": "Purchase",
|
|
48
50
|
"amount": "$15,001 - $50,000",
|
|
51
|
+
"disclosureDate": "2024-02-05",
|
|
49
52
|
},
|
|
50
53
|
{
|
|
51
54
|
"date": "2024-01-10",
|
|
52
55
|
"politician": "Pete Sessions",
|
|
53
56
|
"transactionType": "Sale",
|
|
54
57
|
"amount": "$1,001 - $15,000",
|
|
58
|
+
"disclosureDate": None,
|
|
55
59
|
},
|
|
56
60
|
],
|
|
57
61
|
})
|
|
@@ -62,7 +66,12 @@ def test_house_trades_dataframe_ok(client, _activate_responses):
|
|
|
62
66
|
|
|
63
67
|
assert isinstance(df, pd.DataFrame)
|
|
64
68
|
assert len(df) == 2
|
|
65
|
-
assert set(df.columns) == {
|
|
69
|
+
assert set(df.columns) == {
|
|
70
|
+
"politician",
|
|
71
|
+
"transactionType",
|
|
72
|
+
"amount",
|
|
73
|
+
"disclosureDate",
|
|
74
|
+
}
|
|
66
75
|
assert df.index.name == "date"
|
|
67
76
|
assert pd.api.types.is_datetime64_any_dtype(df.index)
|
|
68
77
|
assert pd.Timestamp("2024-01-15") in df.index
|
|
@@ -70,6 +79,10 @@ def test_house_trades_dataframe_ok(client, _activate_responses):
|
|
|
70
79
|
assert df.loc["2024-01-10", "politician"] == "Pete Sessions"
|
|
71
80
|
assert df.loc["2024-01-15", "transactionType"] == "Purchase"
|
|
72
81
|
assert df.loc["2024-01-15", "amount"] == "$15,001 - $50,000"
|
|
82
|
+
assert df.loc["2024-01-15", "disclosureDate"] == "2024-02-05"
|
|
83
|
+
# Historical rows predate the disclosure-date field; JSON null becomes
|
|
84
|
+
# NaN once pandas builds the column.
|
|
85
|
+
assert pd.isna(df.loc["2024-01-10", "disclosureDate"])
|
|
73
86
|
|
|
74
87
|
|
|
75
88
|
# ─────────── error mapping ──────────────────────────────────────────────
|
|
@@ -230,6 +230,9 @@ class TestHouseTrades:
|
|
|
230
230
|
assert "date" in trades[0]
|
|
231
231
|
assert "politician" in trades[0]
|
|
232
232
|
assert "transactionType" in trades[0]
|
|
233
|
+
# Present on every row; null on rows collected before the
|
|
234
|
+
# disclosure-date field was added to the pipeline.
|
|
235
|
+
assert "disclosureDate" in trades[0]
|
|
233
236
|
|
|
234
237
|
def test_dataframe(self, fb):
|
|
235
238
|
df = fb.house_trades.ticker(TICKER, as_dataframe=True)
|
|
@@ -237,6 +240,9 @@ class TestHouseTrades:
|
|
|
237
240
|
if len(df) > 0:
|
|
238
241
|
assert df.index.name == "date"
|
|
239
242
|
assert "politician" in df.columns
|
|
243
|
+
# Survives the DataFrame conversion as a column (values may be
|
|
244
|
+
# null on rows predating the upstream field).
|
|
245
|
+
assert "disclosureDate" in df.columns
|
|
240
246
|
|
|
241
247
|
|
|
242
248
|
# =====================================================================
|
|
@@ -252,6 +258,9 @@ class TestSenateTrades:
|
|
|
252
258
|
if len(trades) > 0:
|
|
253
259
|
assert "date" in trades[0]
|
|
254
260
|
assert "politician" in trades[0]
|
|
261
|
+
# Present on every row; null on rows collected before the
|
|
262
|
+
# disclosure-date field was added to the pipeline.
|
|
263
|
+
assert "disclosureDate" in trades[0]
|
|
255
264
|
|
|
256
265
|
def test_dataframe(self, fb):
|
|
257
266
|
df = fb.senate_trades.ticker(TICKER, as_dataframe=True)
|
|
@@ -259,6 +268,9 @@ class TestSenateTrades:
|
|
|
259
268
|
if len(df) > 0:
|
|
260
269
|
assert df.index.name == "date"
|
|
261
270
|
assert "politician" in df.columns
|
|
271
|
+
# Survives the DataFrame conversion as a column (values may be
|
|
272
|
+
# null on rows predating the upstream field).
|
|
273
|
+
assert "disclosureDate" in df.columns
|
|
262
274
|
|
|
263
275
|
|
|
264
276
|
# =====================================================================
|
|
@@ -503,10 +515,14 @@ class TestScreener:
|
|
|
503
515
|
def test_congress_house(self, fb):
|
|
504
516
|
data = fb.screener.congress_house(limit=5)
|
|
505
517
|
assert isinstance(data, list)
|
|
518
|
+
if data:
|
|
519
|
+
assert "disclosureDate" in data[0]
|
|
506
520
|
|
|
507
521
|
def test_congress_senate(self, fb):
|
|
508
522
|
data = fb.screener.congress_senate(limit=5)
|
|
509
523
|
assert isinstance(data, list)
|
|
524
|
+
if data:
|
|
525
|
+
assert "disclosureDate" in data[0]
|
|
510
526
|
|
|
511
527
|
def test_news(self, fb):
|
|
512
528
|
data = fb.screener.news(limit=5)
|
|
@@ -95,53 +95,93 @@ def test_screener_insider_trading_dataframe(client, _activate_responses):
|
|
|
95
95
|
# ── congress house ─────────────────────────────────────────────────────
|
|
96
96
|
def test_screener_congress_house(client, _activate_responses):
|
|
97
97
|
payload = wrap_v2([
|
|
98
|
-
{
|
|
98
|
+
{
|
|
99
|
+
"symbol": "AAPL",
|
|
100
|
+
"name": "Apple",
|
|
101
|
+
"politician": "Nancy Pelosi",
|
|
102
|
+
"disclosureDate": "2026-02-05",
|
|
103
|
+
},
|
|
99
104
|
])
|
|
100
105
|
stub_json(_activate_responses, "GET", "screener/congress/house", payload, params={"limit": "5"})
|
|
101
106
|
data = client.screener.congress_house(limit=5)
|
|
102
107
|
assert isinstance(data, list)
|
|
103
108
|
assert len(data) == 1
|
|
104
109
|
assert data[0]["politician"] == "Nancy Pelosi"
|
|
110
|
+
assert data[0]["disclosureDate"] == "2026-02-05"
|
|
105
111
|
|
|
106
112
|
|
|
107
113
|
def test_screener_congress_house_dataframe(client, _activate_responses):
|
|
108
114
|
payload = wrap_v2([
|
|
109
|
-
{
|
|
110
|
-
|
|
115
|
+
{
|
|
116
|
+
"symbol": "AAPL",
|
|
117
|
+
"name": "Apple",
|
|
118
|
+
"politician": "Nancy Pelosi",
|
|
119
|
+
"disclosureDate": "2026-02-05",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"symbol": "MSFT",
|
|
123
|
+
"name": "Microsoft",
|
|
124
|
+
"politician": "Dan Crenshaw",
|
|
125
|
+
"disclosureDate": None,
|
|
126
|
+
},
|
|
111
127
|
])
|
|
112
128
|
stub_json(_activate_responses, "GET", "screener/congress/house", payload, params={"limit": "5"})
|
|
113
129
|
df = client.screener.congress_house(limit=5, as_dataframe=True)
|
|
114
130
|
assert isinstance(df, pd.DataFrame)
|
|
115
131
|
assert len(df) == 2
|
|
116
132
|
assert df.index.name == "symbol"
|
|
117
|
-
assert set(df.columns) == {"name", "politician"}
|
|
133
|
+
assert set(df.columns) == {"name", "politician", "disclosureDate"}
|
|
118
134
|
assert df.loc["AAPL", "politician"] == "Nancy Pelosi"
|
|
135
|
+
assert df.loc["AAPL", "disclosureDate"] == "2026-02-05"
|
|
136
|
+
# Historical rows predate the disclosure-date field; JSON null becomes
|
|
137
|
+
# NaN once pandas builds the column.
|
|
138
|
+
assert pd.isna(df.loc["MSFT", "disclosureDate"])
|
|
119
139
|
|
|
120
140
|
|
|
121
141
|
# ── congress senate ────────────────────────────────────────────────────
|
|
122
142
|
def test_screener_congress_senate(client, _activate_responses):
|
|
123
143
|
payload = wrap_v2([
|
|
124
|
-
{
|
|
144
|
+
{
|
|
145
|
+
"symbol": "AAPL",
|
|
146
|
+
"name": "Apple",
|
|
147
|
+
"politician": "John Boozman",
|
|
148
|
+
"disclosureDate": "2026-01-22",
|
|
149
|
+
},
|
|
125
150
|
])
|
|
126
151
|
stub_json(_activate_responses, "GET", "screener/congress/senate", payload, params={"limit": "5"})
|
|
127
152
|
data = client.screener.congress_senate(limit=5)
|
|
128
153
|
assert isinstance(data, list)
|
|
129
154
|
assert len(data) == 1
|
|
130
155
|
assert data[0]["politician"] == "John Boozman"
|
|
156
|
+
assert data[0]["disclosureDate"] == "2026-01-22"
|
|
131
157
|
|
|
132
158
|
|
|
133
159
|
def test_screener_congress_senate_dataframe(client, _activate_responses):
|
|
134
160
|
payload = wrap_v2([
|
|
135
|
-
{
|
|
136
|
-
|
|
161
|
+
{
|
|
162
|
+
"symbol": "AAPL",
|
|
163
|
+
"name": "Apple",
|
|
164
|
+
"politician": "John Boozman",
|
|
165
|
+
"disclosureDate": "2026-01-22",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"symbol": "NVDA",
|
|
169
|
+
"name": "NVIDIA",
|
|
170
|
+
"politician": "Tommy Tuberville",
|
|
171
|
+
"disclosureDate": None,
|
|
172
|
+
},
|
|
137
173
|
])
|
|
138
174
|
stub_json(_activate_responses, "GET", "screener/congress/senate", payload, params={"limit": "5"})
|
|
139
175
|
df = client.screener.congress_senate(limit=5, as_dataframe=True)
|
|
140
176
|
assert isinstance(df, pd.DataFrame)
|
|
141
177
|
assert len(df) == 2
|
|
142
178
|
assert df.index.name == "symbol"
|
|
143
|
-
assert set(df.columns) == {"name", "politician"}
|
|
179
|
+
assert set(df.columns) == {"name", "politician", "disclosureDate"}
|
|
144
180
|
assert df.loc["NVDA", "politician"] == "Tommy Tuberville"
|
|
181
|
+
assert df.loc["AAPL", "disclosureDate"] == "2026-01-22"
|
|
182
|
+
# Historical rows predate the disclosure-date field; JSON null becomes
|
|
183
|
+
# NaN once pandas builds the column.
|
|
184
|
+
assert pd.isna(df.loc["NVDA", "disclosureDate"])
|
|
145
185
|
|
|
146
186
|
|
|
147
187
|
# ── news ───────────────────────────────────────────────────────────────
|
|
@@ -21,6 +21,7 @@ def test_senate_trades_raw_ok(client, _activate_responses):
|
|
|
21
21
|
"politician": "Shelley Moore Capito",
|
|
22
22
|
"transactionType": "Purchase",
|
|
23
23
|
"amount": "$1,001 - $15,000",
|
|
24
|
+
"disclosureDate": "2024-01-30",
|
|
24
25
|
}
|
|
25
26
|
],
|
|
26
27
|
})
|
|
@@ -31,6 +32,7 @@ def test_senate_trades_raw_ok(client, _activate_responses):
|
|
|
31
32
|
assert data["symbol"] == TICKER
|
|
32
33
|
assert isinstance(data["trades"], list)
|
|
33
34
|
assert data["trades"][0]["politician"] == "Shelley Moore Capito"
|
|
35
|
+
assert data["trades"][0]["disclosureDate"] == "2024-01-30"
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
# ─────────── DataFrame branch ───────────────────────────────────────────
|
|
@@ -46,12 +48,14 @@ def test_senate_trades_dataframe_ok(client, _activate_responses):
|
|
|
46
48
|
"politician": "Shelley Moore Capito",
|
|
47
49
|
"transactionType": "Purchase",
|
|
48
50
|
"amount": "$1,001 - $15,000",
|
|
51
|
+
"disclosureDate": "2024-01-30",
|
|
49
52
|
},
|
|
50
53
|
{
|
|
51
54
|
"date": "2024-01-08",
|
|
52
55
|
"politician": "John Boozman",
|
|
53
56
|
"transactionType": "Purchase",
|
|
54
57
|
"amount": "$1,001 - $15,000",
|
|
58
|
+
"disclosureDate": None,
|
|
55
59
|
},
|
|
56
60
|
],
|
|
57
61
|
})
|
|
@@ -62,7 +66,12 @@ def test_senate_trades_dataframe_ok(client, _activate_responses):
|
|
|
62
66
|
|
|
63
67
|
assert isinstance(df, pd.DataFrame)
|
|
64
68
|
assert len(df) == 2
|
|
65
|
-
assert set(df.columns) == {
|
|
69
|
+
assert set(df.columns) == {
|
|
70
|
+
"politician",
|
|
71
|
+
"transactionType",
|
|
72
|
+
"amount",
|
|
73
|
+
"disclosureDate",
|
|
74
|
+
}
|
|
66
75
|
assert df.index.name == "date"
|
|
67
76
|
assert pd.api.types.is_datetime64_any_dtype(df.index)
|
|
68
77
|
assert pd.Timestamp("2024-01-15") in df.index
|
|
@@ -70,6 +79,10 @@ def test_senate_trades_dataframe_ok(client, _activate_responses):
|
|
|
70
79
|
assert df.loc["2024-01-08", "politician"] == "John Boozman"
|
|
71
80
|
assert df.loc["2024-01-15", "transactionType"] == "Purchase"
|
|
72
81
|
assert df.loc["2024-01-08", "amount"] == "$1,001 - $15,000"
|
|
82
|
+
assert df.loc["2024-01-15", "disclosureDate"] == "2024-01-30"
|
|
83
|
+
# Historical rows predate the disclosure-date field; JSON null becomes
|
|
84
|
+
# NaN once pandas builds the column.
|
|
85
|
+
assert pd.isna(df.loc["2024-01-08", "disclosureDate"])
|
|
73
86
|
|
|
74
87
|
|
|
75
88
|
# ─────────── error mapping ──────────────────────────────────────────────
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/analyst_ratings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/corporate_lobbying.py
RENAMED
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/government_contracts.py
RENAMED
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/insider_transactions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/patent_filings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/aio/endpoints/reddit_mentions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/corporate_lobbying.py
RENAMED
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/government_contracts.py
RENAMED
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain/endpoints/insider_transactions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{finbrain_python-0.2.5 → finbrain_python-0.2.7}/src/finbrain_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|