Time-weighted vs money-weighted returns from a transaction log
Compute time-weighted and money-weighted returns for each account and the household over multiple horizons from a transaction log with contributions, withdrawals, fees and dividends.
instruction.md
What the agent sees (CONVENTIONS.md is appended automatically)
TWR vs MWR
From /app/portfolio/transactions.csv and prices, reconstruct daily end-of-day account values (positions x close + cash). For 1Y, 3Y, 5Y and since-inception compute per account and for the household:
- TWR: daily sub-periods with external flows at start of day, r_t = V_t / (V_{t-1} + F_t) - 1, geometrically linked, annualised with actual/365.25 for horizons of one year or more.
- MWR: XIRR with actual/365 day count, solved to 1e-10 by bracketing plus Brent. Only external flows (contributions, withdrawals) are cash flows; dividends and fees are internal.
Write /app/output/returns.json with the numbers and, per horizon, preferred_measure ("twr" or "mwr") and reason. The preferred measure is mwr only when discretionary external flows in the horizon exceed 20% of the starting value.
Verification
reward.json metrics · weights sum to 1.00
| Metric | Weight | Check |
|---|---|---|
| twr | 0.4 | rel 1e-6 vs oracle. |
| mwr | 0.4 | abs 1e-6 vs oracle. |
| preferred | 0.2 | preferred_measure exact for every (account, horizon). |
Harbor scaffold
Generated from this record — task.toml, Dockerfile, verifier, oracle stub
schema_version = "1.4" [task] name = "portfolio-agent-evals/pf-analyze-twr-vs-mwr" version = "1.0.0" description = "Compute time-weighted and money-weighted returns for each account and the household over multiple horizons from a transaction log with contributions, withdrawals, fees and dividends." keywords = ["etf", "portfolio", "analyze", "portfolio-analytics", "performance-measurement", "cashflow-handling", "root-finding"] [metadata] author_name = "portfolio-agent-evals" difficulty = "medium" category = "quant-finance" tags = ["portfolio-analytics", "tier-2", "analyze", "multi-metric"] theme = "Portfolio Analytics & Exposure" tier = 2 reward_type = "multi-metric" [agent] timeout_sec = 1800.0 [verifier] timeout_sec = 180.0 [environment] # Offline by design: all data is synthetic and generated at build time. network_mode = "none" cpus = 2 memory_mb = 4096 storage_mb = 10240 build_timeout_sec = 900.0
Traps
Each must carry signal: a trap-blind solution must lose credit
- A contribution lands on a day where one ticker has no print: value using the last available close.
- Fees are internal (reduce value, not a flow); withholding lines are internal too.
- A withdrawal on the horizon start date belongs to the horizon.
- One account's XIRR has multiple sign changes — naive Newton diverges; bracketing is required.
Inputs
Fixtures mounted in the environment
- /app/portfolio/transactions.csv, /app/portfolio/accounts.csv
- /app/data/prices/
Outputs
What the verifier reads from /app/output
- /app/output/returns.jsonJSONTWR, MWR and preferred measure per account and horizon.
Anti-gaming
Flow schedule and the awkward XIRR case are seed-generated.
Oracle notes
solution/solve.sh must score 1.0 on five seeds
Oracle in numpy with scipy.optimize.brentq on a bracket found by sign scan on [-0.99, 10].
Reviews (0)
Design review before a task is marked ready