mainsequence 3.0.1__py3-none-any.whl → 3.0.2__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.
@@ -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
- raise Exception(f"Error in request {r.text}")
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
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
- import streamlit as st
3
2
  from pathlib import Path
3
+ import streamlit as st
4
4
 
5
5
 
6
+ # --------------------- small theme helpers ---------------------
7
+
6
8
  def inject_css_for_dark_accents():
7
9
  st.markdown(
8
10
  """
@@ -22,126 +24,173 @@ def explain_theming():
22
24
  )
23
25
 
24
26
 
27
+ # --------------------- spinner frame loader (runs once on import) ---------------------
25
28
 
29
+ def _read_txt(p: Path) -> str:
30
+ return p.read_text(encoding="utf-8").strip()
26
31
 
27
- # --- Load spinner frames ONCE from two levels above, files: image_1_base64.txt ... image_5_base64.txt ---
28
32
  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()
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
40
88
 
41
89
 
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
90
+ # --------------------- spinner override (CSS only) ---------------------
44
91
 
45
92
  def override_spinners(
46
93
  hide_deploy_button: bool = False,
47
94
  *,
48
95
  # Sizes
49
- top_px: int = 35, # top-right toolbar & st.status icon base size
50
- inline_px: int = 288, # animation size when centered
96
+ top_px: int = 20, # top-right toolbar spinner size
97
+ inline_px: int = 96, # inline/status spinner size
51
98
  # Timing
52
99
  duration_ms: int = 900,
53
- # Toolbar nudges / spacing
54
- toolbar_nudge_px: int = -3,
100
+ # Toolbar micro-positioning
101
+ toolbar_nudge_px: int = -2,
55
102
  toolbar_gap_left_px: int = 2,
56
103
  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
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,
63
110
  ) -> 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.
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.
67
115
  """
68
116
 
69
- def as_data_uri(s: str, mime="image/png") -> str:
70
- s = s.strip()
117
+ def as_data_uri(s: str, mime: str = "image/png") -> str:
118
+ s = (s or "").strip()
71
119
  return s if s.startswith("data:") else f"data:{mime};base64,{s}"
72
120
 
73
121
  i1 = as_data_uri(IMAGE_1_B64)
74
122
  i2 = as_data_uri(IMAGE_2_B64)
75
123
  i3 = as_data_uri(IMAGE_3_B64)
76
124
  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})"
125
+ i5 = as_data_uri(IMAGE_5_B64)
80
126
 
81
127
  st.markdown(f"""
82
128
  <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}"); }}
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}"); }}
91
137
  }}
92
138
 
93
- /* ---- CSS variables ---- */
94
139
  :root {{
95
- --st-spin-top:{top_px}px; /* toolbar/status base size */
96
- --st-spin-inline:{inline_px}px; /* centered spinner size */
140
+ --st-spin-top:{top_px}px;
141
+ --st-spin-inline:{inline_px}px;
97
142
  --st-spin-dur:{duration_ms}ms;
98
143
 
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;
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;
102
147
 
103
148
  --st-overlay-z:{overlay_z_index};
104
- --st-overlay-bg:{veil_bg};
149
+ --st-overlay-bg: rgba(0,0,0,{overlay_opacity});
105
150
  --st-overlay-blur:{overlay_blur_px}px;
106
151
  }}
107
152
 
108
- /* Lift toolbar above any overlay so Stop/Deploy remain clickable */
153
+ /* ===== ensure toolbar itself stays clickable above overlays ===== */
109
154
  div[data-testid="stToolbar"],
110
155
  [data-testid="stStatusWidget"] {{
111
156
  position: relative;
112
157
  z-index: calc(var(--st-overlay-z) + 5);
113
158
  }}
114
159
 
115
- /* =======================================================================
116
- 1) Top-right toolbar widget (kept in place, not centered)
117
- ======================================================================= */
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) ===== */
118
169
  [data-testid="stStatusWidget"] {{
119
170
  position:relative;
120
- padding-left: calc(var(--st-spin-top) + var(--st-spin-toolbar-gap));
171
+ padding-left: calc(var(--st-spin-top) + var(--st-toolbar-gap));
121
172
  }}
122
- [data-testid="stStatusWidget"] svg,
123
- [data-testid="stStatusWidget"] img {{ display:none !important; }}
124
173
  [data-testid="stStatusWidget"]::before {{
125
174
  content:"";
126
175
  position:absolute;
127
- left: var(--st-spin-toolbar-left);
176
+ left: var(--st-toolbar-left);
128
177
  top:50%;
129
- transform:translateY(-50%) translateY(var(--st-spin-toolbar-nudge));
178
+ transform: translateY(calc(-50% + var(--st-toolbar-nudge)));
130
179
  width:var(--st-spin-top);
131
180
  height:var(--st-spin-top);
132
- background:no-repeat center/contain;
133
- animation:st-fourframe var(--st-spin-dur) linear infinite;
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;
134
186
  }}
135
187
 
136
- /* Hide the entire toolbar if requested */
188
+ /* Optionally hide Deploy/Stop toolbar entirely */
137
189
  {"div[data-testid='stToolbar']{display:none !important;}" if hide_deploy_button else ""}
138
190
 
139
- /* =======================================================================
140
- 2) Inline spinner (st.spinner) — centered overlay
141
- ======================================================================= */
142
- [data-testid="stSpinner"] svg {{ display:none !important; }}
191
+ /* ===== inline st.spinner ===== */
143
192
  [data-testid="stSpinner"] {{
144
- min-height: 0 !important; /* avoid layout jump, since we center globally */
193
+ min-height: 0 !important;
145
194
  }}
146
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 "" }
147
196
  [data-testid="stSpinner"]::before {{
@@ -152,61 +201,31 @@ div[data-testid="stToolbar"],
152
201
  transform: translate(-50%,-50%);
153
202
  width: var(--st-spin-inline);
154
203
  height: var(--st-spin-inline);
155
- background:no-repeat center/contain;
156
- animation:st-fourframe var(--st-spin-dur) linear infinite;
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;
157
209
  z-index: calc(var(--st-overlay-z) + 1);
158
210
  }}
159
211
 
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 ""}
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 "" }
193
215
  [data-testid="stStatus"] [data-testid="stStatusIcon"]::before {{
194
216
  content:"";
195
217
  position: fixed;
196
218
  left: 50%;
197
219
  top: 50%;
198
220
  transform: translate(-50%,-50%);
199
- width: var(--st-spin-inline); /* use same size as inline */
221
+ width: var(--st-spin-inline);
200
222
  height: var(--st-spin-inline);
201
- background:no-repeat center/contain;
202
- animation:st-fourframe var(--st-spin-dur) linear infinite;
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;
203
228
  z-index: calc(var(--st-overlay-z) + 1);
204
229
  }}
205
-
206
- /* Optional: allow 'esc' feel without blocking clicks — achieved via pointer-events:none above. */
207
230
  </style>
208
231
  """, unsafe_allow_html=True)
209
-
210
-
211
-
212
-
@@ -10,6 +10,9 @@ from importlib.resources import files as _pkg_files
10
10
  import sys
11
11
  import os
12
12
 
13
+
14
+
15
+
13
16
  def _detect_app_dir() -> Path:
14
17
  """
15
18
  Best-effort detection of the directory that contains the running Streamlit app.
@@ -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
- update_statistics = data_node.get_update_statistics()
296
- target_date = update_statistics.asset_time_statistics[curve_name]
297
- print("REMOVE ABOCVE ONLY FOR TESTING")
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
- try:
301
- limit = target_date + datetime.timedelta(days=1)
302
- except Exception as e:
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
 
@@ -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
- _C.get_value(name="REFERENCE_RATE__UST"): dict(
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.GovernmentBond),
131
- day_counter=ql.ActualActual(ql.ActualActual.Bond), # Treasuries accrue Act/Act (Bond/ICMA)
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.Following, # “next banking business day” => Following
132
+ bdc=ql.ModifiedFollowing,
136
133
  end_of_month=False, # Irrelevant when scheduling by days
137
134
  ),
138
135
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -8,9 +8,10 @@ mainsequence/cli/config.py,sha256=UAi0kfxro2oJhzaRdd2D3F72eGpqkYaWjSsCzBrb2LY,27
8
8
  mainsequence/cli/ssh_utils.py,sha256=cqodXCJd1EK-PHfG8K_poeg004K6cyqRz6ahpYnbEdk,6127
9
9
  mainsequence/client/__init__.py,sha256=dkiXwFedl9yrmKHprzL_B9BfHRWHRj3d9f_o4-aRH7U,867
10
10
  mainsequence/client/base.py,sha256=yqSu9lUMPDr7ewU1DWJm6jBqXrNUUVGqsihhFcZPs2s,15025
11
+ mainsequence/client/exceptions.py,sha256=pon9585d5RyMfCVy6dyfpDJ9tCH2ugQ4ptq-eqAl2rY,295
11
12
  mainsequence/client/models_helpers.py,sha256=aXANVSt8qMhI4Diwe_1k4-P6NnJANYvCAfDWQhqGQX8,4095
12
13
  mainsequence/client/models_report_studio.py,sha256=mKu7k0STyUZMTzTK98w46t2b1jv8hVhDpmCzI4EeSnQ,13971
13
- mainsequence/client/models_tdag.py,sha256=FRcbf3TYDCjTFZ6JRxHiHKZyu9M9mGrUjNLfSnzgirQ,95293
14
+ mainsequence/client/models_tdag.py,sha256=bzHnu0_dFY2-DZ8CeS9Um8cc8aaXLldmlBL2abOgxXw,95529
14
15
  mainsequence/client/models_vam.py,sha256=4dqJkGyVboabo844LjUWO1sTxt40OshzrRoTfSTo-lU,72891
15
16
  mainsequence/client/utils.py,sha256=4aLctRMmQdFtLzaI9b28ifP141pO-VAmiQ7wTdHpqGg,11665
16
17
  mainsequence/client/data_sources_interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -18,7 +19,7 @@ mainsequence/client/data_sources_interfaces/duckdb.py,sha256=cOvq_241eU6jPUw3DqL
18
19
  mainsequence/client/data_sources_interfaces/timescale.py,sha256=UkbFnYKv50-qPJDdYXgYOh_e3ildpJyzjC6oD7q6uUk,20318
19
20
  mainsequence/dashboards/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
21
  mainsequence/dashboards/streamlit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- mainsequence/dashboards/streamlit/scaffold.py,sha256=oOhwHmzwgeDTDQgDwzPvTkWrkf_-KmI1ankOi7r5IUk,7532
22
+ mainsequence/dashboards/streamlit/scaffold.py,sha256=ww-ON3-ZPSPBPPJPISAOXl0000dI93s3_258uJLhjGA,7535
22
23
  mainsequence/dashboards/streamlit/assets/config.toml,sha256=GCsLTasaF9kJlRRY1SnXLY-X5PyzLkGG4LX48gefkcc,314
23
24
  mainsequence/dashboards/streamlit/assets/favicon.png,sha256=-CHfKx6jfMlXb8TnFQh2BNIBiKeVFfxK7g9K9BaqX6I,347
24
25
  mainsequence/dashboards/streamlit/assets/image_1_base64.txt,sha256=Smm9HT5A6FhC8FUvj9HYEREwUostku8igxiJsS5CT4Q,315018
@@ -28,7 +29,7 @@ mainsequence/dashboards/streamlit/assets/image_4_base64.txt,sha256=4KiKYChYUNLB-
28
29
  mainsequence/dashboards/streamlit/assets/image_5_base64.txt,sha256=E9Xl4dSMhc_2z-ipHh7BAXsd6Gz2si8dP5NjjhQhyVc,592742
29
30
  mainsequence/dashboards/streamlit/assets/logo.png,sha256=20KBis07H579XnXi0vMNzcjFyaAfW0CmI_dOtj-UYc4,34970
30
31
  mainsequence/dashboards/streamlit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- mainsequence/dashboards/streamlit/core/theme.py,sha256=Uq0xcv9seSo_FEzwWPPON9Ab5xZ1k0JuffhRL5k2vy8,7939
32
+ mainsequence/dashboards/streamlit/core/theme.py,sha256=CBmrv7mu7V7lDzml8inMP41hYfaUpk7mC7tbpa9ug4s,8087
32
33
  mainsequence/dashboards/streamlit/pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
34
  mainsequence/instrumentation/__init__.py,sha256=qfHOseVGOW_WdoTgw7TWZYpF1REwf14eF9awxWcLtfI,154
34
35
  mainsequence/instrumentation/utils.py,sha256=XgVg9FijubSy5qMNw0yCkl5TMD2n56TnT4Q58WfZXbw,3354
@@ -37,10 +38,10 @@ mainsequence/instruments/instruments.default.toml,sha256=RnCWtS7YJ4t_JqTHJWUdvJh
37
38
  mainsequence/instruments/settings.py,sha256=QbgNar9gEkaFrpMUM79T7SWkoBcvFVMAKVL8CvSlB4g,279
38
39
  mainsequence/instruments/utils.py,sha256=eiqaTfcq4iulSdEwgi1sMq8xAKK8ONl22a-FNItcbYk,698
39
40
  mainsequence/instruments/data_interface/__init__.py,sha256=aQNXx0csDadrwwFqDlYKRwnqIk1OREsYF7ybjm29Ipo,556
40
- mainsequence/instruments/data_interface/data_interface.py,sha256=tK_-8XooHGWCTVRWdYRQk38-2c9ZVSS78xdhPnMK0Nk,15142
41
+ mainsequence/instruments/data_interface/data_interface.py,sha256=XDTk7BUApt_H5sGhhtQKycrBLT3iPoF1EUarfM22lEg,16014
41
42
  mainsequence/instruments/instruments/__init__.py,sha256=Xe1hCMsOGc3IyyfA90Xuv7pO9t8TctpM7fo-6hML9Cg,154
42
43
  mainsequence/instruments/instruments/base_instrument.py,sha256=XQeZxDSGhZ_ZNut3867I_q3UvDvp9VZDW4VRzyyFe-A,3185
43
- mainsequence/instruments/instruments/bond.py,sha256=tm1Xr-OX2kbAzEZin5qbFBPhWyrFNUjLf7aWVQ6FTzg,17432
44
+ mainsequence/instruments/instruments/bond.py,sha256=DNFm9zAFdiLoEjZaEoJJUMuPYHzZ3krVFxZ0VBhRmoU,17925
44
45
  mainsequence/instruments/instruments/european_option.py,sha256=amyl52DsgOSMDiMZn4CbmWA4QGfVn38ojVVMhdOLKXM,2760
45
46
  mainsequence/instruments/instruments/interest_rate_swap.py,sha256=GJLi4yiIZNwjASLiOnEGw2owCh-v4NDQdcONNWS-wGs,7893
46
47
  mainsequence/instruments/instruments/json_codec.py,sha256=94sOEL2N7nPMUsleB3GD3FzwYy5sVYsgthvQ2g8u9gE,22070
@@ -52,7 +53,7 @@ mainsequence/instruments/pricing_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5J
52
53
  mainsequence/instruments/pricing_models/black_scholes.py,sha256=bs7fsgiDKRuBZo7fvemVdmUHN-NjC2v6XUjhPaLNC-4,1588
53
54
  mainsequence/instruments/pricing_models/bond_pricer.py,sha256=JXBOsDonlLwYG3GyHGD7rV1cddCPot8ZgE1Th-tHiqY,6566
54
55
  mainsequence/instruments/pricing_models/fx_option_pricer.py,sha256=cMRejaIo3ZEhKCS960C7lg8ejh3b9RqIfL3Hhj4LB0A,3292
55
- mainsequence/instruments/pricing_models/indices.py,sha256=NEYu94uBw11afIPohnQ3qhqjLXyd1FEI1lKhyvTvrRM,13749
56
+ mainsequence/instruments/pricing_models/indices.py,sha256=yptDA8joZ5wD28Xljp7jcE77MCEMK3EF1ScRCBanNEs,13552
56
57
  mainsequence/instruments/pricing_models/knockout_fx_pricer.py,sha256=FrO1uRn63zktkm6LwfLKZf0vPn5x0NOCBIPfSPEIO9c,6772
57
58
  mainsequence/instruments/pricing_models/swap_pricer.py,sha256=vQXL1pgWPS9EUu8xXDyrA6QZm-t7wH0Hx3GU3yl1HHw,17994
58
59
  mainsequence/reportbuilder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -109,9 +110,9 @@ mainsequence/virtualfundbuilder/resource_factory/app_factory.py,sha256=XSZo9ImdT
109
110
  mainsequence/virtualfundbuilder/resource_factory/base_factory.py,sha256=jPXdK2WCaNw3r6kP3sZGIL7M4ygfIMs8ek3Yq4QYQZg,9434
110
111
  mainsequence/virtualfundbuilder/resource_factory/rebalance_factory.py,sha256=ysEeJrlbxrMPA7wFw7KDtuCTzXYkdfYZuxUFpPPY7vE,3732
111
112
  mainsequence/virtualfundbuilder/resource_factory/signal_factory.py,sha256=ywa7vxxLlQopuRwwRKyj866ftgaj8uKVoiPQ9YJ2IIo,7198
112
- mainsequence-3.0.1.dist-info/licenses/LICENSE,sha256=fXoCKgEuZXmP84_QDXpvO18QHze_6AAhd-zvZBUjJxQ,4524
113
- mainsequence-3.0.1.dist-info/METADATA,sha256=0wfqq0c1BIKAVkvSZE-qLkQs13IqzVDHV9q10cj8Wyo,8028
114
- mainsequence-3.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
115
- mainsequence-3.0.1.dist-info/entry_points.txt,sha256=2J8TprrUndh7AttNTlXAaxgGtkXFUAHgXs-M7DCj5MU,58
116
- mainsequence-3.0.1.dist-info/top_level.txt,sha256=uSLD9rXMDMN0cc1x0p808bwyQMoRmYY2pdQZEWLajX8,13
117
- mainsequence-3.0.1.dist-info/RECORD,,
113
+ mainsequence-3.0.2.dist-info/licenses/LICENSE,sha256=fXoCKgEuZXmP84_QDXpvO18QHze_6AAhd-zvZBUjJxQ,4524
114
+ mainsequence-3.0.2.dist-info/METADATA,sha256=Sjbad1ij3ayfAZzZkzzr0-se9kqL6vQU9X5XHWTKC9Y,8028
115
+ mainsequence-3.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
116
+ mainsequence-3.0.2.dist-info/entry_points.txt,sha256=2J8TprrUndh7AttNTlXAaxgGtkXFUAHgXs-M7DCj5MU,58
117
+ mainsequence-3.0.2.dist-info/top_level.txt,sha256=uSLD9rXMDMN0cc1x0p808bwyQMoRmYY2pdQZEWLajX8,13
118
+ mainsequence-3.0.2.dist-info/RECORD,,