https://github.com/lubasinkal/v-star
High-performance actuarial engine in Go — zero dependencies, CSV streaming at millions/sec, Monte Carlo, VaR/CTE, annuities, and mortality tables. Doesn't suck and Its NOT slow.
https://github.com/lubasinkal/v-star
actuarial actuarial-science annuity financial-modeling go golang high-performance monte-carlo mortality-rates quantitative-finance risk-management zero-dependencies
Last synced: 21 days ago
JSON representation
High-performance actuarial engine in Go — zero dependencies, CSV streaming at millions/sec, Monte Carlo, VaR/CTE, annuities, and mortality tables. Doesn't suck and Its NOT slow.
- Host: GitHub
- URL: https://github.com/lubasinkal/v-star
- Owner: lubasinkal
- License: mit
- Created: 2026-03-17T19:27:27.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-05-27T21:24:11.000Z (23 days ago)
- Last Synced: 2026-05-27T22:13:53.436Z (23 days ago)
- Topics: actuarial, actuarial-science, annuity, financial-modeling, go, golang, high-performance, monte-carlo, mortality-rates, quantitative-finance, risk-management, zero-dependencies
- Language: Go
- Homepage:
- Size: 13.8 MB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Roadmap: ROADMAP.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# v-star: The Actuarial Engine That Doesn't Suck
**Your calculations just got millions of times faster.**
Ever tried to run a valuation on a million-policy census? Watched Excel freeze, crash, or take hours? v-star is the answer. Built in Go — an actually fast language compared to R, Python, or VBA — it handles massive datasets and calculations in milliseconds while your coffee is still hot.


---
## The Story
Actuarial science grad. Tired of:
- Excel crashing on big files
- VBA scripts that nobody understands
- Python code that felt slow (still better than VBA)
- Proprietary tools where you can't see the math
- Waiting to get accepted for a job
So I built v-star. Zero dependencies. All the actuarial stuff a gradute would think you'd need. Fast enough to make your laptop feel like a supercomputer.
Why the name? Comes from a joke in class: if premiums compound at rate **j** but you're discounting at **i**, the new discount factor is **v\*** = (1+j) × v. The star marks the difference.
---
## What Can It Do?
| Feature | What it means for you |
|---------|----------------------|
| **Present Value** | Standard & v\* discount factors — the core of everything |
| **Annuities** | Whole life, term, deferred — with real mortality tables |
| **Reserves** | Net premium, gross premium, prospective, retrospective |
| **Monte Carlo** | GBM + Vasicek mean-reverting models. 100k paths in ~27ms |
| **Risk Measures** | VaR, CTE, Expected Shortfall, confidence intervals |
| **Multiple Decrements** | Combine death, lapse, disability into a single table |
| **Big CSV Streaming** | Stream millions of rows without blowing up your RAM |
| **HTTP API** | Call from Python, R, Excel via REST endpoints |
| **Zero Dependencies** | Standard library only. No pip, no npm, no version hell |
**New:** Core interfaces added — `PathGenerator` (stochastic), `ContingencyCalculator` (annuities), `RecordWriter` (writer). Stream from any `io.Reader` via `StreamCensusFromReader`. `JSONRecord`/`CSVRecord` unified into `Record` type. `MortalityTable` now includes `Ex()` and `Lx()`. Parallel Monte Carlo (2.2× faster at 1M paths × 180 steps).
---
## Speed
Benchmarked on an Intel i5-8250U laptop (1.6-3.4 GHz, 8 cores, NVMe SSD). Plugged in.
| Benchmark | Time | Throughput |
|-----------|------|------------|
| **CSV Parsing** (10M rows, 288 MB) | 0.80s | **12.6M rows/s** |
| **Present Value** (single call, direct) | 2.6 ns | **380M / second** |
| **Present Value** (single call, constructor) | 22.8 ns | **44M / second** |
| **Annuity** (whole life, 90 terms) | 512 ns | **2M / second** |
| **Monte Carlo** (100k paths, 10 steps) | 27 ms | **3.7M paths/sec** |
| **Risk Report** (VaR + CTE, 100k losses) | 0.42 ms | **237M losses/sec** |
| **Valuation** (10M policies, parallel) | 37 ms | **272M policies/sec** |
### CSV Comparison (10M Rows)
| Tool | Time | Memory |
|------|------|--------|
| **v-star** (mmap) | **0.80s** | **1.3 GB (OS page cache)** |
| **v-star** (streaming) | 1.12s | ~0.2 MB |
| Polars | ~535ms | ~500 MB |
| Pandas | ~30s | >2 GB |
*v-star uses memory-mapped I/O for zero-copy parsing. The mmap path uses OS page cache (lazily paged, released to OS under memory pressure). The streaming path keeps memory constant regardless of file size.*
### Monte Carlo (100k Paths)
| Tool | Time | VaR/CTE |
|------|------|---------|
| **v-star** | **27ms** | ✓ (with confidence intervals) |
| Python/Numpy | ~2s | ✓ |
| R | ~5s | ✓ |
---
## Quick Start
```bash
# Install
go get github.com/lubasinkal/v-star
# Run examples
go run ./examples/quickstart # PV and duration
go run ./examples/monte_carlo_risk # Monte Carlo + VaR
go run ./examples/csv_valuation # Big CSV processing
# Build CLI
go build -o v-star ./cmd/v-star
# Process a policy CSV
./v-star read policies.csv --benchmark
# Run Monte Carlo
./v-star montecarlo --paths=100000 --steps=10
# Start HTTP server
./v-star serve
```
---
## Code Examples
### For Actuarial Students & Professionals
You already know the math:
```go
package main
import (
"fmt"
"log"
"github.com/lubasinkal/v-star/pkg/annuities"
"github.com/lubasinkal/v-star/pkg/mortality"
"github.com/lubasinkal/v-star/pkg/rates"
"github.com/lubasinkal/v-star/pkg/reserves"
)
func main() {
// Present value — like Excel's =PV(0.05, 20, 0, -100000)
converter := rates.NewRateConverter(0.05)
pv := converter.PresentValue(100000, 20)
fmt.Printf("PV: %.2f\n", pv) // 37,688.95
// Build a mortality table inline
qx := []float64{0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10}
mort := mortality.NewTable("example", qx)
// Annuity with mortality table
ann := annuities.NewAnnuityCalculator(converter, mort)
pv = ann.WholeLifeImmediate(65, 1000)
fmt.Printf("Annuity PV: %.2f\n", pv)
// Reserve calculation
policy := reserves.PolicySpec{Age: 50, Term: 20, SumAssured: 100000}
reserve := reserves.NetPremiumReserve(policy, converter, mort)
fmt.Printf("Reserve: %.2f\n", reserve)
// Multiple decrements (death + lapse combined)
death := mortality.NewTable("death", []float64{0, 0.01, 0.02})
lapse := mortality.NewTable("lapse", []float64{0, 0.05, 0.10})
dt := mortality.NewDecrementTable([]*mortality.Table{death, lapse}, nil)
fmt.Printf("Total qx at age 1: %.4f\n", dt.Qx(1))
fmt.Printf("Death cause qx at age 1: %.4f\n", dt.QxByCause(1, 0))
}
```
### For Developers
```go
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/lubasinkal/v-star/pkg/concurrency"
"github.com/lubasinkal/v-star/pkg/rates"
"github.com/lubasinkal/v-star/pkg/reader"
"github.com/lubasinkal/v-star/pkg/risk"
"github.com/lubasinkal/v-star/pkg/stochastic"
"github.com/lubasinkal/v-star/pkg/writer"
)
func main() {
converter := rates.NewRateConverter(0.05)
// Vasicek mean-reverting rates (instead of GBM)
vg := stochastic.NewVasicekGenerator(0.05, 0.04, 0.5, 0.02)
path := vg.GeneratePath(10, 1.0)
fmt.Println("Vasicek final rate:", path[10])
// Monte Carlo + VaR with confidence intervals
rg := stochastic.NewRateGeneratorWithSeed(0.05, 0.02, 0.15, 42)
paths := rg.GeneratePaths(100000, 10, 1.0)
losses := make([]float64, len(paths))
for i, p := range paths {
losses[i] = 1000000 * (0.05/p[10] - 1)
if losses[i] < 0 {
losses[i] = 0
}
}
report := risk.ComputeReport(losses)
fmt.Printf("VaR 95%%: %.2f, CTE 95%%: %.2f\n", report.VaR95, report.CTE95)
// Stream census records (simulating from memory)
records := []reader.CensusRecord{
{Age: 30, Sex: "M", PolicyType: "term", SumAssured: 100000, Term: 20},
{Age: 45, Sex: "F", PolicyType: "whole", SumAssured: 200000, Term: 15},
}
// Generic parallel worker pool with context cancellation
wp := concurrency.NewWorkerPool(4, func(r reader.CensusRecord) float64 {
return converter.PresentValue(r.SumAssured, r.Term)
})
totalPV := wp.ProcessBatch(records)
fmt.Printf("Total PV: %.2f\n", totalPV)
ctx := context.Background()
result, err := wp.ProcessBatchContext(ctx, records)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Context result: %.2f\n", result)
// Monte Carlo parallel — concrete types satisfy PathGenerator implicitly
gen := stochastic.NewRateGenerator(0.05, 0.02, 0.15)
paths = gen.GeneratePathsParallel(100000, 10, 0, 1.0)
fmt.Printf("Generated %d paths in parallel\n", len(paths))
// Pick output format at runtime
w := writer.NewRecordWriter(os.Stdout, "json")
defer w.Close()
w.WriteRecord(writer.Record{
Age: 30, Sex: "M", SumAssured: 100000, Term: 20, PresentValue: 37688.95,
})
}
```
### CSV Reader — Which one to use
| Function | When to use |
|----------|-------------|
| `StreamCensus` | Process an actuarial census CSV row by row (auto-detects columns); accumulate your own metrics |
| `StreamCensusChunked` | Batch processing (database inserts, API calls) |
| `StreamCensusFromReader` | Stream census data from any `io.Reader` (stdin, HTTP body, in-memory) |
| `StreamCSV` | Generic CSV with string fields (non-standard column layout) |
| `StreamCSVRaw` | Generic CSV with zero-allocation byte slices |
| `GetHeaders` | Inspect column headers before deciding parsing strategy |
**All standard library. Zero external dependencies.**
---
## CLI
```bash
# Calculate discount factors
./v-star -i 0.05 -j 0.02
# Process a policy CSV
./v-star read policies.csv --benchmark
./v-star read policies.csv --table=mortality.csv --output=json
./v-star read policies.csv --output=csv --limit=10000
./v-star read policies.csv --output=report
./v-star read policies.csv --interest=0.04 --header=true
# Run Monte Carlo
./v-star montecarlo --paths=100000 --steps=10 --seed=42
./v-star montecarlo --paths=1000000 --steps=180 --workers=8 --drift=0.03 --volatility=0.20
# Run benchmark suite
./v-star bench
# Start HTTP server
./v-star serve --port=8080
```
---
## HTTP API
Start the server:
```bash
./v-star serve --port=8080
```
All endpoints return JSON. The server includes CORS (cross-origin), request logging, and graceful shutdown on Ctrl+C.
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/health` | GET | Health check |
| `/value` | POST | Present value calculation (batch records) |
| `/simulate` | POST | Stochastic simulation — GBM or Vasicek + risk metrics (VaR, CTE) |
| `/annuity` | POST | Life-contingent annuity and net single premium calculations |
| `/reserve` | POST | Policy reserve calculations (net/gross/premium/prospective/retrospective) |
### Examples
```bash
# Present value
curl -s -X POST http://localhost:8080/value \
-H "Content-Type: application/json" \
-d '{"interest_rate":0.05,"records":[{"sum_assured":100000,"term":20}]}'
# Monte Carlo
curl -s -X POST http://localhost:8080/simulate \
-H "Content-Type: application/json" \
-d '{"num_paths":10000,"steps":10,"initial_rate":0.05,"drift":0.02,"volatility":0.15}'
# Annuity
curl -s -X POST http://localhost:8080/annuity \
-H "Content-Type: application/json" \
-d '{"interest_rate":0.05,"qxs":[0.001],"age":30,"amount":1000,"computation":"whole_life_immediate"}'
# Reserve
curl -s -X POST http://localhost:8080/reserve \
-H "Content-Type: application/json" \
-d '{"interest_rate":0.05,"qxs":[0.001],"age":30,"term":20,"sum_assured":100000,"method":"net_premium"}'
```
### Python
```python
import requests
# Present value
resp = requests.post("http://localhost:8080/value", json={
"interest_rate": 0.05,
"records": [{"sum_assured": 100000, "term": 20}]
})
print(resp.json())
# Monte Carlo
resp = requests.post("http://localhost:8080/simulate", json={
"num_paths": 100000, "steps": 10,
"initial_rate": 0.05, "drift": 0.02, "volatility": 0.15
})
print(resp.json()) # {"var_95": ..., "cte_95": ...}
```
**v-star's HTTP API works from any language** — Python, R, JavaScript,
TypeScript, Excel VBA, Julia, Rust, etc. See
[examples/api-clients/](./examples/api-clients/) for full examples in
Python, R, JavaScript, TypeScript, and cURL.
---
## Why Go?
- **Speed** — Compiles to native code, no interpreter overhead. 380M PV calculations/sec
- **Zero deps** — Standard library only. No pip, no npm, no version conflicts
- **Readable** — Every formula is right there in the source. Audit-friendly
- **Concurrent** — Goroutines make parallelism trivial
- **Portable** — One binary, runs anywhere
---
## Who's It For?
| Person | Why v-star |
|--------|------------|
| **Actuarial student** | Learn by reading the code. Fast calculations for assignments. |
| **Actuary** | Replace slow Excel/VBA. Process big censuses in seconds. |
| **Analyst** | Stream big CSVs without crashing. Get results, not errors. |
| **Developer** | Build insurance/risk tools via HTTP API from any language. |
| **Risk manager** | Run Monte Carlo + VaR in production. Fast. |
---
## What's Coming Next?
- **v0.8.0** — Reserve methods, CensusSource interface, profit testing, Dockerfile, API freeze
- **v1.0.0** — Stable API, 90%+ test coverage,
Full roadmap: [ROADMAP.md](./ROADMAP.md)
---
## Contributing
PRs welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md). Found a bug? [Open an issue](https://github.com/lubasinkal/v-star/issues).
---
## License
MIT — do whatever you want with it. See [LICENSE](./LICENSE).