lambda-risk 0.0.0.1__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.
- lambda_risk-0.0.0.1/PKG-INFO +17 -0
- lambda_risk-0.0.0.1/README.md +3 -0
- lambda_risk-0.0.0.1/lambda-risk/__init__.py +0 -0
- lambda_risk-0.0.0.1/lambda-risk/client.py +396 -0
- lambda_risk-0.0.0.1/lambda_risk.egg-info/PKG-INFO +17 -0
- lambda_risk-0.0.0.1/lambda_risk.egg-info/SOURCES.txt +9 -0
- lambda_risk-0.0.0.1/lambda_risk.egg-info/dependency_links.txt +1 -0
- lambda_risk-0.0.0.1/lambda_risk.egg-info/requires.txt +6 -0
- lambda_risk-0.0.0.1/lambda_risk.egg-info/top_level.txt +1 -0
- lambda_risk-0.0.0.1/setup.cfg +4 -0
- lambda_risk-0.0.0.1/setup.py +24 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lambda-risk
|
|
3
|
+
Version: 0.0.0.1
|
|
4
|
+
Description-Content-Type: text/markdown
|
|
5
|
+
Requires-Dist: pandas>=2.2.3
|
|
6
|
+
Requires-Dist: requests>=2.32.3
|
|
7
|
+
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: scipy
|
|
9
|
+
Requires-Dist: PyQuantimClient
|
|
10
|
+
Requires-Dist: holidays
|
|
11
|
+
Dynamic: description
|
|
12
|
+
Dynamic: description-content-type
|
|
13
|
+
Dynamic: requires-dist
|
|
14
|
+
|
|
15
|
+
# Lambda SDK
|
|
16
|
+
|
|
17
|
+
Sistema integral de gestión de riesgo financiero
|
|
File without changes
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Config:
|
|
15
|
+
base_url: str
|
|
16
|
+
token: str
|
|
17
|
+
timeout: int = 120
|
|
18
|
+
poll_seconds: int = 3
|
|
19
|
+
max_polls: int = 120
|
|
20
|
+
|
|
21
|
+
def __post_init__(self) -> None:
|
|
22
|
+
self.base_url = self.base_url.rstrip("/")
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def headers(self) -> dict[str, str]:
|
|
26
|
+
return {
|
|
27
|
+
"Authorization": f"Bearer {self.token}",
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def _format_elapsed(self, seconds: float) -> str:
|
|
32
|
+
if seconds < 60:
|
|
33
|
+
return f"{seconds:.2f}s"
|
|
34
|
+
minutes, remaining_seconds = divmod(seconds, 60)
|
|
35
|
+
return f"{int(minutes)}m {remaining_seconds:.2f}s"
|
|
36
|
+
|
|
37
|
+
def _print(self, message: str, log: bool = False) -> None:
|
|
38
|
+
if log:
|
|
39
|
+
print(f"[LOG] {message}")
|
|
40
|
+
|
|
41
|
+
def _get(self, endpoint: str, params: Optional[dict[str, Any]] = None) -> dict[str, Any]:
|
|
42
|
+
response = requests.get(
|
|
43
|
+
f"{self.base_url}/{endpoint.lstrip('/')}",
|
|
44
|
+
headers=self.headers,
|
|
45
|
+
params=params,
|
|
46
|
+
timeout=self.timeout,
|
|
47
|
+
)
|
|
48
|
+
response.raise_for_status()
|
|
49
|
+
return response.json()
|
|
50
|
+
|
|
51
|
+
def _post(self, endpoint: str, payload: Optional[dict[str, Any]] = None) -> dict[str, Any]:
|
|
52
|
+
response = requests.post(
|
|
53
|
+
f"{self.base_url}/{endpoint.lstrip('/')}",
|
|
54
|
+
headers=self.headers,
|
|
55
|
+
json=payload or {},
|
|
56
|
+
timeout=self.timeout,
|
|
57
|
+
)
|
|
58
|
+
response.raise_for_status()
|
|
59
|
+
return response.json()
|
|
60
|
+
|
|
61
|
+
def _start_export(self, endpoint_export: str, payload: dict[str, Any], log: bool = False) -> dict[str, Any]:
|
|
62
|
+
started_at = time.perf_counter()
|
|
63
|
+
export_data = self._post(endpoint_export, payload)
|
|
64
|
+
elapsed = self._format_elapsed(time.perf_counter() - started_at)
|
|
65
|
+
self._print(
|
|
66
|
+
f"API create_export={elapsed} | endpoint=/{endpoint_export.lstrip('/')} | module={export_data['module_name']} "
|
|
67
|
+
f"| export_id={export_data['export_id']} | job_id={export_data['job_id']}",
|
|
68
|
+
log,
|
|
69
|
+
)
|
|
70
|
+
return export_data
|
|
71
|
+
|
|
72
|
+
def get_health(self) -> dict[str, Any]:
|
|
73
|
+
response = requests.get(f"{self.base_url}/health", timeout=self.timeout)
|
|
74
|
+
response.raise_for_status()
|
|
75
|
+
return response.json()
|
|
76
|
+
|
|
77
|
+
def get_export_status(
|
|
78
|
+
self,
|
|
79
|
+
module_name: str,
|
|
80
|
+
job_id: str,
|
|
81
|
+
export_id: str,
|
|
82
|
+
export_format: str = "parquet",
|
|
83
|
+
) -> dict[str, Any]:
|
|
84
|
+
return self._get(
|
|
85
|
+
f"exports/{module_name}/{job_id}",
|
|
86
|
+
params={"export_id": export_id, "format": export_format},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def wait_until_done(
|
|
90
|
+
self,
|
|
91
|
+
module_name: str,
|
|
92
|
+
job_id: str,
|
|
93
|
+
export_id: str,
|
|
94
|
+
export_format: str = "parquet",
|
|
95
|
+
log: bool = False,
|
|
96
|
+
) -> dict[str, Any]:
|
|
97
|
+
started_at = time.perf_counter()
|
|
98
|
+
|
|
99
|
+
for attempt in range(1, self.max_polls + 1):
|
|
100
|
+
status_data = self.get_export_status(module_name, job_id, export_id, export_format)
|
|
101
|
+
status = status_data["status"]
|
|
102
|
+
state = status_data["state"]
|
|
103
|
+
elapsed = self._format_elapsed(time.perf_counter() - started_at)
|
|
104
|
+
|
|
105
|
+
if status == "done":
|
|
106
|
+
self._print(
|
|
107
|
+
f"API export_job={elapsed} | polls={attempt} | module={module_name} | state={state}",
|
|
108
|
+
log,
|
|
109
|
+
)
|
|
110
|
+
return status_data
|
|
111
|
+
if status == "failed":
|
|
112
|
+
self._print(f"API export_job_failed={elapsed} | polls={attempt} | module={module_name}", log)
|
|
113
|
+
raise RuntimeError(
|
|
114
|
+
f"Export failed. error_result={status_data.get('error_result')} "
|
|
115
|
+
f"errors={status_data.get('errors')}"
|
|
116
|
+
)
|
|
117
|
+
time.sleep(self.poll_seconds)
|
|
118
|
+
|
|
119
|
+
raise TimeoutError("El export no termino dentro del tiempo maximo configurado.")
|
|
120
|
+
|
|
121
|
+
def _read_files(self, files: list[dict[str, Any]], log: bool = False):
|
|
122
|
+
import pandas as pd
|
|
123
|
+
|
|
124
|
+
if not files:
|
|
125
|
+
self._print("No hay archivos para descargar", log)
|
|
126
|
+
return pd.DataFrame()
|
|
127
|
+
|
|
128
|
+
dataframes: list[pd.DataFrame] = []
|
|
129
|
+
started_at = time.perf_counter()
|
|
130
|
+
total_files = len(files)
|
|
131
|
+
download_seconds = 0.0
|
|
132
|
+
read_seconds = 0.0
|
|
133
|
+
total_bytes = 0
|
|
134
|
+
|
|
135
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
136
|
+
for idx, file_info in enumerate(files, start=1):
|
|
137
|
+
file_name = file_info["file_name"]
|
|
138
|
+
local_path = os.path.join(tmpdir, f"{idx}_{file_name}")
|
|
139
|
+
file_started_at = time.perf_counter()
|
|
140
|
+
|
|
141
|
+
with requests.get(file_info["signed_url"], stream=True, timeout=600) as response:
|
|
142
|
+
response.raise_for_status()
|
|
143
|
+
with open(local_path, "wb") as file_obj:
|
|
144
|
+
for chunk in response.iter_content(chunk_size=1024 * 1024):
|
|
145
|
+
if chunk:
|
|
146
|
+
file_obj.write(chunk)
|
|
147
|
+
|
|
148
|
+
download_seconds += time.perf_counter() - file_started_at
|
|
149
|
+
total_bytes += os.path.getsize(local_path)
|
|
150
|
+
|
|
151
|
+
read_started_at = time.perf_counter()
|
|
152
|
+
dataframes.append(pd.read_parquet(local_path))
|
|
153
|
+
read_seconds += time.perf_counter() - read_started_at
|
|
154
|
+
|
|
155
|
+
if not dataframes:
|
|
156
|
+
return pd.DataFrame()
|
|
157
|
+
|
|
158
|
+
concat_started_at = time.perf_counter()
|
|
159
|
+
dataframe = pd.concat(dataframes, ignore_index=True)
|
|
160
|
+
concat_elapsed = self._format_elapsed(time.perf_counter() - concat_started_at)
|
|
161
|
+
total_elapsed = self._format_elapsed(time.perf_counter() - started_at)
|
|
162
|
+
self._print(
|
|
163
|
+
f"DataFrame listo | rows={len(dataframe)} | cols={len(dataframe.columns)} "
|
|
164
|
+
f"| files={total_files} | mb={total_bytes / (1024 * 1024):.2f} "
|
|
165
|
+
f"| signed_url_download={self._format_elapsed(download_seconds)} "
|
|
166
|
+
f"| parquet_read={self._format_elapsed(read_seconds)} "
|
|
167
|
+
f"| concat={concat_elapsed} | client_download_read_total={total_elapsed}",
|
|
168
|
+
log,
|
|
169
|
+
)
|
|
170
|
+
return dataframe
|
|
171
|
+
|
|
172
|
+
def _export_dataframe(
|
|
173
|
+
self,
|
|
174
|
+
module_name: str,
|
|
175
|
+
endpoint_export: str,
|
|
176
|
+
payload: dict[str, Any],
|
|
177
|
+
log: bool = False,
|
|
178
|
+
):
|
|
179
|
+
started_at = time.perf_counter()
|
|
180
|
+
export_data = self._start_export(endpoint_export, payload, log=log)
|
|
181
|
+
|
|
182
|
+
final_status = self.wait_until_done(
|
|
183
|
+
module_name=module_name,
|
|
184
|
+
job_id=export_data["job_id"],
|
|
185
|
+
export_id=export_data["export_id"],
|
|
186
|
+
export_format=export_data.get("format", "parquet"),
|
|
187
|
+
log=log,
|
|
188
|
+
)
|
|
189
|
+
dataframe = self._read_files(final_status.get("files", []), log)
|
|
190
|
+
total_elapsed = self._format_elapsed(time.perf_counter() - started_at)
|
|
191
|
+
self._print(f"Flujo completo finalizado en {total_elapsed}", log)
|
|
192
|
+
return dataframe
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class Datamart(Config):
|
|
196
|
+
def start_vc(
|
|
197
|
+
self,
|
|
198
|
+
fecha_inicio: str,
|
|
199
|
+
fecha_fin: str,
|
|
200
|
+
moneda: str,
|
|
201
|
+
categoria: Optional[str] = None,
|
|
202
|
+
agf: Optional[str] = None,
|
|
203
|
+
runs: Optional[list[str]] = None,
|
|
204
|
+
bruto: bool = True,
|
|
205
|
+
format: str = "parquet",
|
|
206
|
+
log: bool = False,
|
|
207
|
+
) -> dict[str, Any]:
|
|
208
|
+
return self._start_export(
|
|
209
|
+
"exports/fm-valores-cuota",
|
|
210
|
+
{
|
|
211
|
+
"fecha_inicio": fecha_inicio,
|
|
212
|
+
"fecha_fin": fecha_fin,
|
|
213
|
+
"moneda": moneda,
|
|
214
|
+
"categoria": categoria,
|
|
215
|
+
"agf": agf,
|
|
216
|
+
"runs": runs or [],
|
|
217
|
+
"bruto": bruto,
|
|
218
|
+
"format": format,
|
|
219
|
+
},
|
|
220
|
+
log=log,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def export_vc(
|
|
224
|
+
self,
|
|
225
|
+
fecha_inicio: str,
|
|
226
|
+
fecha_fin: str,
|
|
227
|
+
moneda: str,
|
|
228
|
+
log: bool = False,
|
|
229
|
+
**params,
|
|
230
|
+
):
|
|
231
|
+
payload = {"fecha_inicio": fecha_inicio, "fecha_fin": fecha_fin, "moneda": moneda, **params}
|
|
232
|
+
payload.setdefault("runs", [])
|
|
233
|
+
payload.setdefault("bruto", True)
|
|
234
|
+
payload["format"] = "parquet"
|
|
235
|
+
return self._export_dataframe("fm_valores_cuota", "exports/fm-valores-cuota", payload, log=log)
|
|
236
|
+
|
|
237
|
+
def start_fondos(self, ignorar_cambio_nombre: bool, log: bool = False, **params) -> dict[str, Any]:
|
|
238
|
+
payload = {"ignorar_cambio_nombre": ignorar_cambio_nombre, **params}
|
|
239
|
+
payload.setdefault("bruto", True)
|
|
240
|
+
return self._start_export("exports/fondos-mutuos", payload, log=log)
|
|
241
|
+
|
|
242
|
+
def export_fondos(self, ignorar_cambio_nombre: bool, log: bool = False, **params):
|
|
243
|
+
payload = {"ignorar_cambio_nombre": ignorar_cambio_nombre, **params}
|
|
244
|
+
payload.setdefault("bruto", True)
|
|
245
|
+
payload["format"] = "parquet"
|
|
246
|
+
return self._export_dataframe("fondos_mutuos", "exports/fondos-mutuos", payload, log=log)
|
|
247
|
+
|
|
248
|
+
def start_lva_aux(
|
|
249
|
+
self,
|
|
250
|
+
runs: Optional[list[str]] = None,
|
|
251
|
+
vigente: Optional[int] = None,
|
|
252
|
+
log: bool = False,
|
|
253
|
+
) -> dict:
|
|
254
|
+
return self._start_export(
|
|
255
|
+
"exports/lva-aux-valores-cuota",
|
|
256
|
+
{"runs": runs or [], "vigente": vigente},
|
|
257
|
+
log=log,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def export_lva_aux(
|
|
261
|
+
self,
|
|
262
|
+
runs: Optional[list[str]] = None,
|
|
263
|
+
vigente: Optional[int] = None,
|
|
264
|
+
log: bool = False,
|
|
265
|
+
):
|
|
266
|
+
return self._export_dataframe(
|
|
267
|
+
"lva_aux_valores_cuota",
|
|
268
|
+
"exports/lva-aux-valores-cuota",
|
|
269
|
+
{"runs": runs or [], "vigente": vigente, "format": "parquet"},
|
|
270
|
+
log=log,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class FrameworkRiesgo(Config):
|
|
275
|
+
def start_benchmarks(self, categoria: str, year: str, log: bool = False) -> dict[str, Any]:
|
|
276
|
+
return self._start_export("exports/benchmarks", {"categoria": categoria, "year": year}, log=log)
|
|
277
|
+
|
|
278
|
+
def export_benchmarks(self, categoria: str, year: str, log: bool = False):
|
|
279
|
+
return self._export_dataframe(
|
|
280
|
+
"benchmarks",
|
|
281
|
+
"exports/benchmarks",
|
|
282
|
+
{"categoria": categoria, "year": year, "format": "parquet"},
|
|
283
|
+
log=log,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
def start_metricas(self, start_date: str, end_date: str, log: bool = False, **payload) -> dict[str, Any]:
|
|
287
|
+
return self._start_export(
|
|
288
|
+
"exports/metricas-expost",
|
|
289
|
+
{"start_date": start_date, "end_date": end_date, **payload},
|
|
290
|
+
log=log,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def export_metricas(
|
|
294
|
+
self,
|
|
295
|
+
start_date: str,
|
|
296
|
+
end_date: str,
|
|
297
|
+
log: bool = False,
|
|
298
|
+
**payload,
|
|
299
|
+
):
|
|
300
|
+
return self._export_dataframe(
|
|
301
|
+
"metricas_expost",
|
|
302
|
+
"exports/metricas-expost",
|
|
303
|
+
{"start_date": start_date, "end_date": end_date, **payload, "format": "parquet"},
|
|
304
|
+
log=log,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def start_alertas(self, start_date: str, end_date: str, log: bool = False, **payload) -> dict[str, Any]:
|
|
308
|
+
return self._start_export(
|
|
309
|
+
"exports/alertas-metricas-expost",
|
|
310
|
+
{"start_date": start_date, "end_date": end_date, **payload},
|
|
311
|
+
log=log,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def export_alertas(
|
|
315
|
+
self,
|
|
316
|
+
start_date: str,
|
|
317
|
+
end_date: str,
|
|
318
|
+
log: bool = False,
|
|
319
|
+
**payload,
|
|
320
|
+
):
|
|
321
|
+
return self._export_dataframe(
|
|
322
|
+
"alertas_metricas_expost",
|
|
323
|
+
"exports/alertas-metricas-expost",
|
|
324
|
+
{"start_date": start_date, "end_date": end_date, **payload, "format": "parquet"},
|
|
325
|
+
log=log,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
def start_niveles(self, fecha_inicio: str, fecha_fin: str, log: bool = False, **payload) -> dict[str, Any]:
|
|
329
|
+
return self._start_export(
|
|
330
|
+
"exports/niveles-riesgo",
|
|
331
|
+
{"fecha_inicio": fecha_inicio, "fecha_fin": fecha_fin, **payload},
|
|
332
|
+
log=log,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def export_niveles(
|
|
336
|
+
self,
|
|
337
|
+
fecha_inicio: str,
|
|
338
|
+
fecha_fin: str,
|
|
339
|
+
log: bool = False,
|
|
340
|
+
**payload,
|
|
341
|
+
):
|
|
342
|
+
return self._export_dataframe(
|
|
343
|
+
"niveles_riesgo",
|
|
344
|
+
"exports/niveles-riesgo",
|
|
345
|
+
{"fecha_inicio": fecha_inicio, "fecha_fin": fecha_fin, **payload, "format": "parquet"},
|
|
346
|
+
log=log,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def start_srri(self, log: bool = False) -> dict[str, Any]:
|
|
350
|
+
return self._start_export("exports/srri", {}, log=log)
|
|
351
|
+
|
|
352
|
+
def export_srri(self, log: bool = False):
|
|
353
|
+
return self._export_dataframe("srri", "exports/srri", {"format": "parquet"}, log=log)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class StorageFiles(Config):
|
|
357
|
+
def get_file_url(self, bucket: str, path: str, log: bool = False) -> dict[str, Any]:
|
|
358
|
+
started_at = time.perf_counter()
|
|
359
|
+
data = self._post("files/signed-url", {"bucket": bucket, "path": path})
|
|
360
|
+
elapsed = self._format_elapsed(time.perf_counter() - started_at)
|
|
361
|
+
self._print(
|
|
362
|
+
f"file_signed_url={elapsed} | bucket={bucket} | path={path} | size_bytes={data['size_bytes']}",
|
|
363
|
+
log,
|
|
364
|
+
)
|
|
365
|
+
return data
|
|
366
|
+
|
|
367
|
+
def download_file(
|
|
368
|
+
self,
|
|
369
|
+
bucket: str,
|
|
370
|
+
path: str,
|
|
371
|
+
destination: str | os.PathLike = ".",
|
|
372
|
+
log: bool = False,
|
|
373
|
+
) -> Path:
|
|
374
|
+
file_info = self.get_file_url(bucket, path, log=log)
|
|
375
|
+
destination_path = Path(destination)
|
|
376
|
+
if destination_path.suffix:
|
|
377
|
+
output_path = destination_path
|
|
378
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
379
|
+
else:
|
|
380
|
+
destination_path.mkdir(parents=True, exist_ok=True)
|
|
381
|
+
output_path = destination_path / file_info["file_name"]
|
|
382
|
+
|
|
383
|
+
started_at = time.perf_counter()
|
|
384
|
+
with requests.get(file_info["signed_url"], stream=True, timeout=600) as response:
|
|
385
|
+
response.raise_for_status()
|
|
386
|
+
with output_path.open("wb") as file_obj:
|
|
387
|
+
for chunk in response.iter_content(chunk_size=1024 * 1024):
|
|
388
|
+
if chunk:
|
|
389
|
+
file_obj.write(chunk)
|
|
390
|
+
|
|
391
|
+
elapsed = self._format_elapsed(time.perf_counter() - started_at)
|
|
392
|
+
self._print(
|
|
393
|
+
f"file_download={elapsed} | path={output_path} | mb={output_path.stat().st_size / (1024 * 1024):.2f}",
|
|
394
|
+
log,
|
|
395
|
+
)
|
|
396
|
+
return output_path
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lambda-risk
|
|
3
|
+
Version: 0.0.0.1
|
|
4
|
+
Description-Content-Type: text/markdown
|
|
5
|
+
Requires-Dist: pandas>=2.2.3
|
|
6
|
+
Requires-Dist: requests>=2.32.3
|
|
7
|
+
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: scipy
|
|
9
|
+
Requires-Dist: PyQuantimClient
|
|
10
|
+
Requires-Dist: holidays
|
|
11
|
+
Dynamic: description
|
|
12
|
+
Dynamic: description-content-type
|
|
13
|
+
Dynamic: requires-dist
|
|
14
|
+
|
|
15
|
+
# Lambda SDK
|
|
16
|
+
|
|
17
|
+
Sistema integral de gestión de riesgo financiero
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lambda-risk
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r") as f:
|
|
4
|
+
description = f.read()
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
setup(
|
|
8
|
+
name='lambda-risk',
|
|
9
|
+
version='0.0.0.1',
|
|
10
|
+
packages=find_packages(),
|
|
11
|
+
include_package_data=True,
|
|
12
|
+
install_requires=[
|
|
13
|
+
"pandas>=2.2.3",
|
|
14
|
+
"requests>=2.32.3",
|
|
15
|
+
'numpy',
|
|
16
|
+
'scipy',
|
|
17
|
+
'PyQuantimClient',
|
|
18
|
+
'holidays'
|
|
19
|
+
|
|
20
|
+
],
|
|
21
|
+
long_description=description,
|
|
22
|
+
long_description_content_type="text/markdown",
|
|
23
|
+
|
|
24
|
+
)
|