Returns Initiative
Goal: Eliminate return tracking error between Sable and BTIG, and provide configurable calculation methods for administrators.
Why This Matters
Returns are the primary performance metric for investors. When Sable and BTIG report different returns for the same fund, it creates:
- Confusion during investor reporting
- Manual reconciliation work each month
- Risk of reporting incorrect performance
The Problem
Sable return calculations differ from BTIG during months with large cash inflows/outflows. The tracking error is stable otherwise but material when cash moves.
Root Cause
Different return calculation methodologies:
| System | Method | Cash Flow Handling |
|---|---|---|
| BTIG | Daily TWR + Modified Dietz | Time-weighted within period |
| Sable (current) | Simple flat capital | Deferred to next month |
See Modified Dietz Method for detailed methodology comparison.
Solution
Make return calculation method configurable by administrator:
- Method 1: Simple flat capital ("AIC way") - current behavior
- Method 2: Modified Dietz method ("BTIG way") - matches broker
Success Criteria
| Criterion | Target | Status |
|---|---|---|
| Monthly return difference ≤ 10 bps | All funds | Pending |
| Admin toggle for calculation method | Per fund/org | Pending |
| Auto-reconciliation flags discrepancies > 10 bps | Automated | Pending |
| Documentation for both methods | Complete | ✓ |
Current Problems
- Cash flow timing: BTIG and Sable may record cash flows on different dates
- Weighting differences: Exact weight calculation may vary slightly
- NAV computation: Sable computes NAV from P&L rollup; BTIG has their own source
- Missing data: Some entities/months lack complete data
Approach: Loss Function Minimization
Rather than manually debugging each discrepancy, we use an iterative optimization approach:
Loss Function
L = Σ (sable_return - btig_return)²
Sum of squared monthly return differences across all funds/months.
Workflow
# 1. Compute current loss
sable returns loss --account AV7K
# 2. Identify largest contributors
sable returns loss --account AV7K --breakdown
# 3. Hypothesize fix (e.g., adjust cash flow timing)
# 4. Implement fix in sable-cli or dbt model
# 5. Re-run loss function
# 6. Repeat until L converges to acceptable threshold
Target
- L < 0.0001 (less than 1 bp² average per month)
- No individual month > 10 bps difference
This treats returns reconciliation as a convergence problem rather than a debugging exercise. Each iteration of sable-cli improvements should reduce L.
Current Status
As of January 2026:
- Modified Dietz implemented in
gold.v_dietz_daily - Returns comparison API available at
/api/returns/comparison - Loss function tooling: not yet built
- Admin configuration UI: not yet built
Implementation Roadmap
Epic 1: sable-cli Returns Loss Function
The foundation for the iterative convergence approach. All CLI commands live in sable-cli.
| Ticket | Description | Priority |
|---|---|---|
sable returns loss | Compute L = Σ(sable - btig)² for account/fund | High |
sable returns loss --breakdown | Show top contributors to loss by month/entity | High |
sable returns compare | Side-by-side comparison table (Sable vs BTIG) | High |
| Integration tests | Test loss function with real BTIG data | Medium |
CLI Usage:
# Compute total loss for an account
sable returns loss --account AV7K
# Output: L = 0.000342 (3.42 bp² total)
# Break down by month to find worst offenders
sable returns loss --account AV7K --breakdown
# Output:
# Month | Sable | BTIG | Diff | Contribution
# 2025-03 | 2.34% | 2.12% | 22 bps | 48.4%
# 2025-07 | -1.21% | -1.08% | 13 bps | 16.9%
# ...
# Compare returns side-by-side
sable returns compare --account AV7K --year 2025
Epic 2: Returns Method Configuration
Admin toggle for calculation method per fund/org.
| Ticket | Description | Priority |
|---|---|---|
| Database schema | org_settings.return_method enum (simple_flat, modified_dietz) | High |
| Admin UI | Settings page to toggle method per fund/org | Medium |
| Apply method in calculations | Use selected method in P&L/returns views | High |
| Migration path | Handle existing data when method changes | Medium |
Schema:
-- org_settings table
ALTER TABLE sable.org ADD COLUMN return_method TEXT
DEFAULT 'simple_flat'
CHECK (return_method IN ('simple_flat', 'modified_dietz'));
-- Or per-entity if needed
ALTER TABLE sable.entity ADD COLUMN return_method TEXT;
Epic 3: Automated Reconciliation
Scheduled flagging of discrepancies for review.
| Ticket | Description | Priority |
|---|---|---|
| Scheduled comparison job | Daily/weekly cron to compare Sable vs BTIG | Medium |
| Discrepancy flagging | Flag months with diff > 10 bps | High |
| Dashboard widget | Show flagged items requiring review | Medium |
| Alert notifications | Email/Slack when new discrepancies detected | Low |
Workflow:
Daily job runs → Compares all funds →
≤ 10 bps: Auto-approve ✓
> 10 bps: Create review ticket → Notify admin
Backlog (Existing)
| Ticket | Type | Description |
|---|---|---|
| Validate 10 bps threshold | Spike | Confirm 10 bps is the right acceptance threshold |
| 2025 YTD comparison report | Task | Generate full year comparison for all funds |
Key Metrics
Returns within:
- ≤ 10 bps → Auto-approve (match)
- 10-50 bps → Review required
- > 50 bps → Investigate
Documentation
- Modified Dietz Method - Calculation methodology comparison
- Troubleshooting - Debugging discrepancies
Related
- BTIG Integration - Data pipeline from BTIG
- Initiative ID:
c11848bc-ac6b-4a15-afa7-f6cc89cf5553