Portfolio Agent EvalsHarbor task suite · ETF analyze → backtest → rebalance
Portfolio Analytics & Exposure/pf-analyze-twr-vs-mwr

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.

T2mediumAnalyzemulti-metricready
Edit
readystatic
Agent budget
30 min
Verifier budget
3 min
Tier target
60–80% pass expected

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

MetricWeightCheck
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

Outputs

What the verifier reads from /app/output

  • /app/output/returns.json
    JSON
    TWR, 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

    Reviews are read-only in static export.
    Tier 2 · Practitioner60–80% pass expected. Created 2026-01-01, updated 2026-01-01.