finbrain-python 0.2.2__tar.gz → 0.2.4__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.2 → finbrain_python-0.2.4}/CHANGELOG.md +21 -3
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/PKG-INFO +14 -6
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/README.md +13 -5
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/client.py +2 -0
- finbrain_python-0.2.4/src/finbrain/aio/endpoints/government_contracts.py +48 -0
- finbrain_python-0.2.4/src/finbrain/aio/endpoints/reddit_mentions.py +48 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/screener.py +26 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/client.py +2 -0
- finbrain_python-0.2.4/src/finbrain/endpoints/government_contracts.py +73 -0
- finbrain_python-0.2.4/src/finbrain/endpoints/reddit_mentions.py +73 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/screener.py +26 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/plotting.py +158 -4
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain_python.egg-info/PKG-INFO +14 -6
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain_python.egg-info/SOURCES.txt +3 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_async_client.py +2 -0
- finbrain_python-0.2.4/tests/test_government_contracts.py +214 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_integration.py +46 -8
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_plotting.py +77 -37
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_reddit_mentions.py +6 -12
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_screener.py +36 -0
- finbrain_python-0.2.2/src/finbrain/aio/endpoints/reddit_mentions.py +0 -79
- finbrain_python-0.2.2/src/finbrain/endpoints/reddit_mentions.py +0 -123
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/.gitattributes +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/.github/workflows/ci.yml +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/.github/workflows/release.yml +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/.gitignore +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/LICENSE +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/RELEASE.md +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/examples/async_example.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/examples/transactions_plotting_example.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/pyproject.toml +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/setup.cfg +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/__init__.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/__init__.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/__init__.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/_utils.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/analyst_ratings.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/app_ratings.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/available.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/corporate_lobbying.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/house_trades.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/insider_transactions.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/linkedin_data.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/news.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/options.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/predictions.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/recent.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/senate_trades.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/aio/endpoints/sentiments.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/__init__.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/_utils.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/analyst_ratings.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/app_ratings.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/available.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/corporate_lobbying.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/house_trades.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/insider_transactions.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/linkedin_data.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/news.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/options.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/predictions.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/recent.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/senate_trades.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/endpoints/sentiments.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/exceptions.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain/py.typed +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain_python.egg-info/dependency_links.txt +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain_python.egg-info/requires.txt +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/src/finbrain_python.egg-info/top_level.txt +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/__init__.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/conftest.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_analyst_ratings.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_app_ratings.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_available.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_corporate_lobbying.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_envelope.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_house_trades.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_insider_transactions.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_linkedin_data.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_news.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_options.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_predictions.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_recent.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_senate_trades.py +0 -0
- {finbrain_python-0.2.2 → finbrain_python-0.2.4}/tests/test_sentiments.py +0 -0
|
@@ -5,15 +5,33 @@ 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.4] - 2026-03-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Government Contracts Endpoint**: `fb.government_contracts.ticker("LMT")` — fetch U.S. government contract awards with award amounts, agencies, NAICS codes, and descriptions (`/government-contracts/{SYMBOL}`)
|
|
13
|
+
- **Government Contracts Screener**: `fb.screener.government_contracts()` — cross-ticker government contracts with summary stats (`/screener/government-contracts`)
|
|
14
|
+
- **Government Contracts Plotting**: `fb.plot.government_contracts()` — visualize contract award amounts as bars on a secondary y-axis overlaid on a price chart, with hover details showing agency, NAICS description, and contract description
|
|
15
|
+
- **Async Government Contracts**: Full async support via `AsyncGovernmentContractsAPI`
|
|
16
|
+
- **Government Contracts Tests**: Unit tests (`tests/test_government_contracts.py`), screener tests, integration tests, and plotting tests
|
|
17
|
+
|
|
18
|
+
## [0.2.3] - 2026-03-17
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **Reddit Mentions Screener**: Moved from `fb.reddit_mentions.screener()` to `fb.screener.reddit_mentions()` for consistency with other screener endpoints
|
|
23
|
+
- **Plotting rename**: `fb.plot.reddit_mentions_screener()` renamed to `fb.plot.reddit_mentions_top()` to better reflect the chart's purpose (top N most mentioned tickers)
|
|
24
|
+
|
|
8
25
|
## [0.2.2] - 2026-03-17
|
|
9
26
|
|
|
10
27
|
### Added
|
|
11
28
|
|
|
12
29
|
- **Reddit Mentions Endpoint**: `fb.reddit_mentions.ticker("TSLA")` — fetch per-subreddit mention counts with full timestamps (sampled every 4 hours) (`/reddit-mentions/{SYMBOL}`)
|
|
13
|
-
- **Reddit Mentions Screener**: `fb.reddit_mentions
|
|
14
|
-
- **Reddit Mentions Plotting**: `fb.plot.reddit_mentions()` — stacked bars per subreddit overlaid on a price chart; `fb.plot.
|
|
30
|
+
- **Reddit Mentions Screener**: `fb.screener.reddit_mentions()` — cross-ticker Reddit mentions with aggregated totals and per-subreddit breakdowns (`/screener/reddit-mentions`)
|
|
31
|
+
- **Reddit Mentions Plotting**: `fb.plot.reddit_mentions()` — stacked bars per subreddit overlaid on a price chart; `fb.plot.reddit_mentions_top()` — horizontal stacked bar chart of top N most mentioned tickers
|
|
15
32
|
- **Async Reddit Mentions**: Full async support via `AsyncRedditMentionsAPI`
|
|
16
|
-
- **Reddit Mentions Tests**: Unit tests
|
|
33
|
+
- **Reddit Mentions Tests**: Unit tests, integration tests, and functional plotting tests
|
|
34
|
+
- **Plotting Test Coverage**: Added functional tests for all 11 plotting methods
|
|
17
35
|
|
|
18
36
|
## [0.2.1] - 2026-03-12
|
|
19
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finbrain-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
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
|
|
@@ -105,8 +105,12 @@ fb.reddit_mentions.ticker("TSLA",
|
|
|
105
105
|
date_to="2026-03-17",
|
|
106
106
|
as_dataframe=True)
|
|
107
107
|
|
|
108
|
-
#
|
|
109
|
-
fb.
|
|
108
|
+
# ---------- government contracts ----------
|
|
109
|
+
fb.government_contracts.ticker("LMT",
|
|
110
|
+
date_from="2025-01-01",
|
|
111
|
+
date_to="2025-12-31",
|
|
112
|
+
limit=50,
|
|
113
|
+
as_dataframe=True)
|
|
110
114
|
|
|
111
115
|
# ---------- insider transactions ----------
|
|
112
116
|
fb.insider_transactions.ticker("AMZN", as_dataframe=True)
|
|
@@ -139,6 +143,8 @@ fb.news.ticker("AMZN", limit=20, as_dataframe=True)
|
|
|
139
143
|
fb.screener.sentiment(market="S&P 500", as_dataframe=True)
|
|
140
144
|
fb.screener.predictions_daily(limit=100, as_dataframe=True)
|
|
141
145
|
fb.screener.insider_trading(limit=50)
|
|
146
|
+
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
147
|
+
fb.screener.government_contracts(limit=100, as_dataframe=True)
|
|
142
148
|
|
|
143
149
|
# ---------- recent data ----------
|
|
144
150
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
@@ -264,10 +270,10 @@ fb.plot.reddit_mentions("TSLA",
|
|
|
264
270
|
```python
|
|
265
271
|
# ---------- Reddit Mentions Screener Chart (no price data needed) ----------
|
|
266
272
|
# Stacked horizontal bar chart of top 15 most mentioned tickers
|
|
267
|
-
fb.plot.
|
|
273
|
+
fb.plot.reddit_mentions_top(market="S&P 500")
|
|
268
274
|
|
|
269
275
|
# Customize the number of tickers shown
|
|
270
|
-
fb.plot.
|
|
276
|
+
fb.plot.reddit_mentions_top(top_n=10, region="US")
|
|
271
277
|
```
|
|
272
278
|
|
|
273
279
|
**Price Data Requirements:**
|
|
@@ -314,13 +320,15 @@ fb = FinBrainClient() # reads from FINBRAIN_API_KEY env var
|
|
|
314
320
|
| Senate trades | `client.senate_trades.ticker()` | `/congress/senate/{SYMBOL}` |
|
|
315
321
|
| Corporate lobbying | `client.corporate_lobbying.ticker()` | `/lobbying/{SYMBOL}` |
|
|
316
322
|
| Reddit mentions | `client.reddit_mentions.ticker()` | `/reddit-mentions/{SYMBOL}` |
|
|
317
|
-
|
|
|
323
|
+
| Gov. contracts | `client.government_contracts.ticker()` | `/government-contracts/{SYMBOL}` |
|
|
318
324
|
| Insider transactions | `client.insider_transactions.ticker()` | `/insider-trading/{SYMBOL}` |
|
|
319
325
|
| LinkedIn | `client.linkedin_data.ticker()` | `/linkedin/{SYMBOL}` |
|
|
320
326
|
| Options – Put/Call | `client.options.put_call()` | `/put-call-ratio/{SYMBOL}` |
|
|
321
327
|
| Screener | `client.screener.sentiment()` | `/screener/sentiment` |
|
|
322
328
|
| | `client.screener.predictions_daily()` | `/screener/predictions/daily` |
|
|
323
329
|
| | `client.screener.insider_trading()` | `/screener/insider-trading` |
|
|
330
|
+
| | `client.screener.reddit_mentions()` | `/screener/reddit-mentions` |
|
|
331
|
+
| | `client.screener.government_contracts()` | `/screener/government-contracts` |
|
|
324
332
|
| | ... and 8 more screener methods | |
|
|
325
333
|
| Recent | `client.recent.news()` | `/recent/news` |
|
|
326
334
|
| | `client.recent.analyst_ratings()` | `/recent/analyst-ratings` |
|
|
@@ -79,8 +79,12 @@ fb.reddit_mentions.ticker("TSLA",
|
|
|
79
79
|
date_to="2026-03-17",
|
|
80
80
|
as_dataframe=True)
|
|
81
81
|
|
|
82
|
-
#
|
|
83
|
-
fb.
|
|
82
|
+
# ---------- government contracts ----------
|
|
83
|
+
fb.government_contracts.ticker("LMT",
|
|
84
|
+
date_from="2025-01-01",
|
|
85
|
+
date_to="2025-12-31",
|
|
86
|
+
limit=50,
|
|
87
|
+
as_dataframe=True)
|
|
84
88
|
|
|
85
89
|
# ---------- insider transactions ----------
|
|
86
90
|
fb.insider_transactions.ticker("AMZN", as_dataframe=True)
|
|
@@ -113,6 +117,8 @@ fb.news.ticker("AMZN", limit=20, as_dataframe=True)
|
|
|
113
117
|
fb.screener.sentiment(market="S&P 500", as_dataframe=True)
|
|
114
118
|
fb.screener.predictions_daily(limit=100, as_dataframe=True)
|
|
115
119
|
fb.screener.insider_trading(limit=50)
|
|
120
|
+
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
121
|
+
fb.screener.government_contracts(limit=100, as_dataframe=True)
|
|
116
122
|
|
|
117
123
|
# ---------- recent data ----------
|
|
118
124
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
@@ -238,10 +244,10 @@ fb.plot.reddit_mentions("TSLA",
|
|
|
238
244
|
```python
|
|
239
245
|
# ---------- Reddit Mentions Screener Chart (no price data needed) ----------
|
|
240
246
|
# Stacked horizontal bar chart of top 15 most mentioned tickers
|
|
241
|
-
fb.plot.
|
|
247
|
+
fb.plot.reddit_mentions_top(market="S&P 500")
|
|
242
248
|
|
|
243
249
|
# Customize the number of tickers shown
|
|
244
|
-
fb.plot.
|
|
250
|
+
fb.plot.reddit_mentions_top(top_n=10, region="US")
|
|
245
251
|
```
|
|
246
252
|
|
|
247
253
|
**Price Data Requirements:**
|
|
@@ -288,13 +294,15 @@ fb = FinBrainClient() # reads from FINBRAIN_API_KEY env var
|
|
|
288
294
|
| Senate trades | `client.senate_trades.ticker()` | `/congress/senate/{SYMBOL}` |
|
|
289
295
|
| Corporate lobbying | `client.corporate_lobbying.ticker()` | `/lobbying/{SYMBOL}` |
|
|
290
296
|
| Reddit mentions | `client.reddit_mentions.ticker()` | `/reddit-mentions/{SYMBOL}` |
|
|
291
|
-
|
|
|
297
|
+
| Gov. contracts | `client.government_contracts.ticker()` | `/government-contracts/{SYMBOL}` |
|
|
292
298
|
| Insider transactions | `client.insider_transactions.ticker()` | `/insider-trading/{SYMBOL}` |
|
|
293
299
|
| LinkedIn | `client.linkedin_data.ticker()` | `/linkedin/{SYMBOL}` |
|
|
294
300
|
| Options – Put/Call | `client.options.put_call()` | `/put-call-ratio/{SYMBOL}` |
|
|
295
301
|
| Screener | `client.screener.sentiment()` | `/screener/sentiment` |
|
|
296
302
|
| | `client.screener.predictions_daily()` | `/screener/predictions/daily` |
|
|
297
303
|
| | `client.screener.insider_trading()` | `/screener/insider-trading` |
|
|
304
|
+
| | `client.screener.reddit_mentions()` | `/screener/reddit-mentions` |
|
|
305
|
+
| | `client.screener.government_contracts()` | `/screener/government-contracts` |
|
|
298
306
|
| | ... and 8 more screener methods | |
|
|
299
307
|
| Recent | `client.recent.news()` | `/recent/news` |
|
|
300
308
|
| | `client.recent.analyst_ratings()` | `/recent/analyst-ratings` |
|
|
@@ -24,6 +24,7 @@ from .endpoints.screener import AsyncScreenerAPI
|
|
|
24
24
|
from .endpoints.recent import AsyncRecentAPI
|
|
25
25
|
from .endpoints.corporate_lobbying import AsyncCorporateLobbyingAPI
|
|
26
26
|
from .endpoints.reddit_mentions import AsyncRedditMentionsAPI
|
|
27
|
+
from .endpoints.government_contracts import AsyncGovernmentContractsAPI
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
# Which status codes merit a retry
|
|
@@ -84,6 +85,7 @@ class AsyncFinBrainClient:
|
|
|
84
85
|
self.recent = AsyncRecentAPI(self)
|
|
85
86
|
self.corporate_lobbying = AsyncCorporateLobbyingAPI(self)
|
|
86
87
|
self.reddit_mentions = AsyncRedditMentionsAPI(self)
|
|
88
|
+
self.government_contracts = AsyncGovernmentContractsAPI(self)
|
|
87
89
|
|
|
88
90
|
async def __aenter__(self) -> "AsyncFinBrainClient":
|
|
89
91
|
"""Context manager entry."""
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as _dt
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, Any, List
|
|
5
|
+
|
|
6
|
+
from ._utils import to_datestr
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..client import AsyncFinBrainClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AsyncGovernmentContractsAPI:
|
|
13
|
+
"""Async wrapper for /government-contracts and /screener/government-contracts endpoints."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, client: "AsyncFinBrainClient") -> None:
|
|
16
|
+
self._c = client
|
|
17
|
+
|
|
18
|
+
async def ticker(
|
|
19
|
+
self,
|
|
20
|
+
symbol: str,
|
|
21
|
+
*,
|
|
22
|
+
date_from: _dt.date | str | None = None,
|
|
23
|
+
date_to: _dt.date | str | None = None,
|
|
24
|
+
limit: int | None = None,
|
|
25
|
+
as_dataframe: bool = False,
|
|
26
|
+
) -> Dict[str, Any] | pd.DataFrame:
|
|
27
|
+
"""Fetch government contract awards for a symbol (async)."""
|
|
28
|
+
params: Dict[str, str] = {}
|
|
29
|
+
if date_from:
|
|
30
|
+
params["startDate"] = to_datestr(date_from)
|
|
31
|
+
if date_to:
|
|
32
|
+
params["endDate"] = to_datestr(date_to)
|
|
33
|
+
if limit is not None:
|
|
34
|
+
params["limit"] = str(limit)
|
|
35
|
+
|
|
36
|
+
path = f"government-contracts/{symbol.upper()}"
|
|
37
|
+
|
|
38
|
+
data: Dict[str, Any] = await self._c._request("GET", path, params=params)
|
|
39
|
+
|
|
40
|
+
if as_dataframe:
|
|
41
|
+
rows: List[Dict[str, Any]] = data.get("contracts", [])
|
|
42
|
+
df = pd.DataFrame(rows)
|
|
43
|
+
if not df.empty and "startDate" in df.columns:
|
|
44
|
+
df["startDate"] = pd.to_datetime(df["startDate"])
|
|
45
|
+
df.set_index("startDate", inplace=True)
|
|
46
|
+
return df
|
|
47
|
+
|
|
48
|
+
return data
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as _dt
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, Any, List
|
|
5
|
+
|
|
6
|
+
from ._utils import to_datestr
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..client import AsyncFinBrainClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AsyncRedditMentionsAPI:
|
|
13
|
+
"""Async wrapper for /reddit-mentions and /screener/reddit-mentions endpoints."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, client: "AsyncFinBrainClient") -> None:
|
|
16
|
+
self._c = client
|
|
17
|
+
|
|
18
|
+
async def ticker(
|
|
19
|
+
self,
|
|
20
|
+
symbol: str,
|
|
21
|
+
*,
|
|
22
|
+
date_from: _dt.date | str | None = None,
|
|
23
|
+
date_to: _dt.date | str | None = None,
|
|
24
|
+
limit: int | None = None,
|
|
25
|
+
as_dataframe: bool = False,
|
|
26
|
+
) -> Dict[str, Any] | pd.DataFrame:
|
|
27
|
+
"""Fetch Reddit mention counts for a symbol across subreddits (async)."""
|
|
28
|
+
params: Dict[str, str] = {}
|
|
29
|
+
if date_from:
|
|
30
|
+
params["startDate"] = to_datestr(date_from)
|
|
31
|
+
if date_to:
|
|
32
|
+
params["endDate"] = to_datestr(date_to)
|
|
33
|
+
if limit is not None:
|
|
34
|
+
params["limit"] = str(limit)
|
|
35
|
+
|
|
36
|
+
path = f"reddit-mentions/{symbol.upper()}"
|
|
37
|
+
|
|
38
|
+
data: Dict[str, Any] = await self._c._request("GET", path, params=params)
|
|
39
|
+
|
|
40
|
+
if as_dataframe:
|
|
41
|
+
rows: List[Dict[str, Any]] = data.get("data", [])
|
|
42
|
+
df = pd.DataFrame(rows)
|
|
43
|
+
if not df.empty and "date" in df.columns:
|
|
44
|
+
df["date"] = pd.to_datetime(df["date"])
|
|
45
|
+
df.set_index("date", inplace=True)
|
|
46
|
+
return df
|
|
47
|
+
|
|
48
|
+
return data
|
|
@@ -208,3 +208,29 @@ class AsyncScreenerAPI:
|
|
|
208
208
|
"""Screen monthly (12-month) predictions across tickers."""
|
|
209
209
|
params = self._build_params(limit=limit, market=market, region=region)
|
|
210
210
|
return await self._get("screener/predictions/monthly", params, as_dataframe)
|
|
211
|
+
|
|
212
|
+
# ── reddit mentions ────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
async def reddit_mentions(
|
|
215
|
+
self,
|
|
216
|
+
*,
|
|
217
|
+
limit: int | None = None,
|
|
218
|
+
market: str | None = None,
|
|
219
|
+
region: str | None = None,
|
|
220
|
+
as_dataframe: bool = False,
|
|
221
|
+
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
222
|
+
"""Screen Reddit mention counts across tickers (async)."""
|
|
223
|
+
params = self._build_params(limit=limit, market=market, region=region)
|
|
224
|
+
return await self._get("screener/reddit-mentions", params, as_dataframe)
|
|
225
|
+
|
|
226
|
+
# ── government contracts ──────────────────────────────────
|
|
227
|
+
|
|
228
|
+
async def government_contracts(
|
|
229
|
+
self,
|
|
230
|
+
*,
|
|
231
|
+
limit: int | None = None,
|
|
232
|
+
as_dataframe: bool = False,
|
|
233
|
+
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
234
|
+
"""Screen government contracts across all tickers (async)."""
|
|
235
|
+
params = self._build_params(limit=limit)
|
|
236
|
+
return await self._get("screener/government-contracts", params, as_dataframe)
|
|
@@ -25,6 +25,7 @@ from .endpoints.screener import ScreenerAPI
|
|
|
25
25
|
from .endpoints.recent import RecentAPI
|
|
26
26
|
from .endpoints.corporate_lobbying import CorporateLobbyingAPI
|
|
27
27
|
from .endpoints.reddit_mentions import RedditMentionsAPI
|
|
28
|
+
from .endpoints.government_contracts import GovernmentContractsAPI
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
# Which status codes merit a retry
|
|
@@ -79,6 +80,7 @@ class FinBrainClient:
|
|
|
79
80
|
self.recent = RecentAPI(self)
|
|
80
81
|
self.corporate_lobbying = CorporateLobbyingAPI(self)
|
|
81
82
|
self.reddit_mentions = RedditMentionsAPI(self)
|
|
83
|
+
self.government_contracts = GovernmentContractsAPI(self)
|
|
82
84
|
|
|
83
85
|
# ---------- private helpers ----------
|
|
84
86
|
def _request(
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as _dt
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, Any, List
|
|
5
|
+
|
|
6
|
+
from ._utils import to_datestr
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING: # imported only by type-checkers
|
|
9
|
+
from ..client import FinBrainClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GovernmentContractsAPI:
|
|
13
|
+
"""
|
|
14
|
+
Endpoints
|
|
15
|
+
---------
|
|
16
|
+
``/government-contracts/<TICKER>`` - U.S. government contract awards.
|
|
17
|
+
``/screener/government-contracts`` - cross-ticker government contracts screener.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# ------------------------------------------------------------------ #
|
|
21
|
+
def __init__(self, client: "FinBrainClient") -> None:
|
|
22
|
+
self._c = client # reference to the parent client
|
|
23
|
+
|
|
24
|
+
# ------------------------------------------------------------------ #
|
|
25
|
+
def ticker(
|
|
26
|
+
self,
|
|
27
|
+
symbol: str,
|
|
28
|
+
*,
|
|
29
|
+
date_from: _dt.date | str | None = None,
|
|
30
|
+
date_to: _dt.date | str | None = None,
|
|
31
|
+
limit: int | None = None,
|
|
32
|
+
as_dataframe: bool = False,
|
|
33
|
+
) -> Dict[str, Any] | pd.DataFrame:
|
|
34
|
+
"""
|
|
35
|
+
Fetch government contract awards for *symbol*.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
symbol :
|
|
40
|
+
Ticker symbol; auto-upper-cased.
|
|
41
|
+
date_from, date_to :
|
|
42
|
+
Optional ISO dates (``YYYY-MM-DD``) bounding the returned rows.
|
|
43
|
+
limit :
|
|
44
|
+
Maximum number of records to return (1-500).
|
|
45
|
+
as_dataframe :
|
|
46
|
+
If *True*, return a **pandas.DataFrame** indexed by ``startDate``;
|
|
47
|
+
otherwise return the raw JSON dict.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
dict | pandas.DataFrame
|
|
52
|
+
"""
|
|
53
|
+
params: Dict[str, str] = {}
|
|
54
|
+
if date_from:
|
|
55
|
+
params["startDate"] = to_datestr(date_from)
|
|
56
|
+
if date_to:
|
|
57
|
+
params["endDate"] = to_datestr(date_to)
|
|
58
|
+
if limit is not None:
|
|
59
|
+
params["limit"] = str(limit)
|
|
60
|
+
|
|
61
|
+
path = f"government-contracts/{symbol.upper()}"
|
|
62
|
+
|
|
63
|
+
data: Dict[str, Any] = self._c._request("GET", path, params=params)
|
|
64
|
+
|
|
65
|
+
if as_dataframe:
|
|
66
|
+
rows: List[Dict[str, Any]] = data.get("contracts", [])
|
|
67
|
+
df = pd.DataFrame(rows)
|
|
68
|
+
if not df.empty and "startDate" in df.columns:
|
|
69
|
+
df["startDate"] = pd.to_datetime(df["startDate"])
|
|
70
|
+
df.set_index("startDate", inplace=True)
|
|
71
|
+
return df
|
|
72
|
+
|
|
73
|
+
return data
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import datetime as _dt
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, Any, List
|
|
5
|
+
|
|
6
|
+
from ._utils import to_datestr
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING: # imported only by type-checkers
|
|
9
|
+
from ..client import FinBrainClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RedditMentionsAPI:
|
|
13
|
+
"""
|
|
14
|
+
Endpoints
|
|
15
|
+
---------
|
|
16
|
+
``/reddit-mentions/<TICKER>`` - Reddit mention counts per subreddit.
|
|
17
|
+
``/screener/reddit-mentions`` - cross-ticker Reddit mentions screener.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# ------------------------------------------------------------------ #
|
|
21
|
+
def __init__(self, client: "FinBrainClient") -> None:
|
|
22
|
+
self._c = client # reference to the parent client
|
|
23
|
+
|
|
24
|
+
# ------------------------------------------------------------------ #
|
|
25
|
+
def ticker(
|
|
26
|
+
self,
|
|
27
|
+
symbol: str,
|
|
28
|
+
*,
|
|
29
|
+
date_from: _dt.date | str | None = None,
|
|
30
|
+
date_to: _dt.date | str | None = None,
|
|
31
|
+
limit: int | None = None,
|
|
32
|
+
as_dataframe: bool = False,
|
|
33
|
+
) -> Dict[str, Any] | pd.DataFrame:
|
|
34
|
+
"""
|
|
35
|
+
Fetch Reddit mention counts for *symbol* across subreddits.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
symbol :
|
|
40
|
+
Ticker symbol; auto-upper-cased.
|
|
41
|
+
date_from, date_to :
|
|
42
|
+
Optional ISO dates (``YYYY-MM-DD``) bounding the returned rows.
|
|
43
|
+
limit :
|
|
44
|
+
Maximum number of records to return (1-500).
|
|
45
|
+
as_dataframe :
|
|
46
|
+
If *True*, return a **pandas.DataFrame** indexed by ``date``;
|
|
47
|
+
otherwise return the raw JSON dict.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
dict | pandas.DataFrame
|
|
52
|
+
"""
|
|
53
|
+
params: Dict[str, str] = {}
|
|
54
|
+
if date_from:
|
|
55
|
+
params["startDate"] = to_datestr(date_from)
|
|
56
|
+
if date_to:
|
|
57
|
+
params["endDate"] = to_datestr(date_to)
|
|
58
|
+
if limit is not None:
|
|
59
|
+
params["limit"] = str(limit)
|
|
60
|
+
|
|
61
|
+
path = f"reddit-mentions/{symbol.upper()}"
|
|
62
|
+
|
|
63
|
+
data: Dict[str, Any] = self._c._request("GET", path, params=params)
|
|
64
|
+
|
|
65
|
+
if as_dataframe:
|
|
66
|
+
rows: List[Dict[str, Any]] = data.get("data", [])
|
|
67
|
+
df = pd.DataFrame(rows)
|
|
68
|
+
if not df.empty and "date" in df.columns:
|
|
69
|
+
df["date"] = pd.to_datetime(df["date"])
|
|
70
|
+
df.set_index("date", inplace=True)
|
|
71
|
+
return df
|
|
72
|
+
|
|
73
|
+
return data
|
|
@@ -213,3 +213,29 @@ class ScreenerAPI:
|
|
|
213
213
|
"""Screen monthly (12-month) predictions across tickers."""
|
|
214
214
|
params = self._build_params(limit=limit, market=market, region=region)
|
|
215
215
|
return self._get("screener/predictions/monthly", params, as_dataframe)
|
|
216
|
+
|
|
217
|
+
# ── reddit mentions ────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
def reddit_mentions(
|
|
220
|
+
self,
|
|
221
|
+
*,
|
|
222
|
+
limit: int | None = None,
|
|
223
|
+
market: str | None = None,
|
|
224
|
+
region: str | None = None,
|
|
225
|
+
as_dataframe: bool = False,
|
|
226
|
+
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
227
|
+
"""Screen Reddit mention counts across tickers."""
|
|
228
|
+
params = self._build_params(limit=limit, market=market, region=region)
|
|
229
|
+
return self._get("screener/reddit-mentions", params, as_dataframe)
|
|
230
|
+
|
|
231
|
+
# ── government contracts ──────────────────────────────────
|
|
232
|
+
|
|
233
|
+
def government_contracts(
|
|
234
|
+
self,
|
|
235
|
+
*,
|
|
236
|
+
limit: int | None = None,
|
|
237
|
+
as_dataframe: bool = False,
|
|
238
|
+
) -> List[Dict[str, Any]] | pd.DataFrame:
|
|
239
|
+
"""Screen government contracts across all tickers."""
|
|
240
|
+
params = self._build_params(limit=limit)
|
|
241
|
+
return self._get("screener/government-contracts", params, as_dataframe)
|