forked from pewapplepie/Qunat_Interview_Algo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
drawdown.py
144 lines (129 loc) · 5.43 KB
/
drawdown.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""
1. Calculate the Drawdown
A portfolio drawdown is the total return of an investment from peak (highest performance)
trough (lowest subsequent performance). The drawdown period is the time after the peak
date to (including) the trough date of the drawdown.
The first largest drawdown is the drawdown which has the largest magnitude. The second
largest drawdown is the largest drawdown in the time either before or after the first largest
drawdown. Drawdowns cannot overlap. The following illustration shows drawdowns for an
example stock:
1.9, 1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1, 1/10
Example Stock Performance
min
M
3rd Largest
Drawdown
1/11 1/12 1/13
1/14
Largest
Drawdown
1/15 1/16
2nd Largest
Drawdown
1/17
In this exercise you will build a function that takes in a list of returns, rets and an integer n;
and return the nth largest drawdown.
To complete this challenge, write a function in the editor that takes two parameters: a list o
period returns, returns and an integer n, and then returns the nth largest drawdown as a
float rounded to 4 decimal places (i.e. -0.1234 = -12.34%). Returns are expressed in
decimal form, i.e. 0.01 in the list = 1% fund return, and ordered from earliest to latest.
In this exercise you will build a function that takes in a list of returns, rets and an integer n; and return the nth largest drawdown.
To complete this challenge, write a function in the editor that takes two parameters: a list of period
returns, returns and an integer n, and then returns the nth largest drawdown as a float rounded to 4 decimal places (i.e. -0.1234 = -12.34%). Returns are expressed in decimal form, i.e. 0.01 in the list = 1% fund return, and ordered from earliest to latest.
In the case of ties, the function should return the same drawdown for each case (ie if the second and third largest drawdowns are the same, the function should return the same value for n=2 and n=3).
14
15
16
# The function is expected to return a FLOAT.
# The function accepts following parameters:
# 1. FLOAT_ARRAY rets
If the nth largest drawdown doesn't exist, the function should return the total number of drawdowns identified in the full time-series (0 if no drawdown exists).
"""
import matplotlib.pyplot as plt
import numpy as np
def cal(rets, n):
curr = 1
peak = 1
valley = 1
drawdowns = []
prices = []
stack = []
for i, ret in enumerate(rets):
# calculate curr price with return
curr *= (1 + ret)
# print(i, curr)
prices.append(curr)
if curr >= peak:
# curr price higher than the all time peak
while len(stack) > 0:
temp_peak, temp_valley = stack.pop()
drawdown = (temp_peak - temp_valley) / temp_peak
drawdowns.append(drawdown)
# set the new peak as the curr price
peak = curr
valley = peak # reset valley as well
stack.append((curr, curr))
elif curr <= valley:
# curr price is lower than previous valley
valley = curr # update valley
stack = [] # reset the stack
stack.append((peak, valley))
else:
# curr price is between the previous peak and valley
if stack[-1][0] > curr and stack[-1][1] < curr:
stack.append((curr, curr))
else:
temp_peak, temp_valley = stack[-1]
if curr >= temp_peak:
# curr price higher than the previous peak in stack
while curr >= temp_peak:
stack.pop()
drawdown = (temp_peak - temp_valley) / temp_peak
drawdowns.append(drawdown)
temp_peak, temp_valley = stack[-1]
stack.append((curr, curr))
elif curr < temp_valley:
# curr price higher than the previous valley in stack
while curr < stack[-1][1]:
temp_peak, temp_valley = stack.pop()
stack.append((temp_peak, curr))
while len(stack):
temp_peak, temp_valley = stack.pop()
drawdown = (temp_peak - temp_valley) / temp_peak
drawdowns.append(drawdown)
drawdowns.sort(reverse=True)
while drawdowns[-1] == 0:
drawdowns.pop()
# print(drawdowns)
if n > len(drawdowns):
return len(drawdowns)
return round(drawdowns[n-1] * -1, 4)
rets = [0.01, -0.01, 0.004, -0.02, 0.01]
n = 1
print(cal(rets, n)) # Output: -0.0259
# print(nth_largest_drawdown(rets, n))
rets = [0.01, -0.04, 0.05, -0.01, -0.01, 0.01]
n = 3
print(cal(rets, n)) # Output: 2
# print(nth_largest_drawdown(rets, n))
rets = [0.01, -0.01, 0.01, 0.01, 0.01, -0.02, 0.01, -0.03, 0.02, -0.04,
0.005, -0.01, 0.01, 0.01, 0.01, -0.02, 0.005, -0.01, 0.01, 0.01, -0.01, 0.01]
n = 2
print(cal(rets, n)) # Output: -0.0249
# print(nth_largest_drawdown(rets, n))
# num = (0.9928340334643394 - 0.9680638171634375) / 0.9928340334643394
# round(num, 4)
# print(round(num, 4))
# # test case 1
# rets = [0.01, -0.01, 0.004, -0.02, 0.01]
# n = 1
# print(nth_largest_drawdown(rets, n)) # -0.0259
# # test case 2
# rets = [0.01, -0.04, 0.05, -0.01, -0.01, 0.01]
# n = 3
# print(nth_largest_drawdown(rets, n)) # 2
# # test case 3
# rets = [0.01, -0.01, 0.01, 0.01, 0.01, -0.02, 0.01, -0.03, 0.02, -0.04,
# 0.005, -0.01, 0.01, 0.01, 0.01, -0.02, 0.005, -0.01, 0.01, 0.01, -0.01, 0.01]
# n = 2
# print(nth_largest_drawdown(rets, n)) # -0.0249