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

python
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

text
fee = (annual_bps / 10000) * (days_in_period / 365.25) * avg_equity_in_period

Three averaging bases:

average_basisComputes
"daily" (default)Time-weighted average (trapezoidal integration). Matches custodian statements.
"end_of_period"Ending equity. Simplest.
"start_of_period"Opening equity. Uncommon.
python
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

text
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:

python
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:

python
# 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. TieredManagementFeeConfig lands 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.