sovai 0.2.32__tar.gz → 0.2.34__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.32 → sovai-0.2.34}/PKG-INFO +1 -1
- {sovai-0.2.32 → sovai-0.2.34}/pyproject.toml +1 -1
- sovai-0.2.34/sovai/.DS_Store +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/api_config.py +1 -1
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/filter_df.py +1 -209
- {sovai-0.2.32 → sovai-0.2.34}/sovai/get_compute.py +1 -1
- {sovai-0.2.32 → sovai-0.2.34}/sovai/get_data.py +70 -15
- {sovai-0.2.32 → sovai-0.2.34}/sovai/get_plots.py +3 -3
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/earnings_surprise/earnings_surprise_plots.py +5 -1
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/client_side_s3.py +50 -15
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/plot.py +1 -0
- {sovai-0.2.32 → sovai-0.2.34}/LICENSE +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/README.md +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/assets/dropdown.css +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/assets/features_mapping.parq +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/basic_auth.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/computations/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/computations/functions.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/errors/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/errors/sovai_errors.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/anomalies.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/ask_df_llm.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/change_point_generator.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/clustering.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/core_kshape.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/cum_sum_torch.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/cusum.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/dimensionality_reduction.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/feature_extraction.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/feature_importance.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/feature_neutralizer.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/fractional_differencing.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/nowcasting.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/pairwise.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/pandas_extensions.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/parallel_functions.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/pfa_feature_selector.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/regime_change.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/regime_change_pca.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/shapley_global_importance.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/shapley_importance.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/signal_evaluation.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/technical_indicators.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/time_decomposition.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/extensions/weight_optimization.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/get_reports.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/get_tools.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/accounting/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/accounting/accounting_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/allocation/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/allocation/allocation_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/bankruptcy/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/bankruptcy/bankruptcy_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/breakout/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/breakout/breakout_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/corp_risk/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/corp_risk/corp_risk_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/earnings_surprise/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/insider/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/insider/insider_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/institutional/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/institutional/institutional_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/news/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/news/news_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/ratios/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/plots/ratios/ratios_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/accounting/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/accounting/accounting_balance_sheet.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/bankruptcy/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/bankruptcy/bankruptcy_monthly_top.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/general/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/general/general_plots.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/news/__inti__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/reports/news/news_econometric_analysis.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/studies/nowcasting.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/token_auth.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/tools/authentication/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/tools/authentication/authentication.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/tools/sec/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/tools/sec/graphs.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/tools/sec/llm_code_generator.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/tools/sec/sec_10_k_8_k_filings.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/tools/sec/sec_edgar_search.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/__init__.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/client_side.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/converter.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/datetime_formats.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/file_management.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/get_tickers.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/helpers.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/pandas_extensions copy.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/port_manager.py +0 -0
- {sovai-0.2.32 → sovai-0.2.34}/sovai/utils/stream.py +0 -0
|
Binary file
|
|
@@ -1,211 +1,3 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
import re
|
|
3
|
-
import time
|
|
4
|
-
from typing import Union, List, Tuple
|
|
5
|
-
from functools import wraps
|
|
6
|
-
from rapidfuzz import process, fuzz
|
|
7
|
-
from sovai.tools.sec.llm_code_generator import get_openai_key
|
|
8
|
-
from collections import deque
|
|
9
|
-
from datetime import datetime, timedelta
|
|
10
|
-
import openai
|
|
11
|
-
from textwrap import dedent
|
|
12
|
-
from functools import lru_cache
|
|
13
|
-
|
|
14
|
-
def with_openai_key(func):
|
|
15
|
-
@wraps(func)
|
|
16
|
-
def wrapper(*args, **kwargs):
|
|
17
|
-
openai.api_key = get_openai_key()
|
|
18
|
-
try:
|
|
19
|
-
return func(*args, **kwargs)
|
|
20
|
-
finally:
|
|
21
|
-
openai.api_key = None
|
|
22
|
-
return wrapper
|
|
23
|
-
|
|
24
|
-
class Filter:
|
|
25
|
-
_call_timestamps = deque()
|
|
26
|
-
_max_calls_per_minute = 10
|
|
27
|
-
_ask_cache = {}
|
|
28
|
-
verbose = False
|
|
29
|
-
mutable = False
|
|
30
|
-
models = ['gpt-3.5-turbo', 'gpt-4']
|
|
31
|
-
completion_config = {}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@staticmethod
|
|
35
|
-
@lru_cache(maxsize=1)
|
|
36
|
-
def _load_combined_df():
|
|
37
|
-
return pd.read_parquet("https://storage.googleapis.com/sovai-public/concats/filters/latest.parquet")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@classmethod
|
|
41
|
-
def _check_rate_limit(cls):
|
|
42
|
-
now = datetime.now()
|
|
43
|
-
while cls._call_timestamps and cls._call_timestamps[0] < now - timedelta(minutes=1):
|
|
44
|
-
cls._call_timestamps.popleft()
|
|
45
|
-
|
|
46
|
-
if len(cls._call_timestamps) >= cls._max_calls_per_minute:
|
|
47
|
-
raise ValueError("Slow down mate! You've reached the maximum number of filtering attempts per minute.")
|
|
48
|
-
|
|
49
|
-
cls._call_timestamps.append(now)
|
|
50
|
-
|
|
51
|
-
@staticmethod
|
|
52
|
-
def rate_limit_decorator(func):
|
|
53
|
-
@wraps(func)
|
|
54
|
-
def wrapper(*args, **kwargs):
|
|
55
|
-
Filter._check_rate_limit()
|
|
56
|
-
return func(*args, **kwargs)
|
|
57
|
-
return wrapper
|
|
58
|
-
|
|
59
|
-
@staticmethod
|
|
60
|
-
def _find_best_match(query: str, choices: List[str], threshold: int = 80) -> Union[str, None]:
|
|
61
|
-
best_match = process.extractOne(query, choices, scorer=fuzz.ratio, score_cutoff=threshold)
|
|
62
|
-
return best_match[0] if best_match else None
|
|
63
|
-
|
|
64
|
-
@staticmethod
|
|
65
|
-
def _parse_value(value: str) -> Union[float, int]:
|
|
66
|
-
multipliers = {'k': 1e3, 'm': 1e6, 'b': 1e9}
|
|
67
|
-
if value.endswith('%'):
|
|
68
|
-
return float(value.rstrip('%')) / 100
|
|
69
|
-
for suffix, multiplier in multipliers.items():
|
|
70
|
-
if value.lower().endswith(suffix):
|
|
71
|
-
return float(value[:-1]) * multiplier
|
|
72
|
-
return float(value)
|
|
73
|
-
|
|
74
|
-
@classmethod
|
|
75
|
-
def _create_standard_query(cls, condition: str, df: pd.DataFrame) -> Tuple[str, str]:
|
|
76
|
-
pattern = r'(\w+)\s*([<>=]+|like|contains)\s*(.+)'
|
|
77
|
-
match = re.match(pattern, condition, re.IGNORECASE)
|
|
78
|
-
if not match:
|
|
79
|
-
raise ValueError(f"Invalid condition format: {condition}")
|
|
80
|
-
|
|
81
|
-
column, operator, value = match.groups()
|
|
82
|
-
best_match = cls._find_best_match(column, df.columns.tolist())
|
|
83
|
-
if not best_match:
|
|
84
|
-
raise ValueError(f"No close match found for column: {column}")
|
|
85
|
-
|
|
86
|
-
original_column = column
|
|
87
|
-
column = best_match
|
|
88
|
-
|
|
89
|
-
operator = operator.lower().strip()
|
|
90
|
-
value = value.strip()
|
|
91
|
-
|
|
92
|
-
is_string_type = df[column].dtype == 'object'
|
|
93
|
-
|
|
94
|
-
if operator in ['like', 'contains'] or (operator == '=' and is_string_type):
|
|
95
|
-
query = f"`{column}`.str.contains('{value}', case=False, na=False)"
|
|
96
|
-
display_operator = 'like'
|
|
97
|
-
else:
|
|
98
|
-
parsed_value = cls._parse_value(value)
|
|
99
|
-
query = f"`{column}` {operator} {parsed_value}"
|
|
100
|
-
display_operator = operator
|
|
101
|
-
|
|
102
|
-
display_condition = f"{column} ({original_column}) {display_operator} {value}" if column != original_column else condition
|
|
103
|
-
return query, display_condition
|
|
104
|
-
|
|
105
|
-
@staticmethod
|
|
106
|
-
def _is_simple_condition(condition: str) -> bool:
|
|
107
|
-
simple_pattern = r'\w+\s*([<>=]+|like|contains)\s*.+'
|
|
108
|
-
return bool(re.match(simple_pattern, condition, re.IGNORECASE))
|
|
109
|
-
|
|
110
|
-
@staticmethod
|
|
111
|
-
def _split_conditions(condition: str) -> List[str]:
|
|
112
|
-
pattern = r'\s+(?:and|or)\s+(?![^()]*\))'
|
|
113
|
-
conditions = re.split(pattern, condition, flags=re.IGNORECASE)
|
|
114
|
-
return [cond.strip() for cond in conditions if cond.strip()]
|
|
115
|
-
|
|
116
|
-
@classmethod
|
|
117
|
-
@rate_limit_decorator
|
|
118
|
-
@with_openai_key
|
|
119
|
-
def _apply_condition_with_retry(cls, df: pd.DataFrame, condition: str, max_retries: int = 3, verbose: bool = False) -> pd.DataFrame:
|
|
120
|
-
original_len = len(df)
|
|
121
|
-
|
|
122
|
-
sub_conditions = cls._split_conditions(condition)
|
|
123
|
-
if len(sub_conditions) > 1:
|
|
124
|
-
if verbose:
|
|
125
|
-
print(f"Detected multiple conditions: {sub_conditions}")
|
|
126
|
-
for sub_condition in sub_conditions:
|
|
127
|
-
df = cls._apply_condition_with_retry(df, sub_condition, max_retries, verbose)
|
|
128
|
-
return df
|
|
129
|
-
|
|
130
|
-
if cls._is_simple_condition(condition):
|
|
131
|
-
try:
|
|
132
|
-
query, display_condition = cls._create_standard_query(condition, df)
|
|
133
|
-
filtered_df = df.query(query)
|
|
134
|
-
if len(filtered_df) < original_len:
|
|
135
|
-
if verbose:
|
|
136
|
-
print(f"Applied standard filter: {display_condition}")
|
|
137
|
-
return filtered_df
|
|
138
|
-
except Exception as e:
|
|
139
|
-
if verbose:
|
|
140
|
-
print(f"Standard filtering failed: {str(e)}. Falling back to GPT method.")
|
|
141
|
-
|
|
142
|
-
for attempt in range(max_retries):
|
|
143
|
-
try:
|
|
144
|
-
filtered_df = df.ask(condition)
|
|
145
|
-
if isinstance(filtered_df, pd.DataFrame) and len(filtered_df) < original_len:
|
|
146
|
-
return filtered_df
|
|
147
|
-
elif verbose:
|
|
148
|
-
print(f"Attempt {attempt + 1}: GPT filtering didn't remove any rows. Retrying...")
|
|
149
|
-
|
|
150
|
-
alternative_conditions = [f"filter {condition}", f"select {condition}", f"find {condition}"]
|
|
151
|
-
for alt_condition in alternative_conditions:
|
|
152
|
-
try:
|
|
153
|
-
filtered_df = df.ask(alt_condition)
|
|
154
|
-
if isinstance(filtered_df, pd.DataFrame) and len(filtered_df) < original_len:
|
|
155
|
-
return filtered_df
|
|
156
|
-
except Exception:
|
|
157
|
-
pass
|
|
158
|
-
|
|
159
|
-
columns_str = ", ".join(df.columns)
|
|
160
|
-
general_condition = f"Based on the columns {columns_str}, {condition}"
|
|
161
|
-
filtered_df = df.ask(general_condition)
|
|
162
|
-
if isinstance(filtered_df, pd.DataFrame) and len(filtered_df) < original_len:
|
|
163
|
-
return filtered_df
|
|
164
|
-
except Exception as e:
|
|
165
|
-
if verbose:
|
|
166
|
-
print(f"Error on GPT attempt {attempt + 1}: {str(e)}")
|
|
167
|
-
time.sleep(1)
|
|
168
|
-
|
|
169
|
-
if verbose:
|
|
170
|
-
print("All filtering attempts failed. Returning original DataFrame.")
|
|
171
|
-
return df
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
@staticmethod
|
|
175
|
-
def create_table(data, headers):
|
|
176
|
-
col_widths = [max(len(str(x)) for x in col) for col in zip(*data, headers)]
|
|
177
|
-
col_widths = [max(15, width) for width in col_widths]
|
|
178
|
-
|
|
179
|
-
row_format = '│ {:<{}} │ {:>{}} │ {:>{}} │ {:>{}} │'
|
|
180
|
-
header = row_format.format(headers[0], col_widths[0],
|
|
181
|
-
headers[1], col_widths[1],
|
|
182
|
-
headers[2], col_widths[2],
|
|
183
|
-
headers[3], col_widths[3])
|
|
184
|
-
|
|
185
|
-
separator = '┼' + '─' * (col_widths[0] + 2) + '┼' + '─' * (col_widths[1] + 2) + '┼' + '─' * (col_widths[2] + 2) + '┼' + '─' * (col_widths[3] + 2) + '┼'
|
|
186
|
-
top_border = '┌' + '─' * (col_widths[0] + 2) + '┬' + '─' * (col_widths[1] + 2) + '┬' + '─' * (col_widths[2] + 2) + '┬' + '─' * (col_widths[3] + 2) + '┐'
|
|
187
|
-
bottom_border = '└' + '─' * (col_widths[0] + 2) + '┴' + '─' * (col_widths[1] + 2) + '┴' + '─' * (col_widths[2] + 2) + '┴' + '─' * (col_widths[3] + 2) + '┘'
|
|
188
|
-
|
|
189
|
-
rows = []
|
|
190
|
-
for i, row in enumerate(data):
|
|
191
|
-
if i == 0: # Initial row
|
|
192
|
-
formatted_row = row_format.format(row[0], col_widths[0],
|
|
193
|
-
f"{row[1]:,}", col_widths[1],
|
|
194
|
-
'-', col_widths[2],
|
|
195
|
-
'-', col_widths[3])
|
|
196
|
-
elif i == len(data) - 1: # Final row
|
|
197
|
-
formatted_row = row_format.format(row[0], col_widths[0],
|
|
198
|
-
f"{data[0][1]:,}", col_widths[1],
|
|
199
|
-
f"{row[2]:,}", col_widths[2],
|
|
200
|
-
f"{row[1]:,}", col_widths[3])
|
|
201
|
-
else: # Other rows
|
|
202
|
-
formatted_row = row_format.format(row[0], col_widths[0],
|
|
203
|
-
f"{row[1]:,}", col_widths[1],
|
|
204
|
-
f"{row[2]:,}", col_widths[2],
|
|
205
|
-
f"{row[1]:,}", col_widths[3])
|
|
206
|
-
rows.append(formatted_row)
|
|
207
|
-
|
|
208
|
-
return '\n'.join([top_border, header, separator] + rows[:-1] + [separator, rows[-1], bottom_border])
|
|
209
1
|
|
|
210
2
|
import pandas as pd
|
|
211
3
|
import re
|
|
@@ -243,7 +35,7 @@ class Filter:
|
|
|
243
35
|
@staticmethod
|
|
244
36
|
@lru_cache(maxsize=1)
|
|
245
37
|
def _load_combined_df():
|
|
246
|
-
return pd.read_parquet("https://storage.googleapis.com/sovai-public/
|
|
38
|
+
return pd.read_parquet("https://storage.googleapis.com/sovai-public/concats/filters/latest.parquet")
|
|
247
39
|
|
|
248
40
|
|
|
249
41
|
@classmethod
|
|
@@ -10,7 +10,7 @@ import pyarrow.parquet as pq
|
|
|
10
10
|
from io import BytesIO
|
|
11
11
|
import boto3
|
|
12
12
|
import polars as pl
|
|
13
|
-
|
|
13
|
+
import plotly.graph_objects as go
|
|
14
14
|
# from fastapi import HTTPException ## Dee
|
|
15
15
|
|
|
16
16
|
import pyarrow.parquet as pq
|
|
@@ -243,7 +243,7 @@ endpoint_to_ticker = {
|
|
|
243
243
|
"/spending/competition": "",
|
|
244
244
|
"/spending/entities": "",
|
|
245
245
|
"/accounting/weekly": "",
|
|
246
|
-
"/
|
|
246
|
+
"/visas/h1b": "",
|
|
247
247
|
"/factors/accounting": "",
|
|
248
248
|
"/factors/alternative": "",
|
|
249
249
|
"/factors/comprehensive": "",
|
|
@@ -257,6 +257,7 @@ endpoint_to_ticker = {
|
|
|
257
257
|
"/complaints/private": "",
|
|
258
258
|
"/complaints/public": "",
|
|
259
259
|
"/short/over_shorted": "",
|
|
260
|
+
"/short/volume": "",
|
|
260
261
|
"/earnings/surprise": "",
|
|
261
262
|
"/news/sentiment_score": "",
|
|
262
263
|
"/news/topic_probability": "",
|
|
@@ -264,8 +265,15 @@ endpoint_to_ticker = {
|
|
|
264
265
|
"/macro/features": "",
|
|
265
266
|
"/congress": "",
|
|
266
267
|
"/market/closeadj": "",
|
|
268
|
+
"/lobbying/public": "",
|
|
267
269
|
|
|
268
270
|
|
|
271
|
+
## remember to add leading "/"
|
|
272
|
+
"/trials/predict": "",
|
|
273
|
+
"/trials/describe": "",
|
|
274
|
+
"/trials/all/predict": "",
|
|
275
|
+
"/trials/all/decribe": "",
|
|
276
|
+
"/trials/all": ""
|
|
269
277
|
|
|
270
278
|
|
|
271
279
|
}
|
|
@@ -282,6 +290,8 @@ def normalize_endpoint(endpoint):
|
|
|
282
290
|
return endpoint.strip("/").strip()
|
|
283
291
|
|
|
284
292
|
|
|
293
|
+
## If you also want access by full file, please also add to endpoint_to_ticker
|
|
294
|
+
|
|
285
295
|
client_side_endpoints_gcs = {
|
|
286
296
|
"ratios/relative",
|
|
287
297
|
"market/prices",
|
|
@@ -292,10 +302,19 @@ client_side_endpoints_gcs = {
|
|
|
292
302
|
"lobbying/public",
|
|
293
303
|
}
|
|
294
304
|
|
|
305
|
+
## If you also want access by full file, please also add to endpoint_to_ticker
|
|
295
306
|
client_side_endpoints_s3 = {
|
|
296
|
-
"sec/10k"
|
|
307
|
+
"sec/10k",
|
|
308
|
+
"trials/predict",
|
|
309
|
+
"trials/describe",
|
|
310
|
+
"trials/all/predict",
|
|
311
|
+
"trials/all/decribe",
|
|
312
|
+
"trials/all",
|
|
313
|
+
|
|
297
314
|
}
|
|
298
315
|
|
|
316
|
+
|
|
317
|
+
|
|
299
318
|
# In your main function or wherever this logic is implemented:
|
|
300
319
|
|
|
301
320
|
|
|
@@ -594,7 +613,7 @@ def data(
|
|
|
594
613
|
|
|
595
614
|
elif (
|
|
596
615
|
normalized_endpoint in client_side_endpoints_s3
|
|
597
|
-
|
|
616
|
+
and tickers is not None
|
|
598
617
|
and frequency is None
|
|
599
618
|
):
|
|
600
619
|
verbose_mode.vprint("Grabbing s3 client side")
|
|
@@ -605,6 +624,7 @@ def data(
|
|
|
605
624
|
if tickers is not None and not is_all(tickers):
|
|
606
625
|
params = ticker_mapper(params,verbose)
|
|
607
626
|
|
|
627
|
+
|
|
608
628
|
res = requests.get(
|
|
609
629
|
url=url,
|
|
610
630
|
headers=headers,
|
|
@@ -613,7 +633,7 @@ def data(
|
|
|
613
633
|
stream=True,
|
|
614
634
|
verify=ApiConfig.verify_ssl,
|
|
615
635
|
)
|
|
616
|
-
|
|
636
|
+
|
|
617
637
|
verbose_mode.vprint(f"Response Status: {res.status_code}")
|
|
618
638
|
verbose_mode.vprint(f"Response Content-Type: {res.headers.get('content-type')}")
|
|
619
639
|
|
|
@@ -624,12 +644,20 @@ def data(
|
|
|
624
644
|
tickers = get_ticker_from_endpoint(endpoint, tickers, endpoint_to_ticker)
|
|
625
645
|
|
|
626
646
|
print_tickers_value(tickers)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
res.raise_for_status()
|
|
627
650
|
|
|
628
651
|
data_format = res.headers.get("X-Data-Format")
|
|
629
652
|
content_type = res.headers["content-type"]
|
|
653
|
+
|
|
654
|
+
plot_header = res.headers.get("X-Plotly-Data")
|
|
655
|
+
# print(data_format)
|
|
656
|
+
# print(plot_header)
|
|
657
|
+
|
|
630
658
|
# print(content_type)
|
|
631
659
|
|
|
632
|
-
if content_type == "application/octet-stream":
|
|
660
|
+
if (content_type == "application/octet-stream") and not plot_header:
|
|
633
661
|
if data_format == "pyarrow":
|
|
634
662
|
verbose_mode.vprint(f"header: {data_format}")
|
|
635
663
|
data = stream_data_pyarrow(res)
|
|
@@ -670,25 +698,54 @@ def data(
|
|
|
670
698
|
data = CustomDataFrame(data)
|
|
671
699
|
|
|
672
700
|
verbose_mode.vprint("It reached the DF")
|
|
673
|
-
|
|
701
|
+
|
|
702
|
+
_query_cache[cache_key] = data
|
|
703
|
+
|
|
704
|
+
verbose_mode.vprint("It passed the DF")
|
|
705
|
+
|
|
706
|
+
elif not plot_header:
|
|
674
707
|
if HAS_FULL_INSTALL:
|
|
675
708
|
data = CustomDataFrame(convert_data2df(res.json()))
|
|
676
709
|
else:
|
|
677
710
|
data = pd.DataFrame(convert_data2df(res.json()))
|
|
678
711
|
|
|
679
|
-
|
|
712
|
+
_query_cache[cache_key] = data
|
|
713
|
+
|
|
714
|
+
verbose_mode.vprint("It passed the DF")
|
|
680
715
|
|
|
681
|
-
|
|
716
|
+
import pickle
|
|
717
|
+
from plotly.graph_objs import Figure
|
|
718
|
+
def set_dark_mode(fig: Figure):
|
|
719
|
+
return fig.update_layout(
|
|
720
|
+
template="plotly_dark",
|
|
721
|
+
plot_bgcolor="rgba(10, 10, 10, 1)",
|
|
722
|
+
paper_bgcolor="rgba(10, 10, 10, 1)",
|
|
723
|
+
)
|
|
682
724
|
|
|
725
|
+
if plot_header:
|
|
726
|
+
# print(res.content[:100])
|
|
727
|
+
if HAS_FULL_INSTALL:
|
|
728
|
+
# Decompress the data
|
|
729
|
+
pickle_bytes = res.content
|
|
730
|
+
# Unpickle the data
|
|
731
|
+
fig = pickle.loads(pickle_bytes)
|
|
732
|
+
fig = go.Figure(json.loads(fig))
|
|
733
|
+
fig = set_dark_mode(fig)
|
|
734
|
+
|
|
735
|
+
return fig
|
|
736
|
+
else:
|
|
737
|
+
print("Plotting is only available with the full installation. Please install 'sovai[full]' to use this feature.")
|
|
738
|
+
return None
|
|
739
|
+
|
|
683
740
|
if plot:
|
|
684
741
|
if HAS_FULL_INSTALL:
|
|
685
|
-
_draw_graphs(data)
|
|
742
|
+
return _draw_graphs(data)
|
|
686
743
|
else:
|
|
687
744
|
print("Plotting is only available with the full installation. Please install 'sovai[full]' to use this feature.")
|
|
688
745
|
return None
|
|
689
746
|
return data
|
|
690
747
|
except Exception as err:
|
|
691
|
-
verbose_mode.vprint("An error occurred:", err)
|
|
748
|
+
verbose_mode.vprint("An error occurred, check dictaionries for 'data/name':'None':", err)
|
|
692
749
|
return data
|
|
693
750
|
if res.status_code == 404:
|
|
694
751
|
msg = res.json()
|
|
@@ -747,9 +804,7 @@ def _draw_graphs(data: Union[Dict, List[Dict]]):
|
|
|
747
804
|
if isinstance(data, list):
|
|
748
805
|
for plot in data:
|
|
749
806
|
for _, val in plot.items():
|
|
750
|
-
plotting_data(val)
|
|
751
|
-
break
|
|
807
|
+
return plotting_data(val)
|
|
752
808
|
else:
|
|
753
809
|
for _, val in data.items():
|
|
754
|
-
plotting_data(val)
|
|
755
|
-
break
|
|
810
|
+
return plotting_data(val)
|
|
@@ -44,12 +44,12 @@ def _draw_graphs(data: Union[Dict, List[Dict]]):
|
|
|
44
44
|
if isinstance(data, list):
|
|
45
45
|
for plot in data:
|
|
46
46
|
for _, val in plot.items():
|
|
47
|
-
plotting_data(val)
|
|
47
|
+
return plotting_data(val)
|
|
48
48
|
break
|
|
49
49
|
else:
|
|
50
50
|
for _, val in data.items():
|
|
51
|
-
plotting_data(val)
|
|
52
|
-
|
|
51
|
+
return plotting_data(val)
|
|
52
|
+
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
from typing import Optional, Union, Tuple, List, Dict
|
|
@@ -127,6 +127,7 @@ def earnings_tree():
|
|
|
127
127
|
if 'ticker' not in df_earn.columns:
|
|
128
128
|
df_earn = df_earn.reset_index()
|
|
129
129
|
|
|
130
|
+
df_earn['market_cap'] = df_earn['market_cap'].replace(0, 1e-6)
|
|
130
131
|
# Calculate market cap deciles and handle NaN values
|
|
131
132
|
df_earn['market_cap_decile'] = pd.qcut(df_earn['market_cap'].rank(method='first'), q=10, labels=range(1, 11))
|
|
132
133
|
df_earn['market_cap_decile'] = df_earn['market_cap_decile'].astype(str).replace('nan', 'Unknown')
|
|
@@ -134,6 +135,7 @@ def earnings_tree():
|
|
|
134
135
|
# Update decile labels
|
|
135
136
|
df_earn['market_cap_decile'] = df_earn['market_cap_decile'].apply(lambda x: f"{x} Cap Decile" if x != 'Unknown' else x)
|
|
136
137
|
|
|
138
|
+
|
|
137
139
|
# Create the main treemap
|
|
138
140
|
fig = px.treemap(
|
|
139
141
|
df_earn,
|
|
@@ -183,4 +185,6 @@ def earnings_tree():
|
|
|
183
185
|
)
|
|
184
186
|
|
|
185
187
|
# Show the plot
|
|
186
|
-
fig
|
|
188
|
+
fig
|
|
189
|
+
|
|
190
|
+
return fig
|
|
@@ -63,6 +63,9 @@ def process_partition(storage_provider, base_path, identifier_column, identifier
|
|
|
63
63
|
|
|
64
64
|
if identifier:
|
|
65
65
|
df[identifier_column] = identifier
|
|
66
|
+
|
|
67
|
+
df = df[[identifier_column] + [col for col in df.columns if col != identifier_column]]
|
|
68
|
+
|
|
66
69
|
|
|
67
70
|
return df
|
|
68
71
|
|
|
@@ -165,6 +168,8 @@ def map_identifier(input_values, input_identifier='ticker', output_identifier='c
|
|
|
165
168
|
else:
|
|
166
169
|
return result.tolist()
|
|
167
170
|
|
|
171
|
+
|
|
172
|
+
|
|
168
173
|
def load_frame_s3(endpoint, tickers=None, columns=None, start_date=None, end_date=None):
|
|
169
174
|
|
|
170
175
|
endpoint_config = {
|
|
@@ -177,12 +182,44 @@ def load_frame_s3(endpoint, tickers=None, columns=None, start_date=None, end_dat
|
|
|
177
182
|
"partitioned": True,
|
|
178
183
|
|
|
179
184
|
},
|
|
180
|
-
"
|
|
185
|
+
"trials/predict": {
|
|
181
186
|
"storage_provider": {
|
|
182
|
-
"digitalocean": "sovai/sovai-
|
|
183
|
-
"wasabi": "sovai-
|
|
187
|
+
"digitalocean": "sovai/sovai-clinical-trials-export/prediction_public",
|
|
188
|
+
"wasabi": "sovai-clinical-trials-export/prediction_public"
|
|
184
189
|
},
|
|
185
|
-
"identifier_column": "
|
|
190
|
+
"identifier_column": "ticker",
|
|
191
|
+
"partitioned": True,
|
|
192
|
+
},
|
|
193
|
+
"trials/describe": {
|
|
194
|
+
"storage_provider": {
|
|
195
|
+
"digitalocean": "sovai/sovai-clinical-trials-export/describe_public",
|
|
196
|
+
"wasabi": "sovai-clinical-trials-export/describe_public"
|
|
197
|
+
},
|
|
198
|
+
"identifier_column": "ticker",
|
|
199
|
+
"partitioned": True,
|
|
200
|
+
},
|
|
201
|
+
"trials/all": {
|
|
202
|
+
"storage_provider": {
|
|
203
|
+
"digitalocean": "sovai/sovai-clinical-trials-export/describe_all",
|
|
204
|
+
"wasabi": "sovai-clinical-trials-export/describe_all"
|
|
205
|
+
},
|
|
206
|
+
"identifier_column": "ticker",
|
|
207
|
+
"partitioned": True,
|
|
208
|
+
},
|
|
209
|
+
"trials/all/predict": {
|
|
210
|
+
"storage_provider": {
|
|
211
|
+
"digitalocean": "sovai/sovai-clinical-trials-export/prediction_all",
|
|
212
|
+
"wasabi": "sovai-clinical-trials-export/prediction_all"
|
|
213
|
+
},
|
|
214
|
+
"identifier_column": "ticker",
|
|
215
|
+
"partitioned": True,
|
|
216
|
+
},
|
|
217
|
+
"trials/all/decribe": {
|
|
218
|
+
"storage_provider": {
|
|
219
|
+
"digitalocean": "sovai/sovai-clinical-trials-export/describe_all",
|
|
220
|
+
"wasabi": "sovai-clinical-trials-export/describe_all"
|
|
221
|
+
},
|
|
222
|
+
"identifier_column": "ticker",
|
|
186
223
|
"partitioned": True,
|
|
187
224
|
}
|
|
188
225
|
}
|
|
@@ -193,17 +230,15 @@ def load_frame_s3(endpoint, tickers=None, columns=None, start_date=None, end_dat
|
|
|
193
230
|
config = endpoint_config[endpoint]
|
|
194
231
|
|
|
195
232
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
# Implement logic for other endpoints if needed
|
|
206
|
-
raise NotImplementedError(f"Endpoint {endpoint} is not yet implemented")
|
|
233
|
+
|
|
234
|
+
df_frame = client_side_s3_frame(
|
|
235
|
+
config,
|
|
236
|
+
tickers,
|
|
237
|
+
columns,
|
|
238
|
+
start_date,
|
|
239
|
+
end_date
|
|
240
|
+
)
|
|
241
|
+
|
|
207
242
|
|
|
208
243
|
if HAS_CUSTOM_DATAFRAME:
|
|
209
244
|
return CustomDataFrame(df_frame)
|
|
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
|