Management and performance fees
Accrue AUM-based management fees and carry above a hurdle, with high-water-mark enforcement.
horizon.accounting.fees computes the two fees a typical advisor or fund charges:
- Management fee. A percentage of AUM, pro-rated over the period. Common: 50 to 150 bps/year for advisors, 1 to 2% for hedge funds. Billed monthly or quarterly.
- Performance fee (carry). A percentage of profit above a hurdle, respecting a high-water mark so the client never pays for the same gain twice. Only qualified clients under SEC Rule 205-3 may be charged performance fees by a U.S. RIA. The module does not enforce eligibility; the firm is responsible for that.
Pure math. Takes equity samples plus config and returns a FeeAssessment. No ledger mutation, no hot-path impact, no opinions on billing cycles. The caller decides when to accrue.
Account configuration lives on the Account type. See Accounts.
Quickest start
from horizon.accounting import accrue_period_fees, HighWaterMark
fees = accrue_period_fees(
account=jane_account, # advisory_fee_bps_per_year=100, performance_fee_pct=0.20
equity_samples=q1_equity_samples,
period_start=datetime(2026, 1, 1, tzinfo=UTC),
period_end=datetime(2026, 3, 31, tzinfo=UTC),
high_water_mark=jane_hwm, # persists across quarters
)
for f in fees:
print(f"{f.kind}: ${f.amount:,.2f} (basis ${f.basis_value:,.2f})")
Management fee
fee = (annual_bps / 10000) * (days_in_period / 365.25) * avg_equity_in_period
Three averaging bases:
average_basis | Computes |
|---|---|
"daily" (default) | Time-weighted average (trapezoidal integration). Matches custodian statements. |
"end_of_period" | Ending equity. Simplest. |
"start_of_period" | Opening equity. Uncommon. |
from horizon.accounting import accrue_management_fee, ManagementFeeConfig
fee = accrue_management_fee(
account=account, # advisory_fee_bps_per_year=100
equity_samples=samples,
period_start=q1_start, period_end=q1_end,
config=ManagementFeeConfig(
annual_bps=100,
average_basis="daily",
minimum_fee_usd=0.0, # optional floor per billing period
),
)
# ~$243.67 for ~89 days on 100k AUM
Performance fee
baseline = HWM (if enabled and > 0) else period_start_equity
hurdle = baseline * (1 + hurdle_annual * days/365.25)
fee_base = max(0, end_equity - hurdle)
fee = carry_pct * fee_base
High-water mark updates in place. Pass a HighWaterMark that persists across periods:
hwm = HighWaterMark()
# Q1: carry applies
fees_q1 = accrue_performance_fee(
account=account, equity_samples=q1_samples,
period_start=q1_start, period_end=q1_end,
high_water_mark=hwm,
)
# hwm.value reflects post-fee end value
# Q2: below HWM, no carry
fees_q2 = accrue_performance_fee(
account=account, equity_samples=q2_samples,
period_start=q2_start, period_end=q2_end,
high_water_mark=hwm,
)
# fees_q2.amount == 0.0
# Q3: recovers and exceeds HWM, carry on the excess only
fees_q3 = accrue_performance_fee(..., high_water_mark=hwm)
Hard vs. soft hurdle
- Hard (default). Fee applies only to the portion above the hurdle. A fund returning exactly the hurdle collects no carry.
- Soft. Once the hurdle is breached, fee applies to the entire gain above baseline.
Toggle with PerformanceFeeConfig(hurdle_is_hard=True).
HWM toggle
performance_fee_high_water_mark=True on the Account is standard. False makes each period standalone (fresh from period-start equity). Uncommon in advisor practice because it can charge the same dollar of gain multiple times.
Integration
Fee assessments are data. Typical uses:
# Add to a statement's cashflow list
from horizon.reporting import Cashflow
fee_cf = Cashflow(
timestamp=fee.period_end,
amount=-fee.amount,
kind="fee",
)
# Record for compliance
audit_log.record(
AuditCategory.FeeAssessed,
account_id=fee.account_id,
message=f"{fee.kind} fee: ${fee.amount:.2f}",
payload=fee.to_dict(),
)
# Track in a billing store
billing_ledger.record(fee)
A dedicated billing / fee-ledger store lands in L2. This module is the math.
Out of scope
- Invoicing or ACH debit. Requires a custodian integration or fee-collection partner.
- Tiered fee schedules. One bps rate per account. Compute each tier separately and sum.
TieredManagementFeeConfiglands in L2. - Fee netting across accounts. Household-level billing requires aggregation the module does not impose.
- Retroactive recalculation. Change the rate mid-period and rerun against the affected window.
- Clawback. Some funds claw back previously earned carry when subsequent losses push past the HWM. Not shipped.
- Eligibility enforcement. SEC Rule 205-3 qualified-client check is the firm’s responsibility.