sovai 0.2.45__tar.gz → 0.2.47__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.
- {sovai-0.2.45 → sovai-0.2.47}/PKG-INFO +1 -1
- sovai-0.2.47/data/tickers.parq +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/pyproject.toml +1 -1
- {sovai-0.2.45 → sovai-0.2.47}/sovai/get_data.py +101 -4
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/client_side_s3_part_high.py +191 -70
- sovai-0.2.45/data/tickers.parq +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/LICENSE +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/README.md +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/data/codes.parq +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/.DS_Store +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/api_config.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/assets/dropdown.css +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/assets/features_mapping.parq +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/basic_auth.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/computations/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/computations/functions.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/errors/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/errors/sovai_errors.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/anomalies.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/ask_df_llm.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/change_point_generator.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/clustering.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/core_kshape.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/cum_sum_torch.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/cusum.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/dimensionality_reduction.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/feature_extraction.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/feature_importance.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/feature_neutralizer.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/filter_df.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/fractional_differencing.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/nowcasting.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/pairwise.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/pandas_extensions.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/parallel_functions.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/pfa_feature_selector.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/regime_change.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/regime_change_pca.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/shapley_global_importance.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/shapley_importance.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/signal_evaluation.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/technical_indicators.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/time_decomposition.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/extensions/weight_optimization.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/get_compute.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/get_data copy.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/get_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/get_reports.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/get_tools.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/accounting/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/accounting/accounting_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/allocation/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/allocation/allocation_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/bankruptcy/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/bankruptcy/bankruptcy_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/breakout/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/breakout/breakout_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/corp_risk/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/corp_risk/corp_risk_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/earnings_surprise/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/earnings_surprise/earnings_surprise_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/insider/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/insider/insider_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/institutional/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/institutional/institutional_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/news/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/news/news_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/ratios/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/plots/ratios/ratios_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/accounting/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/accounting/accounting_balance_sheet.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/bankruptcy/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/bankruptcy/bankruptcy_monthly_top.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/general/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/general/general_plots.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/news/__inti__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/reports/news/news_econometric_analysis.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/studies/nowcasting.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/token_auth.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/tools/authentication/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/tools/authentication/authentication.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/tools/sec/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/tools/sec/graphs.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/tools/sec/llm_code_generator.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/tools/sec/sec_10_k_8_k_filings.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/tools/sec/sec_edgar_search.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/__init__.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/client_side.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/client_side_s3.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/converter.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/datetime_formats.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/file_management.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/get_tickers.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/helpers.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/pandas_extensions copy.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/plot.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/port_manager.py +0 -0
- {sovai-0.2.45 → sovai-0.2.47}/sovai/utils/stream.py +0 -0
|
Binary file
|
|
@@ -308,7 +308,9 @@ ENDPOINT_TO_TICKER = {
|
|
|
308
308
|
"/news/pronouns": "",
|
|
309
309
|
"/news/word_count": "",
|
|
310
310
|
"/news/sentiment_score": "", # was None before?
|
|
311
|
+
|
|
311
312
|
"/insider/trading": "",
|
|
313
|
+
|
|
312
314
|
"/wikipedia/views": "",
|
|
313
315
|
"/accounting/weekly": "",
|
|
314
316
|
"/visas/h1b": "",
|
|
@@ -346,6 +348,8 @@ ENDPOINT_TO_TICKER = {
|
|
|
346
348
|
|
|
347
349
|
"/clinical/trials": "",
|
|
348
350
|
|
|
351
|
+
|
|
352
|
+
|
|
349
353
|
"/spending/awards": "",
|
|
350
354
|
"/spending/compensation": "",
|
|
351
355
|
"/spending/competition": "",
|
|
@@ -399,6 +403,8 @@ CLIENT_SIDE_ENDPOINTS_S3_PART_HIGH = {
|
|
|
399
403
|
|
|
400
404
|
"accounting/weekly",
|
|
401
405
|
|
|
406
|
+
"insider/trading",
|
|
407
|
+
|
|
402
408
|
"ratios/normal",
|
|
403
409
|
"ratios/relative",
|
|
404
410
|
|
|
@@ -414,15 +420,42 @@ CLIENT_SIDE_ENDPOINTS_S3_PART_HIGH = {
|
|
|
414
420
|
|
|
415
421
|
"breakout",
|
|
416
422
|
|
|
417
|
-
|
|
423
|
+
"corprisk/risks",
|
|
424
|
+
"corprisk/accounting",
|
|
425
|
+
"corprisk/events",
|
|
426
|
+
"corprisk/misstatements",
|
|
418
427
|
|
|
419
428
|
"visas/h1b",
|
|
420
429
|
|
|
421
|
-
"wikipedia/views"
|
|
430
|
+
"wikipedia/views",
|
|
431
|
+
|
|
432
|
+
"short/volume",
|
|
433
|
+
"short/maker",
|
|
434
|
+
"short/over_shorted",
|
|
435
|
+
|
|
436
|
+
"institutional/trading"
|
|
422
437
|
|
|
423
438
|
}
|
|
424
439
|
|
|
425
440
|
|
|
441
|
+
|
|
442
|
+
ENDPOINT_ALIASES = {
|
|
443
|
+
"clinical/predict": {
|
|
444
|
+
"target_endpoint": "clinical/trials",
|
|
445
|
+
"columns": [
|
|
446
|
+
'ticker', 'date', 'source', 'subsidiary', 'sponsor',
|
|
447
|
+
'trial_id', 'official_title', 'success_prediction',
|
|
448
|
+
'economic_effect', 'duration_prediction', 'success_composite'
|
|
449
|
+
]
|
|
450
|
+
}
|
|
451
|
+
# --- Add other aliases here in the future ---
|
|
452
|
+
# "some/alias": {
|
|
453
|
+
# "target_endpoint": "real/endpoint",
|
|
454
|
+
# "columns": ["col1", "col2", "date", "ticker"] # Ensure essential columns are included
|
|
455
|
+
# }
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
|
|
426
459
|
# Define handlers for different endpoint types
|
|
427
460
|
ENDPOINT_HANDLERS = [
|
|
428
461
|
(CLIENT_SIDE_ENDPOINTS_GCS, client_side_frame, "Grabbing GCS client side"),
|
|
@@ -1095,6 +1128,38 @@ def data(
|
|
|
1095
1128
|
params = params or {}
|
|
1096
1129
|
params = map_synonyms(params)
|
|
1097
1130
|
|
|
1131
|
+
# --- MODIFIED SECTION: Alias Handling ---
|
|
1132
|
+
original_endpoint_request = endpoint # Keep track for logging
|
|
1133
|
+
forced_columns = None # Will store columns if an alias forces them
|
|
1134
|
+
normalized_req_endpoint = normalize_endpoint(endpoint)
|
|
1135
|
+
|
|
1136
|
+
if normalized_req_endpoint in ENDPOINT_ALIASES:
|
|
1137
|
+
alias_config = ENDPOINT_ALIASES[normalized_req_endpoint]
|
|
1138
|
+
target_endpoint = alias_config['target_endpoint']
|
|
1139
|
+
forced_columns_list = alias_config['columns']
|
|
1140
|
+
verbose_mode.log(f"Endpoint alias detected: '{original_endpoint_request}' maps to '{target_endpoint}' with predefined columns.")
|
|
1141
|
+
|
|
1142
|
+
endpoint = target_endpoint # *Overwrite* endpoint for the rest of the function
|
|
1143
|
+
columns = forced_columns_list # *Overwrite* columns parameter
|
|
1144
|
+
|
|
1145
|
+
# Ensure the 'columns' value in the main 'params' dict is also updated
|
|
1146
|
+
# This is crucial for cache key generation and potentially for client-side handlers
|
|
1147
|
+
# Convert list to string if needed for _prepare_params consistency
|
|
1148
|
+
params['columns'] = ",".join(forced_columns_list)
|
|
1149
|
+
|
|
1150
|
+
else:
|
|
1151
|
+
# If not an alias, handle the columns parameter normally
|
|
1152
|
+
# Convert list to string if provided as list initially
|
|
1153
|
+
if isinstance(columns, list):
|
|
1154
|
+
columns_str = ",".join(columns)
|
|
1155
|
+
else:
|
|
1156
|
+
columns_str = columns # It's already a string or None
|
|
1157
|
+
# Update params dict if columns were passed directly to the function
|
|
1158
|
+
if columns is not None:
|
|
1159
|
+
params['columns'] = columns_str
|
|
1160
|
+
# --- END MODIFIED SECTION ---
|
|
1161
|
+
|
|
1162
|
+
|
|
1098
1163
|
# Force full_history if date range is specified
|
|
1099
1164
|
if start_date is not None or end_date is not None:
|
|
1100
1165
|
full_history = True
|
|
@@ -1102,6 +1167,14 @@ def data(
|
|
|
1102
1167
|
|
|
1103
1168
|
# Update params with prepared values
|
|
1104
1169
|
try:
|
|
1170
|
+
|
|
1171
|
+
# Make sure to use the potentially updated 'columns' value from params dict
|
|
1172
|
+
# if it exists, otherwise use the function argument (which might be None or overridden)
|
|
1173
|
+
current_columns = params.get("columns", columns)
|
|
1174
|
+
# Convert back to string if it was forced as a list earlier
|
|
1175
|
+
if isinstance(current_columns, list):
|
|
1176
|
+
current_columns = ",".join(current_columns)
|
|
1177
|
+
|
|
1105
1178
|
params.update(
|
|
1106
1179
|
_prepare_params(
|
|
1107
1180
|
tickers=params.get("tickers", tickers),
|
|
@@ -1150,6 +1223,10 @@ def data(
|
|
|
1150
1223
|
|
|
1151
1224
|
# Try client-side handlers for specific endpoints
|
|
1152
1225
|
handler_result = None
|
|
1226
|
+
|
|
1227
|
+
# Get the columns value to pass to handlers (use the string version from params)
|
|
1228
|
+
handler_columns_arg = params.get('columns')
|
|
1229
|
+
|
|
1153
1230
|
for endpoint_set, handler_func, message in ENDPOINT_HANDLERS:
|
|
1154
1231
|
if (
|
|
1155
1232
|
normalized_endpoint in endpoint_set and
|
|
@@ -1160,7 +1237,7 @@ def data(
|
|
|
1160
1237
|
verbose_mode.log(f"Calling client-side handler with tickers={tickers}, start_date={start_date}, end_date={end_date}")
|
|
1161
1238
|
try:
|
|
1162
1239
|
handler_result = handler_func(
|
|
1163
|
-
normalized_endpoint, tickers,
|
|
1240
|
+
normalized_endpoint, tickers, handler_columns_arg, start_date, end_date
|
|
1164
1241
|
)
|
|
1165
1242
|
_query_cache[cache_key] = handler_result
|
|
1166
1243
|
result_data = handler_result
|
|
@@ -1241,11 +1318,15 @@ def data(
|
|
|
1241
1318
|
logger.error(error_msg)
|
|
1242
1319
|
raise Exception(error_msg)
|
|
1243
1320
|
|
|
1321
|
+
filter_columns_arg = columns
|
|
1322
|
+
if isinstance(columns, str): # filter_data might prefer list
|
|
1323
|
+
filter_columns_arg = [c.strip() for c in columns.split(',')] if columns else None
|
|
1324
|
+
|
|
1244
1325
|
# Apply filters
|
|
1245
1326
|
verbose_mode.log("Applying filters to data")
|
|
1246
1327
|
data_result = filter_data(
|
|
1247
1328
|
data_result,
|
|
1248
|
-
columns=
|
|
1329
|
+
columns=filter_columns_arg,
|
|
1249
1330
|
start_date=start_date,
|
|
1250
1331
|
end_date=end_date,
|
|
1251
1332
|
use_polars=use_polars
|
|
@@ -1331,6 +1412,22 @@ def data(
|
|
|
1331
1412
|
|
|
1332
1413
|
result_data = process_dataframe(result_data)
|
|
1333
1414
|
|
|
1415
|
+
|
|
1416
|
+
# --- Final Check: Ensure alias columns are applied if data wasn't filtered before ---
|
|
1417
|
+
# This is a safety net in case a data path (e.g., direct API return without 'all tickers')
|
|
1418
|
+
# didn't explicitly filter columns based on the overridden parameter.
|
|
1419
|
+
if forced_columns is not None and result_data is not None and isinstance(result_data, (pd.DataFrame, pl.DataFrame)):
|
|
1420
|
+
# Check if columns need filtering (some might be missing if already filtered)
|
|
1421
|
+
current_cols = list(result_data.columns)
|
|
1422
|
+
cols_to_keep = [col for col in forced_columns_list if col in current_cols]
|
|
1423
|
+
if set(cols_to_keep) != set(current_cols):
|
|
1424
|
+
verbose_mode.log(f"Applying final column filter for alias: {cols_to_keep}")
|
|
1425
|
+
if isinstance(result_data, pd.DataFrame):
|
|
1426
|
+
result_data = result_data[cols_to_keep]
|
|
1427
|
+
elif isinstance(result_data, pl.DataFrame):
|
|
1428
|
+
result_data = result_data.select(cols_to_keep)
|
|
1429
|
+
|
|
1430
|
+
|
|
1334
1431
|
return result_data
|
|
1335
1432
|
|
|
1336
1433
|
|
|
@@ -32,16 +32,23 @@ from sovai.tools.authentication import authentication
|
|
|
32
32
|
# Logging Configuration
|
|
33
33
|
# =========================================================================
|
|
34
34
|
|
|
35
|
+
# Near top of load_frame_s3_partitioned_high.py
|
|
35
36
|
logger = logging.getLogger(__name__)
|
|
36
37
|
if not logger.handlers:
|
|
37
|
-
logging.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
38
|
+
log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - [%(funcName)s] - %(message)s') # Added function name
|
|
39
|
+
|
|
40
|
+
# File Handler
|
|
41
|
+
file_handler = logging.FileHandler("data_operations.log")
|
|
42
|
+
file_handler.setFormatter(log_formatter)
|
|
43
|
+
|
|
44
|
+
# Console Handler (add this)
|
|
45
|
+
console_handler = logging.StreamHandler()
|
|
46
|
+
console_handler.setFormatter(log_formatter)
|
|
47
|
+
|
|
48
|
+
logger.addHandler(file_handler)
|
|
49
|
+
logger.addHandler(console_handler) # Add console output
|
|
50
|
+
logger.setLevel(logging.INFO) # Set level (INFO or DEBUG)
|
|
51
|
+
# To see DEBUG level messages, set logger.setLevel(logging.DEBUG)
|
|
45
52
|
|
|
46
53
|
# =========================================================================
|
|
47
54
|
# Filesystem Access
|
|
@@ -295,10 +302,13 @@ class PartitionFinder:
|
|
|
295
302
|
# =========================================================================
|
|
296
303
|
# Data Loading
|
|
297
304
|
# =========================================================================
|
|
305
|
+
# =========================================================================
|
|
306
|
+
# Data Loading
|
|
307
|
+
# =========================================================================
|
|
298
308
|
|
|
299
309
|
class DataLoader:
|
|
300
310
|
"""Core data loading functionality with filtering and parallelism."""
|
|
301
|
-
|
|
311
|
+
|
|
302
312
|
@staticmethod
|
|
303
313
|
def load_partition(
|
|
304
314
|
path: str,
|
|
@@ -308,77 +318,127 @@ class DataLoader:
|
|
|
308
318
|
end_date: Optional[str] = None
|
|
309
319
|
) -> pd.DataFrame:
|
|
310
320
|
"""
|
|
311
|
-
Load and filter data from a single partition.
|
|
312
|
-
|
|
321
|
+
Load and filter data from a single partition. Added logging for debugging column selection.
|
|
322
|
+
|
|
313
323
|
Args:
|
|
314
324
|
path: S3 path to the partition
|
|
315
325
|
ticker_filter: Optional list of tickers to filter by
|
|
316
326
|
columns: Optional list of columns to load
|
|
317
327
|
start_date: Optional start date for filtering
|
|
318
328
|
end_date: Optional end date for filtering
|
|
319
|
-
|
|
329
|
+
|
|
320
330
|
Returns:
|
|
321
331
|
Filtered pandas DataFrame
|
|
322
332
|
"""
|
|
323
333
|
fs = get_s3_filesystem()
|
|
324
|
-
|
|
334
|
+
logger.info(f"--- Loading Partition Start: {path} ---")
|
|
335
|
+
logger.debug(f"Parameters received: ticker_filter={ticker_filter}, columns={columns}, start_date={start_date}, end_date={end_date}")
|
|
336
|
+
|
|
325
337
|
try:
|
|
326
338
|
# Create dataset
|
|
327
|
-
|
|
328
|
-
|
|
339
|
+
logger.debug(f"[{path}] Creating dataset object...")
|
|
340
|
+
dataset = ds.dataset(path, filesystem=fs, format='parquet', partitioning="hive") # Assuming Hive partitioning
|
|
341
|
+
logger.info(f"[{path}] Initial dataset schema: {dataset.schema}")
|
|
342
|
+
|
|
329
343
|
# Build filter expressions
|
|
330
344
|
filters = DataLoader._build_date_filters(start_date, end_date)
|
|
331
|
-
|
|
332
|
-
|
|
345
|
+
logger.debug(f"[{path}] Built date filters: {filters}")
|
|
346
|
+
|
|
347
|
+
# Apply filters to dataset before reading columns
|
|
333
348
|
if filters:
|
|
334
349
|
filter_expr = filters[0]
|
|
335
350
|
for f in filters[1:]:
|
|
336
351
|
filter_expr = filter_expr & f
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
352
|
+
try:
|
|
353
|
+
logger.info(f"[{path}] Applying filter expression: {filter_expr}")
|
|
354
|
+
dataset = dataset.filter(filter_expr)
|
|
355
|
+
# Note: Checking row count here might be slow as it forces some reading
|
|
356
|
+
# fragment_count = len(list(dataset.get_fragments()))
|
|
357
|
+
# logger.info(f"[{path}] Filter applied. Found {fragment_count} fragments. Schema after filtering: {dataset.schema}")
|
|
358
|
+
logger.info(f"[{path}] Filter applied. Schema after filtering: {dataset.schema}")
|
|
359
|
+
except Exception as filter_e:
|
|
360
|
+
logger.error(f"[{path}] Error applying filters: {filter_e}", exc_info=True)
|
|
361
|
+
logger.info(f"--- Loading Partition Failed (Filter Error): {path} ---")
|
|
362
|
+
return pd.DataFrame()
|
|
363
|
+
|
|
364
|
+
# Load data using to_table
|
|
365
|
+
logger.info(f"[{path}] Calling dataset.to_table() with columns: {columns}")
|
|
366
|
+
try:
|
|
367
|
+
# Explicitly handle None columns case for logging clarity
|
|
368
|
+
cols_to_load = columns if columns else "ALL"
|
|
369
|
+
table = dataset.to_table(columns=columns, use_threads=True) # Pass None or list
|
|
370
|
+
logger.info(f"[{path}] dataset.to_table(columns={cols_to_load}) successful.")
|
|
371
|
+
logger.info(f"[{path}] Resulting Arrow Table shape: {table.shape}")
|
|
372
|
+
logger.debug(f"[{path}] Resulting Arrow Table columns: {table.column_names}")
|
|
373
|
+
|
|
374
|
+
# Check if table is empty AFTER the potentially selective read
|
|
375
|
+
if table.num_rows == 0:
|
|
376
|
+
logger.warning(f"[{path}] Arrow Table is empty after to_table(columns={cols_to_load}). "
|
|
377
|
+
f"This might be expected if date filter removed all rows, "
|
|
378
|
+
f"or problematic if columns were requested but not found/read.")
|
|
379
|
+
# Proceed to convert empty table to empty DataFrame
|
|
380
|
+
|
|
381
|
+
except Exception as to_table_e:
|
|
382
|
+
logger.error(f"[{path}] Error during dataset.to_table(columns={columns}): {to_table_e}", exc_info=True)
|
|
383
|
+
logger.error(f"[{path}] Dataset schema at time of error: {dataset.schema}")
|
|
384
|
+
logger.info(f"--- Loading Partition Failed (to_table Error): {path} ---")
|
|
385
|
+
return pd.DataFrame()
|
|
386
|
+
|
|
387
|
+
# Convert to pandas
|
|
388
|
+
logger.debug(f"[{path}] Converting Arrow Table to pandas DataFrame...")
|
|
341
389
|
df = table.to_pandas()
|
|
342
|
-
|
|
343
|
-
|
|
390
|
+
logger.info(f"[{path}] Converted to pandas DataFrame. Shape: {df.shape}")
|
|
391
|
+
if df.empty and table.num_rows > 0:
|
|
392
|
+
logger.warning(f"[{path}] Conversion to pandas resulted in empty DataFrame, but Arrow table had {table.num_rows} rows.")
|
|
393
|
+
logger.debug(f"[{path}] Pandas DataFrame columns: {list(df.columns)}")
|
|
394
|
+
|
|
395
|
+
# Apply ticker filter if needed (post-load)
|
|
344
396
|
if ticker_filter and 'ticker' in df.columns:
|
|
397
|
+
logger.info(f"[{path}] Applying post-load ticker filter: {ticker_filter}")
|
|
398
|
+
initial_rows = len(df)
|
|
345
399
|
df = df[df['ticker'].isin(ticker_filter)]
|
|
346
|
-
|
|
400
|
+
logger.info(f"[{path}] Ticker filter applied. Rows reduced from {initial_rows} to {len(df)}")
|
|
401
|
+
elif ticker_filter:
|
|
402
|
+
logger.warning(f"[{path}] Ticker filter {ticker_filter} requested but 'ticker' column not found in loaded columns: {list(df.columns)}.")
|
|
403
|
+
|
|
404
|
+
logger.info(f"--- Loading Partition End: {path} --- Returning DataFrame shape: {df.shape}")
|
|
347
405
|
return df
|
|
406
|
+
|
|
348
407
|
except Exception as e:
|
|
349
|
-
|
|
408
|
+
# Catch-all for other errors (e.g., ds.dataset creation failed)
|
|
409
|
+
logger.error(f"Unhandled error processing partition {path}: {e}", exc_info=True)
|
|
410
|
+
logger.info(f"--- Loading Partition Failed (Unhandled Error): {path} ---")
|
|
350
411
|
return pd.DataFrame()
|
|
351
|
-
|
|
412
|
+
|
|
413
|
+
|
|
352
414
|
@staticmethod
|
|
353
415
|
def _build_date_filters(
|
|
354
|
-
start_date: Optional[str],
|
|
416
|
+
start_date: Optional[str],
|
|
355
417
|
end_date: Optional[str]
|
|
356
418
|
) -> List[ds.Expression]:
|
|
357
419
|
"""
|
|
358
|
-
Build PyArrow date filter expressions.
|
|
359
|
-
|
|
360
|
-
Args:
|
|
361
|
-
start_date: Optional start date string
|
|
362
|
-
end_date: Optional end date string
|
|
363
|
-
|
|
364
|
-
Returns:
|
|
365
|
-
List of PyArrow filter expressions
|
|
420
|
+
Build PyArrow date filter expressions. (Ensure 'date' column exists)
|
|
366
421
|
"""
|
|
367
422
|
filters = []
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
423
|
+
try:
|
|
424
|
+
# Date filters - Assuming 'date' column exists and is date32 or similar
|
|
425
|
+
if start_date:
|
|
426
|
+
start_date_obj = pd.to_datetime(start_date).date()
|
|
427
|
+
# Use pa.date32() explicitly assuming the Parquet schema uses it
|
|
428
|
+
start_date_pa = pa.scalar(start_date_obj, type=pa.date32())
|
|
429
|
+
filters.append(ds.field('date') >= start_date_pa)
|
|
430
|
+
|
|
431
|
+
if end_date:
|
|
432
|
+
end_date_obj = pd.to_datetime(end_date).date()
|
|
433
|
+
end_date_pa = pa.scalar(end_date_obj, type=pa.date32())
|
|
434
|
+
filters.append(ds.field('date') <= end_date_pa)
|
|
435
|
+
except Exception as e:
|
|
436
|
+
logger.error(f"Failed to build date filters (start={start_date}, end={end_date}): {e}", exc_info=True)
|
|
437
|
+
# Decide if you want to raise or return empty filters
|
|
438
|
+
# return [] # Return empty filters if parsing fails?
|
|
439
|
+
|
|
380
440
|
return filters
|
|
381
|
-
|
|
441
|
+
|
|
382
442
|
@staticmethod
|
|
383
443
|
def load_data_parallel(
|
|
384
444
|
tasks: List[Tuple],
|
|
@@ -386,34 +446,42 @@ class DataLoader:
|
|
|
386
446
|
) -> List[pd.DataFrame]:
|
|
387
447
|
"""
|
|
388
448
|
Process loading tasks in parallel with progress tracking.
|
|
389
|
-
|
|
390
|
-
Args:
|
|
391
|
-
tasks: List of (path, ticker_filter, columns, start_date, end_date) tuples
|
|
392
|
-
max_workers: Maximum number of concurrent loading threads
|
|
393
|
-
|
|
394
|
-
Returns:
|
|
395
|
-
List of loaded DataFrames
|
|
396
449
|
"""
|
|
397
450
|
results = []
|
|
398
|
-
|
|
451
|
+
if not tasks:
|
|
452
|
+
logger.warning("load_data_parallel received no tasks.")
|
|
453
|
+
return results
|
|
454
|
+
|
|
455
|
+
logger.info(f"Starting parallel load of {len(tasks)} tasks with max_workers={max_workers}")
|
|
456
|
+
|
|
399
457
|
# Process tasks in parallel
|
|
400
458
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
459
|
+
# Prepare arguments properly for submit
|
|
460
|
+
future_to_path = {
|
|
461
|
+
executor.submit(DataLoader.load_partition, path, ticker_filter, task_columns, task_start_date, task_end_date): path
|
|
462
|
+
for path, ticker_filter, task_columns, task_start_date, task_end_date in tasks # Unpack task tuple
|
|
404
463
|
}
|
|
405
|
-
|
|
406
|
-
|
|
464
|
+
|
|
465
|
+
# Use tqdm for progress bar
|
|
466
|
+
for future in tqdm(as_completed(future_to_path), total=len(tasks), desc="Loading partitions", unit="partition"):
|
|
467
|
+
path = future_to_path[future]
|
|
407
468
|
try:
|
|
408
|
-
|
|
409
|
-
if not
|
|
410
|
-
|
|
469
|
+
df_partition = future.result() # Get result from load_partition
|
|
470
|
+
if df_partition is not None and not df_partition.empty:
|
|
471
|
+
logger.debug(f"Successfully loaded non-empty DataFrame from {path}, shape: {df_partition.shape}")
|
|
472
|
+
results.append(df_partition)
|
|
473
|
+
elif df_partition is not None:
|
|
474
|
+
logger.info(f"Loaded empty DataFrame from {path}")
|
|
475
|
+
else:
|
|
476
|
+
# Should not happen if load_partition always returns a DataFrame
|
|
477
|
+
logger.warning(f"Load task for {path} returned None.")
|
|
478
|
+
|
|
411
479
|
except Exception as e:
|
|
412
|
-
|
|
413
|
-
logger.error(f"Task failed for {path}: {e}")
|
|
414
|
-
|
|
415
|
-
return results
|
|
480
|
+
# This catches errors raised *after* submit, e.g., during future.result() processing if not caught inside load_partition
|
|
481
|
+
logger.error(f"Task failed for partition {path} during result retrieval: {e}", exc_info=True)
|
|
416
482
|
|
|
483
|
+
logger.info(f"Parallel loading finished. Collected results from {len(results)} non-empty partitions.")
|
|
484
|
+
return results
|
|
417
485
|
# =========================================================================
|
|
418
486
|
# Main Loading Interface
|
|
419
487
|
# =========================================================================
|
|
@@ -698,7 +766,55 @@ ENDPOINT_CONFIG = {
|
|
|
698
766
|
"ticker_path": "sovai/sovai-wiki/views/partitioned/ticker",
|
|
699
767
|
"date_path": "sovai/sovai-wiki/views/partitioned/date",
|
|
700
768
|
"has_year": False
|
|
701
|
-
}
|
|
769
|
+
},
|
|
770
|
+
|
|
771
|
+
"insider/trading": {
|
|
772
|
+
"ticker_path": "sovai/sovai-insider/partitioned/ticker",
|
|
773
|
+
"date_path": "sovai/sovai-insider/partitioned/date",
|
|
774
|
+
"has_year": False
|
|
775
|
+
},
|
|
776
|
+
|
|
777
|
+
"corprisk/risks": {
|
|
778
|
+
"ticker_path": "sovai/sovai-flags/risks/partitioned/ticker",
|
|
779
|
+
"date_path": "sovai/sovai-flags/risks/partitioned/date",
|
|
780
|
+
"has_year": False
|
|
781
|
+
},
|
|
782
|
+
"corprisk/accounting": {
|
|
783
|
+
"ticker_path": "sovai/sovai-flags/accounting/partitioned/ticker",
|
|
784
|
+
"date_path": "sovai/sovai-flags/accounting/partitioned/date",
|
|
785
|
+
"has_year": False
|
|
786
|
+
},
|
|
787
|
+
"corprisk/events": {
|
|
788
|
+
"ticker_path": "sovai/sovai-flags/events/partitioned/ticker",
|
|
789
|
+
"date_path": "sovai/sovai-flags/events/partitioned/date",
|
|
790
|
+
"has_year": False
|
|
791
|
+
},
|
|
792
|
+
"corprisk/misstatements": {
|
|
793
|
+
"ticker_path": "sovai/sovai-flags/misstatements/partitioned/ticker",
|
|
794
|
+
"date_path": "sovai/sovai-flags/misstatements/partitioned/date",
|
|
795
|
+
"has_year": False
|
|
796
|
+
},
|
|
797
|
+
"short/volume": {
|
|
798
|
+
"ticker_path": "sovai/sovai-short/short_volume_weekly/partitioned/ticker",
|
|
799
|
+
"date_path": "sovai/sovai-short/short_volume_weekly/partitioned/date",
|
|
800
|
+
"has_year": False
|
|
801
|
+
},
|
|
802
|
+
## probably more to do with short later.
|
|
803
|
+
"short/maker": {
|
|
804
|
+
"ticker_path": "sovai/sovai-short/over_shorted/partitioned/ticker",
|
|
805
|
+
"date_path": "sovai/sovai-short/over_shorted/partitioned/date",
|
|
806
|
+
"has_year": False
|
|
807
|
+
},
|
|
808
|
+
"short/over_shorted": {
|
|
809
|
+
"ticker_path": "sovai/sovai-short/over_shorted/partitioned/ticker",
|
|
810
|
+
"date_path": "sovai/sovai-short/over_shorted/partitioned/date",
|
|
811
|
+
"has_year": False
|
|
812
|
+
},
|
|
813
|
+
"institutional/trading": {
|
|
814
|
+
"ticker_path": "sovai/sovai-institutional/trading/partitioned/ticker",
|
|
815
|
+
"date_path": "sovai/sovai-institutional/trading/partitioned/date",
|
|
816
|
+
"has_year": False
|
|
817
|
+
},
|
|
702
818
|
|
|
703
819
|
}
|
|
704
820
|
|
|
@@ -788,10 +904,15 @@ def load_frame_s3_partitioned_high(
|
|
|
788
904
|
tickers=tickers,
|
|
789
905
|
start_date=start_date,
|
|
790
906
|
end_date=end_date,
|
|
791
|
-
columns=columns,
|
|
907
|
+
columns=[col.strip() for col in columns.split(',')] ,
|
|
792
908
|
max_workers=8,
|
|
793
909
|
post_process=post_process
|
|
794
910
|
)
|
|
911
|
+
|
|
912
|
+
# print(columns)
|
|
913
|
+
# columns = [col.strip() for col in columns.split(',')]
|
|
914
|
+
|
|
915
|
+
# df = df[columns] if columns else df
|
|
795
916
|
|
|
796
917
|
# Convert to CustomDataFrame if available
|
|
797
918
|
if HAS_CUSTOM_DATAFRAME:
|
sovai-0.2.45/data/tickers.parq
DELETED
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|