Loading...
Searching...
No Matches
markowitz.py
Go to the documentation of this file.
10
11import os
12import sys
13from math import fabs
14import matplotlib.pyplot as plt
15from gams import GamsModifier, GamsWorkspace
16
17
18GAMS_MODEL = """
19Set
20 s 'selected stocks'
21 upper(s,s) 'parts of covar matrix'
22 lower(s,s) 'parts of covar matrix';
23
24Alias(s,t);
25
26Parameter
27 mean(s) 'mean of daily return'
28 covar(s,s) 'covariance matrix of returns (upper)';
29
30$if not set data $abort 'no include file name for data file provided'
31$gdxIn %data%
32$load s covar upper lower mean
33$gdxIn
34
35Variable
36 z 'objective variable'
37 ret 'return'
38 var 'variance'
39 x(s) 'investments';
40
41Positive Variables x;
42
43Equation
44 obj 'objective'
45 budget
46 varcon 'variance constraint'
47 retcon 'return constraint';
48
49Scalar lambda / 0 /;
50
51obj.. z =e= lambda*ret - (1-lambda)*var;
52
53budget.. sum(s, x(s)) =e= 1.0;
54
55varcon.. var =e= sum(upper(s,t), x(s)*covar(s,t)*x(t)) +
56 sum(lower(s,t), x(s)*covar(t,s)*x(t));
57
58retcon.. ret =e= sum(s, mean(s)*x(s));
59
60Model markowitz / all /;
61"""
62
63if __name__ == "__main__":
64 sys_dir = sys.argv[1] if len(sys.argv) > 1 else None
65 ws = GamsWorkspace(system_directory=sys_dir)
66
67 job = ws.add_job_from_string(GAMS_MODEL)
68 opt = ws.add_options()
69 opt.all_model_types = "conopt"
70 opt.defines["data"] = os.path.join(
71 ws.system_directory, "apifiles", "Data", "markowitz.gdx"
72 )
73
74 cp = ws.add_checkpoint()
75 job.run(opt, cp)
76 mi = cp.add_modelinstance()
77 l = mi.sync_db.add_parameter("lambda", 0, "")
78 mi.instantiate("markowitz use nlp max z", GamsModifier(l))
79
80 # a list for collecting the data points
81 data_points = []
82
83 # get minimum and maximum return (lambda=0/1)
84 l.add_record().value = 0
85 mi.solve()
86 min_ret = mi.sync_db["ret"].first_record().level
87 data_points.append((min_ret, mi.sync_db["var"].first_record().level))
88 l.first_record().value = 1
89 mi.solve()
90 max_ret = mi.sync_db["ret"].first_record().level
91 data_points.append((max_ret, mi.sync_db["var"].first_record().level))
92
93 # gap that specifies the max distance (return) between two data points
94 gap = 0.02
95
96 # a list (stack) of intervals, where an interval is represented by a 2-tuple containing two data points
97 # each represented by a 2-tuple as well (lambda, return)
98 intervals = [((0.0, min_ret), (1.0, max_ret))]
99
100 # algorithm that solves the model instance for multiple lambda values
101 # using a max gap between two data points to achieve a smooth graph
102 # lambda is dynamically calculated for an even distribution of data points
103 while intervals:
104 # pick the first interval and calculate a new value for lambda
105 i = intervals.pop()
106 min_l, min_ret = i[0][0], i[0][1]
107 max_l, max_ret = i[1][0], i[1][1]
108
109 l_val = (min_l + max_l) / 2
110 l.first_record().value = l_val
111
112 # solve the model instance with the new lambda value
113 mi.solve()
114
115 # retrieve the return and variance results
116 cur_ret = mi.sync_db["ret"].first_record().level
117 data_points.append((cur_ret, mi.sync_db["var"].first_record().level))
118
119 # add new intervals if the gap is still to big
120 if fabs(cur_ret - min_ret) > gap:
121 intervals.append(((min_l, min_ret), (l_val, cur_ret)))
122 if fabs(cur_ret - max_ret) > gap:
123 intervals.append(((l_val, cur_ret), (max_l, max_ret)))
124
125 # sort data points and create two lists for the plot function
126 data_points.sort(key=lambda tup: tup[0])
127 ret = [x[0] for x in data_points]
128 var = [x[1] for x in data_points]
129 plt.plot(ret, var, marker=".", markersize=10)
130 plt.xlabel("return")
131 plt.ylabel("variance")
132 plt.show()