sovai 0.2.28__tar.gz → 0.2.30__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.
Files changed (96) hide show
  1. {sovai-0.2.28 → sovai-0.2.30}/PKG-INFO +2 -1
  2. {sovai-0.2.28 → sovai-0.2.30}/pyproject.toml +2 -1
  3. {sovai-0.2.28 → sovai-0.2.30}/sovai/__init__.py +3 -1
  4. sovai-0.2.30/sovai/get_tools.py +82 -0
  5. {sovai-0.2.28 → sovai-0.2.30}/sovai/tools/sec/graphs.py +45 -33
  6. sovai-0.2.28/sovai/get_tools.py +0 -32
  7. {sovai-0.2.28 → sovai-0.2.30}/LICENSE +0 -0
  8. {sovai-0.2.28 → sovai-0.2.30}/README.md +0 -0
  9. {sovai-0.2.28 → sovai-0.2.30}/sovai/api_config.py +0 -0
  10. {sovai-0.2.28 → sovai-0.2.30}/sovai/assets/dropdown.css +0 -0
  11. {sovai-0.2.28 → sovai-0.2.30}/sovai/assets/features_mapping.parq +0 -0
  12. {sovai-0.2.28 → sovai-0.2.30}/sovai/basic_auth.py +0 -0
  13. {sovai-0.2.28 → sovai-0.2.30}/sovai/computations/__init__.py +0 -0
  14. {sovai-0.2.28 → sovai-0.2.30}/sovai/computations/functions.py +0 -0
  15. {sovai-0.2.28 → sovai-0.2.30}/sovai/errors/__init__.py +0 -0
  16. {sovai-0.2.28 → sovai-0.2.30}/sovai/errors/sovai_errors.py +0 -0
  17. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/__init__.py +0 -0
  18. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/anomalies.py +0 -0
  19. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/ask_df_llm.py +0 -0
  20. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/change_point_generator.py +0 -0
  21. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/clustering.py +0 -0
  22. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/core_kshape.py +0 -0
  23. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/cum_sum_torch.py +0 -0
  24. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/cusum.py +0 -0
  25. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/dimensionality_reduction.py +0 -0
  26. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/feature_extraction.py +0 -0
  27. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/feature_importance.py +0 -0
  28. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/feature_neutralizer.py +0 -0
  29. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/filter_df.py +0 -0
  30. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/fractional_differencing.py +0 -0
  31. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/nowcasting.py +0 -0
  32. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/pairwise.py +0 -0
  33. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/pandas_extensions.py +0 -0
  34. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/parallel_functions.py +0 -0
  35. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/pfa_feature_selector.py +0 -0
  36. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/regime_change.py +0 -0
  37. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/regime_change_pca.py +0 -0
  38. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/shapley_global_importance.py +0 -0
  39. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/shapley_importance.py +0 -0
  40. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/signal_evaluation.py +0 -0
  41. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/technical_indicators.py +0 -0
  42. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/time_decomposition.py +0 -0
  43. {sovai-0.2.28 → sovai-0.2.30}/sovai/extensions/weight_optimization.py +0 -0
  44. {sovai-0.2.28 → sovai-0.2.30}/sovai/get_compute.py +0 -0
  45. {sovai-0.2.28 → sovai-0.2.30}/sovai/get_data.py +0 -0
  46. {sovai-0.2.28 → sovai-0.2.30}/sovai/get_plots.py +0 -0
  47. {sovai-0.2.28 → sovai-0.2.30}/sovai/get_reports.py +0 -0
  48. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/__init__.py +0 -0
  49. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/accounting/__init__.py +0 -0
  50. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/accounting/accounting_plots.py +0 -0
  51. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/allocation/__init__.py +0 -0
  52. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/allocation/allocation_plots.py +0 -0
  53. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/bankruptcy/__init__.py +0 -0
  54. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/bankruptcy/bankruptcy_plots.py +0 -0
  55. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/breakout/__init__.py +0 -0
  56. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/breakout/breakout_plots.py +0 -0
  57. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/corp_risk/__init__.py +0 -0
  58. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/corp_risk/corp_risk_plots.py +0 -0
  59. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/earnings_surprise/__init__.py +0 -0
  60. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/earnings_surprise/earnings_surprise_plots.py +0 -0
  61. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/insider/__init__.py +0 -0
  62. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/insider/insider_plots.py +0 -0
  63. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/institutional/__init__.py +0 -0
  64. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/institutional/institutional_plots.py +0 -0
  65. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/news/__init__.py +0 -0
  66. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/news/news_plots.py +0 -0
  67. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/ratios/__init__.py +0 -0
  68. {sovai-0.2.28 → sovai-0.2.30}/sovai/plots/ratios/ratios_plots.py +0 -0
  69. /sovai-0.2.28/sovai/reports/accounting/__inti__.py → /sovai-0.2.30/sovai/reports/accounting/__init__.py +0 -0
  70. {sovai-0.2.28 → sovai-0.2.30}/sovai/reports/accounting/accounting_balance_sheet.py +0 -0
  71. /sovai-0.2.28/sovai/reports/bankruptcy/__inti__.py → /sovai-0.2.30/sovai/reports/bankruptcy/__init__.py +0 -0
  72. {sovai-0.2.28 → sovai-0.2.30}/sovai/reports/bankruptcy/bankruptcy_monthly_top.py +0 -0
  73. /sovai-0.2.28/sovai/reports/general/__inti__.py → /sovai-0.2.30/sovai/reports/general/__init__.py +0 -0
  74. {sovai-0.2.28 → sovai-0.2.30}/sovai/reports/general/general_plots.py +0 -0
  75. {sovai-0.2.28 → sovai-0.2.30}/sovai/reports/news/__inti__.py +0 -0
  76. {sovai-0.2.28 → sovai-0.2.30}/sovai/reports/news/news_econometric_analysis.py +0 -0
  77. {sovai-0.2.28 → sovai-0.2.30}/sovai/studies/nowcasting.py +0 -0
  78. {sovai-0.2.28 → sovai-0.2.30}/sovai/token_auth.py +0 -0
  79. {sovai-0.2.28/sovai/utils → sovai-0.2.30/sovai/tools/authentication}/__init__.py +0 -0
  80. {sovai-0.2.28 → sovai-0.2.30}/sovai/tools/authentication/authentication.py +0 -0
  81. /sovai-0.2.28/sovai/tools/authentication/__inti__.py → /sovai-0.2.30/sovai/tools/sec/__init__.py +0 -0
  82. {sovai-0.2.28 → sovai-0.2.30}/sovai/tools/sec/llm_code_generator.py +0 -0
  83. {sovai-0.2.28 → sovai-0.2.30}/sovai/tools/sec/sec_10_k_8_k_filings.py +0 -0
  84. {sovai-0.2.28 → sovai-0.2.30}/sovai/tools/sec/sec_edgar_search.py +0 -0
  85. /sovai-0.2.28/sovai/tools/sec/__inti__.py → /sovai-0.2.30/sovai/utils/__init__.py +0 -0
  86. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/client_side.py +0 -0
  87. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/client_side_s3.py +0 -0
  88. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/converter.py +0 -0
  89. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/datetime_formats.py +0 -0
  90. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/file_management.py +0 -0
  91. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/get_tickers.py +0 -0
  92. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/helpers.py +0 -0
  93. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/pandas_extensions copy.py +0 -0
  94. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/plot.py +0 -0
  95. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/port_manager.py +0 -0
  96. {sovai-0.2.28 → sovai-0.2.30}/sovai/utils/stream.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sovai
3
- Version: 0.2.28
3
+ Version: 0.2.30
4
4
  Summary: python for asset management
5
5
  License: Proprietary License
6
6
  Author: Derek Snow
@@ -40,6 +40,7 @@ Requires-Dist: requests (>=2.20)
40
40
  Requires-Dist: ruptures (>=1.0) ; extra == "full"
41
41
  Requires-Dist: scikit-learn (>=1.0) ; extra == "full"
42
42
  Requires-Dist: scipy (>=1.0) ; extra == "full"
43
+ Requires-Dist: seaborn (>=0.13.2,<0.14.0)
43
44
  Requires-Dist: shap (>=0.40) ; extra == "full"
44
45
  Requires-Dist: skfolio (>=0.3) ; extra == "full"
45
46
  Requires-Dist: statsforecast (>=1.0) ; extra == "full"
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sovai"
3
- version = "0.2.28"
3
+ version = "0.2.30"
4
4
  description = "python for asset management"
5
5
  authors = ["Derek Snow <d.snow@outlook.com>"]
6
6
  license = "Proprietary License"
@@ -64,6 +64,7 @@ dash-bootstrap-components = {version = ">=1.6.0", optional = true}
64
64
  yachalk = "^0.1.6"
65
65
  pyvis = "^0.3.2"
66
66
  networkx = "^3.3"
67
+ seaborn = "^0.13.2"
67
68
 
68
69
 
69
70
  [tool.poetry.extras]
@@ -27,9 +27,11 @@ try:
27
27
  from .extensions.pandas_extensions import CustomDataFrame as extension
28
28
  from .get_tools import sec_search, sec_filing, code, sec_graph
29
29
  HAS_FULL_INSTALL = True
30
- except ImportError:
30
+ except ImportError as e:
31
+ print(f"ImportError: {e}")
31
32
  HAS_FULL_INSTALL = False
32
33
 
34
+
33
35
  __all__ = ['read_key', 'save_key', 'ApiConfig', 'data', 'basic_auth', 'token_auth']
34
36
 
35
37
  if HAS_FULL_INSTALL:
@@ -0,0 +1,82 @@
1
+ from .tools.sec.sec_edgar_search import edgar_search_report
2
+
3
+ from .tools.sec.sec_10_k_8_k_filings import large_filing_module
4
+
5
+ from .tools.sec.llm_code_generator import generate_sovai_code
6
+
7
+ from .tools.sec.graphs import analyze_10k_graph
8
+
9
+ import pandas as pd
10
+
11
+
12
+ def sec_search(search="CFO Resgination"):
13
+
14
+ return edgar_search_report(search)
15
+
16
+
17
+
18
+
19
+ def sec_filing(ticker="AAPL", form="10-Q", date_input="2023-Q3", verbose=False):
20
+
21
+ return large_filing_module(ticker, form=form, date_input=date_input, verbose=verbose)
22
+
23
+
24
+
25
+ def code(prompt="get bankruptcy data for Tesla", verbose=False, run=False):
26
+
27
+ return generate_sovai_code(prompt, verbose=verbose, run=run)
28
+
29
+
30
+ from typing import Optional, Union
31
+
32
+ def sec_graph(
33
+ ticker: str = "AAPL",
34
+ date: str = "2024-Q3",
35
+ verbose: bool = False,
36
+ ontology_type: str = "causal",
37
+ oai_model: str = "gpt-4o-mini",
38
+ batch: bool = True,
39
+ batch_size: int = 10,
40
+ sentiment_filter: Optional[Union[float, bool]] = None, # Can be a float or a boolean for flexibility
41
+ output_dir: str = "./docs",
42
+ use_cache: bool = True
43
+ ) -> pd.DataFrame:
44
+ """
45
+ Generate a knowledge graph from 10-K SEC filings for a given ticker using the specified ontology type.
46
+
47
+ Parameters:
48
+ - ticker (str): Ticker symbol (e.g., AAPL for Apple Inc.)
49
+ - date (str): Filing date or quarter (default: "2024-Q3").
50
+ - verbose (bool): Whether to print detailed logs.
51
+ - ontology_type (str): The ontology type to use for analysis. Choose from:
52
+ - "connection", "causal", "temporal", "stakeholder", "innovation", "esg", "sentiment"
53
+ - oai_model (str): OpenAI model to use (default: "gpt-4o-mini").
54
+ - batch (bool): Whether to process documents in batches.
55
+ - batch_size (int): Number of documents to process in a batch (default: 10).
56
+ - sentiment_filter (Optional[Union[float, bool]]): Filter by sentiment scores or leave as None for no filter.
57
+ - output_dir (str): Directory to save graph outputs (default: "./docs").
58
+ - use_cache (bool): Whether to use cached results to speed up analysis.
59
+
60
+ Returns:
61
+ - pd.DataFrame: DataFrame representing the generated graph with nodes and relationships.
62
+ """
63
+
64
+ # Ensure ontology type is valid
65
+ ontology = ["connection", "causal", "temporal", "stakeholder", "innovation", "esg", "sentiment"]
66
+
67
+ if ontology_type not in ontology:
68
+ raise ValueError(f"Invalid ontology type: {ontology_type}. Must be one of: {ontology}")
69
+
70
+ return analyze_10k_graph(
71
+ ticker=ticker,
72
+ date=date,
73
+ section_select=True,
74
+ ontology_type=ontology_type,
75
+ oai_model=oai_model,
76
+ batch=batch,
77
+ batch_size=batch_size,
78
+ sentiment_filter=sentiment_filter,
79
+ output_dir=output_dir,
80
+ use_cache=use_cache,
81
+ verbose=verbose
82
+ )
@@ -7,33 +7,40 @@ from abc import ABC, abstractmethod
7
7
  import logging
8
8
  from yachalk import chalk
9
9
  import os
10
+
10
11
  import logging
11
12
 
13
+ # Disable logging by setting the highest log level (CRITICAL) globally
14
+ logging.disable(logging.CRITICAL)
15
+ logging.getLogger().addHandler(logging.NullHandler())
12
16
 
13
- class GraphLogger:
14
17
 
18
+ class GraphLogger:
15
19
  def __init__(self, name="Graph Logger", color="white"):
16
- "Set the log level (optional, can be DEBUG, INFO, WARNING, ERROR, CRITICAL)"
20
+ # Set the log level (optional, can be DEBUG, INFO, WARNING, ERROR, CRITICAL)
21
+ # log_level = os.environ.get("LOG_LEVEL", "WARNING").upper()
17
22
 
18
- log_level = os.environ.get("LOG_LEVEL", "INFO").upper()
19
- logging.basicConfig(level=log_level)
20
-
21
- ## Formatter
22
- self.time_format = "%Y-%m-%d %H:%M:%S"
23
- format = self.format(color)
24
- self.formatter = logging.Formatter(
25
- fmt=format,
26
- datefmt=self.time_format,
27
- )
28
-
29
- ## Handler
30
- handler = logging.StreamHandler()
31
- handler.setFormatter(self.formatter)
23
+ log_level = logging.CRITICAL
32
24
 
33
25
  ## Logger
34
26
  self.logger = logging.getLogger(name)
35
- self.logger.addHandler(handler)
36
- self.logger.propagate = False
27
+ self.logger.setLevel(log_level)
28
+ self.logger.propagate = False # Avoid duplicate logs from propagating to the root logger
29
+
30
+ # Check if the logger already has handlers
31
+ if not self.logger.hasHandlers():
32
+ ## Formatter
33
+ self.time_format = "%Y-%m-%d %H:%M:%S"
34
+ format = self.format(color)
35
+ self.formatter = logging.Formatter(fmt=format, datefmt=self.time_format)
36
+
37
+ ## Handler
38
+ handler = logging.StreamHandler()
39
+ handler.setFormatter(self.formatter)
40
+ handler.setLevel(log_level) # Ensure the handler follows the same level as the logger
41
+
42
+ # Add handler to logger
43
+ self.logger.addHandler(handler)
37
44
 
38
45
  def getLogger(self):
39
46
  return self.logger
@@ -175,7 +182,7 @@ class OpenAIClient(LLMClient):
175
182
  self.client = OpenAI(api_key=sov.tools.sec.llm_code_generator.get_openai_key())
176
183
 
177
184
  def generate(self, user_message: str, system_message: str) -> str:
178
- print("Using Model: ", self._model)
185
+ # print("Using Model: ", self._model)
179
186
 
180
187
  response = self.client.chat.completions.create(
181
188
  messages=[
@@ -200,7 +207,7 @@ class OpenAIClientBatch(LLMClient):
200
207
  self.client = OpenAI(api_key="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")
201
208
 
202
209
  def generate(self, user_messages: List[str], system_message: str) -> List[str]:
203
- print("Using Model: ", self._model)
210
+ # print("Using Model: ", self._model)
204
211
 
205
212
  messages = [
206
213
  {"role": "system", "content": system_message},
@@ -560,6 +567,9 @@ verbose_logger = GraphLogger(name="GRAPH MAKER VERBOSE", color="blue").getLogger
560
567
  red_logger = GraphLogger(name="GRAPH MAKER ERROR", color="red_bright").getLogger()
561
568
  yellow_logger = GraphLogger(name="GRAPH MAKER WARNING", color="yellow_bright").getLogger()
562
569
 
570
+
571
+
572
+
563
573
  import pandas as pd
564
574
  import numpy as np
565
575
  import uuid
@@ -586,18 +596,20 @@ class GraphMaker:
586
596
  self._ontology = ontology_dict[ontology_type]
587
597
  self._llm_client = llm_client
588
598
  self._verbose = verbose
589
- if self._verbose:
590
- verbose_logger.setLevel("INFO")
591
- green_logger.setLevel("INFO")
592
- json_parse_logger.setLevel("INFO")
593
- red_logger.setLevel("INFO")
594
- yellow_logger.setLevel("INFO")
595
- else:
596
- verbose_logger.setLevel(logging.NOTSET)
597
- green_logger.setLevel(logging.NOTSET)
598
- json_parse_logger.setLevel(logging.NOTSET)
599
- red_logger.setLevel(logging.NOTSET)
600
- yellow_logger.setLevel(logging.NOTSET)
599
+ # green_logger.setLevel(logging.NOTSET)
600
+ green_logger.setLevel(logging.CRITICAL)
601
+ # if self._verbose:
602
+ # verbose_logger.setLevel("INFO")
603
+ # green_logger.setLevel("INFO")
604
+ # json_parse_logger.setLevel("INFO")
605
+ # red_logger.setLevel("INFO")
606
+ # yellow_logger.setLevel("INFO")
607
+ # else:
608
+ # verbose_logger.setLevel(logging.NOTSET)
609
+ # green_logger.setLevel(logging.NOTSET)
610
+ # json_parse_logger.setLevel(logging.NOTSET)
611
+ # red_logger.setLevel(logging.NOTSET)
612
+ # yellow_logger.setLevel(logging.NOTSET)
601
613
 
602
614
  def user_message(self, text: str) -> str:
603
615
  return f"input text: ```\n{text}\n```"
@@ -622,7 +634,7 @@ class GraphMaker:
622
634
  return response
623
635
 
624
636
  def parse_json(self, text: str):
625
- green_logger.info(f"Trying JSON Parsing: \n{text}")
637
+ green_logger.info(f"JSON Parsing: \n{text}")
626
638
  try:
627
639
  parsed_json = json.loads(text)
628
640
  green_logger.info(f"JSON Parsing Successful!")
@@ -1,32 +0,0 @@
1
- from .tools.sec.sec_edgar_search import edgar_search_report
2
-
3
- from .tools.sec.sec_10_k_8_k_filings import large_filing_module
4
-
5
- from .tools.sec.llm_code_generator import generate_sovai_code
6
-
7
- from .tools.sec.graphs import analyze_10k_graph
8
-
9
-
10
-
11
- def sec_search(search="CFO Resgination"):
12
-
13
- return edgar_search_report(search)
14
-
15
-
16
-
17
-
18
- def sec_filing(ticker="AAPL", form="10-Q", date_input="2023-Q3", verbose=False):
19
-
20
- return large_filing_module(ticker, form=form, date_input=date_input, verbose=verbose)
21
-
22
-
23
-
24
- def code(prompt="get bankruptcy data for Tesla", verbose=False, run=False):
25
-
26
- return generate_sovai_code(prompt, verbose=verbose, run=run)
27
-
28
-
29
-
30
- def sec_graph(ticker="AAPL", date_input="2024-Q3", verbose=False, ontology_type="causal", oai_model="gpt-4o-mini", batch=True, batch_size=10, sentiment_filter=None, output_dir="./docs", use_cache=True):
31
-
32
- return analyze_10k_graph(ticker, date_input, section_select=True, ontology_type=ontology_type, oai_model=oai_model, batch=batch, batch_size=batch_size, sentiment_filter=sentiment_filter, output_dir=output_dir, use_cache=use_cache, verbose=verbose)
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