Modified Dietz Return¶
1. Calculation Name¶
Modified Dietz Money-Weighted Return
2. Description and Mathematical Formula¶
The modified Dietz method measures the money-weighted return over a period while accounting for the timing and size of external flows. Each flow is weighted by the proportion of the period that capital is invested.
Let:
- \( B \) = beginning market value
- \( E \) = ending market value
- \( F_j \) = external flow \( j \) occurring on day \( d_j \) (where \( d_j = 0 \) is the period start)
- \( T \) = total number of days in the period
- \( w_j = \frac{T - d_j}{T} \) = weight applied to flow \( j \)
The modified Dietz return \( R_{\text{MD}} \) is
\[ R_{\text{MD}} = \frac{E - B - \sum_j F_j}{B + \sum_j w_j F_j} \]
3. Input Sample Data¶
| Event | Date | Cash Amount (USD) | Notes |
|---|---|---|---|
| Beginning Market Value | 2024-01-01 | 1,000,000 | Portfolio opening balance |
| Contribution | 2024-01-05 | 50,000 | Client top-up (day 4) |
| Withdrawal | 2024-01-15 | -20,000 | Mid-month rebalance (day 14) |
| Contribution | 2024-01-25 | 10,000 | Income sweep (day 24) |
| Ending Market Value | 2024-01-31 | 1,080,000 | Valuation after fees |
4. Mathematical Solution¶
- Total flows: \( 50{,}000 - 20{,}000 + 10{,}000 = 40{,}000 \)
- Weighted flows:
\( 50{,}000 \times \frac{26}{30} = 43{,}333.33 \)
\( -20{,}000 \times \frac{16}{30} = -10{,}666.67 \)
\( 10{,}000 \times \frac{6}{30} = 2{,}000.00 \)
Sum of weighted flows \( = 34{,}666.67 \) - Numerator: \( 1{,}080{,}000 - 1{,}000{,}000 - 40{,}000 = 40{,}000 \)
- Denominator: \( 1{,}000{,}000 + 34{,}666.67 = 1{,}034{,}666.67 \)
- Return: \( R_{\text{MD}} = \frac{40{,}000}{1{,}034{,}666.67} = 0.03866 \) → 3.87%
5. Sample Python and R Code¶
import pandas as pd
data = pd.DataFrame(
{
"event": ["BOP", "Flow", "Flow", "Flow", "EOP"],
"date": pd.to_datetime(
["2024-01-01", "2024-01-05", "2024-01-15", "2024-01-25", "2024-01-31"]
),
"amount": [1_000_000, 50_000, -20_000, 10_000, 1_080_000],
}
)
start = data.loc[0, "date"]
end = data.loc[len(data) - 1, "date"]
period_days = (end - start).days
flows = data.iloc[1:-1].copy()
flows["days"] = (flows["date"] - start).dt.days
flows["weight"] = (period_days - flows["days"]) / period_days
B = data.loc[data["event"] == "BOP", "amount"].iloc[0]
E = data.loc[data["event"] == "EOP", "amount"].iloc[0]
weighted_flows = (flows["amount"] * flows["weight"]).sum()
net_flows = flows["amount"].sum()
return_md = (E - B - net_flows) / (B + weighted_flows)
print(f"Modified Dietz return: {return_md:.4%}")
library(dplyr)
library(lubridate)
data <- tibble::tibble(
event = c("BOP", "Flow", "Flow", "Flow", "EOP"),
date = as_date(c("2024-01-01", "2024-01-05", "2024-01-15", "2024-01-25", "2024-01-31")),
amount = c(1000000, 50000, -20000, 10000, 1080000)
)
start <- data$date[1]
end <- data$date[n()]
period_days <- as.numeric(end - start)
flows <- data %>%
slice(2:(n() - 1)) %>%
mutate(
days = as.numeric(date - start),
weight = (period_days - days) / period_days
)
B <- data$amount[1]
E <- data$amount[n()]
weighted_flows <- sum(flows$amount * flows$weight)
net_flows <- sum(flows$amount)
return_md <- (E - B - net_flows) / (B + weighted_flows)
scales::percent(return_md, accuracy = 0.01)
6. Output Table¶
| Metric | Value |
|---|---|
| Modified Dietz Return | 3.87% |
| Net External Flows | 40,000 |
| Weighted Capital Base | 1,034,666.67 |
7. Conclusion¶
The modified Dietz template captures capital-weighted performance while respecting cash flow timing. Use this structure when you need a quick sanity check against custodial money-weighted returns or when documenting validation test cases in FinFacts.