Epic 4.8: Entity-Level Comparison
Status: ✅ Complete (January 15, 2026)
Overview
Five critical fixes deployed to achieve near-perfect return matching:
- Capital denominator fix: Changed from prior month-end NAV to prior day NAV + same-day cash flow
- Multi-account aggregation fix: Changed from incorrect geometric linking to market-value weighted average
- Account mapping fix: Now includes both
entity.btig_account_codeand allaccount.account_codevalues - Returns start date filter: Added daily-level filtering to exclude data before entity's valid return period
- BTIG/Cash aggregation fix: Fixed
DISTINCT ONbug and cash transaction duplication
Final Results
Anthracite Holdings LP:
| Month | Before | After |
|---|---|---|
| Jan 2023 | +1426 bps | -17 bps (review) ✓ |
| Feb 2023 | -192 bps | 2 bps (match) ✓ |
| Dec 2025 | -82 bps | -3 bps (match) ✓ |
ARP (Anthracite Realty Partners):
| Month | Before | After |
|---|---|---|
| Jan 2023 | +2812 bps | 0 bps (match) ✓ |
| Feb 2023 | N/A | 0 bps (match) ✓ |
| Mar 2023 | N/A | 0 bps (match) ✓ |
ARP overall: 35/37 months matching (94.6%)
Daily returns now match exactly where both systems have data (0 bps diff on trading days).
Root Cause: Granularity Mismatch
The Problem: We were comparing at the wrong level.
| Aspect | Account-Level | Entity-Level |
|---|---|---|
| Match rate | 34.7% | ~95% |
| Investigate cases | Many | 0 |
| Internal transfers | Show as cash flows | Cancel out |
| BTIG reporting | Not used | ✅ Matches |
Example: Anthracite Holdings LP (Entity 2)
Entity 2 has two accounts: AV7K and XPB006152.
When $50M transfers from AV7K to XPB006152:
- Account-level: AV7K shows -$50M outflow, XPB006152 shows +$50M inflow
- Entity-level: Net zero cash flow (transfers cancel)
BTIG reports entity-level returns, so internal transfers don't affect their calculations.
Key Discovery: BTIG daily_ror Format
BTIG stores daily_ror as percentage, not decimal.
-- WRONG: Treats 1.4 as 140%
1 + daily_ror -- Gives impossible returns like +2371%
-- CORRECT: Treats 1.4 as 1.4%
1 + daily_ror/100 -- Gives reasonable returns
Results: Entity-Level Comparison (Final)
Summary Statistics
| Status | Result |
|---|---|
| Investigate cases | 0 (was 8) |
| Match rate | ~95% (was 69.4%) |
All Investigate Cases Resolved
| Entity | Month | Before | After | Fix |
|---|---|---|---|---|
| ARP | Jan 2023 | +2812 bps | 0 bps | returns_start_date filter |
| Anthracite Holdings LP | Jan 2023 | +1426 bps | -17 bps | Duplicate removal + returns_start_date |
| Anthracite Holdings LP | Feb 2023 | -192 bps | 2 bps | Capital denominator fix |
| Anthracite Holdings LP | Dec 2025 | -82 bps | -3 bps | BTIG aggregation + cash dedup |
Critical Fixes (January 15, 2026)
Fix 1: Capital Denominator
Problem: Using prior month-end NAV as denominator caused massive discrepancies when cash flows occurred mid-month.
BTIG Formula (verified):
daily_ror = (End MV - Prior MV - CF) / (Prior MV + CF)
Fix 2: Multi-Account Aggregation
Problem: Was geometric-linking daily returns from ALL accounts, which is mathematically incorrect.
Solution:
-- First: market-value weighted daily return per entity
SUM(account_mv * daily_ror) / SUM(account_mv) AS entity_daily_ror
-- Then: compound entity daily returns over month
EXP(SUM(LN(1 + entity_daily_ror/100))) - 1
Fix 3: Account Mapping
Problem: Only used entity.btig_account_code, missing additional accounts.
Solution:
entity_accounts AS (
SELECT entity_id, entity_name, LOWER(btig_account_code) AS account_code
FROM entity WHERE btig_account_code IS NOT NULL
UNION
SELECT e.entity_id, e.entity_name, LOWER(a.account_code)
FROM entity e JOIN account a ON e.entity_id = a.entity_id
)
Fix 4: Returns Start Date Filter
Problem: Erroneous year-end positions (e.g., ARP Dec 31 phantom $39M) polluted TWR calculations.
Solution: Filter daily data at returns_start_date:
WHERE pnl.pnl_date >= ei.returns_start_date
Fix 5: BTIG Aggregation & Cash Deduplication
Problem 1: DISTINCT ON only took one account's market value, missing xpb006152's $9M.
Problem 2: Multiple cash transactions per day caused row duplication via LEFT JOIN.
Solution: Aggregate BTIG data and cash transactions by entity/date with GROUP BY.
Success Criteria
| Criterion | Target | Current |
|---|---|---|
| Entity-level match rate | Greater than 80% | ✅ ~95% |
| Investigate cases | Less than 5 | ✅ 0 |
| View deployed | Yes | ✅ Complete |
| CLI updated | Yes | ⏳ Pending |
| All fixes deployed | Yes | ✅ Complete |
Tickets
| Ticket | Description | Priority | Status |
|---|---|---|---|
| - | Create v_returns_comparison_entity view | High | ✅ Complete |
| - | Fix Jan 2023 duplicate deposits (AV7K) | Critical | ✅ Fixed |
| - | Fix Jan 2023 duplicate REC (ARP) | Critical | ✅ Fixed |
77b39a3a | ARP Jan 2023 year-end P&L spike | High | ✅ Fixed |
9f7a0824 | Update sable-cli to use entity-level | Normal | Backlog |
Data Quality Fixes Applied
AV7K - Duplicate $15M Deposit
- Deleted: Jan 16, 2023 - $15M DEP (duplicate)
- Kept: Jan 17, 2023 - $15M DEP (matches BTIG perf)
ARP - Duplicate REC Transactions
- Deleted: Jan 19, 2023 - 55 REC transactions ($7.98M total)
- Kept: Jan 20, 2023 - 55 REC transactions (matches BTIG perf)
ARP - Returns Start Date Filter
- Set:
returns_start_date = '2023-01-20' - Reason: Erroneous phantom positions Dec 31, 2022 ($39.5M) vanish Jan 1
v_dietz_daily - Aggregation Fixes
- BTIG: Changed from
DISTINCT ONtoGROUP BYwith market-value weighted average - Cash: Added
GROUP BYto aggregate multiple transactions per day
Artifacts
- View:
gold.v_returns_comparison_entity- Entity-level returns comparison - View:
gold.v_dietz_daily- Daily returns with correct capital calculation - dbt Model:
sable-data/dbt/models/returns/v_returns_comparison_entity.sql - dbt Model:
sable-data/dbt/models/returns/v_dietz_daily.sql - Devlog: Fixed capital denominator in v_dietz_daily
- Devlog: Fixed entity-level BTIG aggregation
- Devlog: Fixed ARP Jan 2023 year-end P&L spike
- Devlog: Epic 4.8 Complete: ~90% match rate
- Devlog: Fixed BTIG aggregation and cash duplication bugs
Related
- Epic 4: Final Convergence - Parent epic
- Epic 4.7: Cash Flow Reconciliation - Previous epic
- Data Lineage - Data flow diagram
- Returns Overview - Initiative overview