PostgreSQL extension for financial calculations.
Current version: 1.2.0 Tested with: PostgreSQL 10-17
xirr(amount float8, date timestamptz [, guess float8]) → float8
Aggregate function similar to Excel/LibreOffice XIRR. Uses Newton's method to find the rate where NPV equals zero.
twr(amount float8, portfolio_value float8, date timestamptz) → float8
Time-weighted return calculation. Requires cashflow amount, portfolio value, and timestamp for each period.
npv(rate float8, amount float8, date timestamptz) → float8
Discounts future cashflows to present value: NPV = Σ(amount / (1 + rate)^years)
make
sudo make installCREATE EXTENSION financial;Basic usage:
SELECT xirr(amount, transaction_date ORDER BY transaction_date)
FROM transactions;Multiple portfolios:
SELECT portfolio_id, xirr(amount, date ORDER BY date)
FROM transactions
GROUP BY portfolio_id;With initial guess:
SELECT xirr(amount, date, 0.1 ORDER BY date)
FROM transactions;Basic usage:
SELECT twr(cashflow, portfolio_value, date ORDER BY date)
FROM portfolio_history;Window function:
SELECT date,
twr(cashflow, portfolio_value, date) OVER (ORDER BY date) as cumulative_twr
FROM portfolio_history;Basic usage:
SELECT npv(0.05::double precision, cashflow, date ORDER BY date)
FROM project_cashflows;Multiple projects:
SELECT project_id, npv(0.08::double precision, amount, date ORDER BY date)
FROM cashflows
GROUP BY project_id;Sorted data optimization:
- XIRR: 20-40% faster when data is sorted by timestamp
- NPV: 30-40% faster when data is sorted by timestamp
- Automatically detected and applied
Adaptive memory allocation:
- Dynamic growth factor based on dataset size
- Reduces memory waste by up to 97% for large datasets
All aggregates support parallel query execution:
SET max_parallel_workers_per_gather = 4;
SELECT portfolio_id, xirr(amount, date ORDER BY date)
FROM transactions
GROUP BY portfolio_id;
-- Automatically parallelizes across portfoliosPerformance impact:
- 2-4x throughput improvement for queries with many groups
- Best with 500+ portfolios and 500+ rows per portfolio
For 1000 portfolios with 100 transactions each:
- v1.0.3 baseline: 58ms
- v1.2.0 parallel + sorted: 18-25ms
- 3-5x overall improvement
- Data is sorted by timestamp: Automatically uses optimized calculation path
- Data is unsorted: Falls back to standard calculation (no performance penalty)
- Memory usage: 16 bytes per row for XIRR, 24 bytes per row for TWR
- Requires portfolio value at each cashflow date
- Cashflow can be zero for periods without transactions
- For partial periods: Include initial row with cashflow=0 and starting portfolio value
XIRR convergence:
- Newton's method may not converge for some inputs
- Returns NULL if no solution found after 50 iterations
- Try different initial guess if needed
Memory constraints:
- All aggregate data held in memory during calculation
- For very large datasets (10M+ rows), monitor memory usage
-- Rolling XIRR
SELECT date,
xirr(amount, date) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM transactions;WITH portfolio_flows AS (
SELECT transaction_date,
CASE
WHEN type IN ('buy', 'contribution') THEN -amount
WHEN type IN ('sell', 'withdrawal') THEN amount
END as cashflow
FROM transactions
UNION ALL
SELECT CURRENT_TIMESTAMP, current_portfolio_value
FROM portfolio_value
)
SELECT xirr(cashflow, transaction_date ORDER BY transaction_date)
FROM portfolio_flows;- Ensure data is sorted:
SELECT xirr(amount, date ORDER BY date) ... -- Explicit ORDER BY- Use appropriate indexes:
CREATE INDEX ON transactions (portfolio_id, date);- Enable parallel execution:
SET max_parallel_workers_per_gather = 4;
SET work_mem = '256MB'; -- For large aggregations- Monitor optimization path (debug mode):
SET client_min_messages = 'debug1';
SELECT xirr(amount, date ORDER BY date) FROM transactions;
-- Check logs for "sorted=1" and "optimized path"ALTER EXTENSION financial UPDATE TO '1.2.0';All upgrades are backward compatible. Existing queries work without modification.
- 1.2.0 (2026-01-23): Incremental sortedness tracking, NPV sorted optimization
- 1.1.0 (2026-01-23): Parallel query support for all aggregates
- 1.0.3 (2026-01-23): Sorted data optimization, adaptive memory allocation
- 1.0.2: TWR and XIRR aggregates
See CHANGELOG.md for detailed release notes.
make installcheckRuns regression tests for all functions.
See LICENSE for details.