"""SYSRISK Monte-Carlo-Simulation – vektorisiert mit NumPy. Schleife: ITERATIONS × TIA-Assets × TIA_SYS-Risiken. Keine Row-Instanzen in der Schleife; Berechnung ueber NumPy-Arrays. """ from __future__ import annotations import os import sqlite3 import tempfile from dataclasses import dataclass, field import numpy as np from ..row.row import Row _HEADER = [ "SimulationNr", "AssetNr", "RiskNr", "RiskRandom", "Quantile", "BaseVal", "RiskValue", ] @dataclass class SysriskSimulation(Row): """Row-Metadaten (Instanzen werden im Normalbetrieb nicht erzeugt).""" inputColumns = ("SimulationNr", "AssetNr", "RiskNr") computedColumns = ("RiskRandom", "Quantile", "BaseVal", "RiskValue") SimulationNr: int = 1 AssetNr: str = None RiskNr: str = None RiskRandom: float = None Quantile: float = None BaseVal: float = None RiskValue: float = None _tia_sys_row: object = field(default=None, init=False, repr=False) _driver_yoy: list = field(default_factory=list, init=False, repr=False) _tia_row: object = field(default=None, init=False, repr=False) class SysriskSimulationTable: """Monte-Carlo: ITERATIONS × TIA-Assets × TIA_SYS-Risiken (vektorisiert).""" rowClass = SysriskSimulation def __init__(self) -> None: self._iterations = 0 self._blocks: list[dict] = [] self._total = 0 @classmethod def run(cls) -> SysriskSimulationTable: sim = cls() settings = Row._settings() iterations = int(settings.get("ITERATIONS", 1000)) sim._iterations = iterations tia_sys_table = settings["TIA_SYS"] tia_table = settings["TIA"] tia_by_asset: dict[str, object] = {} for tia_row in tia_table.rows: tia_row.compute() tia_by_asset[tia_row.AssetNr] = tia_row driver_yoy_arr: dict[str, np.ndarray] = {} for sys_row in tia_sys_table.rows: driver = sys_row.Driver if driver in driver_yoy_arr: continue driver_table = settings[driver] for r in driver_table.rows: r.compute() arr = np.array([r.YoY for r in driver_table.rows], dtype=np.float64) driver_yoy_arr[driver] = arr for asset_nr, tia_row in tia_by_asset.items(): for sys_row in tia_sys_table.rows: yoy = driver_yoy_arr.get(sys_row.Driver) if yoy is None or len(yoy) == 0: randoms = np.random.randint(1, 10000, size=iterations) / 10000.0 quants = np.zeros(iterations) else: randoms = np.random.randint(1, 10000, size=iterations) / 10000.0 quants = np.quantile(yoy, randoms, method="linear") val = getattr(tia_row, sys_row.Measure, None) base_val = float(val) if val is not None else 0.0 values = quants * base_val sim._blocks.append({ "asset_nr": asset_nr, "risk_nr": sys_row.RiskNr, "randoms": randoms, "quants": quants, "base_val": base_val, "values": values, }) sim._total = len(sim._blocks) * iterations return sim def getData(self, limit: int = 0) -> dict: cap = limit if limit > 0 else self._total footer: list = [] if self._blocks and self._total > 0: avg_r = float(np.mean([blk["randoms"].mean() for blk in self._blocks])) avg_q = float(np.mean([blk["quants"].mean() for blk in self._blocks])) avg_bv = float(np.mean([blk["base_val"] for blk in self._blocks])) avg_v = float(np.mean([blk["values"].mean() for blk in self._blocks])) footer = [self._total, "", "", avg_r, avg_q, avg_bv, avg_v] rows: list[list] = [] for j in range(self._iterations): for blk in self._blocks: rows.append([ j + 1, blk["asset_nr"], blk["risk_nr"], float(blk["randoms"][j]), float(blk["quants"][j]), blk["base_val"], float(blk["values"][j]), ]) if len(rows) >= cap: return {"header": list(_HEADER), "rows": rows, "total": self._total, "footer": footer} return {"header": list(_HEADER), "rows": rows, "total": self._total, "footer": footer} def toSqlite(self, table_name: str = "sysrisk_simulation") -> bytes: if self._total == 0: from helpers import to_sqlite_bytes return to_sqlite_bytes([], [], table_name) fd, tmp = tempfile.mkstemp(suffix=".sqlite") os.close(fd) try: conn = sqlite3.connect(tmp) conn.execute( f'CREATE TABLE "{table_name}" ' '("SimulationNr" INTEGER, "AssetNr" TEXT, "RiskNr" TEXT, ' '"RiskRandom" REAL, "Quantile" REAL, "BaseVal" REAL, "RiskValue" REAL)' ) sims = list(range(1, self._iterations + 1)) for blk in self._blocks: assets = [blk["asset_nr"]] * self._iterations risks = [blk["risk_nr"]] * self._iterations bvs = [blk["base_val"]] * self._iterations conn.executemany( f'INSERT INTO "{table_name}" VALUES (?,?,?,?,?,?,?)', zip(sims, assets, risks, blk["randoms"].tolist(), blk["quants"].tolist(), bvs, blk["values"].tolist()), ) conn.commit() conn.close() with open(tmp, "rb") as f: return f.read() finally: try: os.unlink(tmp) except OSError: pass