finbrain-python 0.2.1__tar.gz → 0.2.3__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.1 → finbrain_python-0.2.3}/CHANGELOG.md +18 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/PKG-INFO +25 -1
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/README.md +24 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/client.py +2 -0
- finbrain_python-0.2.3/src/finbrain/aio/endpoints/reddit_mentions.py +48 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/screener.py +14 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/client.py +2 -0
- finbrain_python-0.2.3/src/finbrain/endpoints/reddit_mentions.py +73 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/screener.py +14 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/plotting.py +247 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain_python.egg-info/PKG-INFO +25 -1
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain_python.egg-info/SOURCES.txt +3 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_integration.py +45 -0
- finbrain_python-0.2.3/tests/test_plotting.py +549 -0
- finbrain_python-0.2.3/tests/test_reddit_mentions.py +162 -0
- finbrain_python-0.2.1/tests/test_plotting.py +0 -134
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/.gitattributes +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/.github/workflows/ci.yml +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/.github/workflows/release.yml +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/.gitignore +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/LICENSE +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/RELEASE.md +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/examples/async_example.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/examples/transactions_plotting_example.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/pyproject.toml +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/setup.cfg +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/__init__.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/__init__.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/__init__.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/_utils.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/analyst_ratings.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/app_ratings.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/available.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/corporate_lobbying.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/house_trades.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/insider_transactions.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/linkedin_data.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/news.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/options.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/predictions.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/recent.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/senate_trades.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/aio/endpoints/sentiments.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/__init__.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/_utils.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/analyst_ratings.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/app_ratings.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/available.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/corporate_lobbying.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/house_trades.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/insider_transactions.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/linkedin_data.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/news.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/options.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/predictions.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/recent.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/senate_trades.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/endpoints/sentiments.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/exceptions.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain/py.typed +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain_python.egg-info/dependency_links.txt +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain_python.egg-info/requires.txt +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/src/finbrain_python.egg-info/top_level.txt +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/__init__.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/conftest.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_analyst_ratings.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_app_ratings.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_async_client.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_available.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_corporate_lobbying.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_envelope.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_house_trades.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_insider_transactions.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_linkedin_data.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_news.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_options.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_predictions.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_recent.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_screener.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_senate_trades.py +0 -0
- {finbrain_python-0.2.1 → finbrain_python-0.2.3}/tests/test_sentiments.py +0 -0
|
@@ -5,6 +5,24 @@ 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.3] - 2026-03-17
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Reddit Mentions Screener**: Moved from `fb.reddit_mentions.screener()` to `fb.screener.reddit_mentions()` for consistency with other screener endpoints
|
|
13
|
+
- **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)
|
|
14
|
+
|
|
15
|
+
## [0.2.2] - 2026-03-17
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **Reddit Mentions Endpoint**: `fb.reddit_mentions.ticker("TSLA")` — fetch per-subreddit mention counts with full timestamps (sampled every 4 hours) (`/reddit-mentions/{SYMBOL}`)
|
|
20
|
+
- **Reddit Mentions Screener**: `fb.screener.reddit_mentions()` — cross-ticker Reddit mentions with aggregated totals and per-subreddit breakdowns (`/screener/reddit-mentions`)
|
|
21
|
+
- **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
|
|
22
|
+
- **Async Reddit Mentions**: Full async support via `AsyncRedditMentionsAPI`
|
|
23
|
+
- **Reddit Mentions Tests**: Unit tests, integration tests, and functional plotting tests
|
|
24
|
+
- **Plotting Test Coverage**: Added functional tests for all 11 plotting methods
|
|
25
|
+
|
|
8
26
|
## [0.2.1] - 2026-03-12
|
|
9
27
|
|
|
10
28
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finbrain-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
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
|
|
@@ -99,6 +99,12 @@ fb.corporate_lobbying.ticker("AAPL",
|
|
|
99
99
|
date_to="2025-06-30",
|
|
100
100
|
as_dataframe=True)
|
|
101
101
|
|
|
102
|
+
# ---------- reddit mentions ----------
|
|
103
|
+
fb.reddit_mentions.ticker("TSLA",
|
|
104
|
+
date_from="2026-03-01",
|
|
105
|
+
date_to="2026-03-17",
|
|
106
|
+
as_dataframe=True)
|
|
107
|
+
|
|
102
108
|
# ---------- insider transactions ----------
|
|
103
109
|
fb.insider_transactions.ticker("AMZN", as_dataframe=True)
|
|
104
110
|
|
|
@@ -130,6 +136,7 @@ fb.news.ticker("AMZN", limit=20, as_dataframe=True)
|
|
|
130
136
|
fb.screener.sentiment(market="S&P 500", as_dataframe=True)
|
|
131
137
|
fb.screener.predictions_daily(limit=100, as_dataframe=True)
|
|
132
138
|
fb.screener.insider_trading(limit=50)
|
|
139
|
+
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
133
140
|
|
|
134
141
|
# ---------- recent data ----------
|
|
135
142
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
@@ -244,6 +251,21 @@ fb.plot.corporate_lobbying("AAPL",
|
|
|
244
251
|
price_data=price_df,
|
|
245
252
|
date_from="2024-01-01",
|
|
246
253
|
date_to="2025-06-30")
|
|
254
|
+
|
|
255
|
+
# Plot Reddit mentions (stacked bars per subreddit) on your price chart
|
|
256
|
+
fb.plot.reddit_mentions("TSLA",
|
|
257
|
+
price_data=price_df,
|
|
258
|
+
date_from="2026-03-01",
|
|
259
|
+
date_to="2026-03-17")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# ---------- Reddit Mentions Screener Chart (no price data needed) ----------
|
|
264
|
+
# Stacked horizontal bar chart of top 15 most mentioned tickers
|
|
265
|
+
fb.plot.reddit_mentions_top(market="S&P 500")
|
|
266
|
+
|
|
267
|
+
# Customize the number of tickers shown
|
|
268
|
+
fb.plot.reddit_mentions_top(top_n=10, region="US")
|
|
247
269
|
```
|
|
248
270
|
|
|
249
271
|
**Price Data Requirements:**
|
|
@@ -289,12 +311,14 @@ fb = FinBrainClient() # reads from FINBRAIN_API_KEY env var
|
|
|
289
311
|
| House trades | `client.house_trades.ticker()` | `/congress/house/{SYMBOL}` |
|
|
290
312
|
| Senate trades | `client.senate_trades.ticker()` | `/congress/senate/{SYMBOL}` |
|
|
291
313
|
| Corporate lobbying | `client.corporate_lobbying.ticker()` | `/lobbying/{SYMBOL}` |
|
|
314
|
+
| Reddit mentions | `client.reddit_mentions.ticker()` | `/reddit-mentions/{SYMBOL}` |
|
|
292
315
|
| Insider transactions | `client.insider_transactions.ticker()` | `/insider-trading/{SYMBOL}` |
|
|
293
316
|
| LinkedIn | `client.linkedin_data.ticker()` | `/linkedin/{SYMBOL}` |
|
|
294
317
|
| Options – Put/Call | `client.options.put_call()` | `/put-call-ratio/{SYMBOL}` |
|
|
295
318
|
| Screener | `client.screener.sentiment()` | `/screener/sentiment` |
|
|
296
319
|
| | `client.screener.predictions_daily()` | `/screener/predictions/daily` |
|
|
297
320
|
| | `client.screener.insider_trading()` | `/screener/insider-trading` |
|
|
321
|
+
| | `client.screener.reddit_mentions()` | `/screener/reddit-mentions` |
|
|
298
322
|
| | ... and 8 more screener methods | |
|
|
299
323
|
| Recent | `client.recent.news()` | `/recent/news` |
|
|
300
324
|
| | `client.recent.analyst_ratings()` | `/recent/analyst-ratings` |
|
|
@@ -73,6 +73,12 @@ fb.corporate_lobbying.ticker("AAPL",
|
|
|
73
73
|
date_to="2025-06-30",
|
|
74
74
|
as_dataframe=True)
|
|
75
75
|
|
|
76
|
+
# ---------- reddit mentions ----------
|
|
77
|
+
fb.reddit_mentions.ticker("TSLA",
|
|
78
|
+
date_from="2026-03-01",
|
|
79
|
+
date_to="2026-03-17",
|
|
80
|
+
as_dataframe=True)
|
|
81
|
+
|
|
76
82
|
# ---------- insider transactions ----------
|
|
77
83
|
fb.insider_transactions.ticker("AMZN", as_dataframe=True)
|
|
78
84
|
|
|
@@ -104,6 +110,7 @@ fb.news.ticker("AMZN", limit=20, as_dataframe=True)
|
|
|
104
110
|
fb.screener.sentiment(market="S&P 500", as_dataframe=True)
|
|
105
111
|
fb.screener.predictions_daily(limit=100, as_dataframe=True)
|
|
106
112
|
fb.screener.insider_trading(limit=50)
|
|
113
|
+
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
107
114
|
|
|
108
115
|
# ---------- recent data ----------
|
|
109
116
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
@@ -218,6 +225,21 @@ fb.plot.corporate_lobbying("AAPL",
|
|
|
218
225
|
price_data=price_df,
|
|
219
226
|
date_from="2024-01-01",
|
|
220
227
|
date_to="2025-06-30")
|
|
228
|
+
|
|
229
|
+
# Plot Reddit mentions (stacked bars per subreddit) on your price chart
|
|
230
|
+
fb.plot.reddit_mentions("TSLA",
|
|
231
|
+
price_data=price_df,
|
|
232
|
+
date_from="2026-03-01",
|
|
233
|
+
date_to="2026-03-17")
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
# ---------- Reddit Mentions Screener Chart (no price data needed) ----------
|
|
238
|
+
# Stacked horizontal bar chart of top 15 most mentioned tickers
|
|
239
|
+
fb.plot.reddit_mentions_top(market="S&P 500")
|
|
240
|
+
|
|
241
|
+
# Customize the number of tickers shown
|
|
242
|
+
fb.plot.reddit_mentions_top(top_n=10, region="US")
|
|
221
243
|
```
|
|
222
244
|
|
|
223
245
|
**Price Data Requirements:**
|
|
@@ -263,12 +285,14 @@ fb = FinBrainClient() # reads from FINBRAIN_API_KEY env var
|
|
|
263
285
|
| House trades | `client.house_trades.ticker()` | `/congress/house/{SYMBOL}` |
|
|
264
286
|
| Senate trades | `client.senate_trades.ticker()` | `/congress/senate/{SYMBOL}` |
|
|
265
287
|
| Corporate lobbying | `client.corporate_lobbying.ticker()` | `/lobbying/{SYMBOL}` |
|
|
288
|
+
| Reddit mentions | `client.reddit_mentions.ticker()` | `/reddit-mentions/{SYMBOL}` |
|
|
266
289
|
| Insider transactions | `client.insider_transactions.ticker()` | `/insider-trading/{SYMBOL}` |
|
|
267
290
|
| LinkedIn | `client.linkedin_data.ticker()` | `/linkedin/{SYMBOL}` |
|
|
268
291
|
| Options – Put/Call | `client.options.put_call()` | `/put-call-ratio/{SYMBOL}` |
|
|
269
292
|
| Screener | `client.screener.sentiment()` | `/screener/sentiment` |
|
|
270
293
|
| | `client.screener.predictions_daily()` | `/screener/predictions/daily` |
|
|
271
294
|
| | `client.screener.insider_trading()` | `/screener/insider-trading` |
|
|
295
|
+
| | `client.screener.reddit_mentions()` | `/screener/reddit-mentions` |
|
|
272
296
|
| | ... and 8 more screener methods | |
|
|
273
297
|
| Recent | `client.recent.news()` | `/recent/news` |
|
|
274
298
|
| | `client.recent.analyst_ratings()` | `/recent/analyst-ratings` |
|
|
@@ -23,6 +23,7 @@ from .endpoints.news import AsyncNewsAPI
|
|
|
23
23
|
from .endpoints.screener import AsyncScreenerAPI
|
|
24
24
|
from .endpoints.recent import AsyncRecentAPI
|
|
25
25
|
from .endpoints.corporate_lobbying import AsyncCorporateLobbyingAPI
|
|
26
|
+
from .endpoints.reddit_mentions import AsyncRedditMentionsAPI
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
# Which status codes merit a retry
|
|
@@ -82,6 +83,7 @@ class AsyncFinBrainClient:
|
|
|
82
83
|
self.screener = AsyncScreenerAPI(self)
|
|
83
84
|
self.recent = AsyncRecentAPI(self)
|
|
84
85
|
self.corporate_lobbying = AsyncCorporateLobbyingAPI(self)
|
|
86
|
+
self.reddit_mentions = AsyncRedditMentionsAPI(self)
|
|
85
87
|
|
|
86
88
|
async def __aenter__(self) -> "AsyncFinBrainClient":
|
|
87
89
|
"""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 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,17 @@ 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)
|
|
@@ -24,6 +24,7 @@ from .endpoints.news import NewsAPI
|
|
|
24
24
|
from .endpoints.screener import ScreenerAPI
|
|
25
25
|
from .endpoints.recent import RecentAPI
|
|
26
26
|
from .endpoints.corporate_lobbying import CorporateLobbyingAPI
|
|
27
|
+
from .endpoints.reddit_mentions import RedditMentionsAPI
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
# Which status codes merit a retry
|
|
@@ -77,6 +78,7 @@ class FinBrainClient:
|
|
|
77
78
|
self.screener = ScreenerAPI(self)
|
|
78
79
|
self.recent = RecentAPI(self)
|
|
79
80
|
self.corporate_lobbying = CorporateLobbyingAPI(self)
|
|
81
|
+
self.reddit_mentions = RedditMentionsAPI(self)
|
|
80
82
|
|
|
81
83
|
# ---------- private helpers ----------
|
|
82
84
|
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 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,17 @@ 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)
|
|
@@ -907,6 +907,253 @@ class _PlotNamespace:
|
|
|
907
907
|
return None
|
|
908
908
|
return fig.to_json() if as_json else fig
|
|
909
909
|
|
|
910
|
+
# --------------------------------------------------------------------- #
|
|
911
|
+
# Reddit Mentions → bars on price chart #
|
|
912
|
+
# --------------------------------------------------------------------- #
|
|
913
|
+
def reddit_mentions(
|
|
914
|
+
self,
|
|
915
|
+
ticker: str,
|
|
916
|
+
price_data: pd.DataFrame,
|
|
917
|
+
*,
|
|
918
|
+
date_from: str | None = None,
|
|
919
|
+
date_to: str | None = None,
|
|
920
|
+
as_json: bool = False,
|
|
921
|
+
show: bool = True,
|
|
922
|
+
template: str = "plotly_dark",
|
|
923
|
+
**kwargs,
|
|
924
|
+
):
|
|
925
|
+
"""
|
|
926
|
+
Plot Reddit mention counts overlaid on a price chart.
|
|
927
|
+
|
|
928
|
+
This method requires user-provided historical price data, as FinBrain
|
|
929
|
+
does not currently offer a price history endpoint.
|
|
930
|
+
|
|
931
|
+
Parameters
|
|
932
|
+
----------
|
|
933
|
+
ticker : str
|
|
934
|
+
Ticker symbol (e.g. ``"AAPL"``).
|
|
935
|
+
price_data : pandas.DataFrame
|
|
936
|
+
**User-provided** price history with a DatetimeIndex and a column
|
|
937
|
+
containing prices (e.g. ``"close"``, ``"Close"``, or ``"price"``).
|
|
938
|
+
The index must be timezone-naive or UTC.
|
|
939
|
+
date_from, date_to : str or None, optional
|
|
940
|
+
Date range for mentions in ``YYYY-MM-DD`` format.
|
|
941
|
+
as_json : bool, default False
|
|
942
|
+
If ``True``, return JSON string instead of Figure object.
|
|
943
|
+
show : bool, default True
|
|
944
|
+
If ``True`` and ``as_json=False``, display the figure immediately.
|
|
945
|
+
template : str, default "plotly_dark"
|
|
946
|
+
Plotly template name.
|
|
947
|
+
**kwargs
|
|
948
|
+
Additional arguments passed to
|
|
949
|
+
:meth:`FinBrainClient.reddit_mentions.ticker`.
|
|
950
|
+
|
|
951
|
+
Returns
|
|
952
|
+
-------
|
|
953
|
+
plotly.graph_objects.Figure or str or None
|
|
954
|
+
Figure object, JSON string, or None (when shown).
|
|
955
|
+
|
|
956
|
+
Raises
|
|
957
|
+
------
|
|
958
|
+
ValueError
|
|
959
|
+
If ``price_data`` is empty or missing required price column.
|
|
960
|
+
"""
|
|
961
|
+
# Validate price_data
|
|
962
|
+
if price_data.empty:
|
|
963
|
+
raise ValueError("price_data cannot be empty")
|
|
964
|
+
|
|
965
|
+
# Flatten MultiIndex columns if present (e.g., from yf.download())
|
|
966
|
+
if isinstance(price_data.columns, pd.MultiIndex):
|
|
967
|
+
price_data = price_data.copy()
|
|
968
|
+
price_data.columns = price_data.columns.get_level_values(0)
|
|
969
|
+
|
|
970
|
+
# Find price column (case-insensitive search)
|
|
971
|
+
price_col = None
|
|
972
|
+
for col in ["close", "Close", "price", "Price", "adj_close", "Adj Close"]:
|
|
973
|
+
if col in price_data.columns:
|
|
974
|
+
price_col = col
|
|
975
|
+
break
|
|
976
|
+
if price_col is None:
|
|
977
|
+
raise ValueError(
|
|
978
|
+
f"price_data must contain a price column (e.g. 'close', 'Close', 'price'). "
|
|
979
|
+
f"Found columns: {price_data.columns.tolist()}"
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
# Fetch Reddit mentions
|
|
983
|
+
mentions_df = self._fb.reddit_mentions.ticker(
|
|
984
|
+
ticker,
|
|
985
|
+
date_from=date_from,
|
|
986
|
+
date_to=date_to,
|
|
987
|
+
as_dataframe=True,
|
|
988
|
+
**kwargs,
|
|
989
|
+
)
|
|
990
|
+
|
|
991
|
+
# Normalize timezones
|
|
992
|
+
price_data_normalized = price_data.copy()
|
|
993
|
+
if price_data_normalized.index.tz is not None:
|
|
994
|
+
price_data_normalized.index = price_data_normalized.index.tz_localize(None)
|
|
995
|
+
|
|
996
|
+
fig = go.Figure(
|
|
997
|
+
layout=dict(
|
|
998
|
+
template=template,
|
|
999
|
+
title=f"Reddit Mentions · {ticker}",
|
|
1000
|
+
xaxis_title="Date",
|
|
1001
|
+
hovermode="x unified",
|
|
1002
|
+
)
|
|
1003
|
+
)
|
|
1004
|
+
|
|
1005
|
+
# Plot price line on primary y-axis
|
|
1006
|
+
fig.add_scatter(
|
|
1007
|
+
name="Price",
|
|
1008
|
+
x=price_data_normalized.index,
|
|
1009
|
+
y=price_data_normalized[price_col],
|
|
1010
|
+
mode="lines",
|
|
1011
|
+
line=dict(width=2, color="#02d2ff"),
|
|
1012
|
+
hovertemplate="<b>%{x|%Y-%m-%d %H:%M}</b><br>Price: $%{y:.2f}<extra></extra>",
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
if not mentions_df.empty:
|
|
1016
|
+
mentions_normalized = mentions_df.copy()
|
|
1017
|
+
if mentions_normalized.index.tz is not None:
|
|
1018
|
+
mentions_normalized.index = mentions_normalized.index.tz_localize(None)
|
|
1019
|
+
|
|
1020
|
+
# Exclude _all (aggregate) — use individual subreddits for stacked bars
|
|
1021
|
+
per_sub = mentions_normalized[
|
|
1022
|
+
mentions_normalized["subreddit"] != "_all"
|
|
1023
|
+
]
|
|
1024
|
+
|
|
1025
|
+
if not per_sub.empty:
|
|
1026
|
+
for subreddit in sorted(per_sub["subreddit"].unique()):
|
|
1027
|
+
sub_data = per_sub[per_sub["subreddit"] == subreddit]
|
|
1028
|
+
fig.add_bar(
|
|
1029
|
+
name=f"r/{subreddit}",
|
|
1030
|
+
x=sub_data.index,
|
|
1031
|
+
y=sub_data["mentions"],
|
|
1032
|
+
yaxis="y2",
|
|
1033
|
+
hovertemplate=(
|
|
1034
|
+
"<b>%{x|%Y-%m-%d %H:%M}</b><br>"
|
|
1035
|
+
f"r/{subreddit}: " + "%{y:,}<extra></extra>"
|
|
1036
|
+
),
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
fig.update_layout(
|
|
1040
|
+
barmode="stack",
|
|
1041
|
+
yaxis=dict(title="Price", showgrid=True),
|
|
1042
|
+
yaxis2=dict(
|
|
1043
|
+
title="Mentions",
|
|
1044
|
+
overlaying="y",
|
|
1045
|
+
side="right",
|
|
1046
|
+
showgrid=False,
|
|
1047
|
+
zeroline=False,
|
|
1048
|
+
rangemode="tozero",
|
|
1049
|
+
),
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
if show and not as_json:
|
|
1053
|
+
fig.show()
|
|
1054
|
+
return None
|
|
1055
|
+
return fig.to_json() if as_json else fig
|
|
1056
|
+
|
|
1057
|
+
# --------------------------------------------------------------------- #
|
|
1058
|
+
# Reddit Mentions Screener → stacked horizontal bars (top N tickers) #
|
|
1059
|
+
# --------------------------------------------------------------------- #
|
|
1060
|
+
def reddit_mentions_top(
|
|
1061
|
+
self,
|
|
1062
|
+
*,
|
|
1063
|
+
top_n: int = 15,
|
|
1064
|
+
market: str | None = None,
|
|
1065
|
+
region: str | None = None,
|
|
1066
|
+
limit: int | None = None,
|
|
1067
|
+
as_json: bool = False,
|
|
1068
|
+
show: bool = True,
|
|
1069
|
+
template: str = "plotly_dark",
|
|
1070
|
+
**kwargs,
|
|
1071
|
+
):
|
|
1072
|
+
"""
|
|
1073
|
+
Plot a stacked horizontal bar chart of the most mentioned tickers
|
|
1074
|
+
from the latest Reddit mentions screener snapshot.
|
|
1075
|
+
|
|
1076
|
+
Parameters
|
|
1077
|
+
----------
|
|
1078
|
+
top_n : int, default 15
|
|
1079
|
+
Number of top-mentioned tickers to display.
|
|
1080
|
+
market : str or None, optional
|
|
1081
|
+
Filter by market name (e.g. ``"S&P 500"``).
|
|
1082
|
+
region : str or None, optional
|
|
1083
|
+
Filter by region (e.g. ``"US"``).
|
|
1084
|
+
limit : int or None, optional
|
|
1085
|
+
Maximum records to fetch from the screener API.
|
|
1086
|
+
as_json : bool, default False
|
|
1087
|
+
If ``True``, return JSON string instead of Figure object.
|
|
1088
|
+
show : bool, default True
|
|
1089
|
+
If ``True`` and ``as_json=False``, display the figure immediately.
|
|
1090
|
+
template : str, default "plotly_dark"
|
|
1091
|
+
Plotly template name.
|
|
1092
|
+
**kwargs
|
|
1093
|
+
Additional arguments passed to
|
|
1094
|
+
:meth:`FinBrainClient.screener.reddit_mentions`.
|
|
1095
|
+
|
|
1096
|
+
Returns
|
|
1097
|
+
-------
|
|
1098
|
+
plotly.graph_objects.Figure or str or None
|
|
1099
|
+
"""
|
|
1100
|
+
data = self._fb.screener.reddit_mentions(
|
|
1101
|
+
market=market,
|
|
1102
|
+
region=region,
|
|
1103
|
+
limit=limit,
|
|
1104
|
+
**kwargs,
|
|
1105
|
+
)
|
|
1106
|
+
|
|
1107
|
+
rows = data if isinstance(data, list) else data.get("data", [])
|
|
1108
|
+
if not rows:
|
|
1109
|
+
raise ValueError("No screener data returned")
|
|
1110
|
+
|
|
1111
|
+
# Keep only the latest snapshot per ticker
|
|
1112
|
+
latest: dict[str, dict] = {}
|
|
1113
|
+
for row in rows:
|
|
1114
|
+
sym = row["symbol"]
|
|
1115
|
+
if sym not in latest or row["date"] > latest[sym]["date"]:
|
|
1116
|
+
latest[sym] = row
|
|
1117
|
+
|
|
1118
|
+
# Sort by totalMentions descending, take top N
|
|
1119
|
+
ranked = sorted(latest.values(), key=lambda r: r.get("totalMentions", 0), reverse=True)
|
|
1120
|
+
top = ranked[:top_n]
|
|
1121
|
+
|
|
1122
|
+
# Reverse so the highest-mentioned ticker is at the top of the chart
|
|
1123
|
+
top = list(reversed(top))
|
|
1124
|
+
|
|
1125
|
+
symbols = [r["symbol"] for r in top]
|
|
1126
|
+
|
|
1127
|
+
# Collect all subreddit names across top tickers
|
|
1128
|
+
all_subs: set[str] = set()
|
|
1129
|
+
for r in top:
|
|
1130
|
+
all_subs.update(r.get("subreddits", {}).keys())
|
|
1131
|
+
|
|
1132
|
+
fig = go.Figure(
|
|
1133
|
+
layout=dict(
|
|
1134
|
+
template=template,
|
|
1135
|
+
title=f"Reddit Mentions · Top {len(top)} Tickers",
|
|
1136
|
+
xaxis_title="Mentions",
|
|
1137
|
+
hovermode="y unified",
|
|
1138
|
+
barmode="stack",
|
|
1139
|
+
)
|
|
1140
|
+
)
|
|
1141
|
+
|
|
1142
|
+
for subreddit in sorted(all_subs):
|
|
1143
|
+
values = [r.get("subreddits", {}).get(subreddit, 0) for r in top]
|
|
1144
|
+
fig.add_bar(
|
|
1145
|
+
name=f"r/{subreddit}",
|
|
1146
|
+
y=symbols,
|
|
1147
|
+
x=values,
|
|
1148
|
+
orientation="h",
|
|
1149
|
+
hovertemplate=f"r/{subreddit}: " + "%{x:,}<extra></extra>",
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
if show and not as_json:
|
|
1153
|
+
fig.show()
|
|
1154
|
+
return None
|
|
1155
|
+
return fig.to_json() if as_json else fig
|
|
1156
|
+
|
|
910
1157
|
# --------------------------------------------------------------------- #
|
|
911
1158
|
# Helper methods #
|
|
912
1159
|
# --------------------------------------------------------------------- #
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finbrain-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
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
|
|
@@ -99,6 +99,12 @@ fb.corporate_lobbying.ticker("AAPL",
|
|
|
99
99
|
date_to="2025-06-30",
|
|
100
100
|
as_dataframe=True)
|
|
101
101
|
|
|
102
|
+
# ---------- reddit mentions ----------
|
|
103
|
+
fb.reddit_mentions.ticker("TSLA",
|
|
104
|
+
date_from="2026-03-01",
|
|
105
|
+
date_to="2026-03-17",
|
|
106
|
+
as_dataframe=True)
|
|
107
|
+
|
|
102
108
|
# ---------- insider transactions ----------
|
|
103
109
|
fb.insider_transactions.ticker("AMZN", as_dataframe=True)
|
|
104
110
|
|
|
@@ -130,6 +136,7 @@ fb.news.ticker("AMZN", limit=20, as_dataframe=True)
|
|
|
130
136
|
fb.screener.sentiment(market="S&P 500", as_dataframe=True)
|
|
131
137
|
fb.screener.predictions_daily(limit=100, as_dataframe=True)
|
|
132
138
|
fb.screener.insider_trading(limit=50)
|
|
139
|
+
fb.screener.reddit_mentions(limit=100, as_dataframe=True)
|
|
133
140
|
|
|
134
141
|
# ---------- recent data ----------
|
|
135
142
|
fb.recent.news(limit=100, as_dataframe=True)
|
|
@@ -244,6 +251,21 @@ fb.plot.corporate_lobbying("AAPL",
|
|
|
244
251
|
price_data=price_df,
|
|
245
252
|
date_from="2024-01-01",
|
|
246
253
|
date_to="2025-06-30")
|
|
254
|
+
|
|
255
|
+
# Plot Reddit mentions (stacked bars per subreddit) on your price chart
|
|
256
|
+
fb.plot.reddit_mentions("TSLA",
|
|
257
|
+
price_data=price_df,
|
|
258
|
+
date_from="2026-03-01",
|
|
259
|
+
date_to="2026-03-17")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# ---------- Reddit Mentions Screener Chart (no price data needed) ----------
|
|
264
|
+
# Stacked horizontal bar chart of top 15 most mentioned tickers
|
|
265
|
+
fb.plot.reddit_mentions_top(market="S&P 500")
|
|
266
|
+
|
|
267
|
+
# Customize the number of tickers shown
|
|
268
|
+
fb.plot.reddit_mentions_top(top_n=10, region="US")
|
|
247
269
|
```
|
|
248
270
|
|
|
249
271
|
**Price Data Requirements:**
|
|
@@ -289,12 +311,14 @@ fb = FinBrainClient() # reads from FINBRAIN_API_KEY env var
|
|
|
289
311
|
| House trades | `client.house_trades.ticker()` | `/congress/house/{SYMBOL}` |
|
|
290
312
|
| Senate trades | `client.senate_trades.ticker()` | `/congress/senate/{SYMBOL}` |
|
|
291
313
|
| Corporate lobbying | `client.corporate_lobbying.ticker()` | `/lobbying/{SYMBOL}` |
|
|
314
|
+
| Reddit mentions | `client.reddit_mentions.ticker()` | `/reddit-mentions/{SYMBOL}` |
|
|
292
315
|
| Insider transactions | `client.insider_transactions.ticker()` | `/insider-trading/{SYMBOL}` |
|
|
293
316
|
| LinkedIn | `client.linkedin_data.ticker()` | `/linkedin/{SYMBOL}` |
|
|
294
317
|
| Options – Put/Call | `client.options.put_call()` | `/put-call-ratio/{SYMBOL}` |
|
|
295
318
|
| Screener | `client.screener.sentiment()` | `/screener/sentiment` |
|
|
296
319
|
| | `client.screener.predictions_daily()` | `/screener/predictions/daily` |
|
|
297
320
|
| | `client.screener.insider_trading()` | `/screener/insider-trading` |
|
|
321
|
+
| | `client.screener.reddit_mentions()` | `/screener/reddit-mentions` |
|
|
298
322
|
| | ... and 8 more screener methods | |
|
|
299
323
|
| Recent | `client.recent.news()` | `/recent/news` |
|
|
300
324
|
| | `client.recent.analyst_ratings()` | `/recent/analyst-ratings` |
|