luxorasap 0.1.0__tar.gz → 0.1.2__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.
- luxorasap-0.1.2/PKG-INFO +241 -0
- luxorasap-0.1.2/README.md +196 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/pyproject.toml +4 -2
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/__init__.py +1 -1
- luxorasap-0.1.2/src/luxorasap/btgapi/__init__.py +16 -0
- luxorasap-0.1.2/src/luxorasap/btgapi/auth.py +58 -0
- luxorasap-0.1.2/src/luxorasap/btgapi/reports.py +189 -0
- luxorasap-0.1.2/src/luxorasap/btgapi/trades.py +181 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/ingest/cloud/__init__.py +2 -2
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/utils/dataframe/__init__.py +3 -1
- luxorasap-0.1.2/src/luxorasap/utils/dataframe/reader.py +19 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/utils/dataframe/transforms.py +0 -2
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/utils/storage/blob.py +7 -3
- luxorasap-0.1.2/src/luxorasap.egg-info/PKG-INFO +241 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap.egg-info/SOURCES.txt +8 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap.egg-info/requires.txt +2 -0
- luxorasap-0.1.2/tests/test_btgapi_auth.py +17 -0
- luxorasap-0.1.2/tests/test_btgapi_reports.py +62 -0
- luxorasap-0.1.2/tests/test_btgapi_trades.py +37 -0
- luxorasap-0.1.0/PKG-INFO +0 -78
- luxorasap-0.1.0/README.md +0 -35
- luxorasap-0.1.0/src/luxorasap.egg-info/PKG-INFO +0 -78
- {luxorasap-0.1.0 → luxorasap-0.1.2}/setup.cfg +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/datareader/__init__.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/datareader/core.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/ingest/__init__.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/ingest/legacy_local/dataloader.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/utils/__init__.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap/utils/storage/__init__.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap.egg-info/dependency_links.txt +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap.egg-info/entry_points.txt +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/src/luxorasap.egg-info/top_level.txt +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/tests/test_datareader.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/tests/test_ingest_cloud.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/tests/test_ingest_legacy_local.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/tests/test_utils_dataframe.py +0 -0
- {luxorasap-0.1.0 → luxorasap-0.1.2}/tests/test_utils_storage.py +0 -0
luxorasap-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: luxorasap
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Luxor’s unified toolbox for data ingestion, querying and analytics.
|
|
5
|
+
Author-email: Luxor Group <backoffice@luxor.com.br>
|
|
6
|
+
License: Proprietary – All rights reserved
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: Other/Proprietary License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: pandas>=2.2
|
|
13
|
+
Requires-Dist: numpy>=1.25
|
|
14
|
+
Requires-Dist: loguru>=0.7
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0
|
|
16
|
+
Requires-Dist: azure-storage-blob>=12.19
|
|
17
|
+
Requires-Dist: pyarrow>=15.0
|
|
18
|
+
Requires-Dist: requests>=2.32
|
|
19
|
+
Requires-Dist: pydantic>=2.7
|
|
20
|
+
Requires-Dist: scipy>=1.13
|
|
21
|
+
Requires-Dist: openpyxl
|
|
22
|
+
Provides-Extra: storage
|
|
23
|
+
Requires-Dist: azure-storage-blob>=12.19; extra == "storage"
|
|
24
|
+
Requires-Dist: pyarrow>=15.0; extra == "storage"
|
|
25
|
+
Provides-Extra: dataframe
|
|
26
|
+
Requires-Dist: pandas>=2.2; extra == "dataframe"
|
|
27
|
+
Provides-Extra: datareader
|
|
28
|
+
Requires-Dist: luxorasap[dataframe,storage]; extra == "datareader"
|
|
29
|
+
Requires-Dist: numpy>=1.25; extra == "datareader"
|
|
30
|
+
Requires-Dist: scipy>=1.13; extra == "datareader"
|
|
31
|
+
Provides-Extra: ingest
|
|
32
|
+
Requires-Dist: luxorasap[dataframe,storage]; extra == "ingest"
|
|
33
|
+
Requires-Dist: pandas>=2.2; extra == "ingest"
|
|
34
|
+
Provides-Extra: btgapi
|
|
35
|
+
Requires-Dist: requests>=2.32; extra == "btgapi"
|
|
36
|
+
Requires-Dist: pydantic>=2.7; extra == "btgapi"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=8.2; extra == "dev"
|
|
39
|
+
Requires-Dist: requests-mock>=1.11; extra == "dev"
|
|
40
|
+
Requires-Dist: black>=24.4.0; extra == "dev"
|
|
41
|
+
Requires-Dist: isort>=5.13; extra == "dev"
|
|
42
|
+
Requires-Dist: bumpver>=2024.3; extra == "dev"
|
|
43
|
+
Requires-Dist: pre-commit>=3.7; extra == "dev"
|
|
44
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
45
|
+
|
|
46
|
+
# 📚 Documentação LuxorASAP
|
|
47
|
+
|
|
48
|
+
> Guia do desenvolvedor para os subpacotes **datareader**, **ingest**, **btgapi** e **utils**.
|
|
49
|
+
>
|
|
50
|
+
> • Instalação rápida • Visão arquitetural • APIs detalhadas • Exemplos de uso • Extras opcional
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Índice
|
|
55
|
+
|
|
56
|
+
1. [Visão Geral](#visao-geral)
|
|
57
|
+
2. [Instalação](#instalacao)
|
|
58
|
+
3. [utils](#utils)
|
|
59
|
+
4. [datareader](#datareader)
|
|
60
|
+
5. [ingest](#ingest)
|
|
61
|
+
6. [btgapi](#btgapi)
|
|
62
|
+
7. [Roadmap & Contribuições](#roadmap)
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 1. Visão Geral
|
|
67
|
+
|
|
68
|
+
LuxorASAP é o *toolbox* unificado da Luxor para ingestão, consulta e automação de dados financeiros.
|
|
69
|
+
|
|
70
|
+
| Subpacote | Função‑chave | Extras PyPI |
|
|
71
|
+
| -------------- | --------------------------------------------------------------------- | ---------------------- |
|
|
72
|
+
| **utils** | Utilidades puras (I/O ADLS, transformação de DataFrame, decorators) | `storage`, `dataframe` |
|
|
73
|
+
| **datareader** | Consulta de preços/tabelas no data lake via `LuxorQuery` | `datareader` |
|
|
74
|
+
| **ingest** | Carga de dados nova (parquet/zip/excel) em ADLS + loader legado | `ingest` |
|
|
75
|
+
| **btgapi** | Wrapper autenticado para as APIs do BTG Pactual (relatórios & trades) | `btgapi` |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 2. Instalação
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# core + leitura de dados
|
|
83
|
+
pip install luxorasap[datareader]
|
|
84
|
+
|
|
85
|
+
# tudo incluído (leitura, ingest, btg)
|
|
86
|
+
pip install luxorasap[datareader,ingest,btgapi]
|
|
87
|
+
|
|
88
|
+
# desenvolvimento
|
|
89
|
+
pip install -e ".[dev]"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Extras podem ser combinados à vontade (`luxorasap[storage]`, etc.).
|
|
93
|
+
|
|
94
|
+
Configuração obrigatória do **ADLS**:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=..."
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 3. utils
|
|
103
|
+
|
|
104
|
+
Camada de utilidades **sem dependências internas**.
|
|
105
|
+
|
|
106
|
+
### 3.1 storage.BlobParquetClient
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from luxorasap.utils.storage import BlobParquetClient
|
|
110
|
+
client = BlobParquetClient(container="luxorasap")
|
|
111
|
+
|
|
112
|
+
# write
|
|
113
|
+
client.write_df(df, "bronze/parquet/mytable.parquet")
|
|
114
|
+
|
|
115
|
+
# read (tuple -> DataFrame, success_flag)
|
|
116
|
+
df, ok = client.read_df("bronze/parquet/mytable.parquet")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 3.2 dataframe
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from luxorasap.utils.dataframe import prep_for_save, persist_column_formatting, read_bytes
|
|
123
|
+
|
|
124
|
+
df2 = prep_for_save(df, index=True, index_name="ID", normalize=True)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3.3 decorators & misc
|
|
128
|
+
|
|
129
|
+
`with_retry`, `chunkify`, `timer`… ficam em *utils.helpers* (caso precise).
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 4. datareader
|
|
134
|
+
|
|
135
|
+
### 4.1 Visão rápida
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from luxorasap.datareader import LuxorQuery
|
|
139
|
+
lq = LuxorQuery()
|
|
140
|
+
|
|
141
|
+
# DataFrame completo
|
|
142
|
+
df = lq.get_table("assets")
|
|
143
|
+
|
|
144
|
+
# Série de preços diária
|
|
145
|
+
aapl = lq.get_prices("aapl us equity", start="2025-01-01", end="2025-03-31")
|
|
146
|
+
|
|
147
|
+
# Preço pontual
|
|
148
|
+
price = lq.get_price("aapl us equity", on="2025-03-31")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
*Caching*: `@lru_cache(maxsize=32)` evita hits repetidos ao Blob.
|
|
152
|
+
|
|
153
|
+
### 4.2 Métodos-chave
|
|
154
|
+
|
|
155
|
+
| Método | Descrição |
|
|
156
|
+
| ----------------------------------------------- | ----------------------------- |
|
|
157
|
+
| `table_exists(name)` | checa metadados no ADLS |
|
|
158
|
+
| `get_table(name)` | DataFrame completo (cached) |
|
|
159
|
+
| `get_prices(asset, start, end, column="Price")` | `pd.Series` |
|
|
160
|
+
| `get_price(asset, on)` | preço pontual (float ou None) |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 5. ingest
|
|
165
|
+
|
|
166
|
+
### 5.1 ingest.cloud (novo)
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from luxorasap.ingest import save_table, incremental_load
|
|
170
|
+
from luxorasap.datareader import LuxorQuery
|
|
171
|
+
|
|
172
|
+
save_table("trades", df)
|
|
173
|
+
|
|
174
|
+
lq = LuxorQuery()
|
|
175
|
+
incremental_load(lq, "prices_daily", df_new, increment_column="Date")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 5.2 ingest.legacy\_local
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from luxorasap.ingest import DataLoader # Deprecado – ainda funcional
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
*Decoration*: ao importar `DataLoader` você verá `DeprecationWarning`.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 6. btgapi
|
|
189
|
+
|
|
190
|
+
### 6.1 Autenticação
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from luxorasap.btgapi import get_access_token
|
|
194
|
+
TOKEN = get_access_token(test_env=True)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 6.2 Relatórios – Portfolio & Investor Transactions
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from luxorasap.btgapi.reports import (
|
|
201
|
+
request_portfolio, await_report_ticket_result, process_zip_to_dfs,
|
|
202
|
+
request_investors_transactions_report,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
ticket = request_portfolio(TOKEN, "LUXOR FUND - CLASS A",
|
|
206
|
+
start=dt.date(2025,1,1), end=dt.date(2025,1,31))
|
|
207
|
+
zip_bytes = await_report_ticket_result(TOKEN, ticket)
|
|
208
|
+
carteiras = process_zip_to_dfs(zip_bytes)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 6.3 Trades offshore
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from luxorasap.btgapi.trades import (
|
|
215
|
+
submit_offshore_equity_trades,
|
|
216
|
+
await_transaction_ticket_result,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
ticket = submit_offshore_equity_trades(TOKEN, trades=[{...}], test_env=True)
|
|
220
|
+
status_df = await_transaction_ticket_result(TOKEN, ticket, test_env=True)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 6.4 Extras
|
|
224
|
+
|
|
225
|
+
* `BTGApiError` — exceção customizada para qualquer falha.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 7. Roadmap & Contribuições
|
|
230
|
+
|
|
231
|
+
* **Remover** `ingest.legacy_local` quando não houver mais dependências.
|
|
232
|
+
* Suporte a partições Parquet (delta‑like) na gravação.
|
|
233
|
+
* Adicionar `pydantic` para validar contratos BTG.
|
|
234
|
+
* Pull requests bem‑vindos! Rode `make lint && pytest -q` antes de enviar.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### Contatos
|
|
239
|
+
|
|
240
|
+
* Dados / Back‑Office – [backoffice@luxor.com.br](mailto:backoffice@luxor.com.br)
|
|
241
|
+
* Mantenedor principal – Sergio
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# 📚 Documentação LuxorASAP
|
|
2
|
+
|
|
3
|
+
> Guia do desenvolvedor para os subpacotes **datareader**, **ingest**, **btgapi** e **utils**.
|
|
4
|
+
>
|
|
5
|
+
> • Instalação rápida • Visão arquitetural • APIs detalhadas • Exemplos de uso • Extras opcional
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Índice
|
|
10
|
+
|
|
11
|
+
1. [Visão Geral](#visao-geral)
|
|
12
|
+
2. [Instalação](#instalacao)
|
|
13
|
+
3. [utils](#utils)
|
|
14
|
+
4. [datareader](#datareader)
|
|
15
|
+
5. [ingest](#ingest)
|
|
16
|
+
6. [btgapi](#btgapi)
|
|
17
|
+
7. [Roadmap & Contribuições](#roadmap)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Visão Geral
|
|
22
|
+
|
|
23
|
+
LuxorASAP é o *toolbox* unificado da Luxor para ingestão, consulta e automação de dados financeiros.
|
|
24
|
+
|
|
25
|
+
| Subpacote | Função‑chave | Extras PyPI |
|
|
26
|
+
| -------------- | --------------------------------------------------------------------- | ---------------------- |
|
|
27
|
+
| **utils** | Utilidades puras (I/O ADLS, transformação de DataFrame, decorators) | `storage`, `dataframe` |
|
|
28
|
+
| **datareader** | Consulta de preços/tabelas no data lake via `LuxorQuery` | `datareader` |
|
|
29
|
+
| **ingest** | Carga de dados nova (parquet/zip/excel) em ADLS + loader legado | `ingest` |
|
|
30
|
+
| **btgapi** | Wrapper autenticado para as APIs do BTG Pactual (relatórios & trades) | `btgapi` |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Instalação
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# core + leitura de dados
|
|
38
|
+
pip install luxorasap[datareader]
|
|
39
|
+
|
|
40
|
+
# tudo incluído (leitura, ingest, btg)
|
|
41
|
+
pip install luxorasap[datareader,ingest,btgapi]
|
|
42
|
+
|
|
43
|
+
# desenvolvimento
|
|
44
|
+
pip install -e ".[dev]"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Extras podem ser combinados à vontade (`luxorasap[storage]`, etc.).
|
|
48
|
+
|
|
49
|
+
Configuração obrigatória do **ADLS**:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=..."
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 3. utils
|
|
58
|
+
|
|
59
|
+
Camada de utilidades **sem dependências internas**.
|
|
60
|
+
|
|
61
|
+
### 3.1 storage.BlobParquetClient
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from luxorasap.utils.storage import BlobParquetClient
|
|
65
|
+
client = BlobParquetClient(container="luxorasap")
|
|
66
|
+
|
|
67
|
+
# write
|
|
68
|
+
client.write_df(df, "bronze/parquet/mytable.parquet")
|
|
69
|
+
|
|
70
|
+
# read (tuple -> DataFrame, success_flag)
|
|
71
|
+
df, ok = client.read_df("bronze/parquet/mytable.parquet")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 3.2 dataframe
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from luxorasap.utils.dataframe import prep_for_save, persist_column_formatting, read_bytes
|
|
78
|
+
|
|
79
|
+
df2 = prep_for_save(df, index=True, index_name="ID", normalize=True)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3.3 decorators & misc
|
|
83
|
+
|
|
84
|
+
`with_retry`, `chunkify`, `timer`… ficam em *utils.helpers* (caso precise).
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 4. datareader
|
|
89
|
+
|
|
90
|
+
### 4.1 Visão rápida
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from luxorasap.datareader import LuxorQuery
|
|
94
|
+
lq = LuxorQuery()
|
|
95
|
+
|
|
96
|
+
# DataFrame completo
|
|
97
|
+
df = lq.get_table("assets")
|
|
98
|
+
|
|
99
|
+
# Série de preços diária
|
|
100
|
+
aapl = lq.get_prices("aapl us equity", start="2025-01-01", end="2025-03-31")
|
|
101
|
+
|
|
102
|
+
# Preço pontual
|
|
103
|
+
price = lq.get_price("aapl us equity", on="2025-03-31")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
*Caching*: `@lru_cache(maxsize=32)` evita hits repetidos ao Blob.
|
|
107
|
+
|
|
108
|
+
### 4.2 Métodos-chave
|
|
109
|
+
|
|
110
|
+
| Método | Descrição |
|
|
111
|
+
| ----------------------------------------------- | ----------------------------- |
|
|
112
|
+
| `table_exists(name)` | checa metadados no ADLS |
|
|
113
|
+
| `get_table(name)` | DataFrame completo (cached) |
|
|
114
|
+
| `get_prices(asset, start, end, column="Price")` | `pd.Series` |
|
|
115
|
+
| `get_price(asset, on)` | preço pontual (float ou None) |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 5. ingest
|
|
120
|
+
|
|
121
|
+
### 5.1 ingest.cloud (novo)
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from luxorasap.ingest import save_table, incremental_load
|
|
125
|
+
from luxorasap.datareader import LuxorQuery
|
|
126
|
+
|
|
127
|
+
save_table("trades", df)
|
|
128
|
+
|
|
129
|
+
lq = LuxorQuery()
|
|
130
|
+
incremental_load(lq, "prices_daily", df_new, increment_column="Date")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 5.2 ingest.legacy\_local
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from luxorasap.ingest import DataLoader # Deprecado – ainda funcional
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
*Decoration*: ao importar `DataLoader` você verá `DeprecationWarning`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 6. btgapi
|
|
144
|
+
|
|
145
|
+
### 6.1 Autenticação
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from luxorasap.btgapi import get_access_token
|
|
149
|
+
TOKEN = get_access_token(test_env=True)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 6.2 Relatórios – Portfolio & Investor Transactions
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from luxorasap.btgapi.reports import (
|
|
156
|
+
request_portfolio, await_report_ticket_result, process_zip_to_dfs,
|
|
157
|
+
request_investors_transactions_report,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
ticket = request_portfolio(TOKEN, "LUXOR FUND - CLASS A",
|
|
161
|
+
start=dt.date(2025,1,1), end=dt.date(2025,1,31))
|
|
162
|
+
zip_bytes = await_report_ticket_result(TOKEN, ticket)
|
|
163
|
+
carteiras = process_zip_to_dfs(zip_bytes)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 6.3 Trades offshore
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from luxorasap.btgapi.trades import (
|
|
170
|
+
submit_offshore_equity_trades,
|
|
171
|
+
await_transaction_ticket_result,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
ticket = submit_offshore_equity_trades(TOKEN, trades=[{...}], test_env=True)
|
|
175
|
+
status_df = await_transaction_ticket_result(TOKEN, ticket, test_env=True)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 6.4 Extras
|
|
179
|
+
|
|
180
|
+
* `BTGApiError` — exceção customizada para qualquer falha.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 7. Roadmap & Contribuições
|
|
185
|
+
|
|
186
|
+
* **Remover** `ingest.legacy_local` quando não houver mais dependências.
|
|
187
|
+
* Suporte a partições Parquet (delta‑like) na gravação.
|
|
188
|
+
* Adicionar `pydantic` para validar contratos BTG.
|
|
189
|
+
* Pull requests bem‑vindos! Rode `make lint && pytest -q` antes de enviar.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### Contatos
|
|
194
|
+
|
|
195
|
+
* Dados / Back‑Office – [backoffice@luxor.com.br](mailto:backoffice@luxor.com.br)
|
|
196
|
+
* Mantenedor principal – Sergio
|
|
@@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta"
|
|
|
10
10
|
#############################
|
|
11
11
|
[project]
|
|
12
12
|
name = "luxorasap"
|
|
13
|
-
version = "0.1.
|
|
13
|
+
version = "0.1.2"
|
|
14
14
|
description = "Luxor’s unified toolbox for data ingestion, querying and analytics."
|
|
15
15
|
readme = "README.md"
|
|
16
16
|
requires-python = ">=3.9"
|
|
@@ -36,6 +36,7 @@ dependencies = [
|
|
|
36
36
|
"requests>=2.32", # usado por btgapi
|
|
37
37
|
"pydantic>=2.7", # usado por btgapi
|
|
38
38
|
"scipy>=1.13",
|
|
39
|
+
"openpyxl"
|
|
39
40
|
]
|
|
40
41
|
|
|
41
42
|
[project.optional-dependencies]
|
|
@@ -47,6 +48,7 @@ ingest = ["luxorasap[storage,dataframe]", "pandas>=2.2"]
|
|
|
47
48
|
btgapi = ["requests>=2.32", "pydantic>=2.7"]
|
|
48
49
|
dev = [
|
|
49
50
|
"pytest>=8.2",
|
|
51
|
+
"requests-mock>=1.11",
|
|
50
52
|
"black>=24.4.0",
|
|
51
53
|
"isort>=5.13",
|
|
52
54
|
"bumpver>=2024.3",
|
|
@@ -72,7 +74,7 @@ exclude = ["tests*"]
|
|
|
72
74
|
# bumpver (sem-ver)
|
|
73
75
|
#############################
|
|
74
76
|
[tool.bumpver]
|
|
75
|
-
current_version = "0.1.
|
|
77
|
+
current_version = "0.1.2"
|
|
76
78
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
77
79
|
|
|
78
80
|
# regex explícito – obrigatório no bumpver 2024+
|
|
@@ -13,7 +13,7 @@ from types import ModuleType
|
|
|
13
13
|
try:
|
|
14
14
|
__version__: str = metadata.version(__name__)
|
|
15
15
|
except metadata.PackageNotFoundError: # editable install
|
|
16
|
-
__version__ = "0.1.
|
|
16
|
+
__version__ = "0.1.2"
|
|
17
17
|
|
|
18
18
|
# ─── Lazy loader ─────────────────────────────────────────────────
|
|
19
19
|
def __getattr__(name: str) -> ModuleType:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Wrapper para as APIs do BTG Pactual."""
|
|
2
|
+
|
|
3
|
+
from .auth import get_access_token, BTGApiError
|
|
4
|
+
from .reports import request_portfolio, await_report_ticket_result, process_zip_to_dfs, request_investors_transactions_report
|
|
5
|
+
from .trades import submit_offshore_equity_trades, await_transaction_ticket_result
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"BTGApiError",
|
|
9
|
+
"get_access_token",
|
|
10
|
+
"request_portfolio",
|
|
11
|
+
"await_report_ticket_result",
|
|
12
|
+
"submit_offshore_equity_trades",
|
|
13
|
+
"await_transaction_ticket_result",
|
|
14
|
+
"process_zip_to_dfs",
|
|
15
|
+
"request_investors_transactions_report"
|
|
16
|
+
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import requests
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
from loguru import logger
|
|
5
|
+
|
|
6
|
+
__all__ = ["BTGApiError", "get_access_token"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BTGApiError(Exception):
|
|
10
|
+
"""Erro genérico da API do BTG."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_access_token(*, client_id=None, client_secret=None, test_env: bool = True,
|
|
14
|
+
timeout: int = 20) -> str:
|
|
15
|
+
"""Obtém JWT válido por ~1 h para autenticação nas APIs BTG.
|
|
16
|
+
Args:
|
|
17
|
+
client_id: ID do cliente (opcional, lê de env var se None).
|
|
18
|
+
client_secret: Segredo do cliente (opcional, lê de env var se None).
|
|
19
|
+
test_env: Ambiente de teste (True) ou produção (False).
|
|
20
|
+
timeout: Timeout da requisição em segundos.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Token de acesso.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
BTGApiError: Se as credenciais não estiverem disponíveis ou a requisição falhar.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
if not client_id or not client_secret:
|
|
30
|
+
load_dotenv()
|
|
31
|
+
client_id = os.getenv("BTG_CLIENT_ID")
|
|
32
|
+
client_secret = os.getenv("BTG_CLIENT_SECRET")
|
|
33
|
+
if not client_id or not client_secret:
|
|
34
|
+
raise BTGApiError("BTG_CLIENT_ID ou BTG_CLIENT_SECRET não definidos no ambiente")
|
|
35
|
+
|
|
36
|
+
url = (
|
|
37
|
+
"https://funds-uat.btgpactual.com/connect/token"
|
|
38
|
+
if test_env
|
|
39
|
+
else "https://funds.btgpactual.com/connect/token"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
resp = requests.post(
|
|
43
|
+
url,
|
|
44
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
45
|
+
data={
|
|
46
|
+
"grant_type": "client_credentials",
|
|
47
|
+
"client_id": client_id,
|
|
48
|
+
"client_secret": client_secret,
|
|
49
|
+
},
|
|
50
|
+
timeout=timeout,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if resp.ok:
|
|
54
|
+
token = resp.json().get("access_token")
|
|
55
|
+
len_token = len(token) if token else None
|
|
56
|
+
logger.debug(f"Token BTG obtido (len={len_token})")
|
|
57
|
+
return token or ""
|
|
58
|
+
raise BTGApiError(f"Falha ao autenticar: HTTP {resp.status_code} – {resp.text}")
|