PyXIRR

repository·main·Indexed 42 days ago

https://github.com/anexen/pyxirr

A high-performance, Rust-powered collection of financial functions for Python. It provides implementations for XIRR, NPV, FV, and IRR, supporting NumPy arrays and Pandas DataFrames. The library includes specialized private equity metrics such as PME+, MOIC, and Kaplan-Schoar PME, as well as support for various day count conventions (e.g., ACT/360, 30E/360) and numpy-like vectorization.

Tokens
13K
Snippets
66
Records
80
Agent score
41%

What's inside PyXIRR

  1. Calculate Long-Nickels PME

    main

    The Long-Nickels method calculates a Public Market Equivalent (PME) by assuming that a VC fund's cash flows (contributions and distributions) are invested in a stock market index to generate a Net Asset Value (NAV) at the end of each period. The final NAV is then used to calculate the Internal Rate of Return (IRR), which represents the Long-Nickels PME.

    To implement this, use the ln_pme_nav function provided by the library.

  2. Use numpy-like vectorization in PyXIRR

    main

    PyXIRR functions support vectorization. If you pass array-like inputs (such as lists or numpy arrays), the function returns a nested sequence of results matching the input shape.

    • If all inputs are scalars, a scalar float is returned.
    • If any input is array-like, a list (or a numpy array if the input was a numpy array) is returned.
    • Multiple array-like inputs must have the same shape or be broadcastable.
    from pyxirr import fv
    import numpy
    
    # Returns a list
    fv([0.03/12, 0.05/12], 10*12, -100, -100)
    
    # Returns a numpy array if input is a numpy array
    fv(numpy.array([0.03/12, 0.05/12]), 10*12, -100, -100)
  3. Understand Modified Public Market Equivalent (mPME)

    main
    mPME (Modified Public Market Equivalent) is a performance metric used to compare private equity fund performance against a public market index. Unlike PME+, which uses a single scaling factor ($\lambda$) for all distributions, mPME applies different scaling factors to cash flows at different time intervals. This approach aims to overcome the limitations of using a single coefficient for all distributions by accounting for time-varying market conditions.
  4. Handle the Multiple IRR problem

    main

    The Multiple IRR problem occurs when cash flows are non-conventional (the signs of cash flows change more than once). This can result in multiple possible internal rates of return or none at all.

    PyXIRR handles this by selecting the lowest IRR to remain conservative.

  5. Leverage Vectorization in PyXIRR

    main

    PyXIRR supports numpy-like vectorization. The return type depends on the input shape:

    • Scalar inputs: Returns a scalar float.
    • One array-like input: Returns values for each element in that array.
    • Multiple array-like inputs: Performs broadcasting and returns values for each element.
    • Multi-dimensional inputs: Supports 2D, 3D, 4D, etc., through broadcasting.
    import pyxirr
    import numpy as np
    
    # Vectorized FV with different rates
    rates = np.array([0.05, 0.06, 0.07])/12
    pyxirr.fv(rates, 10*12, -100, -100)
    
    # Multi-dimensional broadcasting
    rates = [[[[[0.01], [0.02]]]]]
    pyxirr.fv(rates, 10*12, -100, -100)
  6. Calculate Multiple on Invested Capital (MOIC)

    main

    Multiple on Invested Capital (MOIC) measures the performance of an investment relative to the initial investment.

    Formula: (Realized investment + Unrealized investment) / Initial Investment

    • Realized investment: The value of investments that have been liquidated.
    • Unrealized investment (Residual value/NAV): The total value of the remaining portfolio's active investments that have not yet been liquidated.
  7. Calculate λ using the PME+ method

    main

    The PME+ (Public Market Equivalent Plus) method can be used to find the multiplier $\lambda$ that accounts for the difference between scaled distributions/contributions and the Net Asset Value (NAV).

    Use the formula: $$\lambda = \frac{\text{Scaled Distributions} - \text{NAV}}{\text{Scaled Contributions}}$$

    Where:

    • Scaled Distributions = $\sum(\text{distributions} \times \frac{\text{index[last]}}{\text{index[current]}})$
    • Scaled Contributions = $\sum(\text{contributions} \times \frac{\text{index[last]}}{\text{index[current]}})$

    For implementation in pyxirr, refer to the pme_plus_flows function.

  8. Use the Long-Nickels method for PME re-calculation

    main

    The Long-Nickels method allows you to re-calculate the Net Asset Value (NAV) to match Public Market Equivalents (PME) for comparison purposes. Instead of using a provided NAV, this method calculates the NAV as the future value of contributions minus the future value of distributions, using a market index.

    For two periods containing both a contribution and a distribution, the formula is:

    nav = c[1] * index[-1]/index[1] + c[2] * index[-1]/index[2] - d[1] * index[-1]/index[1] - d[2] * index[-1]/index[2]

  9. Handle Multiple IRR problems

    main

    Non-conventional cash flows (where signs change more than once) can lead to multiple IRRs or no IRR. PyXIRR handles this by:

    1. Searching for a positive result around 0.1 (Excel default).
    2. If not found, using multiple attempts and selecting the lowest IRR to be conservative.

    You can identify and solve these problems using:

    • pyxirr.is_conventional_cash_flow(amounts): Returns True if the cash flow is conventional.
    • pyxirr.xnpv(rates, cash_flow): Calculates Net Present Value for multiple rates to build an NPV profile.
    • pyxirr.zero_crossing_points(values): Finds the indices where the NPV function crosses zero.
    • pyxirr.xirr(cash_flow, guess=rate): Allows providing a specific guess to find a particular IRR from the zero-crossing points.
    import pyxirr
    import numpy as np
    
    # Example workflow for non-conventional cash flows
    cf = ... # your cash flow data
    rates = np.linspace(-0.5, 0.5, 50)
    values = pyxirr.xnpv(rates, cf)
    
    indexes = pyxirr.zero_crossing_points(values)
    for idx in indexes:
        rate = pyxirr.xirr(cf, guess=rates[idx])
        print(f"Found IRR: {rate}")
  10. Calculate Kaplan-Schoar Public Market Equivalent (KS PME)

    main

    KS PME (Kaplan-Schoar Public Market Equivalent) is a market-adjusted metric used to compare private equity performance against a public market index. It calculates the ratio of the future value of distributions and Net Asset Value (NAV) to the future value of contributions, where future values are determined using stock market index returns.

    Formula: (FV(Distributions) + NAV) / FV(Contributions)

    To perform this calculation, use the ks_pme_flows function (or related functions depending on your data structure). For related performance metrics, see tvpi.

  11. Calculate PME+ (Public Market Equivalent Plus)

    main

    PME+ is a method used to compare fund performance against a public market index. It works by discounting every distribution by a factor computed so that the Net Asset Value (NAV) of the index investment matches the NAV of the fund. The result is an Internal Rate of Return (IRR) value of the discounted distributions.

    To implement PME+, you can use the following related functions:

    • pme_plus_flows: Likely used to prepare or handle the cash flow sequences.
    • pme_plus_lambda: Likely used to compute the specific discount factor (lambda) required for the calculation.