vates 0.1.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.
Files changed (53) hide show
  1. vates/__init__.py +30 -0
  2. vates/_core/__init__.py +16 -0
  3. vates/_core/_html_generator.py +529 -0
  4. vates/_core/cli.py +28 -0
  5. vates/_core/keyed_array.py +340 -0
  6. vates/_core/proj_model_engine.py +518 -0
  7. vates/_core/proj_variables.py +283 -0
  8. vates/_core/stoch_executor.py +309 -0
  9. vates/_core/utils.py +233 -0
  10. vates/alm/__init__.py +27 -0
  11. vates/alm/assets/__init__.py +25 -0
  12. vates/alm/assets/_bond_fixed_component.py +376 -0
  13. vates/alm/assets/_derivative_pricer.py +255 -0
  14. vates/alm/assets/asset_base.py +226 -0
  15. vates/alm/assets/bond_fixed.py +375 -0
  16. vates/alm/assets/builders/__init__.py +11 -0
  17. vates/alm/assets/builders/asset_creation.py +48 -0
  18. vates/alm/assets/builders/bond_fixed_builder.py +251 -0
  19. vates/alm/assets/builders/equity_option_builder.py +125 -0
  20. vates/alm/assets/cash.py +147 -0
  21. vates/alm/assets/equity.py +184 -0
  22. vates/alm/assets/equity_option.py +248 -0
  23. vates/alm/econs/__init__.py +14 -0
  24. vates/alm/econs/credit_band.py +95 -0
  25. vates/alm/econs/currency.py +57 -0
  26. vates/alm/econs/equity_index.py +120 -0
  27. vates/alm/econs/market_info.py +49 -0
  28. vates/alm/econs/yield_curve.py +135 -0
  29. vates/alm/enums.py +34 -0
  30. vates/alm/funds/__init__.py +10 -0
  31. vates/alm/funds/_asset_allocator.py +425 -0
  32. vates/alm/funds/_fund_calculator.py +332 -0
  33. vates/alm/funds/fund.py +322 -0
  34. vates/alm/liabs/__init__.py +7 -0
  35. vates/alm/liabs/ext_proj_liab.py +130 -0
  36. vates/alm/liabs/liab_base.py +208 -0
  37. vates/solvency/__init__.py +6 -0
  38. vates/solvency/cn_cross2/__init__.py +27 -0
  39. vates/solvency/cn_cross2/params.py +186 -0
  40. vates/solvency/cn_cross2/quant_risk_min_cap.py +366 -0
  41. vates/utils/__init__.py +96 -0
  42. vates/utils/curve_conversion.py +297 -0
  43. vates/utils/curve_extrapolation.py +334 -0
  44. vates/utils/curve_interpolation.py +168 -0
  45. vates/utils/data_classes.py +26 -0
  46. vates/utils/qfi.py +83 -0
  47. vates/utils/risk_module.py +55 -0
  48. vates/utils/uncategorized.py +28 -0
  49. vates-0.1.0.dist-info/METADATA +106 -0
  50. vates-0.1.0.dist-info/RECORD +53 -0
  51. vates-0.1.0.dist-info/WHEEL +5 -0
  52. vates-0.1.0.dist-info/licenses/LICENSE +21 -0
  53. vates-0.1.0.dist-info/top_level.txt +1 -0
vates/__init__.py ADDED
@@ -0,0 +1,30 @@
1
+ from vates._core import (
2
+ cli_main,
3
+ cli_run,
4
+ ProjModelEngine,
5
+ StochExecutor,
6
+ ConstVariable,
7
+ TDepVariable,
8
+ KeyedArray,
9
+ )
10
+ from vates import utils
11
+
12
+ from vates import alm
13
+ from vates import solvency
14
+
15
+ __all__ = [
16
+ # core
17
+ 'cli_main',
18
+ 'cli_run',
19
+ 'ProjModelEngine',
20
+ 'StochExecutor',
21
+ 'ConstVariable',
22
+ 'TDepVariable',
23
+ 'KeyedArray',
24
+ # utils
25
+ 'utils',
26
+ # libs
27
+ 'alm',
28
+ 'solvency',
29
+
30
+ ]
@@ -0,0 +1,16 @@
1
+ from vates._core.proj_model_engine import ProjModelEngine
2
+ from vates._core.stoch_executor import StochExecutor
3
+ from vates._core.proj_variables import ConstVariable, TDepVariable
4
+ from vates._core.cli import cli_main, cli_run
5
+ from vates._core.keyed_array import KeyedArray
6
+
7
+
8
+ __all__ = [
9
+ 'cli_main',
10
+ 'cli_run',
11
+ 'ProjModelEngine',
12
+ 'StochExecutor',
13
+ 'ConstVariable',
14
+ 'TDepVariable',
15
+ 'KeyedArray',
16
+ ]
@@ -0,0 +1,529 @@
1
+ from datetime import datetime
2
+ import os
3
+ from typing import Dict, Any
4
+
5
+ def generate_runlog_html(runlog: Dict[str, Any]) -> str:
6
+ """
7
+ Convert a runlog dictionary to a beautiful HTML visualization.
8
+
9
+ Args:
10
+ runlog: Dictionary containing model run information
11
+
12
+ Returns:
13
+ HTML content as string
14
+ """
15
+
16
+ def calculate_duration(start_str: str, end_str: str) -> str:
17
+ """Calculate duration between start and end times"""
18
+ start = datetime.strptime(start_str, "%Y-%m-%d %H:%M:%S")
19
+ end = datetime.strptime(end_str, "%Y-%m-%d %H:%M:%S")
20
+ sec = (end - start).total_seconds()
21
+
22
+ if sec < 60:
23
+ return f"~{int(sec)} second{'s' if sec != 1 else ''}"
24
+ elif sec < 3600:
25
+ minutes = round(sec / 60, 1)
26
+ return f"~{minutes} minute{'s' if int(minutes) != 1 else ''}"
27
+ else:
28
+ hours = round(sec / 3600, 2)
29
+ return f"~{hours} hour{'s' if int(hours) != 1 else ''}"
30
+
31
+ def get_file_info(file_path: str) -> tuple:
32
+ """Get file modification time and size"""
33
+ try:
34
+ if os.path.exists(file_path):
35
+ stat_info = os.stat(file_path)
36
+ file_mtime = datetime.fromtimestamp(stat_info.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
37
+ size_bytes = stat_info.st_size
38
+
39
+ # Format file size
40
+ if size_bytes < 1024 * 1024:
41
+ size_str = f"{size_bytes / 1024:.1f} KB"
42
+ elif size_bytes < 1024 * 1024 * 1024:
43
+ size_str = f"{size_bytes / (1024 * 1024):.1f} MB"
44
+ else:
45
+ size_str = f"{size_bytes / (1024 * 1024 * 1024):.1f} GB"
46
+
47
+ return file_mtime, size_str
48
+ else:
49
+ return "File not found", "N/A"
50
+ except Exception as e:
51
+ return "Unknown", "Unknown"
52
+
53
+ # Extract data from runlog
54
+ model_name = runlog['model_name']
55
+ model_desc = runlog['model_desc']
56
+ exe_start_time = runlog['exe_start_time']
57
+ exe_end_time = runlog['exe_end_time']
58
+ failed_simulations = runlog.get('failed_simulations',[])
59
+ settings = runlog['setting']
60
+ input_files = runlog['input_files']
61
+ proj_result_files = runlog['proj_result_files']
62
+ stoch_result_files = runlog['stoch_result_files']
63
+ other_result_files = runlog['other_result_files']
64
+
65
+ # Calculate execution duration
66
+ exe_duration = calculate_duration(exe_start_time, exe_end_time)
67
+
68
+ # Generate HTML
69
+ html_content = f"""<!DOCTYPE html>
70
+ <html lang="en">
71
+ <head>
72
+ <meta charset="UTF-8">
73
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
74
+ <title>Model Run Log - {model_name}</title>
75
+ <style>
76
+ body {{
77
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
78
+ line-height: 1.6;
79
+ margin: 0;
80
+ padding: 20px;
81
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
82
+ min-height: 100vh;
83
+ }}
84
+
85
+ .container {{
86
+ max-width: 1200px;
87
+ margin: 0 auto;
88
+ background: white;
89
+ border-radius: 12px;
90
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
91
+ overflow: hidden;
92
+ }}
93
+
94
+ .header {{
95
+ background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
96
+ color: white;
97
+ padding: 30px;
98
+ text-align: center;
99
+ }}
100
+
101
+ .header h1 {{
102
+ margin: 0;
103
+ font-size: 2.5em;
104
+ font-weight: 300;
105
+ }}
106
+
107
+ .header .subtitle {{
108
+ opacity: 0.9;
109
+ font-size: 1.1em;
110
+ margin-top: 10px;
111
+ }}
112
+
113
+ .content {{
114
+ padding: 30px;
115
+ }}
116
+
117
+ .section {{
118
+ margin-bottom: 30px;
119
+ border-radius: 8px;
120
+ overflow: hidden;
121
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
122
+ }}
123
+
124
+ .section-header {{
125
+ background: linear-gradient(135deg, #34495e 0%, #2c3e50 100%);
126
+ color: white;
127
+ padding: 15px 20px;
128
+ font-size: 1.3em;
129
+ font-weight: 500;
130
+ cursor: pointer;
131
+ transition: background 0.3s ease;
132
+ }}
133
+
134
+ .section-header:hover {{
135
+ background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
136
+ }}
137
+
138
+ .section-content {{
139
+ background: #f8f9fa;
140
+ padding: 20px;
141
+ }}
142
+
143
+ .info-grid {{
144
+ display: grid;
145
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
146
+ gap: 15px;
147
+ }}
148
+
149
+ .info-item {{
150
+ background: white;
151
+ padding: 15px;
152
+ border-radius: 6px;
153
+ border-left: 4px solid #3498db;
154
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
155
+ }}
156
+
157
+ .info-label {{
158
+ font-weight: 600;
159
+ color: #2c3e50;
160
+ margin-bottom: 5px;
161
+ font-size: 0.9em;
162
+ text-transform: uppercase;
163
+ letter-spacing: 0.5px;
164
+ }}
165
+
166
+ .info-value {{
167
+ color: #34495e;
168
+ font-family: 'Consolas', 'Monaco', monospace;
169
+ background: #ecf0f1;
170
+ padding: 8px;
171
+ border-radius: 4px;
172
+ word-break: break-all;
173
+ }}
174
+
175
+ .file-list {{
176
+ display: flex;
177
+ flex-direction: column;
178
+ gap: 8px;
179
+ }}
180
+
181
+ .file-item {{
182
+ background: white;
183
+ padding: 15px;
184
+ border-radius: 6px;
185
+ border-left: 4px solid #e74c3c;
186
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
187
+ display: grid;
188
+ grid-template-columns: 40px 1fr auto auto;
189
+ gap: 15px;
190
+ align-items: center;
191
+ }}
192
+
193
+ .file-item.result {{
194
+ border-left-color: #27ae60;
195
+ }}
196
+
197
+ .file-number {{
198
+ font-weight: bold;
199
+ color: #3498db;
200
+ font-size: 1.1em;
201
+ }}
202
+
203
+ .file-details {{
204
+ display: flex;
205
+ flex-direction: column;
206
+ }}
207
+
208
+ .file-name {{
209
+ font-weight: 600;
210
+ color: #2c3e50;
211
+ margin-bottom: 2px;
212
+ }}
213
+
214
+ .file-path {{
215
+ font-family: 'Consolas', 'Monaco', monospace;
216
+ color: #7f8c8d;
217
+ font-size: 0.85em;
218
+ }}
219
+
220
+ .file-mod-time {{
221
+ font-size: 0.8em;
222
+ color: #95a5a6;
223
+ text-align: center;
224
+ }}
225
+
226
+ .file-size {{
227
+ font-size: 0.8em;
228
+ color: #95a5a6;
229
+ font-weight: 500;
230
+ text-align: right;
231
+ }}
232
+
233
+ .collapsible {{
234
+ max-height: 0;
235
+ overflow: hidden;
236
+ transition: max-height 0.3s ease;
237
+ }}
238
+
239
+ .collapsible.active {{
240
+ max-height: 2000px;
241
+ }}
242
+
243
+ .toggle-btn {{
244
+ float: right;
245
+ font-size: 1.2em;
246
+ transition: transform 0.3s ease;
247
+ }}
248
+
249
+ .toggle-btn.rotated {{
250
+ transform: rotate(180deg);
251
+ }}
252
+
253
+ .duration {{
254
+ background: linear-gradient(135deg, #27ae60 0%, #2ecc71 100%);
255
+ color: white;
256
+ padding: 10px 20px;
257
+ border-radius: 20px;
258
+ display: inline-block;
259
+ font-weight: 600;
260
+ margin-top: 10px;
261
+ }}
262
+
263
+ .list-value {{
264
+ color: #34495e;
265
+ }}
266
+
267
+ @media (max-width: 768px) {{
268
+ .container {{
269
+ margin: 10px;
270
+ border-radius: 8px;
271
+ }}
272
+
273
+ .content {{
274
+ padding: 20px;
275
+ }}
276
+
277
+ .info-grid {{
278
+ grid-template-columns: 1fr;
279
+ }}
280
+
281
+ .file-list {{
282
+ flex-direction: column;
283
+ }}
284
+
285
+ .file-item {{
286
+ grid-template-columns: 30px 1fr;
287
+ gap: 10px;
288
+ }}
289
+
290
+ .file-mod-time,
291
+ .file-size {{
292
+ display: none;
293
+ }}
294
+ }}
295
+ </style>
296
+ </head>
297
+ <body>
298
+ <div class="container">
299
+ <div class="header">
300
+ <h1>Model Run Log</h1>
301
+ <div class="subtitle">{model_name.replace('_', ' ').title()} Execution Flash Report</div>
302
+ </div>
303
+
304
+ <div class="content">
305
+ <!-- Basic Info Section -->
306
+ <div class="section">
307
+ <div class="section-header" onclick="toggleSection('basic-info')">
308
+ 📊 Basic Information
309
+ <span class="toggle-btn" id="basic-info-btn">▼</span>
310
+ </div>
311
+ <div class="section-content collapsible active" id="basic-info-content">
312
+ <div class="info-grid">
313
+ <div class="info-item" style="grid-column: 1 / 1;">
314
+ <div class="info-label">Model Name</div>
315
+ <div class="info-value">{model_name}</div>
316
+ </div>
317
+ <div class="info-item" style="grid-column: 2 / -1;">
318
+ <div class="info-label">Model Description</div>
319
+ <div class="info-value">{model_desc}</div>
320
+ </div>
321
+ <div class="info-item">
322
+ <div class="info-label">Execution Start Time</div>
323
+ <div class="info-value">{exe_start_time}</div>
324
+ </div>
325
+ <div class="info-item">
326
+ <div class="info-label">Execution End Time</div>
327
+ <div class="info-value">{exe_end_time}</div>
328
+ </div>
329
+ <div class="info-item">
330
+ <div class="info-label">Execution Duration</div>
331
+ <div class="duration">{exe_duration}</div>
332
+ </div>
333
+ </div>
334
+ </div>
335
+ </div>"""
336
+
337
+ # Settings Section
338
+ if settings:
339
+ html_content += """
340
+ <!-- Settings Section -->
341
+ <div class="section">
342
+ <div class="section-header" onclick="toggleSection('settings')">
343
+ ⚙️ Run Setting
344
+ <span class="toggle-btn" id="settings-btn">▼</span>
345
+ </div>
346
+ <div class="section-content collapsible active" id="settings-content">
347
+ <div class="info-grid">"""
348
+
349
+ for key, value in settings.items():
350
+ if isinstance(value, list):
351
+ value_str = ', '.join(str(v) for v in value)
352
+ else:
353
+ value_str = str(value)
354
+
355
+ # Special formatting for specific keys
356
+ label = key.replace('_', ' ').title()
357
+ html_content += f"""
358
+ <div class="info-item">
359
+ <div class="info-label">{label}</div>
360
+ <div class="info-value">{value_str}</div>
361
+ </div>"""
362
+
363
+ html_content += """
364
+ </div>
365
+ </div>
366
+ </div>"""
367
+
368
+ # Input Section
369
+ if input_files:
370
+ html_content += """
371
+ <!-- Input Section -->
372
+ <div class="section">
373
+ <div class="section-header" onclick="toggleSection('input')">
374
+ 📁 Input Files
375
+ <span class="toggle-btn" id="input-btn">▼</span>
376
+ </div>
377
+ <div class="section-content collapsible active" id="input-content">
378
+ <div class="file-list">"""
379
+
380
+ for i, (key, path) in enumerate(input_files.items(), 1):
381
+ mod_time, file_size = get_file_info(path)
382
+ html_content += f"""
383
+ <div class="file-item">
384
+ <div class="file-number">{i}</div>
385
+ <div class="file-details">
386
+ <div class="file-name">{key}</div>
387
+ <div class="file-path">{path}</div>
388
+ </div>
389
+ <div class="file-mod-time">{mod_time}</div>
390
+ <div class="file-size">{file_size}</div>
391
+ </div>"""
392
+
393
+ html_content += """
394
+ </div>
395
+ </div>
396
+ </div>"""
397
+
398
+ # Projection Results Section
399
+ if proj_result_files:
400
+ html_content += """
401
+ <!-- Projection Results Section -->
402
+ <div class="section">
403
+ <div class="section-header" onclick="toggleSection('projres')">
404
+ 📈 Output - Projection Results
405
+ <span class="toggle-btn" id="projres-btn">▼</span>
406
+ </div>
407
+ <div class="section-content collapsible active" id="projres-content">
408
+ <div class="file-list">"""
409
+
410
+ for i, (key, path) in enumerate(proj_result_files.items(), 1):
411
+ mod_time, file_size = get_file_info(path)
412
+ html_content += f"""
413
+ <div class="file-item result">
414
+ <div class="file-number">{i}</div>
415
+ <div class="file-details">
416
+ <div class="file-name">{key}</div>
417
+ <div class="file-path">{path}</div>
418
+ </div>
419
+ <div class="file-mod-time">{mod_time}</div>
420
+ <div class="file-size">{file_size}</div>
421
+ </div>"""
422
+
423
+ html_content += """
424
+ </div>
425
+ </div>
426
+ </div>"""
427
+
428
+ # Stochastic Results Section
429
+ if stoch_result_files:
430
+ html_content += """
431
+ <!-- Stochastic Results Section -->
432
+ <div class="section">
433
+ <div class="section-header" onclick="toggleSection('stochres')">
434
+ 📈 Output - Stochastic Results
435
+ <span class="toggle-btn" id="stochres-btn">▼</span>
436
+ </div>
437
+ <div class="section-content collapsible active" id="stochres-content">
438
+ <div class="file-list">"""
439
+
440
+ for i, (key, path) in enumerate(stoch_result_files.items(), 1):
441
+ mod_time, file_size = get_file_info(path)
442
+ html_content += f"""
443
+ <div class="file-item result">
444
+ <div class="file-number">{i}</div>
445
+ <div class="file-details">
446
+ <div class="file-name">{key}</div>
447
+ <div class="file-path">{path}</div>
448
+ </div>
449
+ <div class="file-mod-time">{mod_time}</div>
450
+ <div class="file-size">{file_size}</div>
451
+ </div>"""
452
+
453
+ html_content += """
454
+ </div>
455
+ </div>
456
+ </div>"""
457
+
458
+ # Failed Simulations Section
459
+ if failed_simulations:
460
+ html_content += """
461
+ <!-- Failed Simulations Section -->
462
+ <div class="section">
463
+ <div class="section-header" onclick="toggleSection('failsim')">
464
+ ⚠️ Failed Simulation
465
+ <span class="toggle-btn" id="failsim-btn">▼</span>
466
+ </div>
467
+ <div class="section-content collapsible active" id="failsim-content">
468
+ <div class="file-list">"""
469
+
470
+ for sim, err in failed_simulations:
471
+ html_content += f"""
472
+ <div class="simulation result">
473
+ <div class="file-number">{sim}</div>
474
+ <div class="file-path">{err}</div>
475
+ </div>"""
476
+
477
+ html_content += """
478
+ </div>
479
+ </div>
480
+ </div>"""
481
+
482
+ # Other Results Section
483
+ if other_result_files:
484
+ html_content += """
485
+ <!-- Other Results Section -->
486
+ <div class="section">
487
+ <div class="section-header" onclick="toggleSection('otherres')">
488
+ 📈 Output - Other
489
+ <span class="toggle-btn" id="otherres-btn">▼</span>
490
+ </div>
491
+ <div class="section-content collapsible active" id="otherres-content">
492
+ <div class="file-list">"""
493
+
494
+ for i, (key, path) in enumerate(other_result_files.items(), 1):
495
+ mod_time, file_size = get_file_info(path)
496
+ html_content += f"""
497
+ <div class="file-item result">
498
+ <div class="file-number">{i}</div>
499
+ <div class="file-details">
500
+ <div class="file-name">{key}</div>
501
+ <div class="file-path">{path}</div>
502
+ </div>
503
+ <div class="file-mod-time">{mod_time}</div>
504
+ <div class="file-size">{file_size}</div>
505
+ </div>"""
506
+
507
+ html_content += """
508
+ </div>
509
+ </div>
510
+ </div>"""
511
+
512
+ # Close HTML
513
+ html_content += """
514
+ </div>
515
+ </div>
516
+
517
+ <script>
518
+ function toggleSection(sectionId) {
519
+ const content = document.getElementById(sectionId + '-content');
520
+ const btn = document.getElementById(sectionId + '-btn');
521
+
522
+ content.classList.toggle('active');
523
+ btn.classList.toggle('rotated');
524
+ }
525
+ </script>
526
+ </body>
527
+ </html>"""
528
+
529
+ return html_content
vates/_core/cli.py ADDED
@@ -0,0 +1,28 @@
1
+ def cli_run(model_cls, stoch_cls = None):
2
+ import argparse
3
+ import json
4
+ parser = argparse.ArgumentParser()
5
+ parser.add_argument("args_json_file", type=str, help='args json file for model instance')
6
+ args = parser.parse_args()
7
+
8
+ with open(args.args_json_file, 'r', encoding='utf-8') as file:
9
+ model_args = json.load(file)
10
+
11
+ if stoch_cls:
12
+ model_instance = stoch_cls(model_cls, **model_args)
13
+ else:
14
+ model_instance = model_cls(**model_args)
15
+
16
+ model_instance.run()
17
+
18
+ def cli_main(main_func):
19
+ import argparse
20
+ import json
21
+ parser = argparse.ArgumentParser()
22
+ parser.add_argument("args_json_file", type=str, help='args json file for model instance')
23
+ args = parser.parse_args()
24
+
25
+ with open(args.args_json_file, 'r', encoding='utf-8') as file:
26
+ model_args = json.load(file)
27
+
28
+ main_func(**model_args)