Portfolio Agent EvalsHarbor task suite · ETF analyze → backtest → rebalance
Tax-Aware & Constrained Optimization/pf-tax-lot-selection-wash-sale

Specific-ID lot selection minimising tax with cross-account wash-sale rules

Choose specific tax lots for a required sell list to minimise tax cost under short/long-term rates while avoiding wash-sale violations against recent and planned purchases across all accounts.

T3hardRebalancemulti-metricready
Edit
readystatic
Agent budget
40 min
Verifier budget
4 min
Tier target
25–50% pass expected

instruction.md

What the agent sees (CONVENTIONS.md is appended automatically)

Specific-ID lot selection with wash-sale rules

/app/input/required_sells.csv lists shares to sell per ticker in the taxable account. Using tax_lots.csv and tax_profile.yaml (short-term rate, long-term rate; holding period strictly greater than 365 days is long-term), choose lots to minimise total tax (gains taxed, losses credited at the applicable rate).

Wash-sale rule: a loss on a lot is disallowed if the same ticker or a substantially identical one (/app/policy/identical_pairs.csv) was bought within 30 days before the sale, or will be bought within 30 days after (planned buys in /app/input/planned_buys.csv and DRIP reinvestments in any account, including IRAs). Disallowed losses earn zero credit.

Write /app/output/lot_selection.csv (lot_id, shares_sold) and /app/output/tax_summary.json (st_gain, lt_gain, st_loss, lt_loss, disallowed_loss, total_tax).

Verification

reward.json metrics · weights sum to 1.00

MetricWeightCheck
total_tax
0.5
<= oracle + 0.01 USD.
wash_sale_violationsgate
0.3
No loss credited on a lot that triggers the rule. GATE at 0.2.
sums_consistent
0.2
Summary equals recomputation from lot_selection.

Gates: wash_sale_violations. A gate failure caps or zeroes the trial reward regardless of other metrics.

Harbor scaffold

Generated from this record — task.toml, Dockerfile, verifier, oracle stub

schema_version = "1.4"

[task]
name = "portfolio-agent-evals/pf-tax-lot-selection-wash-sale"
version = "1.0.0"
description = "Choose specific tax lots for a required sell list to minimise tax cost under short/long-term rates while avoiding wash-sale violations against recent and planned purchases across all accounts."
keywords = ["etf", "portfolio", "rebalance", "tax-and-optimization", "tax-lots", "wash-sale", "optimization"]

[metadata]
author_name = "portfolio-agent-evals"
difficulty = "hard"
category = "quant-finance"
tags = ["tax-and-optimization", "tier-3", "rebalance", "multi-metric"]
theme = "Tax-Aware & Constrained Optimization"
tier = 3
reward_type = "multi-metric"

[agent]
timeout_sec = 2400.0

[verifier]
timeout_sec = 240.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

  • An IRA DRIP purchase of an identical pair within the window disallows a taxable loss.
  • A lot acquired exactly 365 days ago is short-term.
  • Partial lots are allowed.
  • HIFO is not optimal when the highest-cost lot's loss would be disallowed.

Inputs

Fixtures mounted in the environment

  • /app/portfolio/tax_lots.csv, /app/portfolio/transactions.csv, /app/portfolio/accounts.csv
  • /app/policy/tax_profile.yaml, /app/policy/identical_pairs.csv
  • /app/data/prices/
  • /app/data/dividends.csv
    Needed to predict DRIP buys in the 30-day window.

Outputs

What the verifier reads from /app/output

  • /app/output/lot_selection.csv
    CSV
    Lots and shares sold.
  • /app/output/tax_summary.json
    JSON
    Tax breakdown.

Anti-gaming

Lot ages, DRIP timing and identical pairs are seeded.

Oracle notes

solution/solve.sh must score 1.0 on five seeds

MILP over lot fractions with binary disallowance indicators.

Reviews (0)

Design review before a task is marked ready

    Reviews are read-only in static export.
    Tier 3 · Expert25–50% pass expected. Created 2026-01-01, updated 2026-01-01.