mainsequence 3.0.1__tar.gz → 3.0.2__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.
- {mainsequence-3.0.1 → mainsequence-3.0.2}/PKG-INFO +1 -1
- mainsequence-3.0.2/mainsequence/client/exceptions.py +11 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/models_tdag.py +8 -1
- mainsequence-3.0.2/mainsequence/dashboards/streamlit/core/theme.py +231 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/scaffold.py +3 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/data_interface/data_interface.py +29 -8
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/bond.py +8 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/indices.py +5 -8
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence.egg-info/PKG-INFO +1 -1
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence.egg-info/SOURCES.txt +1 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/pyproject.toml +1 -1
- mainsequence-3.0.1/mainsequence/dashboards/streamlit/core/theme.py +0 -212
- {mainsequence-3.0.1 → mainsequence-3.0.2}/LICENSE +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/README.md +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/__main__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/cli/api.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/cli/cli.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/cli/config.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/base.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/data_sources_interfaces/timescale.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/models_report_studio.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/models_vam.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/config.toml +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/favicon.png +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/image_1_base64.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/image_2_base64.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/image_3_base64.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/image_4_base64.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/image_5_base64.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/logo.png +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/core/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/pages/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/data_interface/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/base_instrument.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/european_option.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/interest_rate_swap.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/json_codec.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/knockout_fx_option.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/position.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/ql_fields.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/vanilla_fx_option.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments.default.toml +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/black_scholes.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/bond_pricer.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/fx_option_pricer.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/knockout_fx_pricer.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/swap_pricer.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/settings.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/logconf.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/reportbuilder/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/reportbuilder/__main__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/reportbuilder/examples/ms_template_report.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/reportbuilder/model.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/reportbuilder/slide_templates.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/__main__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/config.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/data_nodes/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/data_nodes/build_operations.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/data_nodes/data_nodes.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/data_nodes/persist_managers.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/data_nodes/run_operations.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/data_nodes/utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/future_registry.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/tdag/utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/__main__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/agent_interface.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/config_handling.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/etf_replicator_app.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/generate_report.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/load_external_portfolio.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/news_app.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/portfolio_report_app.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/portfolio_table.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/run_named_portfolio.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/run_portfolio.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/templates/base.html +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/templates/report.html +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/external_weights.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/intraday_trend.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/market_cap.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/mock_signal.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/portfolio_replicator.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/prices/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/prices/data_nodes.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/prices/utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/rebalance_strategies.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/data_nodes.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/enums.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/models.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/notebook_handling.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/portfolio_interface.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/resource_factory/__init__.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/resource_factory/app_factory.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/resource_factory/base_factory.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/resource_factory/rebalance_factory.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/resource_factory/signal_factory.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/utils.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/setup.cfg +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/tests/test_agent.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/tests/test_portfolio.py +0 -0
- {mainsequence-3.0.1 → mainsequence-3.0.2}/tests/test_replicator.py +0 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class ApiError(Exception):
|
4
|
+
def __init__(self, message, response=None, payload=None):
|
5
|
+
super().__init__(message)
|
6
|
+
self.response = response
|
7
|
+
self.payload = payload
|
8
|
+
self.status_code = getattr(response, "status_code", None)
|
9
|
+
|
10
|
+
class ConflictError(ApiError):
|
11
|
+
pass
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from asyncio import exceptions
|
3
4
|
|
4
5
|
import yaml
|
5
6
|
|
@@ -33,6 +34,9 @@ import concurrent.futures
|
|
33
34
|
|
34
35
|
from cachetools import TTLCache, cachedmethod
|
35
36
|
from operator import attrgetter
|
37
|
+
from mainsequence.client import exceptions
|
38
|
+
|
39
|
+
|
36
40
|
|
37
41
|
_default_data_source = None # Module-level cache
|
38
42
|
|
@@ -818,7 +822,10 @@ class DataNodeStorage(BasePydanticModel, BaseObjectOrm):
|
|
818
822
|
s = self.build_session()
|
819
823
|
r = make_request(s=s, loaders=self.LOADERS, r_type="PATCH", url=url, payload=payload, time_out=time_out)
|
820
824
|
if r.status_code != 200:
|
821
|
-
|
825
|
+
data = r.json() # guaranteed JSON from your backend
|
826
|
+
if r.status_code == 409:
|
827
|
+
raise exceptions.ConflictError(data["error"])
|
828
|
+
raise exceptions.ApiError(data["error"])
|
822
829
|
return self.__class__(**r.json())
|
823
830
|
|
824
831
|
@classmethod
|
@@ -0,0 +1,231 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
3
|
+
import streamlit as st
|
4
|
+
|
5
|
+
|
6
|
+
# --------------------- small theme helpers ---------------------
|
7
|
+
|
8
|
+
def inject_css_for_dark_accents():
|
9
|
+
st.markdown(
|
10
|
+
"""
|
11
|
+
<style>
|
12
|
+
/* Subtle tweaks; Streamlit theme itself comes from .streamlit/config.toml */
|
13
|
+
.stMetric > div { background: rgba(255,255,255,0.04); border-radius: 6px; padding: .5rem .75rem; }
|
14
|
+
div[data-testid="stMetricDelta"] svg { display: none; } /* clean deltas, hide the arrow */
|
15
|
+
</style>
|
16
|
+
""",
|
17
|
+
unsafe_allow_html=True
|
18
|
+
)
|
19
|
+
|
20
|
+
def explain_theming():
|
21
|
+
st.info(
|
22
|
+
"Theme colors come from `.streamlit/config.toml`. "
|
23
|
+
"You can’t switch Streamlit’s theme at runtime, but you can tune Plotly’s colors and inject light CSS."
|
24
|
+
)
|
25
|
+
|
26
|
+
|
27
|
+
# --------------------- spinner frame loader (runs once on import) ---------------------
|
28
|
+
|
29
|
+
def _read_txt(p: Path) -> str:
|
30
|
+
return p.read_text(encoding="utf-8").strip()
|
31
|
+
|
32
|
+
def _load_spinner_frames_for_this_template() -> list[str]:
|
33
|
+
"""
|
34
|
+
Looks under: <repo>/mainsequence/dashboards/streamlit/assets/
|
35
|
+
|
36
|
+
Order of precedence:
|
37
|
+
1) image_1_base64.txt ... image_5_base64.txt
|
38
|
+
2) image_base64.txt (single file, replicated to 5 frames)
|
39
|
+
3) spinner_1.txt ... spinner_5.txt
|
40
|
+
4) Any *base64.txt (sorted) or *.txt (sorted), up to 5 frames
|
41
|
+
- If only one file is found, it is replicated to 5 frames.
|
42
|
+
- If 2-4 files are found, the last one is repeated to reach 5.
|
43
|
+
On total failure, returns five copies of a 1x1 transparent PNG.
|
44
|
+
"""
|
45
|
+
assets = Path(__file__).resolve().parent.parent / "assets"
|
46
|
+
|
47
|
+
# 1) Named sequence: image_1_base64.txt .. image_5_base64.txt
|
48
|
+
seq = [assets / f"image_{i}_base64.txt" for i in range(1, 6)]
|
49
|
+
if all(p.exists() for p in seq):
|
50
|
+
return [_read_txt(p) for p in seq]
|
51
|
+
|
52
|
+
# 2) Single file replicated
|
53
|
+
single = assets / "image_base64.txt"
|
54
|
+
if single.exists():
|
55
|
+
s = _read_txt(single)
|
56
|
+
return [s] * 5
|
57
|
+
|
58
|
+
# 3) Alternate sequence
|
59
|
+
alt_seq = [assets / f"spinner_{i}.txt" for i in range(1, 6)]
|
60
|
+
if all(p.exists() for p in alt_seq):
|
61
|
+
return [_read_txt(p) for p in alt_seq]
|
62
|
+
|
63
|
+
# 4) Any *base64.txt, then any *.txt
|
64
|
+
candidates = sorted(assets.glob("*base64.txt")) or sorted(assets.glob("*.txt"))
|
65
|
+
frames = [_read_txt(p) for p in candidates[:5]]
|
66
|
+
if frames:
|
67
|
+
if len(frames) == 1:
|
68
|
+
frames = frames * 5
|
69
|
+
elif len(frames) < 5:
|
70
|
+
frames += [frames[-1]] * (5 - len(frames))
|
71
|
+
return frames
|
72
|
+
|
73
|
+
# Fallback: 1x1 transparent PNG
|
74
|
+
transparent_png = ("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8"
|
75
|
+
"/w8AAn8B9p7u3t8AAAAASUVORK5CYII=")
|
76
|
+
return [transparent_png] * 5
|
77
|
+
|
78
|
+
try:
|
79
|
+
_SPINNER_FRAMES_RAW = _load_spinner_frames_for_this_template()
|
80
|
+
except Exception:
|
81
|
+
# Never break import due to spinner assets
|
82
|
+
transparent_png = ("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8"
|
83
|
+
"/w8AAn8B9p7u3t8AAAAASUVORK5CYII=")
|
84
|
+
_SPINNER_FRAMES_RAW = [transparent_png] * 5
|
85
|
+
|
86
|
+
# Public constants (used only within this module, but left as globals for clarity)
|
87
|
+
IMAGE_1_B64, IMAGE_2_B64, IMAGE_3_B64, IMAGE_4_B64, IMAGE_5_B64 = _SPINNER_FRAMES_RAW
|
88
|
+
|
89
|
+
|
90
|
+
# --------------------- spinner override (CSS only) ---------------------
|
91
|
+
|
92
|
+
def override_spinners(
|
93
|
+
hide_deploy_button: bool = False,
|
94
|
+
*,
|
95
|
+
# Sizes
|
96
|
+
top_px: int = 20, # top-right toolbar spinner size
|
97
|
+
inline_px: int = 96, # inline/status spinner size
|
98
|
+
# Timing
|
99
|
+
duration_ms: int = 900,
|
100
|
+
# Toolbar micro-positioning
|
101
|
+
toolbar_nudge_px: int = -2,
|
102
|
+
toolbar_gap_left_px: int = 2,
|
103
|
+
toolbar_left_offset_px: int = 0,
|
104
|
+
# Overlay options (for inline/status)
|
105
|
+
center_non_toolbar: bool = True,
|
106
|
+
dim_backdrop: bool = False,
|
107
|
+
overlay_blur_px: float = 0.0,
|
108
|
+
overlay_opacity: float = 0.0,
|
109
|
+
overlay_z_index: int = 9990,
|
110
|
+
) -> None:
|
111
|
+
"""Replace Streamlit's spinners with a 5‑frame bitmap animation.
|
112
|
+
|
113
|
+
This injects CSS only (no JS). It hides native SVGs and applies the frames
|
114
|
+
to the toolbar spinner, inline st.spinner, and st.status icon.
|
115
|
+
"""
|
116
|
+
|
117
|
+
def as_data_uri(s: str, mime: str = "image/png") -> str:
|
118
|
+
s = (s or "").strip()
|
119
|
+
return s if s.startswith("data:") else f"data:{mime};base64,{s}"
|
120
|
+
|
121
|
+
i1 = as_data_uri(IMAGE_1_B64)
|
122
|
+
i2 = as_data_uri(IMAGE_2_B64)
|
123
|
+
i3 = as_data_uri(IMAGE_3_B64)
|
124
|
+
i4 = as_data_uri(IMAGE_4_B64)
|
125
|
+
i5 = as_data_uri(IMAGE_5_B64)
|
126
|
+
|
127
|
+
st.markdown(f"""
|
128
|
+
<style>
|
129
|
+
/* ===== 5-frame animation (fixed: do NOT use '0%%') ===== */
|
130
|
+
@keyframes st-fiveframe {{
|
131
|
+
0% {{ background-image:url("{i1}"); }}
|
132
|
+
20% {{ background-image:url("{i2}"); }}
|
133
|
+
40% {{ background-image:url("{i3}"); }}
|
134
|
+
60% {{ background-image:url("{i4}"); }}
|
135
|
+
80% {{ background-image:url("{i5}"); }}
|
136
|
+
100%{{ background-image:url("{i5}"); }}
|
137
|
+
}}
|
138
|
+
|
139
|
+
:root {{
|
140
|
+
--st-spin-top:{top_px}px;
|
141
|
+
--st-spin-inline:{inline_px}px;
|
142
|
+
--st-spin-dur:{duration_ms}ms;
|
143
|
+
|
144
|
+
--st-toolbar-nudge:{toolbar_nudge_px}px;
|
145
|
+
--st-toolbar-gap:{toolbar_gap_left_px}px;
|
146
|
+
--st-toolbar-left:{toolbar_left_offset_px}px;
|
147
|
+
|
148
|
+
--st-overlay-z:{overlay_z_index};
|
149
|
+
--st-overlay-bg: rgba(0,0,0,{overlay_opacity});
|
150
|
+
--st-overlay-blur:{overlay_blur_px}px;
|
151
|
+
}}
|
152
|
+
|
153
|
+
/* ===== ensure toolbar itself stays clickable above overlays ===== */
|
154
|
+
div[data-testid="stToolbar"],
|
155
|
+
[data-testid="stStatusWidget"] {{
|
156
|
+
position: relative;
|
157
|
+
z-index: calc(var(--st-overlay-z) + 5);
|
158
|
+
}}
|
159
|
+
|
160
|
+
/* ===== hide every built-in spinner glyph (SVG/img) ===== */
|
161
|
+
[data-testid="stSpinner"] svg,
|
162
|
+
[data-testid="stSpinnerIcon"] svg,
|
163
|
+
[data-testid="stStatusWidget"] svg,
|
164
|
+
header [data-testid="stSpinner"] svg {{
|
165
|
+
display: none !important;
|
166
|
+
}}
|
167
|
+
|
168
|
+
/* ===== toolbar spinner (top-right) ===== */
|
169
|
+
[data-testid="stStatusWidget"] {{
|
170
|
+
position:relative;
|
171
|
+
padding-left: calc(var(--st-spin-top) + var(--st-toolbar-gap));
|
172
|
+
}}
|
173
|
+
[data-testid="stStatusWidget"]::before {{
|
174
|
+
content:"";
|
175
|
+
position:absolute;
|
176
|
+
left: var(--st-toolbar-left);
|
177
|
+
top:50%;
|
178
|
+
transform: translateY(calc(-50% + var(--st-toolbar-nudge)));
|
179
|
+
width:var(--st-spin-top);
|
180
|
+
height:var(--st-spin-top);
|
181
|
+
background-image:url("{i1}");
|
182
|
+
background-repeat:no-repeat;
|
183
|
+
background-position:center center;
|
184
|
+
background-size:contain;
|
185
|
+
animation: st-fiveframe var(--st-spin-dur) steps(1, end) infinite;
|
186
|
+
}}
|
187
|
+
|
188
|
+
/* Optionally hide Deploy/Stop toolbar entirely */
|
189
|
+
{"div[data-testid='stToolbar']{display:none !important;}" if hide_deploy_button else ""}
|
190
|
+
|
191
|
+
/* ===== inline st.spinner ===== */
|
192
|
+
[data-testid="stSpinner"] {{
|
193
|
+
min-height: 0 !important;
|
194
|
+
}}
|
195
|
+
{ "[data-testid='stSpinner']::after { content:''; position:fixed; inset:0; background:var(--st-overlay-bg); backdrop-filter: blur(var(--st-overlay-blur)); z-index: var(--st-overlay-z); pointer-events: none; }" if dim_backdrop else "" }
|
196
|
+
[data-testid="stSpinner"]::before {{
|
197
|
+
content:"";
|
198
|
+
position: fixed;
|
199
|
+
left: 50%;
|
200
|
+
top: 50%;
|
201
|
+
transform: translate(-50%,-50%);
|
202
|
+
width: var(--st-spin-inline);
|
203
|
+
height: var(--st-spin-inline);
|
204
|
+
background-image:url("{i1}");
|
205
|
+
background-repeat:no-repeat;
|
206
|
+
background-position:center center;
|
207
|
+
background-size:contain;
|
208
|
+
animation: st-fiveframe var(--st-spin-dur) steps(1, end) infinite;
|
209
|
+
z-index: calc(var(--st-overlay-z) + 1);
|
210
|
+
}}
|
211
|
+
|
212
|
+
/* ===== st.status(...) icon ===== */
|
213
|
+
[data-testid="stStatus"] [data-testid="stStatusIcon"] svg {{ display:none !important; }}
|
214
|
+
{ "[data-testid='stStatus']::after { content:''; position:fixed; inset:0; background:var(--st-overlay-bg); backdrop-filter: blur(var(--st-overlay-blur)); z-index: var(--st-overlay-z); pointer-events: none; }" if dim_backdrop else "" }
|
215
|
+
[data-testid="stStatus"] [data-testid="stStatusIcon"]::before {{
|
216
|
+
content:"";
|
217
|
+
position: fixed;
|
218
|
+
left: 50%;
|
219
|
+
top: 50%;
|
220
|
+
transform: translate(-50%,-50%);
|
221
|
+
width: var(--st-spin-inline);
|
222
|
+
height: var(--st-spin-inline);
|
223
|
+
background-image:url("{i1}");
|
224
|
+
background-repeat:no-repeat;
|
225
|
+
background-position:center center;
|
226
|
+
background-size:contain;
|
227
|
+
animation: st-fiveframe var(--st-spin-dur) steps(1, end) infinite;
|
228
|
+
z-index: calc(var(--st-overlay-z) + 1);
|
229
|
+
}}
|
230
|
+
</style>
|
231
|
+
""", unsafe_allow_html=True)
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/data_interface/data_interface.py
RENAMED
@@ -9,6 +9,7 @@ import pandas as pd
|
|
9
9
|
from pathlib import Path
|
10
10
|
|
11
11
|
|
12
|
+
|
12
13
|
DISCOUNT_CURVES_TABLE=msc.Constant.get_value(name="DISCOUNT_CURVES_TABLE")
|
13
14
|
REFERENCE_RATES_FIXING_TABLE = msc.Constant.get_value(name="REFERENCE_RATES_FIXING_TABLE")
|
14
15
|
|
@@ -287,20 +288,22 @@ class MSInterface():
|
|
287
288
|
@cachedmethod(cache=attrgetter("_curve_cache"), lock=attrgetter("_curve_cache_lock"))
|
288
289
|
def get_historical_discount_curve(self, curve_name, target_date):
|
289
290
|
from mainsequence.tdag import APIDataNode
|
291
|
+
from mainsequence.logconf import logger
|
290
292
|
data_node = APIDataNode.build_from_identifier(identifier=DISCOUNT_CURVES_TABLE)
|
291
293
|
|
292
294
|
|
293
295
|
|
294
296
|
# for test purposes only get lats observations
|
295
|
-
|
296
|
-
|
297
|
-
|
297
|
+
use_last_observation=os.environ.get("USE_LAST_OBSERVATION_MS_INSTRUMENT","true").lower()=="true"
|
298
|
+
if use_last_observation:
|
299
|
+
update_statistics = data_node.get_update_statistics()
|
300
|
+
target_date = update_statistics.asset_time_statistics[curve_name]
|
301
|
+
logger.warning("Curve is using last observation")
|
298
302
|
|
299
303
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
raise e
|
304
|
+
|
305
|
+
limit = target_date + datetime.timedelta(days=1)
|
306
|
+
|
304
307
|
|
305
308
|
|
306
309
|
|
@@ -336,18 +339,36 @@ class MSInterface():
|
|
336
339
|
:return:
|
337
340
|
"""
|
338
341
|
from mainsequence.tdag import APIDataNode
|
342
|
+
from mainsequence.logconf import logger
|
343
|
+
import pytz # patch
|
339
344
|
|
340
345
|
data_node = APIDataNode.build_from_identifier(identifier=REFERENCE_RATES_FIXING_TABLE)
|
341
346
|
|
342
|
-
import pytz # patch
|
343
347
|
start_date = datetime.datetime(2024, 9, 10, tzinfo=pytz.utc)
|
344
348
|
end_date=datetime.datetime(2025, 9, 17, tzinfo=pytz.utc)
|
349
|
+
|
350
|
+
|
351
|
+
|
345
352
|
|
346
353
|
fixings_df = data_node.get_ranged_data_per_asset(
|
347
354
|
range_descriptor={reference_rate_uid: {"start_date": start_date, "start_date_operand": ">=",
|
348
355
|
"end_date": end_date, "end_date_operand": "<=", }}
|
349
356
|
)
|
350
357
|
if fixings_df.empty:
|
358
|
+
|
359
|
+
use_last_observation = os.environ.get("USE_LAST_OBSERVATION_MS_INSTRUMENT", "true").lower() == "true"
|
360
|
+
if use_last_observation:
|
361
|
+
logger.warning("Fixings are using last observation and filled forward")
|
362
|
+
fixings_df = data_node.get_ranged_data_per_asset(
|
363
|
+
range_descriptor={reference_rate_uid: {"start_date": datetime.datetime(1900,1,1,tzinfo=pytz.utc),
|
364
|
+
"start_date_operand": ">=",
|
365
|
+
}}
|
366
|
+
|
367
|
+
|
368
|
+
)
|
369
|
+
|
370
|
+
a=5
|
371
|
+
|
351
372
|
raise Exception(f"{reference_rate_uid} has not data between {start_date} and {end_date}.")
|
352
373
|
fixings_df = fixings_df.reset_index().rename(columns={"time_index": "date"})
|
353
374
|
fixings_df["date"] = fixings_df["date"].dt.date
|
@@ -40,6 +40,10 @@ class Bond(InstrumentModel):
|
|
40
40
|
settlement_days: int = Field(default=2)
|
41
41
|
schedule: Optional[QSchedule] = Field(None)
|
42
42
|
|
43
|
+
benchmark_rate_index_name: Optional[str] = Field(...,description="A default index benchmark rate, helpful when doing"
|
44
|
+
"analysis and we want to map the bond to a bencharmk for example to"
|
45
|
+
"the SOFR Curve or to de US Treasury curve etc")
|
46
|
+
|
43
47
|
model_config = {"arbitrary_types_allowed": True}
|
44
48
|
|
45
49
|
_bond: Optional[ql.Bond] = PrivateAttr(default=None)
|
@@ -279,11 +283,15 @@ class FixedRateBond(Bond):
|
|
279
283
|
"""Plain-vanilla fixed-rate bond following the shared Bond lifecycle."""
|
280
284
|
|
281
285
|
coupon_rate: float = Field(...)
|
286
|
+
|
282
287
|
# Optional market curve if you want to discount off a curve instead of a flat yield
|
283
288
|
discount_curve: Optional[ql.YieldTermStructureHandle] = Field(default=None)
|
284
289
|
|
285
290
|
model_config = {"arbitrary_types_allowed": True}
|
286
291
|
|
292
|
+
def reset_curve(self, curve: ql.YieldTermStructureHandle) -> None:
|
293
|
+
self.discount_curve = curve
|
294
|
+
|
287
295
|
def _get_default_discount_curve(self) -> Optional[ql.YieldTermStructureHandle]:
|
288
296
|
return self.discount_curve
|
289
297
|
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/indices.py
RENAMED
@@ -42,11 +42,7 @@ _INDEX_CACHE: Dict[_IndexCacheKey, ql.Index] = {}
|
|
42
42
|
def clear_index_cache() -> None:
|
43
43
|
_INDEX_CACHE.clear()
|
44
44
|
|
45
|
-
constants_to_create = dict(
|
46
|
-
UST="UST",
|
47
|
-
)
|
48
45
|
|
49
|
-
_C.create_constants_if_not_exist(constants_to_create)
|
50
46
|
# ----------------------------- Config ----------------------------- #
|
51
47
|
# Put every supported identifier here with its curve + index construction config.
|
52
48
|
# No tenor tokens; we store the QuantLib Period directly.
|
@@ -125,14 +121,15 @@ INDEX_CONFIGS: Dict[str, Dict] = {
|
|
125
121
|
end_of_month=False, # Irrelevant when scheduling by days
|
126
122
|
),
|
127
123
|
|
128
|
-
|
124
|
+
|
125
|
+
_C.get_value(name="REFERENCE_RATE__USD_SOFR"): dict(
|
129
126
|
curve_uid=_C.get_value(name="ZERO_CURVE__UST_CMT_ZERO_CURVE_UID"),
|
130
|
-
calendar=ql.UnitedStates(ql.UnitedStates.
|
131
|
-
day_counter=ql.
|
127
|
+
calendar=ql.UnitedStates(ql.UnitedStates.FederalReserve),
|
128
|
+
day_counter=ql.Actual360(),
|
132
129
|
currency=ql.USDCurrency(),
|
133
130
|
period=ql.Period(6, ql.Months), # Semiannual coupons
|
134
131
|
settlement_days=1, # T+1
|
135
|
-
bdc=ql.
|
132
|
+
bdc=ql.ModifiedFollowing,
|
136
133
|
end_of_month=False, # Irrelevant when scheduling by days
|
137
134
|
),
|
138
135
|
}
|
@@ -17,6 +17,7 @@ mainsequence/cli/config.py
|
|
17
17
|
mainsequence/cli/ssh_utils.py
|
18
18
|
mainsequence/client/__init__.py
|
19
19
|
mainsequence/client/base.py
|
20
|
+
mainsequence/client/exceptions.py
|
20
21
|
mainsequence/client/models_helpers.py
|
21
22
|
mainsequence/client/models_report_studio.py
|
22
23
|
mainsequence/client/models_tdag.py
|
@@ -1,212 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
import streamlit as st
|
3
|
-
from pathlib import Path
|
4
|
-
|
5
|
-
|
6
|
-
def inject_css_for_dark_accents():
|
7
|
-
st.markdown(
|
8
|
-
"""
|
9
|
-
<style>
|
10
|
-
/* Subtle tweaks; Streamlit theme itself comes from .streamlit/config.toml */
|
11
|
-
.stMetric > div { background: rgba(255,255,255,0.04); border-radius: 6px; padding: .5rem .75rem; }
|
12
|
-
div[data-testid="stMetricDelta"] svg { display: none; } /* clean deltas, hide the arrow */
|
13
|
-
</style>
|
14
|
-
""",
|
15
|
-
unsafe_allow_html=True
|
16
|
-
)
|
17
|
-
|
18
|
-
def explain_theming():
|
19
|
-
st.info(
|
20
|
-
"Theme colors come from `.streamlit/config.toml`. "
|
21
|
-
"You can’t switch Streamlit’s theme at runtime, but you can tune Plotly’s colors and inject light CSS."
|
22
|
-
)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# --- Load spinner frames ONCE from two levels above, files: image_1_base64.txt ... image_5_base64.txt ---
|
28
|
-
def _load_spinner_frames_for_this_template() -> list[str]:
|
29
|
-
base_dir = Path(__file__).resolve().parent.parent
|
30
|
-
frames: list[str] = []
|
31
|
-
for i in range(1, 6):
|
32
|
-
p = base_dir / f"assets/image_{i}_base64.txt"
|
33
|
-
if not p.exists():
|
34
|
-
raise FileNotFoundError(f"Missing spinner frame file: {p}")
|
35
|
-
frames.append(p.read_text(encoding="utf-8").strip())
|
36
|
-
return frames
|
37
|
-
|
38
|
-
|
39
|
-
_SPINNER_FRAMES_RAW = _load_spinner_frames_for_this_template()
|
40
|
-
|
41
|
-
|
42
|
-
# Expose constants for the function (keeps the code below simple)
|
43
|
-
IMAGE_1_B64, IMAGE_2_B64, IMAGE_3_B64, IMAGE_4_B64, IMAGE_5_B64 = _SPINNER_FRAMES_RAW
|
44
|
-
|
45
|
-
def override_spinners(
|
46
|
-
hide_deploy_button: bool = False,
|
47
|
-
*,
|
48
|
-
# Sizes
|
49
|
-
top_px: int = 35, # top-right toolbar & st.status icon base size
|
50
|
-
inline_px: int = 288, # animation size when centered
|
51
|
-
# Timing
|
52
|
-
duration_ms: int = 900,
|
53
|
-
# Toolbar nudges / spacing
|
54
|
-
toolbar_nudge_px: int = -3,
|
55
|
-
toolbar_gap_left_px: int = 2,
|
56
|
-
toolbar_left_offset_px: int = 0,
|
57
|
-
# Centered overlay styling
|
58
|
-
center_non_toolbar: bool = True, # << keep True to center inline + status
|
59
|
-
dim_backdrop: bool = True, # << set False to hide the dark veil
|
60
|
-
overlay_blur_px: float = 1.5,
|
61
|
-
overlay_opacity: float = 0.35,
|
62
|
-
overlay_z_index: int = 9990, # keep below toolbar; we also lift toolbar above
|
63
|
-
) -> None:
|
64
|
-
"""Override Streamlit spinners with a 4-frame animation.
|
65
|
-
- Toolbar spinner stays in the toolbar (top-right).
|
66
|
-
- All other spinners (inline + st.status icon) are centered on screen.
|
67
|
-
"""
|
68
|
-
|
69
|
-
def as_data_uri(s: str, mime="image/png") -> str:
|
70
|
-
s = s.strip()
|
71
|
-
return s if s.startswith("data:") else f"data:{mime};base64,{s}"
|
72
|
-
|
73
|
-
i1 = as_data_uri(IMAGE_1_B64)
|
74
|
-
i2 = as_data_uri(IMAGE_2_B64)
|
75
|
-
i3 = as_data_uri(IMAGE_3_B64)
|
76
|
-
i4 = as_data_uri(IMAGE_4_B64)
|
77
|
-
i5= as_data_uri(IMAGE_5_B64)
|
78
|
-
|
79
|
-
veil_bg = f"rgba(0,0,0,{overlay_opacity})"
|
80
|
-
|
81
|
-
st.markdown(f"""
|
82
|
-
<style>
|
83
|
-
/* ---- 4-frame animation ---- */
|
84
|
-
@keyframes st-fourframe {{
|
85
|
-
0%% {{ background-image:url("{i1}"); }}
|
86
|
-
20% {{ background-image:url("{i2}"); }}
|
87
|
-
40% {{ background-image:url("{i3}"); }}
|
88
|
-
60% {{ background-image:url("{i4}"); }}
|
89
|
-
80% {{ background-image:url("{i5}"); }}
|
90
|
-
100% {{ background-image:url("{i5}"); }}
|
91
|
-
}}
|
92
|
-
|
93
|
-
/* ---- CSS variables ---- */
|
94
|
-
:root {{
|
95
|
-
--st-spin-top:{top_px}px; /* toolbar/status base size */
|
96
|
-
--st-spin-inline:{inline_px}px; /* centered spinner size */
|
97
|
-
--st-spin-dur:{duration_ms}ms;
|
98
|
-
|
99
|
-
--st-spin-toolbar-nudge:{toolbar_nudge_px}px;
|
100
|
-
--st-spin-toolbar-gap:{toolbar_gap_left_px}px;
|
101
|
-
--st-spin-toolbar-left:{toolbar_left_offset_px}px;
|
102
|
-
|
103
|
-
--st-overlay-z:{overlay_z_index};
|
104
|
-
--st-overlay-bg:{veil_bg};
|
105
|
-
--st-overlay-blur:{overlay_blur_px}px;
|
106
|
-
}}
|
107
|
-
|
108
|
-
/* Lift toolbar above any overlay so Stop/Deploy remain clickable */
|
109
|
-
div[data-testid="stToolbar"],
|
110
|
-
[data-testid="stStatusWidget"] {{
|
111
|
-
position: relative;
|
112
|
-
z-index: calc(var(--st-overlay-z) + 5);
|
113
|
-
}}
|
114
|
-
|
115
|
-
/* =======================================================================
|
116
|
-
1) Top-right toolbar widget (kept in place, not centered)
|
117
|
-
======================================================================= */
|
118
|
-
[data-testid="stStatusWidget"] {{
|
119
|
-
position:relative;
|
120
|
-
padding-left: calc(var(--st-spin-top) + var(--st-spin-toolbar-gap));
|
121
|
-
}}
|
122
|
-
[data-testid="stStatusWidget"] svg,
|
123
|
-
[data-testid="stStatusWidget"] img {{ display:none !important; }}
|
124
|
-
[data-testid="stStatusWidget"]::before {{
|
125
|
-
content:"";
|
126
|
-
position:absolute;
|
127
|
-
left: var(--st-spin-toolbar-left);
|
128
|
-
top:50%;
|
129
|
-
transform:translateY(-50%) translateY(var(--st-spin-toolbar-nudge));
|
130
|
-
width:var(--st-spin-top);
|
131
|
-
height:var(--st-spin-top);
|
132
|
-
background:no-repeat center/contain;
|
133
|
-
animation:st-fourframe var(--st-spin-dur) linear infinite;
|
134
|
-
}}
|
135
|
-
|
136
|
-
/* Hide the entire toolbar if requested */
|
137
|
-
{"div[data-testid='stToolbar']{display:none !important;}" if hide_deploy_button else ""}
|
138
|
-
|
139
|
-
/* =======================================================================
|
140
|
-
2) Inline spinner (st.spinner) — centered overlay
|
141
|
-
======================================================================= */
|
142
|
-
[data-testid="stSpinner"] svg {{ display:none !important; }}
|
143
|
-
[data-testid="stSpinner"] {{
|
144
|
-
min-height: 0 !important; /* avoid layout jump, since we center globally */
|
145
|
-
}}
|
146
|
-
{ "[data-testid='stSpinner']::after { content:''; position:fixed; inset:0; background:var(--st-overlay-bg); backdrop-filter: blur(var(--st-overlay-blur)); z-index: var(--st-overlay-z); pointer-events: none; }" if dim_backdrop else "" }
|
147
|
-
[data-testid="stSpinner"]::before {{
|
148
|
-
content:"";
|
149
|
-
position: fixed;
|
150
|
-
left: 50%;
|
151
|
-
top: 50%;
|
152
|
-
transform: translate(-50%,-50%);
|
153
|
-
width: var(--st-spin-inline);
|
154
|
-
height: var(--st-spin-inline);
|
155
|
-
background:no-repeat center/contain;
|
156
|
-
animation:st-fourframe var(--st-spin-dur) linear infinite;
|
157
|
-
z-index: calc(var(--st-overlay-z) + 1);
|
158
|
-
}}
|
159
|
-
|
160
|
-
/* Center the spinner message below the animation (works in sidebar or main) */
|
161
|
-
[data-testid="stSpinner"] [data-testid="stSpinnerMessage"],
|
162
|
-
[data-testid="stSpinner"] > div > div:last-child,
|
163
|
-
[data-testid="stSpinner"] > div > div:only-child {{
|
164
|
-
position: fixed !important;
|
165
|
-
left: 50% !important;
|
166
|
-
top: calc(50% + var(--st-spin-inline) / 2 + 12px) !important;
|
167
|
-
transform: translateX(-50%) !important;
|
168
|
-
z-index: calc(var(--st-overlay-z) + 2) !important;
|
169
|
-
text-align: center !important;
|
170
|
-
margin: 0 !important;
|
171
|
-
padding: .25rem .75rem !important;
|
172
|
-
max-width: min(80vw, 900px) !important; /* keeps long text from stretching off-screen */
|
173
|
-
white-space: normal !important; /* use `nowrap` if you prefer single-line */
|
174
|
-
font-weight: 500 !important;
|
175
|
-
}}
|
176
|
-
|
177
|
-
/* Kill the tiny default glyph wrapper so you don't get a stray dot in the sidebar */
|
178
|
-
[data-testid="stSpinner"] > div > div:first-child {{
|
179
|
-
display: none !important;
|
180
|
-
}}
|
181
|
-
|
182
|
-
/* We still hide the default SVG everywhere */
|
183
|
-
[data-testid="stSpinner"] svg {{
|
184
|
-
display: none !important;
|
185
|
-
}}
|
186
|
-
|
187
|
-
/* =======================================================================
|
188
|
-
3) st.status(...) icon — centered overlay
|
189
|
-
======================================================================= */
|
190
|
-
[data-testid="stStatus"] [data-testid="stStatusIcon"] svg,
|
191
|
-
[data-testid="stStatus"] [data-testid="stStatusIcon"] img {{ display:none !important; }}
|
192
|
-
{"[data-testid='stStatus']::after { content:''; position:fixed; inset:0; background:var(--st-overlay-bg); backdrop-filter: blur(var(--st-overlay-blur)); z-index: var(--st-overlay-z); pointer-events: none; }" if dim_backdrop else ""}
|
193
|
-
[data-testid="stStatus"] [data-testid="stStatusIcon"]::before {{
|
194
|
-
content:"";
|
195
|
-
position: fixed;
|
196
|
-
left: 50%;
|
197
|
-
top: 50%;
|
198
|
-
transform: translate(-50%,-50%);
|
199
|
-
width: var(--st-spin-inline); /* use same size as inline */
|
200
|
-
height: var(--st-spin-inline);
|
201
|
-
background:no-repeat center/contain;
|
202
|
-
animation:st-fourframe var(--st-spin-dur) linear infinite;
|
203
|
-
z-index: calc(var(--st-overlay-z) + 1);
|
204
|
-
}}
|
205
|
-
|
206
|
-
/* Optional: allow 'esc' feel without blocking clicks — achieved via pointer-events:none above. */
|
207
|
-
</style>
|
208
|
-
""", unsafe_allow_html=True)
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/data_sources_interfaces/__init__.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/data_sources_interfaces/duckdb.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/client/data_sources_interfaces/timescale.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/config.toml
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/assets/favicon.png
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/core/__init__.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/dashboards/streamlit/pages/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/data_interface/__init__.py
RENAMED
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/base_instrument.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/european_option.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/interest_rate_swap.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/json_codec.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/knockout_fx_option.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/instruments/vanilla_fx_option.py
RENAMED
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/__init__.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/black_scholes.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/bond_pricer.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/instruments/pricing_models/swap_pricer.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/reportbuilder/examples/ms_template_report.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/agent_interface.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/config_handling.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/__init__.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/apps/news_app.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/prices/__init__.py
RENAMED
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/contrib/prices/utils.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/notebook_handling.py
RENAMED
File without changes
|
{mainsequence-3.0.1 → mainsequence-3.0.2}/mainsequence/virtualfundbuilder/portfolio_interface.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|