mainsequence 2.0.0__py3-none-any.whl
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.
- mainsequence/__init__.py +0 -0
- mainsequence/__main__.py +9 -0
- mainsequence/cli/__init__.py +1 -0
- mainsequence/cli/api.py +157 -0
- mainsequence/cli/cli.py +442 -0
- mainsequence/cli/config.py +78 -0
- mainsequence/cli/ssh_utils.py +126 -0
- mainsequence/client/__init__.py +17 -0
- mainsequence/client/base.py +431 -0
- mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- mainsequence/client/data_sources_interfaces/duckdb.py +1468 -0
- mainsequence/client/data_sources_interfaces/timescale.py +479 -0
- mainsequence/client/models_helpers.py +113 -0
- mainsequence/client/models_report_studio.py +412 -0
- mainsequence/client/models_tdag.py +2276 -0
- mainsequence/client/models_vam.py +1983 -0
- mainsequence/client/utils.py +387 -0
- mainsequence/dashboards/__init__.py +0 -0
- mainsequence/dashboards/streamlit/__init__.py +0 -0
- mainsequence/dashboards/streamlit/assets/config.toml +12 -0
- mainsequence/dashboards/streamlit/assets/favicon.png +0 -0
- mainsequence/dashboards/streamlit/assets/logo.png +0 -0
- mainsequence/dashboards/streamlit/core/__init__.py +0 -0
- mainsequence/dashboards/streamlit/core/theme.py +212 -0
- mainsequence/dashboards/streamlit/pages/__init__.py +0 -0
- mainsequence/dashboards/streamlit/scaffold.py +220 -0
- mainsequence/instrumentation/__init__.py +7 -0
- mainsequence/instrumentation/utils.py +101 -0
- mainsequence/instruments/__init__.py +1 -0
- mainsequence/instruments/data_interface/__init__.py +10 -0
- mainsequence/instruments/data_interface/data_interface.py +361 -0
- mainsequence/instruments/instruments/__init__.py +3 -0
- mainsequence/instruments/instruments/base_instrument.py +85 -0
- mainsequence/instruments/instruments/bond.py +447 -0
- mainsequence/instruments/instruments/european_option.py +74 -0
- mainsequence/instruments/instruments/interest_rate_swap.py +217 -0
- mainsequence/instruments/instruments/json_codec.py +585 -0
- mainsequence/instruments/instruments/knockout_fx_option.py +146 -0
- mainsequence/instruments/instruments/position.py +475 -0
- mainsequence/instruments/instruments/ql_fields.py +239 -0
- mainsequence/instruments/instruments/vanilla_fx_option.py +107 -0
- mainsequence/instruments/pricing_models/__init__.py +0 -0
- mainsequence/instruments/pricing_models/black_scholes.py +49 -0
- mainsequence/instruments/pricing_models/bond_pricer.py +182 -0
- mainsequence/instruments/pricing_models/fx_option_pricer.py +90 -0
- mainsequence/instruments/pricing_models/indices.py +350 -0
- mainsequence/instruments/pricing_models/knockout_fx_pricer.py +209 -0
- mainsequence/instruments/pricing_models/swap_pricer.py +502 -0
- mainsequence/instruments/settings.py +175 -0
- mainsequence/instruments/utils.py +29 -0
- mainsequence/logconf.py +284 -0
- mainsequence/reportbuilder/__init__.py +0 -0
- mainsequence/reportbuilder/__main__.py +0 -0
- mainsequence/reportbuilder/examples/ms_template_report.py +706 -0
- mainsequence/reportbuilder/model.py +713 -0
- mainsequence/reportbuilder/slide_templates.py +532 -0
- mainsequence/tdag/__init__.py +8 -0
- mainsequence/tdag/__main__.py +0 -0
- mainsequence/tdag/config.py +129 -0
- mainsequence/tdag/data_nodes/__init__.py +12 -0
- mainsequence/tdag/data_nodes/build_operations.py +751 -0
- mainsequence/tdag/data_nodes/data_nodes.py +1292 -0
- mainsequence/tdag/data_nodes/persist_managers.py +812 -0
- mainsequence/tdag/data_nodes/run_operations.py +543 -0
- mainsequence/tdag/data_nodes/utils.py +24 -0
- mainsequence/tdag/future_registry.py +25 -0
- mainsequence/tdag/utils.py +40 -0
- mainsequence/virtualfundbuilder/__init__.py +45 -0
- mainsequence/virtualfundbuilder/__main__.py +235 -0
- mainsequence/virtualfundbuilder/agent_interface.py +77 -0
- mainsequence/virtualfundbuilder/config_handling.py +86 -0
- mainsequence/virtualfundbuilder/contrib/__init__.py +0 -0
- mainsequence/virtualfundbuilder/contrib/apps/__init__.py +8 -0
- mainsequence/virtualfundbuilder/contrib/apps/etf_replicator_app.py +164 -0
- mainsequence/virtualfundbuilder/contrib/apps/generate_report.py +292 -0
- mainsequence/virtualfundbuilder/contrib/apps/load_external_portfolio.py +107 -0
- mainsequence/virtualfundbuilder/contrib/apps/news_app.py +437 -0
- mainsequence/virtualfundbuilder/contrib/apps/portfolio_report_app.py +91 -0
- mainsequence/virtualfundbuilder/contrib/apps/portfolio_table.py +95 -0
- mainsequence/virtualfundbuilder/contrib/apps/run_named_portfolio.py +45 -0
- mainsequence/virtualfundbuilder/contrib/apps/run_portfolio.py +40 -0
- mainsequence/virtualfundbuilder/contrib/apps/templates/base.html +147 -0
- mainsequence/virtualfundbuilder/contrib/apps/templates/report.html +77 -0
- mainsequence/virtualfundbuilder/contrib/data_nodes/__init__.py +5 -0
- mainsequence/virtualfundbuilder/contrib/data_nodes/external_weights.py +61 -0
- mainsequence/virtualfundbuilder/contrib/data_nodes/intraday_trend.py +149 -0
- mainsequence/virtualfundbuilder/contrib/data_nodes/market_cap.py +310 -0
- mainsequence/virtualfundbuilder/contrib/data_nodes/mock_signal.py +78 -0
- mainsequence/virtualfundbuilder/contrib/data_nodes/portfolio_replicator.py +269 -0
- mainsequence/virtualfundbuilder/contrib/prices/__init__.py +1 -0
- mainsequence/virtualfundbuilder/contrib/prices/data_nodes.py +810 -0
- mainsequence/virtualfundbuilder/contrib/prices/utils.py +11 -0
- mainsequence/virtualfundbuilder/contrib/rebalance_strategies/__init__.py +1 -0
- mainsequence/virtualfundbuilder/contrib/rebalance_strategies/rebalance_strategies.py +313 -0
- mainsequence/virtualfundbuilder/data_nodes.py +637 -0
- mainsequence/virtualfundbuilder/enums.py +23 -0
- mainsequence/virtualfundbuilder/models.py +282 -0
- mainsequence/virtualfundbuilder/notebook_handling.py +42 -0
- mainsequence/virtualfundbuilder/portfolio_interface.py +272 -0
- mainsequence/virtualfundbuilder/resource_factory/__init__.py +0 -0
- mainsequence/virtualfundbuilder/resource_factory/app_factory.py +170 -0
- mainsequence/virtualfundbuilder/resource_factory/base_factory.py +238 -0
- mainsequence/virtualfundbuilder/resource_factory/rebalance_factory.py +101 -0
- mainsequence/virtualfundbuilder/resource_factory/signal_factory.py +183 -0
- mainsequence/virtualfundbuilder/utils.py +381 -0
- mainsequence-2.0.0.dist-info/METADATA +105 -0
- mainsequence-2.0.0.dist-info/RECORD +110 -0
- mainsequence-2.0.0.dist-info/WHEEL +5 -0
- mainsequence-2.0.0.dist-info/licenses/LICENSE +40 -0
- mainsequence-2.0.0.dist-info/top_level.txt +1 -0
mainsequence/logconf.py
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
import logging
|
2
|
+
import logging.config
|
3
|
+
import os
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
import requests
|
7
|
+
import structlog
|
8
|
+
from typing import Union
|
9
|
+
|
10
|
+
from requests.structures import CaseInsensitiveDict
|
11
|
+
from structlog.dev import ConsoleRenderer
|
12
|
+
from .instrumentation import OTelJSONRenderer
|
13
|
+
logger = None
|
14
|
+
from structlog.stdlib import BoundLogger
|
15
|
+
from typing import Dict, Any
|
16
|
+
import inspect
|
17
|
+
import importlib
|
18
|
+
from structlog.threadlocal import wrap_dict
|
19
|
+
import sys
|
20
|
+
|
21
|
+
def ensure_dir(file_path):
|
22
|
+
directory = os.path.dirname(file_path)
|
23
|
+
Path(directory).mkdir(parents=True, exist_ok=True)
|
24
|
+
|
25
|
+
def add_structlog_event_to_record(logger, method_name, event_dict):
|
26
|
+
record = event_dict.get('_record')
|
27
|
+
if record is not None:
|
28
|
+
# Remove '_record' to prevent circular reference
|
29
|
+
event_dict.pop('_record', None)
|
30
|
+
record.structlog_event = event_dict.copy()
|
31
|
+
return event_dict
|
32
|
+
|
33
|
+
class CustomConsoleRenderer(ConsoleRenderer):
|
34
|
+
def __call__(self, logger, name, event_dict):
|
35
|
+
# Extract call site parameters
|
36
|
+
lineno = event_dict.pop('lineno', None)
|
37
|
+
filename = event_dict.pop('filename', None)
|
38
|
+
func_name = event_dict.pop('func_name', None)
|
39
|
+
# application_name = event_dict.pop('application_name', None)
|
40
|
+
# update_hash=event_dict.pop('update_hash', "")
|
41
|
+
# Call the parent renderer
|
42
|
+
rendered = super().__call__(logger, name, event_dict)
|
43
|
+
# Append the call site information to the rendered output
|
44
|
+
if filename and lineno and func_name:
|
45
|
+
rendered += f" (at {filename}:{lineno} in {func_name}())"
|
46
|
+
elif filename and lineno:
|
47
|
+
rendered += f" (at {filename}:{lineno})"
|
48
|
+
return rendered
|
49
|
+
|
50
|
+
def build_application_logger(
|
51
|
+
application_name:str="ms-sdk",
|
52
|
+
**metadata
|
53
|
+
):
|
54
|
+
"""
|
55
|
+
Create a logger that logs to console and file in JSON format.
|
56
|
+
"""
|
57
|
+
|
58
|
+
# do initial request when on logger initialization
|
59
|
+
headers = CaseInsensitiveDict()
|
60
|
+
headers["Content-Type"] = "application/json"
|
61
|
+
headers["Authorization"] = "Token " + os.getenv("MAINSEQUENCE_TOKEN")
|
62
|
+
|
63
|
+
project_info_endpoint = f'{os.getenv("TDAG_ENDPOINT")}/orm/api/pods/job/get_job_startup_state/'
|
64
|
+
|
65
|
+
command_id = os.getenv("COMMAND_ID")
|
66
|
+
params = {}
|
67
|
+
if command_id:
|
68
|
+
params['command_id'] = command_id
|
69
|
+
|
70
|
+
response = requests.get(project_info_endpoint, headers=headers, params=params)
|
71
|
+
|
72
|
+
if response.status_code != 200:
|
73
|
+
print(f"Got Status Code {response.status_code} with response {response.text}")
|
74
|
+
|
75
|
+
json_response = response.json()
|
76
|
+
if "project_id" not in json_response:
|
77
|
+
raise ValueError(f"Project ID not found, server response {json_response}")
|
78
|
+
|
79
|
+
# set additional args from backend
|
80
|
+
if "additional_environment" in json_response:
|
81
|
+
for key, value in json_response["additional_environment"].items():
|
82
|
+
os.environ[key] = value
|
83
|
+
|
84
|
+
# Get logger path in home directory if no path is set in environemnt
|
85
|
+
tdag_base_path = Path(os.getenv("TDAG_ROOT_PATH", Path.home() / ".tdag"))
|
86
|
+
default_log_path = tdag_base_path / "logs" / "tdag.log"
|
87
|
+
logger_file = os.getenv("LOGGER_FILE_PATH", str(default_log_path))
|
88
|
+
|
89
|
+
logger_name = "tdag"
|
90
|
+
|
91
|
+
# Define the timestamper and pre_chain processors
|
92
|
+
timestamper = structlog.processors.TimeStamper( fmt="iso",
|
93
|
+
utc=True,)
|
94
|
+
pre_chain = [
|
95
|
+
structlog.stdlib.add_log_level,
|
96
|
+
structlog.stdlib.ExtraAdder(),
|
97
|
+
timestamper,
|
98
|
+
]
|
99
|
+
|
100
|
+
handlers = {
|
101
|
+
"console": {
|
102
|
+
"class": "logging.StreamHandler",
|
103
|
+
"formatter": "colored",
|
104
|
+
"level":os.getenv("LOG_LEVEL", "DEBUG")
|
105
|
+
},
|
106
|
+
}
|
107
|
+
if logger_file is not None:
|
108
|
+
ensure_dir(logger_file) # Ensure the directory for the log file exists
|
109
|
+
|
110
|
+
handlers.update(
|
111
|
+
{
|
112
|
+
"file": {
|
113
|
+
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
114
|
+
"formatter": "plain",
|
115
|
+
"level": os.getenv("LOG_LEVEL_FILE", "DEBUG"),
|
116
|
+
"filename": logger_file,
|
117
|
+
"mode": "a",
|
118
|
+
"delay": True,
|
119
|
+
"maxBytes": 5 * 1024 * 1024, # Rotate after 5 MB
|
120
|
+
"backupCount": 5, # Keep up to 5 backup files
|
121
|
+
}
|
122
|
+
}
|
123
|
+
)
|
124
|
+
|
125
|
+
logging_config = {
|
126
|
+
"version": 1,
|
127
|
+
"disable_existing_loggers": False,
|
128
|
+
"formatters": {
|
129
|
+
"plain": {
|
130
|
+
"()": structlog.stdlib.ProcessorFormatter,
|
131
|
+
"processor":OTelJSONRenderer(),#structlog.processors.JSONRenderer(),
|
132
|
+
"foreign_pre_chain": pre_chain,
|
133
|
+
},
|
134
|
+
"colored": {
|
135
|
+
"()": structlog.stdlib.ProcessorFormatter,
|
136
|
+
"processor": CustomConsoleRenderer(colors=True),
|
137
|
+
"foreign_pre_chain": pre_chain,
|
138
|
+
},
|
139
|
+
},
|
140
|
+
"handlers":handlers,
|
141
|
+
"loggers": {
|
142
|
+
logger_name: {
|
143
|
+
"handlers":list(handlers.keys()),
|
144
|
+
"level": os.getenv("LOG_LEVEL_STDOUT", "INFO"),
|
145
|
+
"propagate": False,
|
146
|
+
},
|
147
|
+
},
|
148
|
+
}
|
149
|
+
try:
|
150
|
+
logging.config.dictConfig(logging_config)
|
151
|
+
except Exception as e:
|
152
|
+
raise e
|
153
|
+
# Configure structlog
|
154
|
+
structlog.configure(
|
155
|
+
processors=[
|
156
|
+
structlog.contextvars.merge_contextvars, # context that always appears in the logs
|
157
|
+
structlog.stdlib.add_log_level,
|
158
|
+
structlog.stdlib.PositionalArgumentsFormatter(),
|
159
|
+
structlog.processors.CallsiteParameterAdder(
|
160
|
+
{
|
161
|
+
structlog.processors.CallsiteParameter.FILENAME,
|
162
|
+
structlog.processors.CallsiteParameter.FUNC_NAME,
|
163
|
+
structlog.processors.CallsiteParameter.LINENO,
|
164
|
+
}
|
165
|
+
),
|
166
|
+
timestamper,
|
167
|
+
structlog.processors.StackInfoRenderer(),
|
168
|
+
structlog.processors.format_exc_info, # suggested to remove for pretty exceptions
|
169
|
+
add_structlog_event_to_record, # Add this processor before wrap_for_formatter
|
170
|
+
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
171
|
+
|
172
|
+
],
|
173
|
+
context_class=dict,
|
174
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
175
|
+
wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG),
|
176
|
+
cache_logger_on_first_use=True,
|
177
|
+
)
|
178
|
+
|
179
|
+
# Create the structlog logger and bind metadata
|
180
|
+
logger = structlog.get_logger(logger_name)
|
181
|
+
logger = logger.bind(application_name=application_name,**metadata)
|
182
|
+
|
183
|
+
try:
|
184
|
+
logger = logger.bind(project_id=json_response["project_id"], **metadata)
|
185
|
+
logger = logger.bind(data_source_id=json_response["data_source_id"], **metadata)
|
186
|
+
logger = logger.bind(job_run_id=json_response["job_run_id"], **metadata)
|
187
|
+
logger = logger.bind(command_id=int(command_id) if command_id else None, **metadata)
|
188
|
+
|
189
|
+
except Exception as e:
|
190
|
+
logger.exception(f"Could not retrive pod project {e}")
|
191
|
+
raise e
|
192
|
+
|
193
|
+
logger = logger.bind()
|
194
|
+
return logger
|
195
|
+
|
196
|
+
|
197
|
+
def dump_structlog_bound_logger(logger: BoundLogger) -> Dict[str, Any]:
|
198
|
+
"""
|
199
|
+
Serialize a fully‑initialized structlog BoundLogger into a dict:
|
200
|
+
- Global structlog config (as import paths)
|
201
|
+
- Underlying stdlib.Logger name & level
|
202
|
+
- Bound key/value context
|
203
|
+
|
204
|
+
Returns:
|
205
|
+
A dict that can be json-serialized and later reloaded to reconstruct
|
206
|
+
the same structlog setup in another process.
|
207
|
+
"""
|
208
|
+
|
209
|
+
# Helper: get module.QualName for function/class or for an instance's class
|
210
|
+
def pathify(obj: Any) -> str:
|
211
|
+
target = obj if inspect.isfunction(obj) or inspect.isclass(obj) else obj.__class__
|
212
|
+
return f"{target.__module__}.{target.__qualname__}"
|
213
|
+
|
214
|
+
# 1) Global structlog config
|
215
|
+
cfg = structlog.get_config()
|
216
|
+
structlog_config = {
|
217
|
+
"processors": [pathify(p) for p in cfg["processors"]],
|
218
|
+
"logger_factory": pathify(cfg["logger_factory"]),
|
219
|
+
"wrapper_class": pathify(cfg["wrapper_class"]),
|
220
|
+
"context_class": pathify(cfg["context_class"]),
|
221
|
+
"cache_logger_on_first_use": cfg["cache_logger_on_first_use"],
|
222
|
+
}
|
223
|
+
|
224
|
+
# 2) Underlying stdlib.Logger info
|
225
|
+
std = logger._logger
|
226
|
+
logger_name = std.name
|
227
|
+
logger_level = std.level
|
228
|
+
|
229
|
+
# 3) Bound context
|
230
|
+
bound_context = dict(logger._context or {})
|
231
|
+
|
232
|
+
# 4) Assemble and return
|
233
|
+
return {
|
234
|
+
"structlog_config": structlog_config,
|
235
|
+
"logger_name": logger_name,
|
236
|
+
"logger_level": logger_level,
|
237
|
+
"bound_context": bound_context,
|
238
|
+
}
|
239
|
+
|
240
|
+
|
241
|
+
def load_structlog_bound_logger(dump: Dict[str, Any]) -> BoundLogger:
|
242
|
+
"""
|
243
|
+
Given the dict from dump_structlog_bound_logger(),
|
244
|
+
return a BoundLogger with the same name, level, and context,
|
245
|
+
but using the EXISTING global structlog configuration.
|
246
|
+
"""
|
247
|
+
name = dump["logger_name"]
|
248
|
+
level = dump["logger_level"]
|
249
|
+
bound_context = dump["bound_context"]
|
250
|
+
|
251
|
+
# 1) Grab the already‐configured logger
|
252
|
+
base: BoundLogger = structlog.get_logger(name)
|
253
|
+
|
254
|
+
# 2) (Optional) restore its stdlib level
|
255
|
+
std = getattr(base, "_logger", None)
|
256
|
+
if std is not None:
|
257
|
+
std.setLevel(level)
|
258
|
+
|
259
|
+
# 3) Re‐bind the original context
|
260
|
+
return base.bind(**bound_context)
|
261
|
+
|
262
|
+
logger = build_application_logger()
|
263
|
+
|
264
|
+
# create a new system exection hook to also log terminating exceptions
|
265
|
+
original_hook = sys.excepthook
|
266
|
+
|
267
|
+
def handle_exception(exc_type, exc_value, exc_traceback):
|
268
|
+
"""
|
269
|
+
A custom exception handler that logs any uncaught exception.
|
270
|
+
"""
|
271
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
272
|
+
# Let the user interrupt the program without a traceback
|
273
|
+
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
274
|
+
return
|
275
|
+
|
276
|
+
# Log the exception using our configured logger
|
277
|
+
logger.error(
|
278
|
+
"Uncaught exception",
|
279
|
+
exc_info=(exc_type, exc_value, exc_traceback)
|
280
|
+
)
|
281
|
+
# Also call the original hook to print the traceback to the console
|
282
|
+
original_hook(exc_type, exc_value, exc_traceback)
|
283
|
+
|
284
|
+
sys.excepthook = handle_exception
|
File without changes
|
File without changes
|