finbrain-python 0.1.2__tar.gz → 0.1.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.1.2 → finbrain_python-0.1.4}/PKG-INFO +55 -18
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/README.md +52 -17
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/pyproject.toml +2 -0
- finbrain_python-0.1.4/src/finbrain/__init__.py +14 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/client.py +4 -0
- finbrain_python-0.1.4/src/finbrain/plotting.py +504 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain_python.egg-info/PKG-INFO +55 -18
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain_python.egg-info/SOURCES.txt +1 -3
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain_python.egg-info/requires.txt +2 -0
- finbrain_python-0.1.2/cli.py +0 -0
- finbrain_python-0.1.2/models.py +0 -0
- finbrain_python-0.1.2/src/finbrain/__init__.py +0 -12
- finbrain_python-0.1.2/utils.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/.gitattributes +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/.github/workflows/ci.yml +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/.github/workflows/release.yml +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/.gitignore +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/LICENSE +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/setup.cfg +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/aio/__init__.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/aio/client.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/__init__.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/analyst_ratings.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/app_ratings.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/available.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/house_trades.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/insider_transactions.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/linkedin_data.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/options.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/predictions.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/endpoints/sentiments.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain/exceptions.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain_python.egg-info/dependency_links.txt +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/src/finbrain_python.egg-info/top_level.txt +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/__init__.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/conftest.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_analyst_ratings.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_app_ratings.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_available.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_house_trades.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_insider_transactions.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_linkedin_data.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_options.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_predictions.py +0 -0
- {finbrain_python-0.1.2 → finbrain_python-0.1.4}/tests/test_sentiments.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finbrain-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.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
|
|
@@ -10,6 +10,8 @@ License-File: LICENSE
|
|
|
10
10
|
Requires-Dist: requests>=2.31
|
|
11
11
|
Requires-Dist: typer[all]>=0.12
|
|
12
12
|
Requires-Dist: pandas>=2.2
|
|
13
|
+
Requires-Dist: plotly>=6.0
|
|
14
|
+
Requires-Dist: numpy>=1.22.4
|
|
13
15
|
Provides-Extra: dev
|
|
14
16
|
Requires-Dist: pytest; extra == "dev"
|
|
15
17
|
Requires-Dist: responses; extra == "dev"
|
|
@@ -27,7 +29,7 @@ Dynamic: license-file
|
|
|
27
29
|
**Official Python client** for the [FinBrain API](https://docs.finbrain.tech).
|
|
28
30
|
Fetch deep-learning price predictions, sentiment scores, insider trades, LinkedIn metrics, options data and more — with a single import.
|
|
29
31
|
|
|
30
|
-
*Python ≥ 3.9 • requests &
|
|
32
|
+
*Python ≥ 3.9 • requests, pandas, numpy & plotly • asyncio optional.*
|
|
31
33
|
|
|
32
34
|
---
|
|
33
35
|
|
|
@@ -60,19 +62,19 @@ fb.available.tickers("daily", as_dataframe=True)
|
|
|
60
62
|
# ---------- app ratings ----------
|
|
61
63
|
fb.app_ratings.ticker("S&P 500", "AMZN",
|
|
62
64
|
date_from="2025-01-01",
|
|
63
|
-
date_to="2025-
|
|
65
|
+
date_to="2025-06-30",
|
|
64
66
|
as_dataframe=True)
|
|
65
67
|
|
|
66
68
|
# ---------- analyst ratings ----------
|
|
67
69
|
fb.analyst_ratings.ticker("S&P 500", "AMZN",
|
|
68
70
|
date_from="2025-01-01",
|
|
69
|
-
date_to="2025-
|
|
71
|
+
date_to="2025-06-30",
|
|
70
72
|
as_dataframe=True)
|
|
71
73
|
|
|
72
74
|
# ---------- house trades ----------
|
|
73
75
|
fb.house_trades.ticker("S&P 500", "AMZN",
|
|
74
76
|
date_from="2025-01-01",
|
|
75
|
-
date_to="2025-
|
|
77
|
+
date_to="2025-06-30",
|
|
76
78
|
as_dataframe=True)
|
|
77
79
|
|
|
78
80
|
# ---------- insider transactions ----------
|
|
@@ -81,13 +83,13 @@ fb.insider_transactions.ticker("S&P 500", "AMZN", as_dataframe=True)
|
|
|
81
83
|
# ---------- LinkedIn metrics ----------
|
|
82
84
|
fb.linkedin_data.ticker("S&P 500", "AMZN",
|
|
83
85
|
date_from="2025-01-01",
|
|
84
|
-
date_to="2025-
|
|
86
|
+
date_to="2025-06-30",
|
|
85
87
|
as_dataframe=True)
|
|
86
88
|
|
|
87
89
|
# ---------- options put/call ----------
|
|
88
90
|
fb.options.put_call("S&P 500", "AMZN",
|
|
89
91
|
date_from="2025-01-01",
|
|
90
|
-
date_to="2025-
|
|
92
|
+
date_to="2025-06-30",
|
|
91
93
|
as_dataframe=True)
|
|
92
94
|
|
|
93
95
|
# ---------- price predictions ----------
|
|
@@ -97,10 +99,43 @@ fb.predictions.ticker("AMZN", as_dataframe=True) # single ticker
|
|
|
97
99
|
# ---------- news sentiment ----------
|
|
98
100
|
fb.sentiments.ticker("S&P 500", "AMZN",
|
|
99
101
|
date_from="2025-01-01",
|
|
100
|
-
date_to="2025-
|
|
102
|
+
date_to="2025-06-30",
|
|
101
103
|
as_dataframe=True)
|
|
102
104
|
```
|
|
103
105
|
|
|
106
|
+
## 📈 Plotting
|
|
107
|
+
|
|
108
|
+
Plot helpers in a nutshell
|
|
109
|
+
|
|
110
|
+
- `show` – defaults to True, so the chart appears immediately.
|
|
111
|
+
|
|
112
|
+
- `as_json=True` – skips display and returns the figure as a Plotly-JSON string, ready to embed elsewhere.
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
# ---------- App Ratings Chart - Apple App Store or Google Play Store ----------
|
|
116
|
+
fb.plot.app_ratings("S&P 500", "AMZN",
|
|
117
|
+
store="app", # "play" for Google Play Store
|
|
118
|
+
date_from="2025-01-01",
|
|
119
|
+
date_to="2025-06-30")
|
|
120
|
+
|
|
121
|
+
# ---------- LinkedIn Metrics Chart ----------
|
|
122
|
+
fb.plot.linkedin("S&P 500", "AMZN",
|
|
123
|
+
date_from="2025-01-01", date_to="2025-06-30")
|
|
124
|
+
|
|
125
|
+
# ---------- Put-Call Ratio Chart ----------
|
|
126
|
+
fb.plot.options("S&P 500", "AMZN",
|
|
127
|
+
kind="put_call",
|
|
128
|
+
date_from="2025-01-01", date_to="2025-06-30")
|
|
129
|
+
|
|
130
|
+
# ---------- Predictions Chart ----------
|
|
131
|
+
fb.plot.predictions("AMZN")
|
|
132
|
+
|
|
133
|
+
# ---------- Sentiments Chart ----------
|
|
134
|
+
fb.plot.sentiments("S&P 500", "AMZN",
|
|
135
|
+
date_from="2025-01-01",
|
|
136
|
+
date_to="2025-06-30")
|
|
137
|
+
```
|
|
138
|
+
|
|
104
139
|
## 🔑 Authentication
|
|
105
140
|
|
|
106
141
|
To call the API you need an **API key**, obtained by purchasing a **FinBrain API subscription**.
|
|
@@ -115,20 +150,20 @@ from finbrain import FinBrainClient
|
|
|
115
150
|
fb = FinBrainClient(api_key="YOUR_KEY")
|
|
116
151
|
```
|
|
117
152
|
|
|
118
|
-
### Async(currently under development)
|
|
153
|
+
### Async (currently under development)
|
|
119
154
|
|
|
120
155
|
```
|
|
121
156
|
import asyncio, os
|
|
122
157
|
from finbrain.aio import FinBrainAsyncClient async
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
data = await fb.sentiments.ticker("
|
|
158
|
+
def main():
|
|
159
|
+
async with FinBrainAsyncClient(api_key=os.getenv("FINBRAIN_API_KEY")) as fb:
|
|
160
|
+
data = await fb.sentiments.ticker("S&P 500", "AMZN")
|
|
126
161
|
print(list(data["sentimentAnalysis"].items())[:3])
|
|
127
162
|
|
|
128
|
-
asyncio.run(main())
|
|
163
|
+
asyncio.run(main())
|
|
129
164
|
```
|
|
130
165
|
|
|
131
|
-
### CLI(currently under development)
|
|
166
|
+
### CLI (currently under development)
|
|
132
167
|
|
|
133
168
|
```
|
|
134
169
|
export FINBRAIN_API_KEY=your_key
|
|
@@ -163,7 +198,7 @@ from finbrain.exceptions import BadRequest
|
|
|
163
198
|
try:
|
|
164
199
|
fb.predictions.ticker("MSFT", prediction_type="weekly")
|
|
165
200
|
except BadRequest as exc:
|
|
166
|
-
|
|
201
|
+
print("Invalid parameters:", exc)
|
|
167
202
|
```
|
|
168
203
|
|
|
169
204
|
| HTTP status | Exception class | Meaning |
|
|
@@ -184,7 +219,7 @@ except BadRequest as exc:
|
|
|
184
219
|
- Version auto-generated from Git tags (setuptools-scm)
|
|
185
220
|
|
|
186
221
|
```
|
|
187
|
-
git tag -a
|
|
222
|
+
git tag -a 0.2.0 -m "Add options.chain endpoint"
|
|
188
223
|
git push --tags # GitHub Actions builds & uploads to PyPI
|
|
189
224
|
```
|
|
190
225
|
|
|
@@ -193,11 +228,13 @@ git push --tags # GitHub Actions builds & uploads to PyPI
|
|
|
193
228
|
## 🧑💻 Development
|
|
194
229
|
|
|
195
230
|
```
|
|
196
|
-
git clone https://github.com/finbrain-tech/finbrain-python
|
|
231
|
+
git clone https://github.com/finbrain-tech/finbrain-python
|
|
232
|
+
cd finbrain-python
|
|
197
233
|
python -m venv .venv && source .venv/bin/activate
|
|
198
234
|
pip install -e .[dev]
|
|
199
235
|
|
|
200
|
-
ruff check . # lint / format
|
|
236
|
+
ruff check . # lint / format
|
|
237
|
+
pytest -q # unit tests (mocked)
|
|
201
238
|
```
|
|
202
239
|
|
|
203
240
|
### Live integration test(currently under development)
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
**Official Python client** for the [FinBrain API](https://docs.finbrain.tech).
|
|
8
8
|
Fetch deep-learning price predictions, sentiment scores, insider trades, LinkedIn metrics, options data and more — with a single import.
|
|
9
9
|
|
|
10
|
-
*Python ≥ 3.9 • requests &
|
|
10
|
+
*Python ≥ 3.9 • requests, pandas, numpy & plotly • asyncio optional.*
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -40,19 +40,19 @@ fb.available.tickers("daily", as_dataframe=True)
|
|
|
40
40
|
# ---------- app ratings ----------
|
|
41
41
|
fb.app_ratings.ticker("S&P 500", "AMZN",
|
|
42
42
|
date_from="2025-01-01",
|
|
43
|
-
date_to="2025-
|
|
43
|
+
date_to="2025-06-30",
|
|
44
44
|
as_dataframe=True)
|
|
45
45
|
|
|
46
46
|
# ---------- analyst ratings ----------
|
|
47
47
|
fb.analyst_ratings.ticker("S&P 500", "AMZN",
|
|
48
48
|
date_from="2025-01-01",
|
|
49
|
-
date_to="2025-
|
|
49
|
+
date_to="2025-06-30",
|
|
50
50
|
as_dataframe=True)
|
|
51
51
|
|
|
52
52
|
# ---------- house trades ----------
|
|
53
53
|
fb.house_trades.ticker("S&P 500", "AMZN",
|
|
54
54
|
date_from="2025-01-01",
|
|
55
|
-
date_to="2025-
|
|
55
|
+
date_to="2025-06-30",
|
|
56
56
|
as_dataframe=True)
|
|
57
57
|
|
|
58
58
|
# ---------- insider transactions ----------
|
|
@@ -61,13 +61,13 @@ fb.insider_transactions.ticker("S&P 500", "AMZN", as_dataframe=True)
|
|
|
61
61
|
# ---------- LinkedIn metrics ----------
|
|
62
62
|
fb.linkedin_data.ticker("S&P 500", "AMZN",
|
|
63
63
|
date_from="2025-01-01",
|
|
64
|
-
date_to="2025-
|
|
64
|
+
date_to="2025-06-30",
|
|
65
65
|
as_dataframe=True)
|
|
66
66
|
|
|
67
67
|
# ---------- options put/call ----------
|
|
68
68
|
fb.options.put_call("S&P 500", "AMZN",
|
|
69
69
|
date_from="2025-01-01",
|
|
70
|
-
date_to="2025-
|
|
70
|
+
date_to="2025-06-30",
|
|
71
71
|
as_dataframe=True)
|
|
72
72
|
|
|
73
73
|
# ---------- price predictions ----------
|
|
@@ -77,10 +77,43 @@ fb.predictions.ticker("AMZN", as_dataframe=True) # single ticker
|
|
|
77
77
|
# ---------- news sentiment ----------
|
|
78
78
|
fb.sentiments.ticker("S&P 500", "AMZN",
|
|
79
79
|
date_from="2025-01-01",
|
|
80
|
-
date_to="2025-
|
|
80
|
+
date_to="2025-06-30",
|
|
81
81
|
as_dataframe=True)
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
## 📈 Plotting
|
|
85
|
+
|
|
86
|
+
Plot helpers in a nutshell
|
|
87
|
+
|
|
88
|
+
- `show` – defaults to True, so the chart appears immediately.
|
|
89
|
+
|
|
90
|
+
- `as_json=True` – skips display and returns the figure as a Plotly-JSON string, ready to embed elsewhere.
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
# ---------- App Ratings Chart - Apple App Store or Google Play Store ----------
|
|
94
|
+
fb.plot.app_ratings("S&P 500", "AMZN",
|
|
95
|
+
store="app", # "play" for Google Play Store
|
|
96
|
+
date_from="2025-01-01",
|
|
97
|
+
date_to="2025-06-30")
|
|
98
|
+
|
|
99
|
+
# ---------- LinkedIn Metrics Chart ----------
|
|
100
|
+
fb.plot.linkedin("S&P 500", "AMZN",
|
|
101
|
+
date_from="2025-01-01", date_to="2025-06-30")
|
|
102
|
+
|
|
103
|
+
# ---------- Put-Call Ratio Chart ----------
|
|
104
|
+
fb.plot.options("S&P 500", "AMZN",
|
|
105
|
+
kind="put_call",
|
|
106
|
+
date_from="2025-01-01", date_to="2025-06-30")
|
|
107
|
+
|
|
108
|
+
# ---------- Predictions Chart ----------
|
|
109
|
+
fb.plot.predictions("AMZN")
|
|
110
|
+
|
|
111
|
+
# ---------- Sentiments Chart ----------
|
|
112
|
+
fb.plot.sentiments("S&P 500", "AMZN",
|
|
113
|
+
date_from="2025-01-01",
|
|
114
|
+
date_to="2025-06-30")
|
|
115
|
+
```
|
|
116
|
+
|
|
84
117
|
## 🔑 Authentication
|
|
85
118
|
|
|
86
119
|
To call the API you need an **API key**, obtained by purchasing a **FinBrain API subscription**.
|
|
@@ -95,20 +128,20 @@ from finbrain import FinBrainClient
|
|
|
95
128
|
fb = FinBrainClient(api_key="YOUR_KEY")
|
|
96
129
|
```
|
|
97
130
|
|
|
98
|
-
### Async(currently under development)
|
|
131
|
+
### Async (currently under development)
|
|
99
132
|
|
|
100
133
|
```
|
|
101
134
|
import asyncio, os
|
|
102
135
|
from finbrain.aio import FinBrainAsyncClient async
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
data = await fb.sentiments.ticker("
|
|
136
|
+
def main():
|
|
137
|
+
async with FinBrainAsyncClient(api_key=os.getenv("FINBRAIN_API_KEY")) as fb:
|
|
138
|
+
data = await fb.sentiments.ticker("S&P 500", "AMZN")
|
|
106
139
|
print(list(data["sentimentAnalysis"].items())[:3])
|
|
107
140
|
|
|
108
|
-
asyncio.run(main())
|
|
141
|
+
asyncio.run(main())
|
|
109
142
|
```
|
|
110
143
|
|
|
111
|
-
### CLI(currently under development)
|
|
144
|
+
### CLI (currently under development)
|
|
112
145
|
|
|
113
146
|
```
|
|
114
147
|
export FINBRAIN_API_KEY=your_key
|
|
@@ -143,7 +176,7 @@ from finbrain.exceptions import BadRequest
|
|
|
143
176
|
try:
|
|
144
177
|
fb.predictions.ticker("MSFT", prediction_type="weekly")
|
|
145
178
|
except BadRequest as exc:
|
|
146
|
-
|
|
179
|
+
print("Invalid parameters:", exc)
|
|
147
180
|
```
|
|
148
181
|
|
|
149
182
|
| HTTP status | Exception class | Meaning |
|
|
@@ -164,7 +197,7 @@ except BadRequest as exc:
|
|
|
164
197
|
- Version auto-generated from Git tags (setuptools-scm)
|
|
165
198
|
|
|
166
199
|
```
|
|
167
|
-
git tag -a
|
|
200
|
+
git tag -a 0.2.0 -m "Add options.chain endpoint"
|
|
168
201
|
git push --tags # GitHub Actions builds & uploads to PyPI
|
|
169
202
|
```
|
|
170
203
|
|
|
@@ -173,11 +206,13 @@ git push --tags # GitHub Actions builds & uploads to PyPI
|
|
|
173
206
|
## 🧑💻 Development
|
|
174
207
|
|
|
175
208
|
```
|
|
176
|
-
git clone https://github.com/finbrain-tech/finbrain-python
|
|
209
|
+
git clone https://github.com/finbrain-tech/finbrain-python
|
|
210
|
+
cd finbrain-python
|
|
177
211
|
python -m venv .venv && source .venv/bin/activate
|
|
178
212
|
pip install -e .[dev]
|
|
179
213
|
|
|
180
|
-
ruff check . # lint / format
|
|
214
|
+
ruff check . # lint / format
|
|
215
|
+
pytest -q # unit tests (mocked)
|
|
181
216
|
```
|
|
182
217
|
|
|
183
218
|
### Live integration test(currently under development)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""FinBrain Python SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import PackageNotFoundError, version as _v
|
|
6
|
+
|
|
7
|
+
try: # installed (wheel / sdist / -e)
|
|
8
|
+
__version__ = _v("finbrain-python") # ← distribution name on PyPI
|
|
9
|
+
except PackageNotFoundError: # fresh git clone, no install
|
|
10
|
+
__version__ = "0.0.0.dev0"
|
|
11
|
+
|
|
12
|
+
from .client import FinBrainClient
|
|
13
|
+
|
|
14
|
+
__all__ = ["FinBrainClient", "__version__"]
|
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, Optional
|
|
|
6
6
|
import requests
|
|
7
7
|
from urllib.parse import urljoin
|
|
8
8
|
|
|
9
|
+
from .plotting import _PlotNamespace
|
|
9
10
|
from .exceptions import http_error_to_exception, InvalidResponse
|
|
10
11
|
from . import __version__
|
|
11
12
|
|
|
@@ -52,6 +53,9 @@ class FinBrainClient:
|
|
|
52
53
|
self.timeout = timeout
|
|
53
54
|
self.retries = retries
|
|
54
55
|
|
|
56
|
+
# expose plotting under .plot
|
|
57
|
+
self.plot = _PlotNamespace(self)
|
|
58
|
+
|
|
55
59
|
# wire endpoint helpers
|
|
56
60
|
self.available = AvailableAPI(self)
|
|
57
61
|
self.predictions = PredictionsAPI(self)
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
# src/finbrain/plotting.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Union, TYPE_CHECKING
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import plotly.graph_objects as go
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING: # imported only by static-type tools
|
|
9
|
+
from .client import FinBrainClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _PlotNamespace:
|
|
13
|
+
"""
|
|
14
|
+
Internal helper that hangs off FinBrainClient as `client.plot`.
|
|
15
|
+
Each public method should return either a Plotly Figure or a JSON string.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, parent: "FinBrainClient"):
|
|
19
|
+
self._fb = parent # keep a reference to the main client
|
|
20
|
+
|
|
21
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
# App-ratings plot • bars = counts • lines = scores
|
|
23
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
def app_ratings(
|
|
25
|
+
self,
|
|
26
|
+
market: str,
|
|
27
|
+
ticker: str,
|
|
28
|
+
*,
|
|
29
|
+
store: str = "play",
|
|
30
|
+
date_from: str | None = None,
|
|
31
|
+
date_to: str | None = None,
|
|
32
|
+
as_json: bool = False,
|
|
33
|
+
show: bool = True,
|
|
34
|
+
template: str = "plotly_dark",
|
|
35
|
+
**kwargs,
|
|
36
|
+
):
|
|
37
|
+
"""
|
|
38
|
+
Plot ratings for a single mobile store (Google Play **or** Apple App Store).
|
|
39
|
+
|
|
40
|
+
Bars → ratings count • primary y-axis (auto-scaled)
|
|
41
|
+
Line → average score • secondary y-axis (auto-scaled within 0-5)
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
store : {'play', 'app'}, default 'play'
|
|
46
|
+
Which store to visualise.
|
|
47
|
+
Other args/kwargs identical to the other plotting wrappers.
|
|
48
|
+
"""
|
|
49
|
+
# 1) pull data
|
|
50
|
+
df = self._fb.app_ratings.ticker(
|
|
51
|
+
market,
|
|
52
|
+
ticker,
|
|
53
|
+
date_from=date_from,
|
|
54
|
+
date_to=date_to,
|
|
55
|
+
as_dataframe=True,
|
|
56
|
+
**kwargs,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# 2) pick columns & colours
|
|
60
|
+
s = store.lower()
|
|
61
|
+
if s in ("play", "playstore", "google"):
|
|
62
|
+
count_col, score_col = "playStoreRatingsCount", "playStoreScore"
|
|
63
|
+
count_name, score_name = "Play Store Ratings Count", "Play Store Score"
|
|
64
|
+
count_color, score_color = "rgba(0,190,0,0.65)", "#02d2ff"
|
|
65
|
+
elif s in ("app", "appstore", "apple"):
|
|
66
|
+
count_col, score_col = "appStoreRatingsCount", "appStoreScore"
|
|
67
|
+
count_name, score_name = "App Store Ratings Count", "App Store Score"
|
|
68
|
+
count_color, score_color = "rgba(0,190,0,0.65)", "#02d2ff"
|
|
69
|
+
else:
|
|
70
|
+
raise ValueError("store must be 'play' or 'app'")
|
|
71
|
+
|
|
72
|
+
# 3) dynamic axis ranges
|
|
73
|
+
max_cnt = float(df[count_col].max())
|
|
74
|
+
min_cnt = float(df[count_col].min())
|
|
75
|
+
|
|
76
|
+
# raw span; fall back to max_cnt when all bars are equal
|
|
77
|
+
span = max_cnt - min_cnt
|
|
78
|
+
pad = (span if span else max_cnt) * 0.10 # 10 % of the data spread
|
|
79
|
+
|
|
80
|
+
cnt_lower = max(0.0, min_cnt - pad)
|
|
81
|
+
cnt_upper = max_cnt + pad
|
|
82
|
+
|
|
83
|
+
# scores (secondary axis) – same as before
|
|
84
|
+
score_min, score_max = float(df[score_col].min()), float(df[score_col].max())
|
|
85
|
+
pad = 0.25
|
|
86
|
+
score_lower = max(0, score_min - pad)
|
|
87
|
+
score_upper = min(5, score_max + pad)
|
|
88
|
+
|
|
89
|
+
# 4) build figure
|
|
90
|
+
fig = go.Figure(
|
|
91
|
+
layout=dict(
|
|
92
|
+
template=template,
|
|
93
|
+
title=f"{score_name.split()[0]} · {ticker}",
|
|
94
|
+
hovermode="x unified",
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
fig.add_bar(
|
|
99
|
+
name=count_name, x=df.index, y=df[count_col], marker_color=count_color
|
|
100
|
+
)
|
|
101
|
+
fig.add_scatter(
|
|
102
|
+
name=score_name,
|
|
103
|
+
x=df.index,
|
|
104
|
+
y=df[score_col],
|
|
105
|
+
mode="lines",
|
|
106
|
+
line=dict(width=2, color=score_color),
|
|
107
|
+
yaxis="y2",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
fig.update_layout(
|
|
111
|
+
xaxis_title="Date",
|
|
112
|
+
yaxis=dict(
|
|
113
|
+
title="Ratings Count",
|
|
114
|
+
range=[cnt_lower, cnt_upper],
|
|
115
|
+
fixedrange=True,
|
|
116
|
+
showgrid=True,
|
|
117
|
+
),
|
|
118
|
+
yaxis2=dict(
|
|
119
|
+
title="Score",
|
|
120
|
+
overlaying="y",
|
|
121
|
+
side="right",
|
|
122
|
+
range=[score_lower, score_upper],
|
|
123
|
+
fixedrange=True,
|
|
124
|
+
showgrid=False,
|
|
125
|
+
zeroline=False,
|
|
126
|
+
),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# 5) show / return
|
|
130
|
+
if show and not as_json:
|
|
131
|
+
fig.show()
|
|
132
|
+
return None
|
|
133
|
+
return fig.to_json() if as_json else fig
|
|
134
|
+
|
|
135
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
136
|
+
# LinkedIn plot • bars = employeeCount • line = followersCount
|
|
137
|
+
# ────────────────────────────────────────────────────────────────────────────
|
|
138
|
+
def linkedin(
|
|
139
|
+
self,
|
|
140
|
+
market: str,
|
|
141
|
+
ticker: str,
|
|
142
|
+
*,
|
|
143
|
+
date_from: str | None = None,
|
|
144
|
+
date_to: str | None = None,
|
|
145
|
+
as_json: bool = False,
|
|
146
|
+
show: bool = True,
|
|
147
|
+
template: str = "plotly_dark",
|
|
148
|
+
**kwargs,
|
|
149
|
+
):
|
|
150
|
+
"""
|
|
151
|
+
Plot LinkedIn company metrics.
|
|
152
|
+
|
|
153
|
+
* **Bars** → ``employeeCount`` (primary y-axis)
|
|
154
|
+
* **Line** → ``followersCount`` (secondary y-axis)
|
|
155
|
+
"""
|
|
156
|
+
df = self._fb.linkedin_data.ticker(
|
|
157
|
+
market,
|
|
158
|
+
ticker,
|
|
159
|
+
date_from=date_from,
|
|
160
|
+
date_to=date_to,
|
|
161
|
+
as_dataframe=True,
|
|
162
|
+
**kwargs,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
fig = go.Figure(
|
|
166
|
+
layout=dict(
|
|
167
|
+
template=template,
|
|
168
|
+
title=f"LinkedIn Metrics · {ticker}",
|
|
169
|
+
hovermode="x unified",
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# employees (bars)
|
|
174
|
+
fig.add_bar(
|
|
175
|
+
name="Employees",
|
|
176
|
+
x=df.index,
|
|
177
|
+
y=df["employeeCount"],
|
|
178
|
+
marker_color="rgba(0,190,0,0.6)",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# followers (line on secondary axis)
|
|
182
|
+
fig.add_scatter(
|
|
183
|
+
name="Followers",
|
|
184
|
+
x=df.index,
|
|
185
|
+
y=df["followersCount"],
|
|
186
|
+
mode="lines",
|
|
187
|
+
line=dict(width=2, color="#f9c80e"),
|
|
188
|
+
yaxis="y2",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
fig.update_layout(
|
|
192
|
+
xaxis_title="Date",
|
|
193
|
+
yaxis=dict(title="Employee Count", showgrid=True),
|
|
194
|
+
yaxis2=dict(
|
|
195
|
+
title="Follower Count",
|
|
196
|
+
overlaying="y",
|
|
197
|
+
side="right",
|
|
198
|
+
showgrid=False,
|
|
199
|
+
zeroline=False,
|
|
200
|
+
),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if show and not as_json:
|
|
204
|
+
fig.show()
|
|
205
|
+
return None
|
|
206
|
+
return fig.to_json() if as_json else fig
|
|
207
|
+
|
|
208
|
+
# --------------------------------------------------------------------- #
|
|
209
|
+
# Sentiment → green/red bar #
|
|
210
|
+
# --------------------------------------------------------------------- #
|
|
211
|
+
def sentiments(
|
|
212
|
+
self,
|
|
213
|
+
market: str,
|
|
214
|
+
ticker: str,
|
|
215
|
+
*,
|
|
216
|
+
date_from: str | None = None,
|
|
217
|
+
date_to: str | None = None,
|
|
218
|
+
as_json: bool = False,
|
|
219
|
+
show: bool = True,
|
|
220
|
+
template: str = "plotly_dark",
|
|
221
|
+
**kw,
|
|
222
|
+
) -> Union[go.Figure, str]:
|
|
223
|
+
"""
|
|
224
|
+
Visualise FinBrain news-sentiment scores for a single ticker.
|
|
225
|
+
|
|
226
|
+
A green bar represents a non-negative score (bullish news); a red
|
|
227
|
+
bar represents a negative score (bearish news). Bars are plotted on
|
|
228
|
+
the primary y-axis, with dates on the x-axis.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
market : str
|
|
233
|
+
Market identifier (e.g. ``"S&P 500"``).
|
|
234
|
+
ticker : str
|
|
235
|
+
Ticker symbol (e.g. ``"AMZN"``).
|
|
236
|
+
date_from, date_to : str or None, optional
|
|
237
|
+
Inclusive date range in ``YYYY-MM-DD`` format. If omitted,
|
|
238
|
+
FinBrain returns its full available range.
|
|
239
|
+
as_json : bool, default ``False``
|
|
240
|
+
• ``False`` → return a :class:`plotly.graph_objects.Figure`.
|
|
241
|
+
• ``True`` → return ``figure.to_json()`` (``str``).
|
|
242
|
+
show : bool, default ``True``
|
|
243
|
+
If ``True`` *and* ``as_json=False``, immediately display the
|
|
244
|
+
figure via :meth:`plotly.graph_objects.Figure.show`. When
|
|
245
|
+
``as_json=True`` this flag is ignored.
|
|
246
|
+
template : str, default ``"plotly_dark"``
|
|
247
|
+
Name of a built-in Plotly template.
|
|
248
|
+
**kwargs
|
|
249
|
+
Passed straight through to
|
|
250
|
+
:meth:`FinBrainClient.sentiments.ticker`.
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
plotly.graph_objects.Figure or str or None
|
|
255
|
+
*Figure*: when ``as_json=False`` **and** ``show=False``
|
|
256
|
+
*str* : JSON representation when ``as_json=True``
|
|
257
|
+
*None* : when ``show=True`` and the figure is already shown.
|
|
258
|
+
|
|
259
|
+
Examples
|
|
260
|
+
--------
|
|
261
|
+
>>> fb.plot.sentiments("S&P 500", "AMZN",
|
|
262
|
+
... date_from="2025-01-01",
|
|
263
|
+
... date_to="2025-05-31")
|
|
264
|
+
"""
|
|
265
|
+
df: pd.DataFrame = self._fb.sentiments.ticker(
|
|
266
|
+
market,
|
|
267
|
+
ticker,
|
|
268
|
+
date_from=date_from,
|
|
269
|
+
date_to=date_to,
|
|
270
|
+
as_dataframe=True,
|
|
271
|
+
**kw,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# 2) build colour array: green for ≥0, red for <0
|
|
275
|
+
colors = np.where(
|
|
276
|
+
df["sentiment"] >= 0, "rgba(0,190,0,0.8)", "rgba(190,0,0,0.8)"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# 3) bar chart (index on x-axis, sentiment on y-axis)
|
|
280
|
+
fig = go.Figure(
|
|
281
|
+
data=[
|
|
282
|
+
go.Bar(
|
|
283
|
+
x=df.index,
|
|
284
|
+
y=df["sentiment"],
|
|
285
|
+
marker_color=colors,
|
|
286
|
+
hovertemplate="<b>%{x|%Y-%m-%d}</b><br>Sentiment: %{y:.3f}<extra></extra>",
|
|
287
|
+
)
|
|
288
|
+
],
|
|
289
|
+
layout=dict(
|
|
290
|
+
template=template,
|
|
291
|
+
title=f"News Sentiment · {ticker}",
|
|
292
|
+
xaxis_title="Date",
|
|
293
|
+
yaxis_title="Sentiment Score",
|
|
294
|
+
hovermode="x unified",
|
|
295
|
+
),
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
if show and not as_json: # don't “show” raw JSON
|
|
299
|
+
fig.show()
|
|
300
|
+
return None # <- silences the echo
|
|
301
|
+
|
|
302
|
+
return fig.to_json() if as_json else fig
|
|
303
|
+
|
|
304
|
+
# --------------------------------------------------------------------- #
|
|
305
|
+
# Put/Call ratios → stacked bars + ratio line #
|
|
306
|
+
# --------------------------------------------------------------------- #
|
|
307
|
+
def options(
|
|
308
|
+
self,
|
|
309
|
+
market: str,
|
|
310
|
+
ticker: str,
|
|
311
|
+
*,
|
|
312
|
+
kind: str = "put_call",
|
|
313
|
+
date_from=None,
|
|
314
|
+
date_to=None,
|
|
315
|
+
as_json=False,
|
|
316
|
+
show=True,
|
|
317
|
+
template="plotly_dark",
|
|
318
|
+
**kw,
|
|
319
|
+
):
|
|
320
|
+
"""
|
|
321
|
+
Plot options-market activity for a given ticker.
|
|
322
|
+
|
|
323
|
+
Currently implemented ``kind`` values
|
|
324
|
+
--------------------------------------
|
|
325
|
+
``"put_call"`` (default)
|
|
326
|
+
*Stacked* bars of ``callCount`` (green, bottom) and
|
|
327
|
+
``putCount`` (red, top) plus a yellow line for the ``ratio``
|
|
328
|
+
on a secondary y-axis.
|
|
329
|
+
|
|
330
|
+
Additional kinds can be added in future without changing the
|
|
331
|
+
public API—just extend the internal ``elif`` block.
|
|
332
|
+
|
|
333
|
+
Parameters
|
|
334
|
+
----------
|
|
335
|
+
market, ticker : str
|
|
336
|
+
Market identifier and ticker symbol.
|
|
337
|
+
kind : {'put_call', ...}, default ``"put_call"``
|
|
338
|
+
Which visualisation to render. Unknown values raise
|
|
339
|
+
:class:`ValueError`.
|
|
340
|
+
date_from, date_to, as_json, show, template, **kwargs
|
|
341
|
+
Same semantics as :pymeth:`~_PlotNamespace.sentiments`.
|
|
342
|
+
|
|
343
|
+
Returns
|
|
344
|
+
-------
|
|
345
|
+
plotly.graph_objects.Figure or str or None
|
|
346
|
+
As described for :pymeth:`~_PlotNamespace.sentiments`.
|
|
347
|
+
|
|
348
|
+
Examples
|
|
349
|
+
--------
|
|
350
|
+
>>> fb.plot.options("S&P 500", "AMZN",
|
|
351
|
+
... kind="put_call",
|
|
352
|
+
... date_from="2025-01-01",
|
|
353
|
+
... date_to="2025-05-31")
|
|
354
|
+
"""
|
|
355
|
+
if kind == "put_call":
|
|
356
|
+
df = self._fb.options.put_call(
|
|
357
|
+
market,
|
|
358
|
+
ticker,
|
|
359
|
+
date_from=date_from,
|
|
360
|
+
date_to=date_to,
|
|
361
|
+
as_dataframe=True,
|
|
362
|
+
**kw,
|
|
363
|
+
)
|
|
364
|
+
fig = self._plot_put_call(df, ticker, template) # helper below
|
|
365
|
+
|
|
366
|
+
if show and not as_json:
|
|
367
|
+
fig.show()
|
|
368
|
+
return None # <- silences the echo
|
|
369
|
+
|
|
370
|
+
return fig.to_json() if as_json else fig
|
|
371
|
+
|
|
372
|
+
# --------------------------------------------------------------------- #
|
|
373
|
+
# Predictions → price + CI band #
|
|
374
|
+
# --------------------------------------------------------------------- #
|
|
375
|
+
def predictions(
|
|
376
|
+
self,
|
|
377
|
+
ticker: str,
|
|
378
|
+
*,
|
|
379
|
+
prediction_type: str = "daily",
|
|
380
|
+
as_json=False,
|
|
381
|
+
show=True,
|
|
382
|
+
template="plotly_dark",
|
|
383
|
+
**kw,
|
|
384
|
+
):
|
|
385
|
+
"""
|
|
386
|
+
Plot FinBrain price predictions with confidence intervals.
|
|
387
|
+
|
|
388
|
+
The figure shows the predicted price (solid line) and a shaded
|
|
389
|
+
confidence band between the upper and lower bounds.
|
|
390
|
+
|
|
391
|
+
Parameters
|
|
392
|
+
----------
|
|
393
|
+
ticker : str
|
|
394
|
+
Ticker symbol.
|
|
395
|
+
prediction_type : {'daily', 'monthly'}, default ``"daily"``
|
|
396
|
+
Granularity of the prediction data requested from FinBrain.
|
|
397
|
+
as_json, show, template, **kwargs
|
|
398
|
+
Same semantics as :pymeth:`~_PlotNamespace.sentiments`.
|
|
399
|
+
|
|
400
|
+
Returns
|
|
401
|
+
-------
|
|
402
|
+
plotly.graph_objects.Figure or str or None
|
|
403
|
+
As described for :pymeth:`~_PlotNamespace.sentiments`.
|
|
404
|
+
|
|
405
|
+
Examples
|
|
406
|
+
--------
|
|
407
|
+
>>> fb.plot.predictions("AMZN", prediction_type="monthly")
|
|
408
|
+
"""
|
|
409
|
+
df = self._fb.predictions.ticker(
|
|
410
|
+
ticker, prediction_type=prediction_type, as_dataframe=True, **kw
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
fig = go.Figure(
|
|
414
|
+
layout=dict(
|
|
415
|
+
template=template,
|
|
416
|
+
title=f"Price Prediction · {ticker}",
|
|
417
|
+
xaxis_title="Date",
|
|
418
|
+
yaxis_title="Price",
|
|
419
|
+
hovermode="x unified",
|
|
420
|
+
)
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# add the three lines
|
|
424
|
+
fig.add_scatter(x=df.index, y=df["main"], mode="lines", name="Predicted")
|
|
425
|
+
fig.add_scatter(
|
|
426
|
+
x=df.index,
|
|
427
|
+
y=df["upper"],
|
|
428
|
+
mode="lines",
|
|
429
|
+
name="Upper CI",
|
|
430
|
+
line=dict(width=0),
|
|
431
|
+
showlegend=False,
|
|
432
|
+
)
|
|
433
|
+
fig.add_scatter(
|
|
434
|
+
x=df.index,
|
|
435
|
+
y=df["lower"],
|
|
436
|
+
mode="lines",
|
|
437
|
+
name="Lower CI",
|
|
438
|
+
line=dict(width=0),
|
|
439
|
+
fill="tonexty",
|
|
440
|
+
fillcolor="rgba(2,210,255,0.2)",
|
|
441
|
+
showlegend=False,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
if show and not as_json:
|
|
445
|
+
fig.show()
|
|
446
|
+
return None # <- silences the echo
|
|
447
|
+
|
|
448
|
+
return fig.to_json() if as_json else fig
|
|
449
|
+
|
|
450
|
+
# --------------------------------------------------------------------- #
|
|
451
|
+
# TODO: insider_transactions, house_trades, analyst_ratings ... #
|
|
452
|
+
# Add more methods here following the same pattern. #
|
|
453
|
+
# --------------------------------------------------------------------- #
|
|
454
|
+
|
|
455
|
+
@staticmethod
|
|
456
|
+
def _plot_put_call(df, ticker, template):
|
|
457
|
+
fig = go.Figure(
|
|
458
|
+
layout=dict(
|
|
459
|
+
template=template,
|
|
460
|
+
title=f"Options Activity · {ticker}",
|
|
461
|
+
hovermode="x unified",
|
|
462
|
+
barmode="stack",
|
|
463
|
+
)
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# Calls (green) - added first so it sits *below* in the stack
|
|
467
|
+
fig.add_bar(
|
|
468
|
+
name="Calls",
|
|
469
|
+
x=df.index,
|
|
470
|
+
y=df["callCount"],
|
|
471
|
+
marker_color="rgba(0,190,0,0.6)",
|
|
472
|
+
)
|
|
473
|
+
# Puts (red) - added second so it appears *on top* of Calls
|
|
474
|
+
fig.add_bar(
|
|
475
|
+
name="Puts", x=df.index, y=df["putCount"], marker_color="rgba(190,0,0,0.6)"
|
|
476
|
+
)
|
|
477
|
+
# Put/Call ratio line (secondary axis)
|
|
478
|
+
fig.add_scatter(
|
|
479
|
+
name="Put/Call Ratio",
|
|
480
|
+
x=df.index,
|
|
481
|
+
y=df["ratio"],
|
|
482
|
+
mode="lines",
|
|
483
|
+
line=dict(width=2, color="#F9C80E"),
|
|
484
|
+
yaxis="y2",
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# axes & layout tweaks
|
|
488
|
+
fig.update_layout(
|
|
489
|
+
xaxis_title="Date",
|
|
490
|
+
yaxis=dict(
|
|
491
|
+
title="Volume",
|
|
492
|
+
showgrid=True,
|
|
493
|
+
),
|
|
494
|
+
yaxis2=dict(
|
|
495
|
+
title="Ratio",
|
|
496
|
+
overlaying="y",
|
|
497
|
+
side="right",
|
|
498
|
+
rangemode="tozero",
|
|
499
|
+
showgrid=False,
|
|
500
|
+
zeroline=False,
|
|
501
|
+
),
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
return fig
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: finbrain-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.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
|
|
@@ -10,6 +10,8 @@ License-File: LICENSE
|
|
|
10
10
|
Requires-Dist: requests>=2.31
|
|
11
11
|
Requires-Dist: typer[all]>=0.12
|
|
12
12
|
Requires-Dist: pandas>=2.2
|
|
13
|
+
Requires-Dist: plotly>=6.0
|
|
14
|
+
Requires-Dist: numpy>=1.22.4
|
|
13
15
|
Provides-Extra: dev
|
|
14
16
|
Requires-Dist: pytest; extra == "dev"
|
|
15
17
|
Requires-Dist: responses; extra == "dev"
|
|
@@ -27,7 +29,7 @@ Dynamic: license-file
|
|
|
27
29
|
**Official Python client** for the [FinBrain API](https://docs.finbrain.tech).
|
|
28
30
|
Fetch deep-learning price predictions, sentiment scores, insider trades, LinkedIn metrics, options data and more — with a single import.
|
|
29
31
|
|
|
30
|
-
*Python ≥ 3.9 • requests &
|
|
32
|
+
*Python ≥ 3.9 • requests, pandas, numpy & plotly • asyncio optional.*
|
|
31
33
|
|
|
32
34
|
---
|
|
33
35
|
|
|
@@ -60,19 +62,19 @@ fb.available.tickers("daily", as_dataframe=True)
|
|
|
60
62
|
# ---------- app ratings ----------
|
|
61
63
|
fb.app_ratings.ticker("S&P 500", "AMZN",
|
|
62
64
|
date_from="2025-01-01",
|
|
63
|
-
date_to="2025-
|
|
65
|
+
date_to="2025-06-30",
|
|
64
66
|
as_dataframe=True)
|
|
65
67
|
|
|
66
68
|
# ---------- analyst ratings ----------
|
|
67
69
|
fb.analyst_ratings.ticker("S&P 500", "AMZN",
|
|
68
70
|
date_from="2025-01-01",
|
|
69
|
-
date_to="2025-
|
|
71
|
+
date_to="2025-06-30",
|
|
70
72
|
as_dataframe=True)
|
|
71
73
|
|
|
72
74
|
# ---------- house trades ----------
|
|
73
75
|
fb.house_trades.ticker("S&P 500", "AMZN",
|
|
74
76
|
date_from="2025-01-01",
|
|
75
|
-
date_to="2025-
|
|
77
|
+
date_to="2025-06-30",
|
|
76
78
|
as_dataframe=True)
|
|
77
79
|
|
|
78
80
|
# ---------- insider transactions ----------
|
|
@@ -81,13 +83,13 @@ fb.insider_transactions.ticker("S&P 500", "AMZN", as_dataframe=True)
|
|
|
81
83
|
# ---------- LinkedIn metrics ----------
|
|
82
84
|
fb.linkedin_data.ticker("S&P 500", "AMZN",
|
|
83
85
|
date_from="2025-01-01",
|
|
84
|
-
date_to="2025-
|
|
86
|
+
date_to="2025-06-30",
|
|
85
87
|
as_dataframe=True)
|
|
86
88
|
|
|
87
89
|
# ---------- options put/call ----------
|
|
88
90
|
fb.options.put_call("S&P 500", "AMZN",
|
|
89
91
|
date_from="2025-01-01",
|
|
90
|
-
date_to="2025-
|
|
92
|
+
date_to="2025-06-30",
|
|
91
93
|
as_dataframe=True)
|
|
92
94
|
|
|
93
95
|
# ---------- price predictions ----------
|
|
@@ -97,10 +99,43 @@ fb.predictions.ticker("AMZN", as_dataframe=True) # single ticker
|
|
|
97
99
|
# ---------- news sentiment ----------
|
|
98
100
|
fb.sentiments.ticker("S&P 500", "AMZN",
|
|
99
101
|
date_from="2025-01-01",
|
|
100
|
-
date_to="2025-
|
|
102
|
+
date_to="2025-06-30",
|
|
101
103
|
as_dataframe=True)
|
|
102
104
|
```
|
|
103
105
|
|
|
106
|
+
## 📈 Plotting
|
|
107
|
+
|
|
108
|
+
Plot helpers in a nutshell
|
|
109
|
+
|
|
110
|
+
- `show` – defaults to True, so the chart appears immediately.
|
|
111
|
+
|
|
112
|
+
- `as_json=True` – skips display and returns the figure as a Plotly-JSON string, ready to embed elsewhere.
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
# ---------- App Ratings Chart - Apple App Store or Google Play Store ----------
|
|
116
|
+
fb.plot.app_ratings("S&P 500", "AMZN",
|
|
117
|
+
store="app", # "play" for Google Play Store
|
|
118
|
+
date_from="2025-01-01",
|
|
119
|
+
date_to="2025-06-30")
|
|
120
|
+
|
|
121
|
+
# ---------- LinkedIn Metrics Chart ----------
|
|
122
|
+
fb.plot.linkedin("S&P 500", "AMZN",
|
|
123
|
+
date_from="2025-01-01", date_to="2025-06-30")
|
|
124
|
+
|
|
125
|
+
# ---------- Put-Call Ratio Chart ----------
|
|
126
|
+
fb.plot.options("S&P 500", "AMZN",
|
|
127
|
+
kind="put_call",
|
|
128
|
+
date_from="2025-01-01", date_to="2025-06-30")
|
|
129
|
+
|
|
130
|
+
# ---------- Predictions Chart ----------
|
|
131
|
+
fb.plot.predictions("AMZN")
|
|
132
|
+
|
|
133
|
+
# ---------- Sentiments Chart ----------
|
|
134
|
+
fb.plot.sentiments("S&P 500", "AMZN",
|
|
135
|
+
date_from="2025-01-01",
|
|
136
|
+
date_to="2025-06-30")
|
|
137
|
+
```
|
|
138
|
+
|
|
104
139
|
## 🔑 Authentication
|
|
105
140
|
|
|
106
141
|
To call the API you need an **API key**, obtained by purchasing a **FinBrain API subscription**.
|
|
@@ -115,20 +150,20 @@ from finbrain import FinBrainClient
|
|
|
115
150
|
fb = FinBrainClient(api_key="YOUR_KEY")
|
|
116
151
|
```
|
|
117
152
|
|
|
118
|
-
### Async(currently under development)
|
|
153
|
+
### Async (currently under development)
|
|
119
154
|
|
|
120
155
|
```
|
|
121
156
|
import asyncio, os
|
|
122
157
|
from finbrain.aio import FinBrainAsyncClient async
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
data = await fb.sentiments.ticker("
|
|
158
|
+
def main():
|
|
159
|
+
async with FinBrainAsyncClient(api_key=os.getenv("FINBRAIN_API_KEY")) as fb:
|
|
160
|
+
data = await fb.sentiments.ticker("S&P 500", "AMZN")
|
|
126
161
|
print(list(data["sentimentAnalysis"].items())[:3])
|
|
127
162
|
|
|
128
|
-
asyncio.run(main())
|
|
163
|
+
asyncio.run(main())
|
|
129
164
|
```
|
|
130
165
|
|
|
131
|
-
### CLI(currently under development)
|
|
166
|
+
### CLI (currently under development)
|
|
132
167
|
|
|
133
168
|
```
|
|
134
169
|
export FINBRAIN_API_KEY=your_key
|
|
@@ -163,7 +198,7 @@ from finbrain.exceptions import BadRequest
|
|
|
163
198
|
try:
|
|
164
199
|
fb.predictions.ticker("MSFT", prediction_type="weekly")
|
|
165
200
|
except BadRequest as exc:
|
|
166
|
-
|
|
201
|
+
print("Invalid parameters:", exc)
|
|
167
202
|
```
|
|
168
203
|
|
|
169
204
|
| HTTP status | Exception class | Meaning |
|
|
@@ -184,7 +219,7 @@ except BadRequest as exc:
|
|
|
184
219
|
- Version auto-generated from Git tags (setuptools-scm)
|
|
185
220
|
|
|
186
221
|
```
|
|
187
|
-
git tag -a
|
|
222
|
+
git tag -a 0.2.0 -m "Add options.chain endpoint"
|
|
188
223
|
git push --tags # GitHub Actions builds & uploads to PyPI
|
|
189
224
|
```
|
|
190
225
|
|
|
@@ -193,11 +228,13 @@ git push --tags # GitHub Actions builds & uploads to PyPI
|
|
|
193
228
|
## 🧑💻 Development
|
|
194
229
|
|
|
195
230
|
```
|
|
196
|
-
git clone https://github.com/finbrain-tech/finbrain-python
|
|
231
|
+
git clone https://github.com/finbrain-tech/finbrain-python
|
|
232
|
+
cd finbrain-python
|
|
197
233
|
python -m venv .venv && source .venv/bin/activate
|
|
198
234
|
pip install -e .[dev]
|
|
199
235
|
|
|
200
|
-
ruff check . # lint / format
|
|
236
|
+
ruff check . # lint / format
|
|
237
|
+
pytest -q # unit tests (mocked)
|
|
201
238
|
```
|
|
202
239
|
|
|
203
240
|
### Live integration test(currently under development)
|
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
.gitignore
|
|
3
3
|
LICENSE
|
|
4
4
|
README.md
|
|
5
|
-
cli.py
|
|
6
|
-
models.py
|
|
7
5
|
pyproject.toml
|
|
8
|
-
utils.py
|
|
9
6
|
.github/workflows/ci.yml
|
|
10
7
|
.github/workflows/release.yml
|
|
11
8
|
src/finbrain/__init__.py
|
|
12
9
|
src/finbrain/client.py
|
|
13
10
|
src/finbrain/exceptions.py
|
|
11
|
+
src/finbrain/plotting.py
|
|
14
12
|
src/finbrain/aio/__init__.py
|
|
15
13
|
src/finbrain/aio/client.py
|
|
16
14
|
src/finbrain/endpoints/__init__.py
|
finbrain_python-0.1.2/cli.py
DELETED
|
File without changes
|
finbrain_python-0.1.2/models.py
DELETED
|
File without changes
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"""FinBrain Python SDK."""
|
|
2
|
-
|
|
3
|
-
from importlib import metadata as _meta
|
|
4
|
-
|
|
5
|
-
try: # installed from a wheel / sdist
|
|
6
|
-
__version__: str = _meta.version(__name__)
|
|
7
|
-
except _meta.PackageNotFoundError: # running from a Git checkout
|
|
8
|
-
__version__ = "0.0.0.dev0"
|
|
9
|
-
|
|
10
|
-
from .client import FinBrainClient
|
|
11
|
-
|
|
12
|
-
__all__ = ["FinBrainClient", "__version__"]
|
finbrain_python-0.1.2/utils.py
DELETED
|
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
|
{finbrain_python-0.1.2 → finbrain_python-0.1.4}/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
|
{finbrain_python-0.1.2 → finbrain_python-0.1.4}/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
|