Skip to content

Commit acbe6ca

Browse files
committed
update docs
1 parent 17b7e63 commit acbe6ca

File tree

2 files changed

+243
-39
lines changed

2 files changed

+243
-39
lines changed

README.md

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -61,63 +61,60 @@ xirr(['2020-01-01', '2021-01-01'], [-1000, 1200])
6161

6262
# Multiple IRR problem
6363

64-
The multiple internal rates of return problem occur when the signs of cash
65-
flows change more than once. In this case, we say that the project has
66-
non-conventional cash flows. This leads to situation, where it can have more
67-
the one (X)IRR or have no (X)IRR at all.
64+
The Multiple IRR problem occur when the signs of cash flows change more than
65+
once. In this case, we say that the project has non-conventional cash flows.
66+
This leads to situation, where it can have more the one IRR or have no IRR at all.
6867

69-
PyXIRR's approach to the Multiple IRR problem: select the lowest IRR to be conservative
68+
PyXIRR's approach to the Multiple IRR problem:
69+
70+
1. It looks for positive result around 0.1 (the same as Excel with the default guess=0.1).
71+
2. If it can't find a result, it uses several other attempts and selects the lowest IRR to be conservative.
72+
73+
Here is an example of how to find multiple IRRs:
7074

7175
```python
72-
import pandas as pd
76+
import numpy as np
7377
import pyxirr
7478

79+
# load cash flow:
7580
cf = pd.read_csv("tests/samples/30-22.csv", names=["date", "amount"])
7681
# check whether the cash flow is conventional:
7782
print(pyxirr.is_conventional_cash_flow(cf["amount"])) # false
7883

79-
r1 = pyxirr.xirr(cf)
80-
print("r1: ", r1) # -0.31540826742734207
81-
# check using NPV
82-
print("XNPV(r1): ", pyxirr.xnpv(r1, cf)) # -2.3283064365386963e-10
83-
84-
# the second root
85-
r2 = pyxirr.xirr(cf, guess=0.0) # -0.028668460065441048
86-
print("r2: ", r2)
87-
print("XNPV(r2): ", pyxirr.xnpv(r2, cf)) # 0.0
88-
89-
# print NPV profile
90-
import numpy as np
91-
84+
# build NPV profile:
85+
# calculate 50 NPV values for different rates
9286
rates = np.linspace(-0.5, 0.5, 50)
87+
# any iterable, any rates, e.g.
88+
# rates = [-0.5, -0.3, -0.1, 0.1, -0.6]
9389
values = pyxirr.xnpv(rates, cf)
94-
series = pd.Series(values, index=rates)
95-
96-
print("Zero crossing points:")
97-
for idx in pyxirr.zero_crossing_points(values):
98-
print(series.iloc[[idx, idx+1]])
9990

91+
# print NPV profile:
10092
# NPV changes sign two times:
10193
# 1) between -0.316 and -0.295
10294
# 2) between -0.03 and -0.01
103-
104-
print(series)
105-
# -0.500000 -8.962169e+08
106-
# ...
107-
# -0.336735 -5.895002e+05
108-
# -0.316327 -1.457451e+04
109-
# -0.295918 1.801890e+05
110-
# -0.275510 2.175858e+05
111-
# ...
112-
# -0.051020 2.612340e+03
113-
# -0.030612 1.857505e+02
114-
# -0.010204 -1.452180e+03
115-
# 0.010204 -2.533466e+03
116-
# ...
117-
# 0.500000 -2.358226e+03
95+
print("NPV profile:")
96+
for rate, value in zip(rates, values):
97+
print(rate, value)
11898

11999
# plot NPV profile
100+
import pandas as pd
101+
series = pd.Series(values, index=rates)
120102
pd.DataFrame(series[series > -1e6]).assign(zero=0).plot()
103+
104+
# find points where NPV function crosses zero
105+
indexes = pyxirr.zero_crossing_points(values)
106+
107+
print("Zero crossing points:")
108+
for idx in indexes:
109+
print("between", rates[idx], "and", rates[idx+1])
110+
111+
# XIRR has two results:
112+
# -0.31540826742734207
113+
# -0.028668460065441048
114+
for i, idx in enumerate(indexes, start=1):
115+
rate = pyxirr.xirr(cf, guess=rates[idx])
116+
npv = pyxirr.xnpv(rate, cf)
117+
print(f"{i}) {rate}; XNPV = {npv}")
121118
```
122119

123120
# More Examples

docs/private_equity.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,258 @@
44

55
{% include_relative _inline/pe/dpi.md %}
66

7+
```python
8+
def dpi(amounts: _AmountArray) -> float:
9+
...
10+
11+
12+
def dpi_2(
13+
contributions: _AmountArray,
14+
distributions: _AmountArray,
15+
) -> float:
16+
...
17+
```
18+
719
## RVPI
820

921
{% include_relative _inline/pe/rvpi.md %}
1022

23+
```python
24+
def rvpi(
25+
contributions: _AmountArray,
26+
nav: _Amount,
27+
) -> float:
28+
...
29+
```
30+
1131
## TVPI
1232

1333
{% include_relative _inline/pe/tvpi.md %}
1434

35+
```python
36+
def tvpi(
37+
amounts: _AmountArray,
38+
nav: _Amount = 0,
39+
) -> float:
40+
...
41+
42+
43+
def tvpi_2(
44+
contributions: _AmountArray,
45+
distributions: _AmountArray,
46+
nav: _Amount = 0,
47+
) -> float:
48+
...
49+
```
1550
## MOIC
1651

1752
{% include_relative _inline/pe/moic.md %}
1853

54+
```python
55+
def moic(
56+
amounts: _AmountArray,
57+
nav: _Amount = 0,
58+
) -> float:
59+
...
60+
61+
62+
def moic_2(
63+
contributions: _AmountArray,
64+
distributions: _AmountArray,
65+
nav: _Amount = 0,
66+
) -> float:
67+
...
68+
```
69+
1970
## LN-PME
2071

2172
{% include_relative _inline/pe/ln_pme.md %}
2273

74+
```python
75+
def ln_pme(
76+
amounts: _AmountArray,
77+
index: _AmountArray,
78+
) -> Optional[float]:
79+
...
80+
81+
82+
def ln_pme_2(
83+
contributions: _AmountArray,
84+
distributions: _AmountArray,
85+
index: _AmountArray,
86+
) -> Optional[float]:
87+
...
88+
```
89+
2390
## LN-PME NAV
2491

2592
{% include_relative _inline/pe/ln_pme_nav.md %}
2693

94+
```python
95+
def ln_pme_nav(
96+
amounts: _AmountArray,
97+
index: _AmountArray,
98+
) -> float:
99+
...
100+
101+
102+
def ln_pme_nav_2(
103+
contributions: _AmountArray,
104+
distributions: _AmountArray,
105+
index: _AmountArray,
106+
) -> float:
107+
...
108+
```
109+
27110
## KS-PME Flows
28111

29112
{% include_relative _inline/pe/ks_pme_flows.md %}
30113

114+
```python
115+
def ks_pme_flows(
116+
amounts: _AmountArray,
117+
index: _AmountArray,
118+
) -> List[float]:
119+
...
120+
121+
122+
def ks_pme_flows_2(
123+
contributions: _AmountArray,
124+
distributions: _AmountArray,
125+
index: _AmountArray,
126+
) -> Tuple[List[float], List[float]]:
127+
...
128+
```
129+
31130
## KS-PME
32131

33132
{% include_relative _inline/pe/ks_pme.md %}
34133

134+
```python
135+
def ks_pme(
136+
amounts: _AmountArray,
137+
index: _AmountArray,
138+
nav: _Amount = 0,
139+
) -> Optional[float]:
140+
...
141+
142+
143+
def ks_pme_2(
144+
contributions: _AmountArray,
145+
distributions: _AmountArray,
146+
index: _AmountArray,
147+
nav: _Amount = 0,
148+
) -> Optional[float]:
149+
...
150+
```
151+
152+
35153
## mPME
36154

37155
{% include_relative _inline/pe/m_pme.md %}
38156

157+
```python
158+
def m_pme(
159+
amounts: _AmountArray,
160+
index: _AmountArray,
161+
nav: _AmountArray,
162+
) -> float:
163+
...
164+
165+
166+
def m_pme_2(
167+
contributions: _AmountArray,
168+
distributions: _AmountArray,
169+
index: _AmountArray,
170+
nav: _AmountArray,
171+
) -> float:
172+
...
173+
```
174+
39175
## PME+ Flows
40176

41177
{% include_relative _inline/pe/pme_plus_flows.md %}
42178

179+
```python
180+
def pme_plus_flows(
181+
amounts: _AmountArray,
182+
index: _AmountArray,
183+
nav: _Amount = 0,
184+
) -> List[float]:
185+
...
186+
187+
188+
def pme_plus_flows_2(
189+
contributions: _AmountArray,
190+
distributions: _AmountArray,
191+
index: _AmountArray,
192+
nav: _Amount = 0,
193+
) -> Tuple[List[float], List[float]]:
194+
...
195+
```
196+
43197
## PME+ Lambda
44198

45199
{% include_relative _inline/pe/pme_plus_lambda.md %}
46200

201+
```python
202+
def pme_plus_lambda(
203+
amounts: _AmountArray,
204+
index: _AmountArray,
205+
nav: _Amount = 0,
206+
) -> float:
207+
...
208+
209+
210+
def pme_plus_lambda_2(
211+
contributions: _AmountArray,
212+
distributions: _AmountArray,
213+
index: _AmountArray,
214+
nav: _Amount = 0,
215+
) -> float:
216+
...
217+
```
218+
47219
## PME+
48220

49221
{% include_relative _inline/pe/pme_plus.md %}
50222

223+
```python
224+
def pme_plus(
225+
amounts: _AmountArray,
226+
index: _AmountArray,
227+
nav: _Amount = 0,
228+
) -> Optional[float]:
229+
...
230+
231+
232+
def pme_plus_2(
233+
contributions: _AmountArray,
234+
distributions: _AmountArray,
235+
index: _AmountArray,
236+
nav: _Amount = 0,
237+
) -> Optional[float]:
238+
...
239+
```
240+
51241
## Direct Alpha
52242

53243
{% include_relative _inline/pe/direct_alpha.md %}
54244

245+
```python
246+
def direct_alpha(
247+
amounts: _AmountArray,
248+
index: _AmountArray,
249+
nav: _Amount = 0,
250+
) -> Optional[float]:
251+
...
252+
253+
254+
def direct_alpha_2(
255+
contributions: _AmountArray,
256+
distributions: _AmountArray,
257+
index: _AmountArray,
258+
nav: _Amount = 0,
259+
) -> Optional[float]:
260+
...
261+
```

0 commit comments

Comments
 (0)