btgsolutions-dataservices-python-client 3.2.10__tar.gz → 3.2.12__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.
Files changed (41) hide show
  1. {btgsolutions_dataservices_python_client-3.2.10/btgsolutions_dataservices_python_client.egg-info → btgsolutions_dataservices_python_client-3.2.12}/PKG-INFO +44 -10
  2. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/README.md +42 -0
  3. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/__init__.py +1 -1
  4. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/__init__.py +2 -0
  5. btgsolutions_dataservices_python_client-3.2.12/btgsolutions_dataservices/rest/book_scope.py +212 -0
  6. btgsolutions_dataservices_python_client-3.2.12/btgsolutions_dataservices/rest/broker_analytics.py +173 -0
  7. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/historical_candles.py +66 -6
  8. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12/btgsolutions_dataservices_python_client.egg-info}/PKG-INFO +44 -10
  9. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices_python_client.egg-info/SOURCES.txt +2 -0
  10. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/LICENSE +0 -0
  11. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/MANIFEST.in +0 -0
  12. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/config.py +0 -0
  13. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/exceptions.py +0 -0
  14. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/authenticator.py +0 -0
  15. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/broker_reference.py +0 -0
  16. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/bulk_data.py +0 -0
  17. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/company_data.py +0 -0
  18. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/corporate_events.py +0 -0
  19. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/hfn.py +0 -0
  20. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/historical_candles_crypto.py +0 -0
  21. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/intraday_candles.py +0 -0
  22. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/intraday_tick_data.py +0 -0
  23. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/public_sources.py +0 -0
  24. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/quotes.py +0 -0
  25. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/reference_data.py +0 -0
  26. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/stock_loan.py +0 -0
  27. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/ticker_last_event.py +0 -0
  28. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/rest/ticker_last_event_polling.py +0 -0
  29. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/websocket/__init__.py +0 -0
  30. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/websocket/broker_analytics.py +0 -0
  31. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/websocket/hfn_websocket_client.py +0 -0
  32. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/websocket/market_data_feed.py +0 -0
  33. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/websocket/market_data_websocket_client.py +0 -0
  34. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices/websocket/websocket_default_functions.py +0 -0
  35. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices_python_client.egg-info/dependency_links.txt +0 -0
  36. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices_python_client.egg-info/requires.txt +0 -0
  37. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/btgsolutions_dataservices_python_client.egg-info/top_level.txt +0 -0
  38. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/pyproject.toml +0 -0
  39. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/requirements.txt +0 -0
  40. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/setup.cfg +0 -0
  41. {btgsolutions_dataservices_python_client-3.2.10 → btgsolutions_dataservices_python_client-3.2.12}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: btgsolutions_dataservices_python_client
3
- Version: 3.2.10
3
+ Version: 3.2.12
4
4
  Summary: Python package containing several classes and data for extracting and manipulating market and trading data.
5
5
  Home-page: https://github.com/BTG-Pactual-Solutions/btgsolutions-dataservices-python-client
6
6
  Author: BTG Solutions Data Services powered by BTG Pactual Solutions
@@ -12,14 +12,6 @@ Requires-Dist: websocket-client>=1.8.0
12
12
  Requires-Dist: PyJWT>=2.8.0
13
13
  Requires-Dist: requests>=2.32.3
14
14
  Requires-Dist: pyarrow>=17.0.0
15
- Dynamic: author
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: home-page
19
- Dynamic: license-file
20
- Dynamic: requires-dist
21
- Dynamic: requires-python
22
- Dynamic: summary
23
15
 
24
16
  # BTG Solutions - Data Services
25
17
 
@@ -280,6 +272,14 @@ hist_candles = btg.HistoricalCandles(api_key='YOUR_API_KEY')
280
272
  hist_candles.get_intraday_history_candles(ticker='PETR4', market_type='stocks', corporate_events_adj=True, date='2023-10-06', candle='1m', rmv_after_market=True, timezone='UTC', raw_data=False, round=True)
281
273
  ```
282
274
 
275
+ ##### Interday Batch
276
+
277
+ ```python
278
+ import btgsolutions_dataservices as btg
279
+ hist_candles = btg.HistoricalCandles(api_key='YOUR_API_KEY')
280
+ hist_candles.get_interday_history_candles_batch(market_type='stocks', tickers=['PETR4', 'VALE3'], start_date='2023-10-01', end_date='2023-10-13', corporate_events_adj=True, rmv_after_market=True, timezone='UTC', raw_data=False, round=True)
281
+ ```
282
+
283
283
  ##### Available Tickers
284
284
 
285
285
  ```python
@@ -503,6 +503,40 @@ broker_reference = btg.BrokerReference(api_key='YOUR_API_KEY')
503
503
  broker_reference.get()
504
504
  ```
505
505
 
506
+ #### Book Scope
507
+
508
+ ```python
509
+ import btgsolutions_dataservices as btg
510
+ book_scope = btg.BookScope(api_key='YOUR_API_KEY')
511
+
512
+ result = book_scope.get(
513
+ symbol='DOLM26',
514
+ market_type='derivatives',
515
+ start_time='2026-05-28T14:12:00Z',
516
+ end_time='2026-05-28T14:15:00Z',
517
+ select=['trades', 'book_snapshot', 'book_incremental'], # choose one, two, or all three
518
+ )
519
+
520
+ single_file_result = book_scope.get(
521
+ symbol='DOLM26',
522
+ market_type='derivatives',
523
+ start_time='2026-05-28T14:12:00Z',
524
+ end_time='2026-05-28T14:15:00Z',
525
+ select=['trades', 'book_snapshot', 'book_incremental'],
526
+ aggregate_info=True,
527
+ )
528
+ ```
529
+
530
+ #### Broker Analytics
531
+
532
+ ```python
533
+ import btgsolutions_dataservices as btg
534
+ broker_analytics = btg.BrokerAnalytics(api_key='YOUR_API_KEY', market_type='stocks')
535
+ summary = broker_analytics.get_summary(brokers=['85', '3'], tickers=['PETR4', 'ABCB4'])
536
+ top_brokers = broker_analytics.get_top_brokers(n=10)
537
+ top_tickers = broker_analytics.get_top_tickers(n=10, brokers=['85', '3'])
538
+ ```
539
+
506
540
  #### Ticker Reference Data
507
541
 
508
542
  ```python
@@ -257,6 +257,14 @@ hist_candles = btg.HistoricalCandles(api_key='YOUR_API_KEY')
257
257
  hist_candles.get_intraday_history_candles(ticker='PETR4', market_type='stocks', corporate_events_adj=True, date='2023-10-06', candle='1m', rmv_after_market=True, timezone='UTC', raw_data=False, round=True)
258
258
  ```
259
259
 
260
+ ##### Interday Batch
261
+
262
+ ```python
263
+ import btgsolutions_dataservices as btg
264
+ hist_candles = btg.HistoricalCandles(api_key='YOUR_API_KEY')
265
+ hist_candles.get_interday_history_candles_batch(market_type='stocks', tickers=['PETR4', 'VALE3'], start_date='2023-10-01', end_date='2023-10-13', corporate_events_adj=True, rmv_after_market=True, timezone='UTC', raw_data=False, round=True)
266
+ ```
267
+
260
268
  ##### Available Tickers
261
269
 
262
270
  ```python
@@ -480,6 +488,40 @@ broker_reference = btg.BrokerReference(api_key='YOUR_API_KEY')
480
488
  broker_reference.get()
481
489
  ```
482
490
 
491
+ #### Book Scope
492
+
493
+ ```python
494
+ import btgsolutions_dataservices as btg
495
+ book_scope = btg.BookScope(api_key='YOUR_API_KEY')
496
+
497
+ result = book_scope.get(
498
+ symbol='DOLM26',
499
+ market_type='derivatives',
500
+ start_time='2026-05-28T14:12:00Z',
501
+ end_time='2026-05-28T14:15:00Z',
502
+ select=['trades', 'book_snapshot', 'book_incremental'], # choose one, two, or all three
503
+ )
504
+
505
+ single_file_result = book_scope.get(
506
+ symbol='DOLM26',
507
+ market_type='derivatives',
508
+ start_time='2026-05-28T14:12:00Z',
509
+ end_time='2026-05-28T14:15:00Z',
510
+ select=['trades', 'book_snapshot', 'book_incremental'],
511
+ aggregate_info=True,
512
+ )
513
+ ```
514
+
515
+ #### Broker Analytics
516
+
517
+ ```python
518
+ import btgsolutions_dataservices as btg
519
+ broker_analytics = btg.BrokerAnalytics(api_key='YOUR_API_KEY', market_type='stocks')
520
+ summary = broker_analytics.get_summary(brokers=['85', '3'], tickers=['PETR4', 'ABCB4'])
521
+ top_brokers = broker_analytics.get_top_brokers(n=10)
522
+ top_tickers = broker_analytics.get_top_tickers(n=10, brokers=['85', '3'])
523
+ ```
524
+
483
525
  #### Ticker Reference Data
484
526
 
485
527
  ```python
@@ -1,4 +1,4 @@
1
- __version__ = "3.2.10"
1
+ __version__ = "3.2.12"
2
2
 
3
3
  from .websocket import *
4
4
  from .rest import *
@@ -14,3 +14,5 @@ from .reference_data import ReferenceData
14
14
  from .stock_loan import StockLoan
15
15
  from .ticker_last_event_polling import TickerLastEventPolling
16
16
  from .broker_reference import BrokerReference
17
+ from .book_scope import BookScope
18
+ from .broker_analytics import BrokerAnalytics
@@ -0,0 +1,212 @@
1
+ from datetime import datetime, timedelta, timezone
2
+ from typing import Optional, List
3
+ from ..exceptions import BadResponse
4
+ import requests
5
+ from ..config import url_api_v1
6
+ from .authenticator import Authenticator
7
+ from io import BytesIO
8
+ import pyarrow as pa
9
+ import pyarrow.parquet as pq
10
+
11
+ VALID_SELECT_OPTIONS = {"trades", "book_snapshot", "book_incremental"}
12
+ VALID_MARKET_TYPES = {"derivatives", "options", "stocks"}
13
+ MAX_MARKET_SCOPE_WINDOW = timedelta(minutes=10)
14
+ ROW_LIMIT = 10000
15
+
16
+
17
+ class BookScope:
18
+ """
19
+ This class provides market scope data (trades, book snapshots, book incremental)
20
+ for a given symbol and time window.
21
+
22
+ * Main use case:
23
+
24
+ >>> from btgsolutions_dataservices import BookScope
25
+ >>> book_scope = BookScope(
26
+ >>> api_key='YOUR_API_KEY',
27
+ >>> )
28
+ >>> book_scope.get(
29
+ >>> symbol='DOLM26',
30
+ >>> market_type='derivatives',
31
+ >>> start_time='2026-05-28T14:12:00Z',
32
+ >>> end_time='2026-05-28T14:15:00Z',
33
+ >>> )
34
+
35
+ Parameters
36
+ ----------------
37
+ api_key: str
38
+ User identification key.
39
+ Field is required.
40
+ """
41
+ def __init__(
42
+ self,
43
+ api_key: Optional[str],
44
+ ):
45
+ self.api_key = api_key
46
+ self.token = Authenticator(self.api_key).token
47
+ self.headers = {"authorization": f"authorization {self.token}"}
48
+ self.base_url = url_api_v1
49
+
50
+ def _fetch_all_pages(self, endpoint_url: str, params: dict) -> bytes:
51
+ """Fetches all pages via rpt_seq cursor and returns a single combined parquet."""
52
+ tables = []
53
+ rpt_seq = None
54
+
55
+ while True:
56
+ page_params = {**params}
57
+ if rpt_seq is not None:
58
+ page_params["rpt_seq"] = rpt_seq
59
+
60
+ response = requests.get(endpoint_url, params=page_params, headers=self.headers, timeout=50)
61
+ if response.status_code != 200:
62
+ try:
63
+ error_body = response.json()
64
+ detail = error_body.get("detail", error_body.get("error", response.text))
65
+ except Exception:
66
+ detail = response.text
67
+ raise BadResponse(f"Error: {detail}")
68
+
69
+ table = pq.read_table(BytesIO(response.content))
70
+ tables.append(table)
71
+
72
+ if table.num_rows < ROW_LIMIT:
73
+ break
74
+ rpt_seq = table.column("rpt_seq")[-1].as_py()
75
+
76
+ combined = pa.concat_tables(tables)
77
+ buffer = BytesIO()
78
+ pq.write_table(combined, buffer)
79
+ return buffer.getvalue()
80
+
81
+ @staticmethod
82
+ def _combine_parquets(parquet_payloads: List[bytes]) -> bytes:
83
+ """Combines multiple parquet payloads into a single parquet file."""
84
+ tables = [BookScope._normalize_timestamps(pq.read_table(BytesIO(payload))) for payload in parquet_payloads]
85
+ combined = pa.concat_tables(tables, promote_options="default")
86
+ buffer = BytesIO()
87
+ pq.write_table(combined, buffer)
88
+ return buffer.getvalue()
89
+
90
+ @staticmethod
91
+ def _normalize_timestamps(table: pa.Table) -> pa.Table:
92
+ """Normalizes timestamp columns so tables can be concatenated safely."""
93
+ normalized = table
94
+
95
+ for field in table.schema:
96
+ if pa.types.is_timestamp(field.type):
97
+ target_type = pa.timestamp("us", tz=field.type.tz)
98
+ column_index = normalized.schema.get_field_index(field.name)
99
+ normalized = normalized.set_column(
100
+ column_index,
101
+ field.name,
102
+ normalized[field.name].cast(target_type),
103
+ )
104
+
105
+ return normalized
106
+
107
+ @staticmethod
108
+ def _parse_iso8601(value: str, field_name: str) -> datetime:
109
+ try:
110
+ return datetime.fromisoformat(value.replace("Z", "+00:00"))
111
+ except ValueError as exc:
112
+ raise ValueError(f"{field_name} must be a valid ISO-8601 datetime string.") from exc
113
+
114
+ @staticmethod
115
+ def _resolve_scope(end_time: datetime) -> str:
116
+ """Determines whether the request is 'intraday' or 'historical' based on end_time."""
117
+ now = datetime.now(timezone.utc)
118
+ today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
119
+ if end_time >= today_start:
120
+ return "intraday"
121
+ return "historical"
122
+
123
+ def get(
124
+ self,
125
+ symbol: str,
126
+ market_type: str,
127
+ start_time: str,
128
+ end_time: str,
129
+ select: List[str] = ["trades", "book_snapshot", "book_incremental"],
130
+ aggregate_info: bool = False,
131
+ ):
132
+ """
133
+ Returns trades, book snapshot and book incremental data for the given symbol and time window.
134
+
135
+ Pagination is handled automatically: if the API returns the maximum number of rows
136
+ (10 000), additional pages are fetched using the rpt_seq cursor until all events
137
+ in the requested horizon are retrieved. The caller always receives a single,
138
+ complete DataFrame per dataset.
139
+
140
+ Parameters
141
+ ----------------
142
+ symbol: str
143
+ The ticker symbol to query.
144
+ Field is required. Example: 'PETR4', 'DOLM26'.
145
+ market_type: str
146
+ Market type.
147
+ Field is required. Allowed values: 'derivatives', 'options', 'stocks'.
148
+ start_time: str
149
+ Start of the analysis window in ISO-8601 UTC format.
150
+ Field is required. Example: '2026-05-28T14:12:00Z'.
151
+ end_time: str
152
+ End of the analysis window in ISO-8601 UTC format.
153
+ Field is required. Example: '2026-05-28T14:15:00Z'.
154
+ select: list of str
155
+ Which datasets to return.
156
+ Field is optional. Default: ['trades', 'book_snapshot', 'book_incremental'].
157
+ Allowed values: 'trades', 'book_snapshot', 'book_incremental'.
158
+ aggregate_info: bool
159
+ When True, returns a single pandas.DataFrame containing the selected datasets.
160
+ Field is optional. Default: False.
161
+
162
+ Returns
163
+ ----------------
164
+ dict or pandas.DataFrame
165
+ When aggregate_info is False (default): dictionary with the selected keys
166
+ ('trades', 'book_snapshot', 'book_incremental'), each mapping directly to
167
+ a pandas.DataFrame with all rows for the requested time window.
168
+ When aggregate_info is True: returns a single pandas.DataFrame with all
169
+ selected datasets combined.
170
+ """
171
+ normalized_market_type = market_type.lower()
172
+ if normalized_market_type not in VALID_MARKET_TYPES:
173
+ raise ValueError(
174
+ f"Invalid market_type: {market_type}. Valid options are: {VALID_MARKET_TYPES}"
175
+ )
176
+
177
+ invalid = set(select) - VALID_SELECT_OPTIONS
178
+ if invalid:
179
+ raise ValueError(f"Invalid select options: {invalid}. Valid options are: {VALID_SELECT_OPTIONS}")
180
+
181
+ parsed_start_time = self._parse_iso8601(start_time, "start_time")
182
+ parsed_end_time = self._parse_iso8601(end_time, "end_time")
183
+
184
+ if parsed_start_time >= parsed_end_time:
185
+ raise ValueError("start_time must be earlier than end_time.")
186
+
187
+ if parsed_end_time - parsed_start_time > MAX_MARKET_SCOPE_WINDOW:
188
+ raise ValueError("The maximum allowed range between start_time and end_time is 10 minutes.")
189
+
190
+ base_params = dict(symbol=symbol, start_time=start_time, end_time=end_time, limit=ROW_LIMIT)
191
+
192
+ scope = self._resolve_scope(parsed_end_time)
193
+
194
+ endpoint_map = {
195
+ "trades": f"{self.base_url}/marketdata/br/b3/book-scope/{scope}/{normalized_market_type}/trades",
196
+ "book_snapshot": f"{self.base_url}/marketdata/br/b3/book-scope/{scope}/{normalized_market_type}/book-snapshot",
197
+ "book_incremental": f"{self.base_url}/marketdata/br/b3/book-scope/{scope}/{normalized_market_type}/book-incremental",
198
+ }
199
+
200
+ result = {}
201
+ parquet_payloads = []
202
+ for key in select:
203
+ data = self._fetch_all_pages(endpoint_map[key], base_params)
204
+ if aggregate_info:
205
+ parquet_payloads.append(data)
206
+ else:
207
+ result[key] = pq.read_table(BytesIO(data)).to_pandas()
208
+
209
+ if aggregate_info:
210
+ return pq.read_table(BytesIO(self._combine_parquets(parquet_payloads))).to_pandas()
211
+
212
+ return result
@@ -0,0 +1,173 @@
1
+ from typing import Optional, List
2
+ from ..exceptions import BadResponse
3
+ import requests
4
+ from ..config import url_api_v1
5
+ from .authenticator import Authenticator
6
+
7
+ VALID_MARKET_TYPES = {"derivatives", "stocks", "options"}
8
+
9
+ class BrokerAnalytics:
10
+ """
11
+ This class provides broker analytics data (summary, top brokers, top tickers)
12
+ for a given market type.
13
+
14
+ * Main use case:
15
+
16
+ >>> from btgsolutions_dataservices import BrokerAnalytics
17
+ >>> broker_analytics = BrokerAnalytics(
18
+ >>> api_key='YOUR_API_KEY',
19
+ >>> market_type='stocks',
20
+ >>> )
21
+ >>> broker_analytics.get_summary(
22
+ >>> brokers=['85', '3'],
23
+ >>> tickers=['ABCB4', 'PETR4'],
24
+ >>> )
25
+ >>> broker_analytics.get_top_brokers(n=10, tickers=['ABCB4'])
26
+ >>> broker_analytics.get_top_tickers(n=10, brokers=['85', '3'])
27
+
28
+ Parameters
29
+ ----------------
30
+ api_key: str
31
+ User identification key.
32
+ Field is required.
33
+ market_type: str
34
+ Market type to query. One of: 'derivatives', 'stocks', 'options'.
35
+ Field is required.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ api_key: str,
41
+ market_type: str,
42
+ ):
43
+ self.api_key = api_key
44
+ self.token = Authenticator(self.api_key).token
45
+ self.headers = {"authorization": f"authorization {self.token}"}
46
+
47
+ market_type = market_type.strip().lower()
48
+ if market_type not in VALID_MARKET_TYPES:
49
+ raise ValueError(
50
+ f"Invalid market_type '{market_type}'. Must be one of: {sorted(VALID_MARKET_TYPES)}"
51
+ )
52
+ self.market_type = market_type
53
+
54
+ def _build_url(self, suffix: str) -> str:
55
+ return f"{url_api_v1}/marketdata/br/b3/realtime/broker-analytics/{self.market_type}/{suffix}"
56
+
57
+ def get_summary(
58
+ self,
59
+ brokers: List[str],
60
+ tickers: List[str],
61
+ side: Optional[str] = None,
62
+ ) -> dict:
63
+ """
64
+ Get day analytics summary for specific brokers and tickers.
65
+
66
+ Parameters
67
+ ----------
68
+ brokers : List[str]
69
+ List of broker names to filter. Required.
70
+ tickers : List[str]
71
+ List of ticker symbols to filter. Required.
72
+ side : str, optional
73
+ Filter by side ('buy' or 'sell'). If None, both sides are returned.
74
+
75
+ Returns
76
+ -------
77
+ dict
78
+ JSON response with broker analytics summary data.
79
+ """
80
+ params = {
81
+ "brokers": ",".join(brokers),
82
+ "tickers": ",".join(tickers),
83
+ }
84
+ if side is not None: params["side"] = side
85
+
86
+ url = self._build_url("summary")
87
+ response = requests.get(url, params=params, headers=self.headers, timeout=30)
88
+
89
+ if response.status_code != 200:
90
+ self._raise_error(response)
91
+
92
+ return response.json()
93
+
94
+ def get_top_brokers(
95
+ self,
96
+ n: int,
97
+ tickers: Optional[List[str]] = None,
98
+ side: Optional[str] = None,
99
+ ) -> dict:
100
+ """
101
+ Get top N brokers ranked by financial volume, with ticker breakdown.
102
+
103
+ Parameters
104
+ ----------
105
+ n : int
106
+ Number of top brokers to return. Required.
107
+ tickers : List[str], optional
108
+ List of ticker symbols to filter.
109
+ side : str, optional
110
+ Filter by side ('buy' or 'sell').
111
+
112
+ Returns
113
+ -------
114
+ dict
115
+ JSON response with top brokers and their top assets breakdown.
116
+ """
117
+ params = {"n": str(n)}
118
+
119
+ if tickers: params["tickers"] = ",".join(tickers)
120
+ if side is not None: params["side"] = side
121
+
122
+ url = self._build_url("top-brokers")
123
+ response = requests.get(url, params=params, headers=self.headers, timeout=30)
124
+
125
+ if response.status_code != 200:
126
+ self._raise_error(response)
127
+
128
+ return response.json()
129
+
130
+ def get_top_tickers(
131
+ self,
132
+ n: int,
133
+ brokers: Optional[List[str]] = None,
134
+ side: Optional[str] = None,
135
+ ) -> dict:
136
+ """
137
+ Get top N tickers ranked by financial volume, with broker breakdown.
138
+
139
+ Parameters
140
+ ----------
141
+ n : int
142
+ Number of top tickers to return. Required.
143
+ brokers : List[str], optional
144
+ List of broker names to filter.
145
+ side : str, optional
146
+ Filter by side ('buy' or 'sell').
147
+
148
+ Returns
149
+ -------
150
+ dict
151
+ JSON response with top tickers and their top brokers breakdown.
152
+ """
153
+ params = {"n": str(n)}
154
+
155
+ if brokers: params["brokers"] = ",".join(brokers)
156
+ if side is not None: params["side"] = side
157
+
158
+ url = self._build_url("top-tickers")
159
+ response = requests.get(url, params=params, headers=self.headers, timeout=30)
160
+
161
+ if response.status_code != 200:
162
+ self._raise_error(response)
163
+
164
+ return response.json()
165
+
166
+ @staticmethod
167
+ def _raise_error(response):
168
+ try:
169
+ error_body = response.json()
170
+ detail = error_body.get("detail", error_body.get("error", response.text))
171
+ except Exception:
172
+ detail = response.text
173
+ raise BadResponse(f"Error {response.status_code}: {detail}")
@@ -74,7 +74,7 @@ class HistoricalCandles:
74
74
  Parameters
75
75
  ----------------
76
76
  market_type: str
77
- Field is required. Allowed values: 'stocks' or 'derivatives'.
77
+ Field is required. Allowed values: 'stocks', 'derivatives' or 'indices'.
78
78
  ticker: str
79
79
  Ticker that needs to be returned.
80
80
  Field is required. Example: 'PETR4'.
@@ -103,8 +103,8 @@ class HistoricalCandles:
103
103
  Field is not required. Default: True.
104
104
  """
105
105
 
106
- if market_type not in ['stocks', 'derivatives']:
107
- raise MarketTypeError(f'Allowed values: "stocks" or "derivatives". Input value: "{market_type}".')
106
+ if market_type not in ['stocks', 'derivatives', 'indices']:
107
+ raise MarketTypeError(f'Allowed values: "stocks", "derivatives" or "indices". Input value: "{market_type}".')
108
108
 
109
109
  url = f"{url_apis_v3}/marketdata/history/candles/intraday/{market_type}?ticker={ticker}&corporate_events_adj={corporate_events_adj}&rmv_after_market={rmv_after_market}&timezone={timezone}&date={date}&candle={candle}&round={round}"
110
110
  response = requests.request("GET", url, headers=self.headers)
@@ -133,7 +133,7 @@ class HistoricalCandles:
133
133
  Parameters
134
134
  ----------------
135
135
  market_type: str
136
- Field is required. Allowed values: 'stocks' or 'derivatives'.
136
+ Field is required. Allowed values: 'stocks', 'derivatives' or 'indices'.
137
137
  ticker: str
138
138
  Ticker that needs to be returned.
139
139
  Field is required. Example: 'PETR4'.
@@ -162,8 +162,8 @@ class HistoricalCandles:
162
162
  Field is not required. Default: True.
163
163
  """
164
164
 
165
- if market_type not in ['stocks', 'derivatives']:
166
- raise MarketTypeError(f'Allowed values: "stocks" or "derivatives". Input value: "{market_type}".')
165
+ if market_type not in ['stocks', 'derivatives', 'indices']:
166
+ raise MarketTypeError(f'Allowed values: "stocks", "derivatives" or "indices". Input value: "{market_type}".')
167
167
 
168
168
  url = f"{url_apis_v3}/marketdata/history/candles/interday/{market_type}?ticker={ticker}&corporate_events_adj={corporate_events_adj}&rmv_after_market={rmv_after_market}&timezone={timezone}&start_date={start_date}&end_date={end_date}&round={round}"
169
169
  response = requests.request("GET", url, headers=self.headers)
@@ -174,6 +174,66 @@ class HistoricalCandles:
174
174
  response = json.loads(response.text)
175
175
  raise BadResponse(f'Error: {response.get("ApiClientError", "")}.\n{response.get("SuggestedAction", "")}')
176
176
 
177
+ def get_interday_history_candles_batch(
178
+ self,
179
+ market_type:str,
180
+ tickers:list,
181
+ start_date:str,
182
+ end_date:str,
183
+ corporate_events_adj:bool,
184
+ rmv_after_market:bool,
185
+ timezone:str,
186
+ raw_data:bool=False,
187
+ round:bool=True
188
+ ):
189
+ """
190
+ This method provides historical candles for a batch of tickers in determined period.
191
+
192
+ Parameters
193
+ ----------------
194
+ market_type: str
195
+ Field is required. Allowed values: 'stocks' or 'derivatives'.
196
+ tickers: list
197
+ Tickers that need to be returned.
198
+ Field is required. Example: ['PETR4', 'VALE3'].
199
+ start_date: string<date>
200
+ Start date of analysis. Format: "YYYY-MM-DD".
201
+ Field is required. Example: '2022-10-06'.
202
+ end_date: string<date>
203
+ End date of analysis. Format: "YYYY-MM-DD".
204
+ Field is required. Example: '2023-01-22'.
205
+ corporate_events_adj: bool
206
+ Corporate events adjustment.
207
+ Field is required. Allowed values: 'true' or 'false'.
208
+ rmv_after_market: bool
209
+ Remove trades after market close.
210
+ Field is required. Allowed values: 'true' or 'false'.
211
+ timezone: str
212
+ Timezone of the datetime.
213
+ Field is required. Allowed values: 'America/Sao_Paulo' or 'UTC'.
214
+ raw_data: bool
215
+ If false, returns data in a dataframe. If true, returns raw data.
216
+ Field is not required. Default: False.
217
+ round: bool
218
+ Apply rounding to prices.
219
+ Useful when corporate_events_adj=True, since price adjustment factors
220
+ may generate values with many decimal places.
221
+ Field is not required. Default: True.
222
+ """
223
+
224
+ if market_type not in ['stocks', 'derivatives']:
225
+ raise MarketTypeError(f'Allowed values: "stocks" or "derivatives". Input value: "{market_type}".')
226
+
227
+ url = f"{url_apis_v3}/marketdata/history/candles/interday/{market_type}/batch?corporate_events_adj={corporate_events_adj}&rmv_after_market={rmv_after_market}&timezone={timezone}&start_date={start_date}&end_date={end_date}&round={round}"
228
+ payload = {"tickers": tickers}
229
+ response = requests.request("POST", url, headers=self.headers, json=payload)
230
+ if response.status_code == 200:
231
+ response_data = json.loads(response.text)
232
+ return response_data if raw_data else pd.DataFrame(pd.concat([pd.DataFrame(v) for v in response_data.values()], ignore_index=True))
233
+
234
+ response = json.loads(response.text)
235
+ raise BadResponse(f'Error: {response.get("ApiClientError", "")}.\n{response.get("SuggestedAction", "")}')
236
+
177
237
  def get_available_tickers(
178
238
  self,
179
239
  market_type:str,
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: btgsolutions_dataservices_python_client
3
- Version: 3.2.10
3
+ Version: 3.2.12
4
4
  Summary: Python package containing several classes and data for extracting and manipulating market and trading data.
5
5
  Home-page: https://github.com/BTG-Pactual-Solutions/btgsolutions-dataservices-python-client
6
6
  Author: BTG Solutions Data Services powered by BTG Pactual Solutions
@@ -12,14 +12,6 @@ Requires-Dist: websocket-client>=1.8.0
12
12
  Requires-Dist: PyJWT>=2.8.0
13
13
  Requires-Dist: requests>=2.32.3
14
14
  Requires-Dist: pyarrow>=17.0.0
15
- Dynamic: author
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: home-page
19
- Dynamic: license-file
20
- Dynamic: requires-dist
21
- Dynamic: requires-python
22
- Dynamic: summary
23
15
 
24
16
  # BTG Solutions - Data Services
25
17
 
@@ -280,6 +272,14 @@ hist_candles = btg.HistoricalCandles(api_key='YOUR_API_KEY')
280
272
  hist_candles.get_intraday_history_candles(ticker='PETR4', market_type='stocks', corporate_events_adj=True, date='2023-10-06', candle='1m', rmv_after_market=True, timezone='UTC', raw_data=False, round=True)
281
273
  ```
282
274
 
275
+ ##### Interday Batch
276
+
277
+ ```python
278
+ import btgsolutions_dataservices as btg
279
+ hist_candles = btg.HistoricalCandles(api_key='YOUR_API_KEY')
280
+ hist_candles.get_interday_history_candles_batch(market_type='stocks', tickers=['PETR4', 'VALE3'], start_date='2023-10-01', end_date='2023-10-13', corporate_events_adj=True, rmv_after_market=True, timezone='UTC', raw_data=False, round=True)
281
+ ```
282
+
283
283
  ##### Available Tickers
284
284
 
285
285
  ```python
@@ -503,6 +503,40 @@ broker_reference = btg.BrokerReference(api_key='YOUR_API_KEY')
503
503
  broker_reference.get()
504
504
  ```
505
505
 
506
+ #### Book Scope
507
+
508
+ ```python
509
+ import btgsolutions_dataservices as btg
510
+ book_scope = btg.BookScope(api_key='YOUR_API_KEY')
511
+
512
+ result = book_scope.get(
513
+ symbol='DOLM26',
514
+ market_type='derivatives',
515
+ start_time='2026-05-28T14:12:00Z',
516
+ end_time='2026-05-28T14:15:00Z',
517
+ select=['trades', 'book_snapshot', 'book_incremental'], # choose one, two, or all three
518
+ )
519
+
520
+ single_file_result = book_scope.get(
521
+ symbol='DOLM26',
522
+ market_type='derivatives',
523
+ start_time='2026-05-28T14:12:00Z',
524
+ end_time='2026-05-28T14:15:00Z',
525
+ select=['trades', 'book_snapshot', 'book_incremental'],
526
+ aggregate_info=True,
527
+ )
528
+ ```
529
+
530
+ #### Broker Analytics
531
+
532
+ ```python
533
+ import btgsolutions_dataservices as btg
534
+ broker_analytics = btg.BrokerAnalytics(api_key='YOUR_API_KEY', market_type='stocks')
535
+ summary = broker_analytics.get_summary(brokers=['85', '3'], tickers=['PETR4', 'ABCB4'])
536
+ top_brokers = broker_analytics.get_top_brokers(n=10)
537
+ top_tickers = broker_analytics.get_top_tickers(n=10, brokers=['85', '3'])
538
+ ```
539
+
506
540
  #### Ticker Reference Data
507
541
 
508
542
  ```python
@@ -9,6 +9,8 @@ btgsolutions_dataservices/config.py
9
9
  btgsolutions_dataservices/exceptions.py
10
10
  btgsolutions_dataservices/rest/__init__.py
11
11
  btgsolutions_dataservices/rest/authenticator.py
12
+ btgsolutions_dataservices/rest/book_scope.py
13
+ btgsolutions_dataservices/rest/broker_analytics.py
12
14
  btgsolutions_dataservices/rest/broker_reference.py
13
15
  btgsolutions_dataservices/rest/bulk_data.py
14
16
  btgsolutions_dataservices/rest/company_data.py