finbrain-python 0.1.7__py3-none-any.whl → 0.1.8__py3-none-any.whl

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/aio/client.py CHANGED
@@ -15,6 +15,7 @@ from .endpoints.sentiments import AsyncSentimentsAPI
15
15
  from .endpoints.app_ratings import AsyncAppRatingsAPI
16
16
  from .endpoints.analyst_ratings import AsyncAnalystRatingsAPI
17
17
  from .endpoints.house_trades import AsyncHouseTradesAPI
18
+ from .endpoints.senate_trades import AsyncSenateTradesAPI
18
19
  from .endpoints.insider_transactions import AsyncInsiderTransactionsAPI
19
20
  from .endpoints.linkedin_data import AsyncLinkedInDataAPI
20
21
  from .endpoints.options import AsyncOptionsAPI
@@ -68,6 +69,7 @@ class AsyncFinBrainClient:
68
69
  self.app_ratings = AsyncAppRatingsAPI(self)
69
70
  self.analyst_ratings = AsyncAnalystRatingsAPI(self)
70
71
  self.house_trades = AsyncHouseTradesAPI(self)
72
+ self.senate_trades = AsyncSenateTradesAPI(self)
71
73
  self.insider_transactions = AsyncInsiderTransactionsAPI(self)
72
74
  self.linkedin_data = AsyncLinkedInDataAPI(self)
73
75
  self.options = AsyncOptionsAPI(self)
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+ import pandas as pd
3
+ from urllib.parse import quote
4
+ import datetime as _dt
5
+ from typing import TYPE_CHECKING, Dict, Any
6
+
7
+ from ._utils import to_datestr
8
+
9
+ if TYPE_CHECKING:
10
+ from ..client import AsyncFinBrainClient
11
+
12
+
13
+ class AsyncSenateTradesAPI:
14
+ """Async wrapper for /senatetrades endpoints."""
15
+
16
+ def __init__(self, client: "AsyncFinBrainClient") -> None:
17
+ self._c = client
18
+
19
+ async def ticker(
20
+ self,
21
+ market: str,
22
+ symbol: str,
23
+ *,
24
+ date_from: _dt.date | str | None = None,
25
+ date_to: _dt.date | str | None = None,
26
+ as_dataframe: bool = False,
27
+ ) -> Dict[str, Any] | pd.DataFrame:
28
+ """Fetch Senate-member trades for symbol in market (async)."""
29
+ params: Dict[str, str] = {}
30
+ if date_from:
31
+ params["dateFrom"] = to_datestr(date_from)
32
+ if date_to:
33
+ params["dateTo"] = to_datestr(date_to)
34
+
35
+ market_slug = quote(market, safe="")
36
+ path = f"senatetrades/{market_slug}/{symbol.upper()}"
37
+
38
+ data: Dict[str, Any] = await self._c._request("GET", path, params=params)
39
+
40
+ if as_dataframe:
41
+ rows = data.get("senateTrades", [])
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
finbrain/client.py CHANGED
@@ -16,6 +16,7 @@ from .endpoints.sentiments import SentimentsAPI
16
16
  from .endpoints.app_ratings import AppRatingsAPI
17
17
  from .endpoints.analyst_ratings import AnalystRatingsAPI
18
18
  from .endpoints.house_trades import HouseTradesAPI
19
+ from .endpoints.senate_trades import SenateTradesAPI
19
20
  from .endpoints.insider_transactions import InsiderTransactionsAPI
20
21
  from .endpoints.linkedin_data import LinkedInDataAPI
21
22
  from .endpoints.options import OptionsAPI
@@ -63,6 +64,7 @@ class FinBrainClient:
63
64
  self.app_ratings = AppRatingsAPI(self)
64
65
  self.analyst_ratings = AnalystRatingsAPI(self)
65
66
  self.house_trades = HouseTradesAPI(self)
67
+ self.senate_trades = SenateTradesAPI(self)
66
68
  self.insider_transactions = InsiderTransactionsAPI(self)
67
69
  self.linkedin_data = LinkedInDataAPI(self)
68
70
  self.options = OptionsAPI(self)
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+ import pandas as pd
3
+ from urllib.parse import quote
4
+ import datetime as _dt
5
+ from typing import TYPE_CHECKING, Dict, Any
6
+
7
+ from ._utils import to_datestr
8
+
9
+ if TYPE_CHECKING: # imported only by type-checkers
10
+ from ..client import FinBrainClient
11
+
12
+
13
+ class SenateTradesAPI:
14
+ """
15
+ Endpoint
16
+ --------
17
+ ``/senatetrades/<MARKET>/<TICKER>`` - trading activity of U.S. Senators
18
+ for the selected ticker.
19
+ """
20
+
21
+ # ------------------------------------------------------------------ #
22
+ def __init__(self, client: "FinBrainClient") -> None:
23
+ self._c = client # reference to the parent client
24
+
25
+ # ------------------------------------------------------------------ #
26
+ def ticker(
27
+ self,
28
+ market: str,
29
+ symbol: str,
30
+ *,
31
+ date_from: _dt.date | str | None = None,
32
+ date_to: _dt.date | str | None = None,
33
+ as_dataframe: bool = False,
34
+ ) -> Dict[str, Any] | pd.DataFrame:
35
+ """
36
+ Fetch Senate-member trades for *symbol* in *market*.
37
+
38
+ Parameters
39
+ ----------
40
+ market :
41
+ Market name **exactly as FinBrain lists it**
42
+ (e.g. ``"NASDAQ"``, ``"NYSE"``, ``"S&P 500"``).
43
+ Spaces and special characters are accepted; they are URL-encoded
44
+ automatically.
45
+ symbol :
46
+ Ticker symbol; auto-upper-cased.
47
+ date_from, date_to :
48
+ Optional ISO dates (``YYYY-MM-DD``) bounding the returned rows.
49
+ as_dataframe :
50
+ If *True*, return a **pandas.DataFrame** indexed by ``date``;
51
+ otherwise return the raw JSON dict.
52
+
53
+ Returns
54
+ -------
55
+ dict | pandas.DataFrame
56
+ """
57
+ params: Dict[str, str] = {}
58
+ if date_from:
59
+ params["dateFrom"] = to_datestr(date_from)
60
+ if date_to:
61
+ params["dateTo"] = to_datestr(date_to)
62
+
63
+ market_slug = quote(market, safe="")
64
+ path = f"senatetrades/{market_slug}/{symbol.upper()}"
65
+
66
+ data: Dict[str, Any] = self._c._request("GET", path, params=params)
67
+
68
+ if as_dataframe:
69
+ rows = data.get("senateTrades", [])
70
+ df = pd.DataFrame(rows)
71
+ if not df.empty and "date" in df.columns:
72
+ df["date"] = pd.to_datetime(df["date"])
73
+ df.set_index("date", inplace=True)
74
+ return df
75
+
76
+ return data
finbrain/plotting.py CHANGED
@@ -661,6 +661,117 @@ class _PlotNamespace:
661
661
  return None
662
662
  return fig.to_json() if as_json else fig
663
663
 
664
+ # --------------------------------------------------------------------- #
665
+ # Senate Trades → markers on price chart #
666
+ # --------------------------------------------------------------------- #
667
+ def senate_trades(
668
+ self,
669
+ market: str,
670
+ ticker: str,
671
+ price_data: pd.DataFrame,
672
+ *,
673
+ date_from: str | None = None,
674
+ date_to: str | None = None,
675
+ as_json: bool = False,
676
+ show: bool = True,
677
+ template: str = "plotly_dark",
678
+ **kwargs,
679
+ ):
680
+ """
681
+ Plot U.S. Senate member trades overlaid on a price chart.
682
+
683
+ This method requires user-provided historical price data, as FinBrain
684
+ does not currently offer a price history endpoint.
685
+
686
+ Parameters
687
+ ----------
688
+ market : str
689
+ Market identifier (e.g. ``"NASDAQ"``).
690
+ ticker : str
691
+ Ticker symbol (e.g. ``"META"``).
692
+ price_data : pandas.DataFrame
693
+ **User-provided** price history with a DatetimeIndex and a column
694
+ containing prices (e.g. ``"close"``, ``"Close"``, or ``"price"``).
695
+ The index must be timezone-naive or UTC.
696
+ date_from, date_to : str or None, optional
697
+ Date range for transactions in ``YYYY-MM-DD`` format.
698
+ as_json : bool, default False
699
+ If ``True``, return JSON string instead of Figure object.
700
+ show : bool, default True
701
+ If ``True`` and ``as_json=False``, display the figure immediately.
702
+ template : str, default "plotly_dark"
703
+ Plotly template name.
704
+ **kwargs
705
+ Additional arguments passed to
706
+ :meth:`FinBrainClient.senate_trades.ticker`.
707
+
708
+ Returns
709
+ -------
710
+ plotly.graph_objects.Figure or str or None
711
+ Figure object, JSON string, or None (when shown).
712
+
713
+ Raises
714
+ ------
715
+ ValueError
716
+ If ``price_data`` is empty or missing required price column.
717
+
718
+ Examples
719
+ --------
720
+ >>> import pandas as pd
721
+ >>> # User provides their own price data from any legal source
722
+ >>> price_df = pd.DataFrame({
723
+ ... "close": [500, 510, 505, 520],
724
+ ... "date": pd.date_range("2024-01-01", periods=4)
725
+ ... }).set_index("date")
726
+ >>> fb.plot.senate_trades("NASDAQ", "META", price_df,
727
+ ... date_from="2024-01-01", date_to="2024-12-31")
728
+ """
729
+ # Validate price_data
730
+ if price_data.empty:
731
+ raise ValueError("price_data cannot be empty")
732
+
733
+ # Flatten MultiIndex columns if present (e.g., from yf.download())
734
+ if isinstance(price_data.columns, pd.MultiIndex):
735
+ # Get the first level (price types like 'Close', 'Open', etc.)
736
+ price_data = price_data.copy()
737
+ price_data.columns = price_data.columns.get_level_values(0)
738
+
739
+ # Find price column (case-insensitive search)
740
+ price_col = None
741
+ for col in ["close", "Close", "price", "Price", "adj_close", "Adj Close"]:
742
+ if col in price_data.columns:
743
+ price_col = col
744
+ break
745
+ if price_col is None:
746
+ raise ValueError(
747
+ f"price_data must contain a price column (e.g. 'close', 'Close', 'price'). "
748
+ f"Found columns: {price_data.columns.tolist()}"
749
+ )
750
+
751
+ # Fetch senate trades
752
+ transactions_df = self._fb.senate_trades.ticker(
753
+ market,
754
+ ticker,
755
+ date_from=date_from,
756
+ date_to=date_to,
757
+ as_dataframe=True,
758
+ **kwargs,
759
+ )
760
+
761
+ fig = self._plot_transactions_on_price(
762
+ price_data=price_data,
763
+ price_col=price_col,
764
+ transactions_df=transactions_df,
765
+ ticker=ticker,
766
+ template=template,
767
+ transaction_type="Senate",
768
+ )
769
+
770
+ if show and not as_json:
771
+ fig.show()
772
+ return None
773
+ return fig.to_json() if as_json else fig
774
+
664
775
  # --------------------------------------------------------------------- #
665
776
  # Helper methods #
666
777
  # --------------------------------------------------------------------- #
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: finbrain-python
3
- Version: 0.1.7
3
+ Version: 0.1.8
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
@@ -86,6 +86,12 @@ fb.house_trades.ticker("S&P 500", "AMZN",
86
86
  date_to="2025-06-30",
87
87
  as_dataframe=True)
88
88
 
89
+ # ---------- senate trades ----------
90
+ fb.senate_trades.ticker("NASDAQ", "META",
91
+ date_from="2025-01-01",
92
+ date_to="2025-06-30",
93
+ as_dataframe=True)
94
+
89
95
  # ---------- insider transactions ----------
90
96
  fb.insider_transactions.ticker("S&P 500", "AMZN", as_dataframe=True)
91
97
 
@@ -185,7 +191,7 @@ fb.plot.sentiments("S&P 500", "AMZN",
185
191
  date_from="2025-01-01",
186
192
  date_to="2025-06-30")
187
193
 
188
- # ---------- Insider Transactions & House Trades (requires user price data) ----------
194
+ # ---------- Insider Transactions, House & Senate Trades (requires user price data) ----------
189
195
  # These plots overlay transaction markers on a price chart.
190
196
  # Since FinBrain doesn't provide historical prices, you must provide your own:
191
197
 
@@ -206,6 +212,12 @@ fb.plot.house_trades("S&P 500", "NVDA",
206
212
  price_data=price_df,
207
213
  date_from="2025-01-01",
208
214
  date_to="2025-06-30")
215
+
216
+ # Plot Senate member trades on your price chart
217
+ fb.plot.senate_trades("NASDAQ", "META",
218
+ price_data=price_df,
219
+ date_from="2025-01-01",
220
+ date_to="2025-06-30")
209
221
  ```
210
222
 
211
223
  **Price Data Requirements:**
@@ -242,6 +254,7 @@ fb = FinBrainClient(api_key="YOUR_KEY")
242
254
  | App ratings | `client.app_ratings.ticker()` | `/appratings/{MARKET}/{TICKER}` |
243
255
  | Analyst ratings | `client.analyst_ratings.ticker()` | `/analystratings/{MARKET}/{TICKER}` |
244
256
  | House trades | `client.house_trades.ticker()` | `/housetrades/{MARKET}/{TICKER}` |
257
+ | Senate trades | `client.senate_trades.ticker()` | `/senatetrades/{MARKET}/{TICKER}` |
245
258
  | Insider transactions | `client.insider_transactions.ticker()` | `/insidertransactions/{MARKET}/{TICKER}` |
246
259
  | LinkedIn | `client.linkedin_data.ticker()` | `/linkedindata/{MARKET}/{TICKER}` |
247
260
  | Options – Put/Call | `client.options.put_call()` | `/putcalldata/{MARKET}/{TICKER}` |
@@ -1,10 +1,10 @@
1
1
  finbrain/__init__.py,sha256=ULIgwHkWbj5sT0udC3SlTROhZCgm2y9BtPox-4joNn4,416
2
- finbrain/client.py,sha256=6Q9euGD-uxUsQFeS-dGziaLZP4i5czrGQA9Vsxxxac4,4178
2
+ finbrain/client.py,sha256=qo1vqkp10U-xJ5FELtnU2SmN5MkYIuz1w0h4eDVknCY,4282
3
3
  finbrain/exceptions.py,sha256=_STlzmP7Z1qDkFic3QU4QqyY07MMhMm46Tmgy7HHuck,5258
4
- finbrain/plotting.py,sha256=EJrap1W-ZKRntnhOM59PXJziIcQ8d6M_bYgVYNaxC4Q,29928
4
+ finbrain/plotting.py,sha256=qbd1-wwTkJ_obCHFJRUgpBSe835BNNw937RMu1HZfNs,33987
5
5
  finbrain/py.typed,sha256=INFR1qO3jdMrtVPs0N0SAVKVbjKV80HQPoPshwGEpGo,27
6
6
  finbrain/aio/__init__.py,sha256=eJra9SqvLMkq5zoZfG6WFw7kd1jGpf9l-ovuPMeWvdw,125
7
- finbrain/aio/client.py,sha256=LwvIR9lZRzKAYckcEi9-wrUtWs6Wci-DV5ouefBq3dM,6039
7
+ finbrain/aio/client.py,sha256=KLC6iHLJd0DNQOEKmLoBNAXDPtXoLuaD9cLrlmlwE4s,6153
8
8
  finbrain/aio/endpoints/__init__.py,sha256=zdNSU1jzvnK6JnHxrvb3ye9KFCkA86xDTu9bBzmwrEQ,48
9
9
  finbrain/aio/endpoints/_utils.py,sha256=4ElYWGCcJ7t_lx02e49NvFHMt2g6DTcnwx3Mvwf3KyY,908
10
10
  finbrain/aio/endpoints/analyst_ratings.py,sha256=JJ0wywQtO2WuEFpJUYsdGyPJj951SrlyFQyCes9UOEs,1468
@@ -15,6 +15,7 @@ finbrain/aio/endpoints/insider_transactions.py,sha256=xMPuTSjIXmyqmlRUloGlkqiYjD
15
15
  finbrain/aio/endpoints/linkedin_data.py,sha256=eQ13rNDYTTQRNN6cjBmqKLCW8DtCRsjVC_f-af-KLXM,1489
16
16
  finbrain/aio/endpoints/options.py,sha256=DspRz6NBWez0AM0g5tHHhq6rw3rZEo6zbFiv005dvp8,1458
17
17
  finbrain/aio/endpoints/predictions.py,sha256=nXoPuXe8KiuLHv8mp6iMz3bcEjkecDOEABipPUdH6bE,2807
18
+ finbrain/aio/endpoints/senate_trades.py,sha256=3TvfxoU_vVpUwoIJtd100XpEbfQevFtUCXLCGKY45-I,1442
18
19
  finbrain/aio/endpoints/sentiments.py,sha256=PcVoeqTatiPMnxcBPOX_mV4GaFbr2efIsf40qAjAh1k,1640
19
20
  finbrain/endpoints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
21
  finbrain/endpoints/_utils.py,sha256=0JIybEiSXVCm5SQI73i80OtoGQ2mJWAjImgA_2ru30U,896
@@ -26,9 +27,10 @@ finbrain/endpoints/insider_transactions.py,sha256=lV3Zaz2SfuEnJvThAyxtfh-WuGjLN0
26
27
  finbrain/endpoints/linkedin_data.py,sha256=CG7eSN2o2pVtKyhY_MAipg-hxZXVSSvfQX9LjRs-0wM,2571
27
28
  finbrain/endpoints/options.py,sha256=K0fE3mZo0aXqPxHjhMgc1R5gkd12zKeZ54_8DQ11sRE,2823
28
29
  finbrain/endpoints/predictions.py,sha256=kDxC1ubKbpapeF12qahiGrD6V68ljBXuWdiu_0jHq64,4166
30
+ finbrain/endpoints/senate_trades.py,sha256=eeQ97fTtxhTGqX-DMUsuZOkbGlBlGrZy8p8JsYezJ4M,2395
29
31
  finbrain/endpoints/sentiments.py,sha256=RKDsTxk4o-dz98Sg-BauMXjwev_77Xt2w93ifmP-2p4,3166
30
- finbrain_python-0.1.7.dist-info/licenses/LICENSE,sha256=x71LjIUPbK7Y8YBulH_AXzEIX7BK90dgFL2zA6LBT-A,1069
31
- finbrain_python-0.1.7.dist-info/METADATA,sha256=1ssiVR4ooKjsw2Q_r1iJn7AmLl_fi-1dO17PV9FkW44,11304
32
- finbrain_python-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- finbrain_python-0.1.7.dist-info/top_level.txt,sha256=dCiQtTx3z2TtO_yQI6q3yODcdb-pABB-iEReJegVAv0,9
34
- finbrain_python-0.1.7.dist-info/RECORD,,
32
+ finbrain_python-0.1.8.dist-info/licenses/LICENSE,sha256=x71LjIUPbK7Y8YBulH_AXzEIX7BK90dgFL2zA6LBT-A,1069
33
+ finbrain_python-0.1.8.dist-info/METADATA,sha256=kixciTwD_ySYtJbAxEe1ZTfyf6EPhgwkkZyZdDsxVsc,11875
34
+ finbrain_python-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
35
+ finbrain_python-0.1.8.dist-info/top_level.txt,sha256=dCiQtTx3z2TtO_yQI6q3yODcdb-pABB-iEReJegVAv0,9
36
+ finbrain_python-0.1.8.dist-info/RECORD,,