sovai 0.2.44__tar.gz → 0.2.46__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.44 → sovai-0.2.46}/PKG-INFO +1 -1
- sovai-0.2.46/data/tickers.parq +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/pyproject.toml +1 -1
- {sovai-0.2.44 → sovai-0.2.46}/sovai/get_data.py +105 -5
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/client_side_s3_part_high.py +198 -70
- sovai-0.2.44/data/tickers.parq +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/LICENSE +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/README.md +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/data/codes.parq +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/.DS_Store +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/api_config.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/assets/dropdown.css +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/assets/features_mapping.parq +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/basic_auth.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/computations/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/computations/functions.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/errors/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/errors/sovai_errors.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/anomalies.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/ask_df_llm.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/change_point_generator.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/clustering.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/core_kshape.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/cum_sum_torch.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/cusum.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/dimensionality_reduction.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/feature_extraction.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/feature_importance.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/feature_neutralizer.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/filter_df.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/fractional_differencing.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/nowcasting.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/pairwise.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/pandas_extensions.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/parallel_functions.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/pfa_feature_selector.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/regime_change.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/regime_change_pca.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/shapley_global_importance.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/shapley_importance.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/signal_evaluation.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/technical_indicators.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/time_decomposition.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/weight_optimization.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/get_compute.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/get_data copy.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/get_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/get_reports.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/get_tools.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/accounting/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/accounting/accounting_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/allocation/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/allocation/allocation_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/bankruptcy/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/bankruptcy/bankruptcy_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/breakout/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/breakout/breakout_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/corp_risk/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/corp_risk/corp_risk_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/earnings_surprise/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/earnings_surprise/earnings_surprise_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/insider/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/insider/insider_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/institutional/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/institutional/institutional_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/news/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/news/news_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/ratios/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/ratios/ratios_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/accounting/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/accounting/accounting_balance_sheet.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/bankruptcy/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/bankruptcy/bankruptcy_monthly_top.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/general/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/general/general_plots.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/news/__inti__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/news/news_econometric_analysis.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/studies/nowcasting.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/token_auth.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/authentication/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/authentication/authentication.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/graphs.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/llm_code_generator.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/sec_10_k_8_k_filings.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/sec_edgar_search.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/__init__.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/client_side.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/client_side_s3.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/converter.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/datetime_formats.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/file_management.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/get_tickers.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/helpers.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/pandas_extensions copy.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/plot.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/port_manager.py +0 -0
- {sovai-0.2.44 → sovai-0.2.46}/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": "",
|
|
@@ -394,11 +398,13 @@ CLIENT_SIDE_ENDPOINTS_S3_PART_HIGH = {
|
|
|
394
398
|
"spending/product",
|
|
395
399
|
|
|
396
400
|
"spending/transactions",
|
|
397
|
-
|
|
401
|
+
|
|
398
402
|
"lobbying",
|
|
399
403
|
|
|
400
404
|
"accounting/weekly",
|
|
401
405
|
|
|
406
|
+
"insider/trading",
|
|
407
|
+
|
|
402
408
|
"ratios/normal",
|
|
403
409
|
"ratios/relative",
|
|
404
410
|
|
|
@@ -414,9 +420,39 @@ 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",
|
|
427
|
+
|
|
428
|
+
"visas/h1b",
|
|
429
|
+
|
|
430
|
+
"wikipedia/views",
|
|
431
|
+
|
|
432
|
+
"short/volume",
|
|
433
|
+
"short/maker",
|
|
434
|
+
"short/over_shorted",
|
|
435
|
+
|
|
436
|
+
"institutional/trading"
|
|
437
|
+
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
418
441
|
|
|
419
|
-
|
|
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
|
+
# }
|
|
420
456
|
}
|
|
421
457
|
|
|
422
458
|
|
|
@@ -1092,6 +1128,38 @@ def data(
|
|
|
1092
1128
|
params = params or {}
|
|
1093
1129
|
params = map_synonyms(params)
|
|
1094
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
|
+
|
|
1095
1163
|
# Force full_history if date range is specified
|
|
1096
1164
|
if start_date is not None or end_date is not None:
|
|
1097
1165
|
full_history = True
|
|
@@ -1099,6 +1167,14 @@ def data(
|
|
|
1099
1167
|
|
|
1100
1168
|
# Update params with prepared values
|
|
1101
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
|
+
|
|
1102
1178
|
params.update(
|
|
1103
1179
|
_prepare_params(
|
|
1104
1180
|
tickers=params.get("tickers", tickers),
|
|
@@ -1147,6 +1223,10 @@ def data(
|
|
|
1147
1223
|
|
|
1148
1224
|
# Try client-side handlers for specific endpoints
|
|
1149
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
|
+
|
|
1150
1230
|
for endpoint_set, handler_func, message in ENDPOINT_HANDLERS:
|
|
1151
1231
|
if (
|
|
1152
1232
|
normalized_endpoint in endpoint_set and
|
|
@@ -1157,7 +1237,7 @@ def data(
|
|
|
1157
1237
|
verbose_mode.log(f"Calling client-side handler with tickers={tickers}, start_date={start_date}, end_date={end_date}")
|
|
1158
1238
|
try:
|
|
1159
1239
|
handler_result = handler_func(
|
|
1160
|
-
normalized_endpoint, tickers,
|
|
1240
|
+
normalized_endpoint, tickers, handler_columns_arg, start_date, end_date
|
|
1161
1241
|
)
|
|
1162
1242
|
_query_cache[cache_key] = handler_result
|
|
1163
1243
|
result_data = handler_result
|
|
@@ -1238,11 +1318,15 @@ def data(
|
|
|
1238
1318
|
logger.error(error_msg)
|
|
1239
1319
|
raise Exception(error_msg)
|
|
1240
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
|
+
|
|
1241
1325
|
# Apply filters
|
|
1242
1326
|
verbose_mode.log("Applying filters to data")
|
|
1243
1327
|
data_result = filter_data(
|
|
1244
1328
|
data_result,
|
|
1245
|
-
columns=
|
|
1329
|
+
columns=filter_columns_arg,
|
|
1246
1330
|
start_date=start_date,
|
|
1247
1331
|
end_date=end_date,
|
|
1248
1332
|
use_polars=use_polars
|
|
@@ -1328,6 +1412,22 @@ def data(
|
|
|
1328
1412
|
|
|
1329
1413
|
result_data = process_dataframe(result_data)
|
|
1330
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
|
+
|
|
1331
1431
|
return result_data
|
|
1332
1432
|
|
|
1333
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
|
# =========================================================================
|
|
@@ -692,7 +760,62 @@ ENDPOINT_CONFIG = {
|
|
|
692
760
|
"ticker_path": "sovai/sovai-employment/partitioned/ticker",
|
|
693
761
|
"date_path": "sovai/sovai-employment/partitioned/date",
|
|
694
762
|
"has_year": False
|
|
695
|
-
}
|
|
763
|
+
},
|
|
764
|
+
|
|
765
|
+
"wikipedia/views": {
|
|
766
|
+
"ticker_path": "sovai/sovai-wiki/views/partitioned/ticker",
|
|
767
|
+
"date_path": "sovai/sovai-wiki/views/partitioned/date",
|
|
768
|
+
"has_year": False
|
|
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
|
+
},
|
|
818
|
+
|
|
696
819
|
}
|
|
697
820
|
|
|
698
821
|
|
|
@@ -781,10 +904,15 @@ def load_frame_s3_partitioned_high(
|
|
|
781
904
|
tickers=tickers,
|
|
782
905
|
start_date=start_date,
|
|
783
906
|
end_date=end_date,
|
|
784
|
-
columns=columns,
|
|
907
|
+
columns=[col.strip() for col in columns.split(',')] ,
|
|
785
908
|
max_workers=8,
|
|
786
909
|
post_process=post_process
|
|
787
910
|
)
|
|
911
|
+
|
|
912
|
+
# print(columns)
|
|
913
|
+
# columns = [col.strip() for col in columns.split(',')]
|
|
914
|
+
|
|
915
|
+
# df = df[columns] if columns else df
|
|
788
916
|
|
|
789
917
|
# Convert to CustomDataFrame if available
|
|
790
918
|
if HAS_CUSTOM_DATAFRAME:
|
sovai-0.2.44/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
|