Command line (gspio)¶
You have written a model. Now you want to run it over a portfolio, trace a single life that disagrees with your spreadsheet, check a data file before you trust it, or look up the accessor or the regulation you need while you work — without writing a runner script each time.
gspio is the command installed with gaspatchio. It does all of the above from the shell:
gspio --help
Model Execution
run-model Execute an actuarial model from a file
run-single-policy Execute an actuarial model for a single policy
calc-graph Generate a calculation graph from a model run
Data Inspection
describe Describe the structure of a data file
Knowledge Discovery
docs Search Gaspatchio framework documentation
knowledge Search the actuarial knowledge base
Tutorial
tutorial List, initialize, and verify gaspatchio tutorials
Two inputs every run takes¶
Every model run takes two files:
- a model file — a
.pywith amain(af)function that takes anActuarialFrameand returns it with your results assigned; - a model-points file — a
.parquet, one row per policy.
The fastest way to get both is to copy a worked model out of the box. gspio tutorial init level-1 writes the Hello World term-life model used throughout this page; pair it with a small portfolio of three policies:
import polars as pl
pl.DataFrame(
{
"policy_id": ["POL001", "POL002", "POL003"],
"age": [30, 45, 60],
"sex": ["M", "F", "M"],
"sum_assured": [500_000, 250_000, 100_000],
"annual_premium": [450, 1_200, 2_800],
"mortality_rate": [0.001, 0.004, 0.015], # annual qx
"expense_rate": [0.10, 0.10, 0.10], # share of premium
}
).write_parquet("model_points.parquet")
The model.py you initialised computes expected claims, expenses, net premium, profit, and a loss ratio for each policy. Everything below runs against these two files. For a full production model that ships its own assumptions and model points, gspio tutorial init level-4 writes a lifelib book reconciled to the reference cashflows.
Run a model over the portfolio — run-model¶
Project every policy and read the portfolio result in one command:
gspio run-model model.py model_points.parquet
Result (Columns filtered by -f/-l):
shape: (3, 13)
┌───────────┬─────┬─────┬─────────────┬────────────────┬────────────────┬──────────────┬─────────────────┬──────────┬─────────────┬────────┬────────────┬───────────────┐
│ policy_id ┆ age ┆ sex ┆ sum_assured ┆ annual_premium ┆ mortality_rate ┆ expense_rate ┆ expected_claims ┆ expenses ┆ net_premium ┆ profit ┆ loss_ratio ┆ is_profitable │
│ str ┆ i64 ┆ str ┆ i64 ┆ i64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═══════════╪═════╪═════╪═════════════╪════════════════╪════════════════╪══════════════╪═════════════════╪══════════╪═════════════╪════════╪════════════╪═══════════════╡
│ POL001 ┆ 30 ┆ M ┆ 500000 ┆ 450 ┆ 0.001 ┆ 0.1 ┆ 500.0 ┆ 45.0 ┆ 405.0 ┆ -95.0 ┆ 1.1111 ┆ No │
│ POL002 ┆ 45 ┆ F ┆ 250000 ┆ 1200 ┆ 0.004 ┆ 0.1 ┆ 1000.0 ┆ 120.0 ┆ 1080.0 ┆ 80.0 ┆ 0.8333 ┆ Yes │
│ POL003 ┆ 60 ┆ M ┆ 100000 ┆ 2800 ┆ 0.015 ┆ 0.1 ┆ 1500.0 ┆ 280.0 ┆ 2520.0 ┆ 1020.0 ┆ 0.5357 ┆ Yes │
└───────────┴─────┴─────┴─────────────┴────────────────┴────────────────┴──────────────┴─────────────────┴──────────┴─────────────┴────────┴────────────┴───────────────┘
The result is the model points plus every column your model computed. A wide result is shown as a window — the first and last columns, with the middle elided — so the table fits your terminal.
| Option | Effect |
|---|---|
--mode, -m |
debug (default) records each step so you can inspect the calculation; optimize skips the bookkeeping for the fastest run. |
--first-n, -f |
Number of leading columns to show (default 5). |
--last-n, -l |
Number of trailing columns to show (default 10). |
--start-at, -s |
Column index to start the window at (default 0). |
--rows, -r |
Number of rows to show (default 15). |
--output-file, -o |
Write the full result to a parquet file instead of printing. |
To keep the full result rather than a printed window, write it out:
gspio run-model model.py model_points.parquet --mode optimize --output-file results.parquet
Trace a single policy — run-single-policy¶
When one life disagrees with your existing model, run that policy on its own and read its result back column by column:
gspio run-single-policy model.py model_points.parquet POL001 --policy-id-column policy_id
Transposed Result (Columns filtered by -f/-l):
shape: (1, 13)
┌───────────┬─────┬─────┬─────────────┬───┬─────────────┬────────┬────────────┬───────────────┐
│ policy_id ┆ age ┆ sex ┆ sum_assured ┆ … ┆ net_premium ┆ profit ┆ loss_ratio ┆ is_profitable │
│ str ┆ i64 ┆ str ┆ i64 ┆ ┆ f64 ┆ f64 ┆ f64 ┆ str │
╞═══════════╪═════╪═════╪═════════════╪═══╪═════════════╪════════╪════════════╪═══════════════╡
│ POL001 ┆ 30 ┆ M ┆ 500000 ┆ … ┆ 405.0 ┆ -95.0 ┆ 1.1111 ┆ No │
└───────────┴─────┴─────┴─────────────┴───┴─────────────┴────────┴────────────┴───────────────┘
The policy id is the third argument. --policy-id-column names the column that holds it — it defaults to Policy number, so set it to match your data (policy_id here). For a projection model with a time dimension, this returns one row per period: the full trace for that life, which is what you reconcile against your spreadsheet step by step. The same --mode and display options as run-model apply.
Export the calculation graph — calc-graph¶
To audit what feeds what — which inputs and which intermediate columns each result depends on — export the calculation graph:
gspio calc-graph model.py model_points.parquet --policy-id-column policy_id -o graph.json
✓ Calculation graph saved to: graph.json
Nodes: 13 (7 inputs, 6 computed)
Edges: 11
graph.json holds nodes (the input columns and the computed ones, each with its dtype, formula, dependencies, and a sample value) and edges (the dependency from each column to the ones it is built from):
{
"id": "expected_claims",
"type": "computed",
"label": "expected_claims = [(col(\"sum_assured\")) * (col(\"mortality_rate\"))]",
"data": {
"dtype": "float",
"dependencies": ["mortality_rate", "sum_assured"],
"formula": "[(col(\"sum_assured\")) * (col(\"mortality_rate\"))]",
"value_sample": 500.0,
...
}
}
The graph is captured for models written as traced column expressions (af.expected_claims = af.sum_assured * af.mortality_rate), which is what the debug run records. Narrow it to one life with --policy-id/-p, and supply sample values from a chosen period with a Polars filter, --filter "col('year') == 1".
Inspect a data file — describe¶
Before you trust a model-points or assumptions file, read its shape — columns, dtypes, and a sample:
gspio describe model_points.parquet
File Analysis: model_points.parquet
Format: LONG
Rows: 3
Columns: 7
Sample Data (first 5 rows):
shape: (3, 7)
┌───────────┬─────┬─────┬─────────────┬────────────────┬────────────────┬──────────────┐
│ policy_id ┆ age ┆ sex ┆ sum_assured ┆ annual_premium ┆ mortality_rate ┆ expense_rate │
│ str ┆ i64 ┆ str ┆ i64 ┆ i64 ┆ f64 ┆ f64 │
╞═══════════╪═════╪═════╪═════════════╪════════════════╪════════════════╪══════════════╡
│ POL001 ┆ 30 ┆ M ┆ 500000 ┆ 450 ┆ 0.001 ┆ 0.1 │
│ POL002 ┆ 45 ┆ F ┆ 250000 ┆ 1200 ┆ 0.004 ┆ 0.1 │
│ POL003 ┆ 60 ┆ M ┆ 100000 ┆ 2800 ┆ 0.015 ┆ 0.1 │
└───────────┴─────┴─────┴─────────────┴────────────────┴────────────────┴──────────────┘
describe reads .parquet, .csv, and .xlsx, detects whether the file is shaped as an assumption table, and names the likely value and key columns. --value-column overrides the detected value column; --json emits the structure as JSON for a tool to consume.
Start from a worked model — tutorial¶
The tutorials are runnable models you copy into your own directory and run with the commands above:
gspio tutorial list
Level Description
level-1 Hello World — term life portfolio, column arithmetic, when/then
level-2 Assumptions — mortality/lapse table lookups, multi-dimension tables
level-3 Mini Variable Annuity — account values, guarantees, dynamic lapse
level-4 Reconciled Lifelib — production model reconciled to 0.0000% vs lifelib
level-5 Scenarios — parameter shocks, sensitivity analysis, stress testing
gspio tutorial init level-3 --dest ./va-model copies a level into ./va-model (--force overwrites an existing one), and gspio tutorial verify level-3 runs it and checks the output against the expected result. The Tutorials page walks each level end to end.
Look up the docs or an actuarial concept — docs and knowledge¶
While building a model you reach for two things: how a gaspatchio feature works, and what a regulation or actuarial concept requires. Search both from the shell — and so can an LLM working alongside you:
gspio docs "cumulative survival probability"
gspio knowledge "IFRS 17 risk adjustment" --jurisdiction EU
Each returns the ranked excerpts as JSON — several sources you can weigh against the model in front of you, rather than a single answer:
{
"results": [
{
"text": "Tests for cumulative_survival() method.",
"score": 0.775,
"content_type": "overview",
"object_path": "test_projection.TestCumulativeSurvival",
"has_code": false
},
...
],
"query": "cumulative survival probability"
}
| Option | Effect |
|---|---|
--limit, -n |
Number of results to return. |
--search-type, -s |
hybrid (default), semantic for concepts, or keyword for exact names. |
--content-type, -t |
(docs) restrict to code_example, overview, when_to_use, or parameters. |
--tag/--jurisdiction/--doc-type |
(knowledge) filter by tag (IFRS17, SolvencyII, …), jurisdiction, or document type. |
--answer, -a |
Summarise the sources into one answer. Use sparingly — the ranked excerpts let you judge each source yourself. |
The Knowledge Store page covers both stores, what they hold, and how to keep them current.
Version and shell completion¶
gspio --version # the installed package and core versions
gspio --install-completion # add tab-completion to your shell