Performance
ultibi FRTB aggregator was built with performance in mind. Bellow table summarizes a large Equity portfolio:
┌───────────┬──────────────┬────────────────┬───────────────┬──────────────┬─────────────────────┬─────────────────────┬─────────────────────────┐
│ RiskClass ┆ RiskCategory ┆ RiskFactorType ┆ TradeId_count ┆ PnL_Up_count ┆ BucketBCBS_n_unique ┆ RiskFactor_n_unique ┆ RiskFactorType_n_unique │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ u32 ┆ u32 ┆ u32 ┆ u32 ┆ u32 │
╞═══════════╪══════════════╪════════════════╪═══════════════╪══════════════╪═════════════════════╪═════════════════════╪═════════════════════════╡
│ Equity ┆ Delta ┆ EqSpot ┆ 648506 ┆ 648506 ┆ 4 ┆ 18316 ┆ 1 │
│ Equity ┆ Delta ┆ EqRepo ┆ 219792 ┆ 219792 ┆ 3 ┆ 18316 ┆ 1 │
└───────────┴──────────────┴────────────────┴───────────────┴──────────────┴─────────────────────┴─────────────────────┴─────────────────────────┘
This portfolio consist of 868298 Equity Delta and Curvature (see PnL_Up_count) Sensitivities splic across 4 buckets, 18316 risk factors(equity names/tickers), with both Spot and Repo Present.
The result as of ultibi v0.1.3: --- Read DF time: 483.5027ms --- --- Assign Weights time: 1.1574655s ---
Just single EQ Delta Charge:
request2 = dict(
measures=[
["EQ DeltaCharge Medium", "scalar"]
],
# Break down results by Group and BucketBCBS
groupby=["Group"],
# Show totals for each Group (note in this example only 1)
totals = True,
# Hide rows where each result is 0
hide_zeros=True,
calc_params={
"jurisdiction": "BCBS",
# Apply 21.98
"apply_fx_curv_div": "true",
},
)
--- Compute time: 0.22789788246154785 seconds ---
request2 = dict(
measures=[
["SBM Charge", "scalar"]
],
# Break down results by Group and BucketBCBS
groupby=["Group"],
# Show totals for each Group (note in this example only 1)
totals = True,
# Hide rows where each result is 0
hide_zeros=True,
calc_params={
"jurisdiction": "BCBS",
# Apply 21.98
"apply_fx_curv_div": "true",
},
)
--- Compute No Deps time: 2.538625478744507 seconds ---
Caching
Note, thanks to ultibi's
internal caching mechanism, basic measures can be reused. For example:
request = dict(
measures=[
["SBM Charge High", "scalar"],
["SBM Charge Low", "scalar"],
["SBM Charge Medium", "scalar"]
],
# Break down results by Group and BucketBCBS
groupby=["Group"],
# Show totals for each Group (note in this example only 1)
totals = True,
# Hide rows where each result is 0
hide_zeros=True,
calc_params={
"jurisdiction": "BCBS",
# Apply 21.98
"apply_fx_curv_div": "true",
},
)
--- Compute No Deps time: 0.010367870330810547 seconds ---
Compute time is almost identical to that of request2
. This is because SBM Charge
is a simple max of SBM Charge Low
, SBM Charge Medium
, SBM Charge High
and therefore almost no additional compute is required.
DataSet cache stores results of basic measures such as EQ DeltaCharge Medium
. Therefore the next request will reuse them.
Standing on the shoulders of a giant: Polars
We use Polars
in the backend, one of the fastest DataBase/DataFrame like ops solutions out there. Read more about the benchmarks here to get a feel for how much data we can process and how fast.