siat 3.10.125__py3-none-any.whl → 3.10.126__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- siat/common.py +106 -2
- siat/exchange_bond_china.pickle +0 -0
- siat/fund_china.pickle +0 -0
- siat/stock.py +10 -2
- siat/stock_info.pickle +0 -0
- {siat-3.10.125.dist-info → siat-3.10.126.dist-info}/METADATA +234 -226
- siat-3.10.126.dist-info/RECORD +76 -0
- {siat-3.10.125.dist-info → siat-3.10.126.dist-info}/WHEEL +1 -1
- {siat-3.10.125.dist-info → siat-3.10.126.dist-info/licenses}/LICENSE +0 -0
- {siat-3.10.125.dist-info → siat-3.10.126.dist-info}/top_level.txt +0 -0
- siat/__init__ -20240701.py +0 -65
- siat/__init__.py.backup_20250214.py +0 -73
- siat/alpha_vantage_test.py +0 -24
- siat/assets_liquidity_test.py +0 -44
- siat/barrons_scraping_test.py +0 -276
- siat/beta_adjustment_test.py +0 -77
- siat/bond_test.py +0 -142
- siat/capm_beta_test.py +0 -49
- siat/cmat_commons.py +0 -961
- siat/compare_cross_test.py +0 -117
- siat/concepts_iwencai.py +0 -86
- siat/concepts_kpl.py +0 -93
- siat/cryptocurrency_test.py +0 -71
- siat/derivative.py +0 -1111
- siat/economy-20230125.py +0 -1206
- siat/economy_test.py +0 -360
- siat/esg_test.py +0 -63
- siat/fama_french_test.py +0 -115
- siat/financial_statements_test.py +0 -31
- siat/financials2 - /321/205/320/231/320/277/321/206/320/254/320/274.py" +0 -341
- siat/financials_china2_test.py +0 -67
- siat/financials_china2_test2.py +0 -88
- siat/financials_china2_test3.py +0 -87
- siat/financials_china_test.py +0 -475
- siat/financials_china_test2.py +0 -197
- siat/financials_china_test2_fin_indicator.py +0 -197
- siat/financials_test.py +0 -713
- siat/fred_test.py +0 -40
- siat/fund_china_test.py +0 -175
- siat/fund_test.py +0 -40
- siat/future_china_test.py +0 -37
- siat/global_index_test.py +0 -66
- siat/grafix_test.py +0 -112
- siat/holding_risk_test.py +0 -13
- siat/local_debug_test.py +0 -100
- siat/markowitz2-20240620.py +0 -2614
- siat/markowitz_ccb_test.py +0 -37
- siat/markowitz_ef_test.py +0 -136
- siat/markowitz_old.py +0 -871
- siat/markowitz_simple-20230709.py +0 -370
- siat/markowitz_test.py +0 -164
- siat/markowitz_test2.py +0 -69
- siat/ml_cases_example1.py +0 -60
- siat/option_china_test.py +0 -447
- siat/option_pricing_test.py +0 -81
- siat/option_sina_api_test.py +0 -112
- siat/proxy_test.py +0 -84
- siat/quandl_test.py +0 -39
- siat/risk_adjusted_return_test.py +0 -81
- siat/risk_evaluation_test.py +0 -96
- siat/risk_free_rate_test.py +0 -127
- siat/sector_china_test.py +0 -203
- siat/security_price.py +0 -831
- siat/security_prices_test.py +0 -310
- siat/security_trend2-20240620.py +0 -493
- siat/setup.py +0 -41
- siat/shenwan index history test.py +0 -41
- siat/stock_china_test.py +0 -38
- siat/stock_info_test.py +0 -189
- siat/stock_list_china_test.py +0 -33
- siat/stock_technical-20240620.py +0 -2736
- siat/stock_test.py +0 -487
- siat/temp.py +0 -36
- siat/test2_graphviz.py +0 -484
- siat/test_graphviz.py +0 -411
- siat/test_markowitz_simple.py +0 -198
- siat/test_markowitz_simple_revised.py +0 -215
- siat/test_markowitz_simple_revised2.py +0 -218
- siat/transaction_test.py +0 -436
- siat/translate-20230125.py +0 -2107
- siat/translate-20230206.py +0 -2109
- siat/translate-20230215.py +0 -2158
- siat/translate_20240606.py +0 -4206
- siat/translate_241003_keep.py +0 -4300
- siat/universal_test.py +0 -100
- siat/valuation_market_china_test.py +0 -36
- siat-3.10.125.dist-info/RECORD +0 -152
siat/markowitz_old.py
DELETED
@@ -1,871 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
"""
|
3
|
-
本模块功能:证券投资组合理论计算函数包
|
4
|
-
所属工具包:证券投资分析工具SIAT
|
5
|
-
SIAT:Security Investment Analysis Tool
|
6
|
-
创建日期:2020年7月1日
|
7
|
-
最新修订日期:2020年7月29日
|
8
|
-
作者:王德宏 (WANG Dehong, Peter)
|
9
|
-
作者单位:北京外国语大学国际商学院
|
10
|
-
作者邮件:wdehong2000@163.com
|
11
|
-
版权所有:王德宏
|
12
|
-
用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
|
13
|
-
特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
|
14
|
-
"""
|
15
|
-
#==============================================================================
|
16
|
-
#统一屏蔽一般性警告
|
17
|
-
import warnings; warnings.filterwarnings("ignore")
|
18
|
-
#==============================================================================
|
19
|
-
|
20
|
-
from siat.common import *
|
21
|
-
from siat.translate import *
|
22
|
-
from siat.security_prices import *
|
23
|
-
|
24
|
-
import pandas as pd
|
25
|
-
import datetime
|
26
|
-
#==============================================================================
|
27
|
-
import matplotlib.pyplot as plt
|
28
|
-
|
29
|
-
#处理绘图汉字乱码问题
|
30
|
-
import sys; czxt=sys.platform
|
31
|
-
if czxt in ['win32','win64']:
|
32
|
-
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
|
33
|
-
mpfrc={'font.family': 'SimHei'}
|
34
|
-
|
35
|
-
if czxt in ['darwin']: #MacOSX
|
36
|
-
plt.rcParams['font.family']= ['Heiti TC']
|
37
|
-
mpfrc={'font.family': 'Heiti TC'}
|
38
|
-
|
39
|
-
if czxt in ['linux']: #website Jupyter
|
40
|
-
plt.rcParams['font.family']= ['Heiti TC']
|
41
|
-
mpfrc={'font.family':'Heiti TC'}
|
42
|
-
|
43
|
-
# 解决保存图像时'-'显示为方块的问题
|
44
|
-
plt.rcParams['axes.unicode_minus'] = False
|
45
|
-
#==============================================================================
|
46
|
-
#==============================================================================
|
47
|
-
def portfolio_config(tickerlist,sharelist):
|
48
|
-
"""
|
49
|
-
将股票列表tickerlist和份额列表sharelist合成为一个字典
|
50
|
-
"""
|
51
|
-
#整理sharelist的小数点
|
52
|
-
ratiolist=[]
|
53
|
-
for s in sharelist:
|
54
|
-
ss=round(s,4); ratiolist=ratiolist+[ss]
|
55
|
-
#合成字典
|
56
|
-
new_dict=dict(zip(tickerlist,ratiolist))
|
57
|
-
return new_dict
|
58
|
-
|
59
|
-
#==============================================================================
|
60
|
-
def ratiolist_round(sharelist,num=4):
|
61
|
-
"""
|
62
|
-
将股票份额列表sharelist中的数值四舍五入
|
63
|
-
"""
|
64
|
-
#整理sharelist的小数点
|
65
|
-
ratiolist=[]
|
66
|
-
for s in sharelist:
|
67
|
-
ss=round(s,num); ratiolist=ratiolist+[ss]
|
68
|
-
return ratiolist
|
69
|
-
|
70
|
-
#==============================================================================
|
71
|
-
def varname(p):
|
72
|
-
"""
|
73
|
-
功能:获得变量的名字本身。
|
74
|
-
"""
|
75
|
-
import inspect
|
76
|
-
import re
|
77
|
-
for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
|
78
|
-
m = re.search(r'\bvarname\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', line)
|
79
|
-
if m:
|
80
|
-
return m.group(1)
|
81
|
-
|
82
|
-
#==============================================================================
|
83
|
-
def get_start_date(end_date,pastyears=1):
|
84
|
-
"""
|
85
|
-
输入参数:一个日期,年数
|
86
|
-
输出参数:几年前的日期
|
87
|
-
start_date, end_date是datetime类型
|
88
|
-
"""
|
89
|
-
import pandas as pd
|
90
|
-
try:
|
91
|
-
end_date=pd.to_datetime(end_date)
|
92
|
-
except:
|
93
|
-
print("#Error(): invalid date,",end_date)
|
94
|
-
return None
|
95
|
-
|
96
|
-
from datetime import datetime,timedelta
|
97
|
-
start_date=datetime(end_date.year-pastyears,end_date.month,end_date.day)
|
98
|
-
start_date=start_date-timedelta(days=1)
|
99
|
-
# 日期-1是为了保证计算收益率时得到足够的样本数量
|
100
|
-
return start_date
|
101
|
-
|
102
|
-
#==============================================================================
|
103
|
-
if __name__=='__main__':
|
104
|
-
retgroup=StockReturns
|
105
|
-
|
106
|
-
def cumulative_returns_plot(retgroup,name_list,titletxt,ylabeltxt,xlabeltxt, \
|
107
|
-
label_list=[]):
|
108
|
-
"""
|
109
|
-
功能:基于传入的name_list绘制多条累计收益率曲线,并从label_list中取出曲线标记
|
110
|
-
注意:最多绘制四条曲线,否则在黑白印刷时无法区分曲线
|
111
|
-
"""
|
112
|
-
if len(label_list) < len(name_list):
|
113
|
-
label_list=name_list
|
114
|
-
|
115
|
-
# 累积收益曲线绘制函数
|
116
|
-
lslist=['-','--',':','-.']
|
117
|
-
for name in name_list:
|
118
|
-
pos=name_list.index(name)
|
119
|
-
rlabel=label_list[pos]
|
120
|
-
if pos < 4: thisls=lslist[pos]
|
121
|
-
else: thisls=(45,(55,20))
|
122
|
-
|
123
|
-
CumulativeReturns = ((1+retgroup[name]).cumprod()-1)*100.0
|
124
|
-
CumulativeReturns.plot(label=ectranslate(rlabel),ls=thisls)
|
125
|
-
#plt.axhline(y=0,ls=":",c="red")
|
126
|
-
plt.legend(loc='best')
|
127
|
-
plt.title(titletxt); plt.ylabel(ylabeltxt); plt.xlabel(xlabeltxt)
|
128
|
-
plt.show()
|
129
|
-
|
130
|
-
return
|
131
|
-
|
132
|
-
if __name__=='__main__':
|
133
|
-
retgroup=StockReturns
|
134
|
-
cumulative_returns_plot(retgroup,name_list,titletxt,ylabeltxt,xlabeltxt, \
|
135
|
-
label_list=[])
|
136
|
-
#==============================================================================
|
137
|
-
if __name__=='__main__':
|
138
|
-
Market={'Market':('US','^GSPC','我的组合001')}
|
139
|
-
Stocks={'BABA':.7,'JD':.3}
|
140
|
-
portfolio=dict(Market,**Stocks)
|
141
|
-
|
142
|
-
today='2021-1-28'
|
143
|
-
pastyears=1
|
144
|
-
|
145
|
-
def portfolio_cumret(portfolio,today,pastyears=1):
|
146
|
-
"""
|
147
|
-
功能:绘制投资组合的累计收益率趋势图,并与等权和市值加权组合比较
|
148
|
-
"""
|
149
|
-
print("\n Searching for portfolio info, which may take time ...")
|
150
|
-
# 解构投资组合
|
151
|
-
_,_,tickerlist,sharelist=decompose_portfolio(portfolio)
|
152
|
-
pname=portfolio_name(portfolio)
|
153
|
-
|
154
|
-
totalshares=round(sum(sharelist),4)
|
155
|
-
if totalshares != 1.0:
|
156
|
-
print("\n #Error(portfolio_cumret): total weights is",totalshares,", it requires 1.0 here")
|
157
|
-
return None
|
158
|
-
|
159
|
-
# 计算历史数据的开始日期
|
160
|
-
start=get_start_date(today,pastyears)
|
161
|
-
|
162
|
-
# 抓取投资组合股价
|
163
|
-
"""
|
164
|
-
StockReturns=get_portfolio_prices(portfolio,start,today)
|
165
|
-
"""
|
166
|
-
prices=get_prices(tickerlist,start,today)
|
167
|
-
if prices is None:
|
168
|
-
print("\n #Error(portfolio_cumret): failed to get portfolio prices",pname)
|
169
|
-
return None
|
170
|
-
if len(prices) == 0:
|
171
|
-
print("\n #Error(portfolio_cumret): retrieved empty prices for",pname)
|
172
|
-
return None
|
173
|
-
|
174
|
-
# 复权后收盘价
|
175
|
-
aclose=prices['Close']
|
176
|
-
#print(aclose.head())
|
177
|
-
# 计算每日收益率,并丢弃缺失值
|
178
|
-
StockReturns = aclose.pct_change().dropna()
|
179
|
-
if len(StockReturns) == 0:
|
180
|
-
print("\n #Error(portfolio_cumret): retrieved empty returns for",pname)
|
181
|
-
return None
|
182
|
-
#print(StockReturns.head())
|
183
|
-
# 将收益率数据拷贝到新的变量 stock_return 中,为了后续调用的方便
|
184
|
-
stock_return = StockReturns.copy()
|
185
|
-
#..........................................................................
|
186
|
-
|
187
|
-
# 设置组合权重,存储为numpy数组类型
|
188
|
-
import numpy as np
|
189
|
-
portfolio_weights = np.array(sharelist)
|
190
|
-
# 计算portfolio的股票收益
|
191
|
-
WeightedReturns = stock_return.mul(portfolio_weights, axis=1)
|
192
|
-
# 计算投资组合的收益
|
193
|
-
StockReturns['Portfolio'] = WeightedReturns.sum(axis=1)
|
194
|
-
|
195
|
-
# 绘制portfolio随时间变化的图
|
196
|
-
#仅用于绘图,以便使用收益率%来显示
|
197
|
-
plotsr = StockReturns['Portfolio']*100.0
|
198
|
-
#plotsr = StockReturns['Ret%']
|
199
|
-
#plotsr.plot(label='Portfolio')
|
200
|
-
plotsr.plot(label=pname)
|
201
|
-
plt.axhline(y=0,ls=":",c="red")
|
202
|
-
|
203
|
-
plt.title("投资组合: 日收益率的变化趋势")
|
204
|
-
plt.ylabel("日收益率%")
|
205
|
-
|
206
|
-
stoday = datetime.date.today()
|
207
|
-
plt.xlabel("数据来源: 新浪/stooq/FRED, "+str(stoday))
|
208
|
-
plt.legend()
|
209
|
-
plt.show()
|
210
|
-
#..........................................................................
|
211
|
-
|
212
|
-
# 计算持有收益率的组合收益,并绘图
|
213
|
-
name_list=["Portfolio"]
|
214
|
-
label_list=[pname]
|
215
|
-
titletxt="投资组合: 持有收益率的变化趋势"
|
216
|
-
ylabeltxt="持有收益率%"
|
217
|
-
stoday = datetime.date.today()
|
218
|
-
xlabeltxt="数据来源: 新浪/stooq/FRED, "+str(stoday)
|
219
|
-
#由函数去处理百分号放大
|
220
|
-
cumulative_returns_plot(StockReturns,name_list,titletxt,ylabeltxt,xlabeltxt,label_list)
|
221
|
-
#..........................................................................
|
222
|
-
|
223
|
-
# 构造等权重组合Portfolio_EW
|
224
|
-
numstocks = len(tickerlist)
|
225
|
-
# 平均分配每一项的权重
|
226
|
-
portfolio_weights_ew = np.repeat(1/numstocks, numstocks)
|
227
|
-
# 计算等权重组合的收益
|
228
|
-
StockReturns['Portfolio_EW']=stock_return.mul(portfolio_weights_ew,axis=1).sum(axis=1)
|
229
|
-
#StockReturns['Portfolio_name']=pname
|
230
|
-
#name_list=['Portfolio','Portfolio_EW']
|
231
|
-
#cumulative_returns_plot(StockReturns,name_list,titletxt,ylabeltxt,xlabeltxt)
|
232
|
-
#..........................................................................
|
233
|
-
|
234
|
-
# 创建流动性组合:按照成交金额计算流动性
|
235
|
-
MCap=prices['Close']*prices['Volume']
|
236
|
-
#MCaplist=MCap.iloc[-1]
|
237
|
-
MCaplist=MCap.mean(axis=0) #求列的均值
|
238
|
-
market_capitalizations = np.array(MCaplist)
|
239
|
-
# 计算成交金额权重
|
240
|
-
mcap_weights = market_capitalizations / np.sum(market_capitalizations)
|
241
|
-
# 计算成交金额加权的组合收益
|
242
|
-
StockReturns['Portfolio_LW'] = stock_return.mul(mcap_weights, axis=1).sum(axis=1)
|
243
|
-
|
244
|
-
#绘制累计收益率对比曲线
|
245
|
-
name_list=['Portfolio', 'Portfolio_EW', 'Portfolio_LW']
|
246
|
-
label_list=[pname, '等权重组合', '流动性组合']
|
247
|
-
titletxt="投资组合: 不同组合策略的持有收益率对比"
|
248
|
-
cumulative_returns_plot(StockReturns,name_list,titletxt,ylabeltxt,xlabeltxt,label_list)
|
249
|
-
|
250
|
-
return [tickerlist,sharelist,StockReturns,stock_return,today, \
|
251
|
-
[portfolio_weights,portfolio_weights_ew,mcap_weights]]
|
252
|
-
|
253
|
-
if __name__=='__main__':
|
254
|
-
X=portfolio_cumret(portfolio,'2021-1-28')
|
255
|
-
|
256
|
-
if __name__=='__main__':
|
257
|
-
Market={'Market':('US','^GSPC','我的组合001')}
|
258
|
-
Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
|
259
|
-
Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
|
260
|
-
portfolio=dict(Market,**Stocks1,**Stocks2)
|
261
|
-
pf_info=portfolio_cumret(portfolio,'2019-12-31')
|
262
|
-
|
263
|
-
|
264
|
-
def portfolio_expret(portfolio,today,pastyears=1):
|
265
|
-
"""
|
266
|
-
功能:绘制投资组合的持有期收益率趋势图,并与等权和市值加权组合比较
|
267
|
-
套壳原来的portfolio_cumret函数,以维持兼容性
|
268
|
-
expret: expanding return,以维持与前述章节名词的一致性
|
269
|
-
hpr: holding period return, 持有(期)收益率
|
270
|
-
"""
|
271
|
-
[tickerlist,sharelist,StockReturns,stock_return,today, \
|
272
|
-
[portfolio_weights,portfolio_weights_ew,mcap_weights]] = \
|
273
|
-
portfolio_cumret(portfolio,today,pastyears)
|
274
|
-
|
275
|
-
return [tickerlist,sharelist,StockReturns,stock_return,today, \
|
276
|
-
[portfolio_weights,portfolio_weights_ew,mcap_weights]]
|
277
|
-
|
278
|
-
if __name__=='__main__':
|
279
|
-
Market={'Market':('US','^GSPC','我的组合001')}
|
280
|
-
Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
|
281
|
-
Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
|
282
|
-
portfolio=dict(Market,**Stocks1,**Stocks2)
|
283
|
-
|
284
|
-
pf_info=portfolio_expret(portfolio,'2019-12-31')
|
285
|
-
|
286
|
-
|
287
|
-
#==============================================================================
|
288
|
-
def portfolio_corr(pf_info):
|
289
|
-
"""
|
290
|
-
功能:绘制投资组合成分股之间相关关系的热力图
|
291
|
-
"""
|
292
|
-
[_,_,StockReturns,stock_return,today,_]=pf_info
|
293
|
-
pname=StockReturns['Portfolio_name'].values[0]
|
294
|
-
|
295
|
-
sr=stock_return.copy()
|
296
|
-
collist=list(sr)
|
297
|
-
for col in collist:
|
298
|
-
sr.rename(columns={col:codetranslate(col)},inplace=True)
|
299
|
-
|
300
|
-
# 计算相关矩阵
|
301
|
-
#correlation_matrix = stock_return.corr()
|
302
|
-
correlation_matrix = sr.corr()
|
303
|
-
|
304
|
-
# 导入seaborn
|
305
|
-
import seaborn as sns
|
306
|
-
# 创建热图
|
307
|
-
sns.heatmap(correlation_matrix,annot=True,cmap="YlGnBu",linewidths=0.3,
|
308
|
-
annot_kws={"size": 8})
|
309
|
-
plt.title(pname+": 成分股收益率之间的相关系数")
|
310
|
-
plt.ylabel("成分股票")
|
311
|
-
import datetime as dt; stoday=dt.date.today()
|
312
|
-
plt.xlabel("数据来源:新浪/EM/stooq,"+str(stoday))
|
313
|
-
plt.xticks(rotation=90); plt.yticks(rotation=0)
|
314
|
-
plt.show()
|
315
|
-
|
316
|
-
return
|
317
|
-
|
318
|
-
if __name__=='__main__':
|
319
|
-
Market={'Market':('US','^GSPC','我的组合001')}
|
320
|
-
Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
|
321
|
-
Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
|
322
|
-
portfolio=dict(Market,**Stocks1,**Stocks2)
|
323
|
-
pf_info=portfolio_expret(portfolio,'2019-12-31')
|
324
|
-
|
325
|
-
portfolio_corr(pf_info)
|
326
|
-
#==============================================================================
|
327
|
-
def portfolio_covar(pf_info):
|
328
|
-
"""
|
329
|
-
功能:计算投资组合成分股之间的协方差
|
330
|
-
"""
|
331
|
-
[_,_,StockReturns,stock_return,today,_]=pf_info
|
332
|
-
pname=StockReturns['Portfolio_name'].values[0]
|
333
|
-
|
334
|
-
# 计算协方差矩阵
|
335
|
-
cov_mat = stock_return.cov()
|
336
|
-
# 年化协方差矩阵,252个交易日
|
337
|
-
cov_mat_annual = cov_mat * 252
|
338
|
-
|
339
|
-
# 导入seaborn
|
340
|
-
import seaborn as sns
|
341
|
-
# 创建热图
|
342
|
-
sns.heatmap(cov_mat_annual,annot=True,cmap="YlGnBu",linewidths=0.3,
|
343
|
-
annot_kws={"size": 8})
|
344
|
-
plt.title(pname+": 成分股之间的协方差")
|
345
|
-
plt.ylabel("成分股票")
|
346
|
-
|
347
|
-
import datetime as dt; stoday=dt.date.today()
|
348
|
-
plt.xlabel("数据来源:新浪/EM/stooq,"+str(stoday))
|
349
|
-
plt.xticks(rotation=90)
|
350
|
-
plt.yticks(rotation=0)
|
351
|
-
plt.show()
|
352
|
-
|
353
|
-
return
|
354
|
-
|
355
|
-
#==============================================================================
|
356
|
-
def portfolio_expectation(pf_info):
|
357
|
-
"""
|
358
|
-
功能:计算投资组合的标准差
|
359
|
-
"""
|
360
|
-
[tickerlist,sharelist,StockReturns,stock_return,today,[portfolio_weights,_,_]]=pf_info
|
361
|
-
pname=StockReturns['Portfolio_name'].values[0]
|
362
|
-
|
363
|
-
#取出观察期
|
364
|
-
hstart0=StockReturns.index[0]
|
365
|
-
hstart=str(hstart0.date())
|
366
|
-
hend0=StockReturns.index[-1]
|
367
|
-
hend=str(hend0.date())
|
368
|
-
|
369
|
-
mean_return=stock_return.mul(portfolio_weights,axis=1).sum(axis=1).mean()
|
370
|
-
annual_return = (1 + mean_return)**252 - 1
|
371
|
-
|
372
|
-
# 计算协方差矩阵
|
373
|
-
cov_mat = stock_return.cov()
|
374
|
-
# 年化协方差矩阵
|
375
|
-
cov_mat_annual = cov_mat * 252
|
376
|
-
|
377
|
-
# 计算投资组合的标准差
|
378
|
-
import numpy as np
|
379
|
-
portfolio_volatility = np.sqrt(np.dot(portfolio_weights.T,
|
380
|
-
np.dot(cov_mat_annual, portfolio_weights)))
|
381
|
-
print("\n ===== 投资组合的预期收益与预期风险 =====")
|
382
|
-
print(" 投资组合:",pname)
|
383
|
-
#print(" 成分股:",codetranslate(tickerlist))
|
384
|
-
#print(" 权重:",sharelist)
|
385
|
-
print(" 观察期 :",hstart+'至'+hend)
|
386
|
-
print(" 预期收益:",round(annual_return,4))
|
387
|
-
print(" 预期风险:",round(portfolio_volatility,4))
|
388
|
-
|
389
|
-
import datetime as dt; stoday=dt.date.today()
|
390
|
-
print(" *数据来源:新浪/EM/stooq,"+str(stoday))
|
391
|
-
|
392
|
-
return
|
393
|
-
|
394
|
-
if __name__=='__main__':
|
395
|
-
Market={'Market':('US','^GSPC','我的组合001')}
|
396
|
-
Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
|
397
|
-
Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
|
398
|
-
portfolio=dict(Market,**Stocks1,**Stocks2)
|
399
|
-
pf_info=portfolio_expret(portfolio,'2019-12-31')
|
400
|
-
|
401
|
-
portfolio_expectation(pf_info)
|
402
|
-
#==============================================================================
|
403
|
-
if __name__=='__main__':
|
404
|
-
simulation=1000
|
405
|
-
|
406
|
-
def portfolio_es(pf_info,simulation=1000):
|
407
|
-
"""
|
408
|
-
功能:计算投资组合的标准差,绘制投资组合的有效集
|
409
|
-
"""
|
410
|
-
[tickerlist,_,StockReturns,stock_return,today,_]=pf_info
|
411
|
-
pname=StockReturns['Portfolio_name'].values[0]
|
412
|
-
#通过列名列表获得成分股个数
|
413
|
-
numstocks=len(tickerlist)
|
414
|
-
|
415
|
-
# 计算协方差矩阵
|
416
|
-
cov_mat = stock_return.cov()
|
417
|
-
# 年化协方差矩阵
|
418
|
-
cov_mat_annual = cov_mat * 252
|
419
|
-
|
420
|
-
# 设置模拟的次数
|
421
|
-
number = simulation
|
422
|
-
# 设置空的numpy数组,用于存储每次模拟得到的成分股权重、组合的收益率和标准差
|
423
|
-
import numpy as np
|
424
|
-
random_p = np.empty((number,numstocks+2))
|
425
|
-
# 设置随机数种子,这里是为了结果可重复
|
426
|
-
np.random.seed(123)
|
427
|
-
|
428
|
-
# 循环模拟n次随机的投资组合
|
429
|
-
print("\n Calculating possible portfolio combinations, please wait ...")
|
430
|
-
for i in range(number):
|
431
|
-
# 生成numstocks个随机数,并归一化,得到一组随机的权重数据
|
432
|
-
random9 = np.random.random(numstocks)
|
433
|
-
random_weight = random9 / np.sum(random9)
|
434
|
-
|
435
|
-
# 计算年化平均收益率
|
436
|
-
mean_return=stock_return.mul(random_weight,axis=1).sum(axis=1).mean()
|
437
|
-
annual_return = (1 + mean_return)**252 - 1
|
438
|
-
|
439
|
-
# 计算年化的标准差,也称为波动率
|
440
|
-
random_volatility=np.sqrt(np.dot(random_weight.T,
|
441
|
-
np.dot(cov_mat_annual,random_weight)))
|
442
|
-
|
443
|
-
# 将上面生成的权重,和计算得到的收益率、标准差存入数组random_p中
|
444
|
-
random_p[i][:numstocks] = random_weight
|
445
|
-
random_p[i][numstocks] = annual_return
|
446
|
-
random_p[i][numstocks+1] = random_volatility
|
447
|
-
|
448
|
-
# 将numpy数组转化成DataFrame数据框
|
449
|
-
import pandas as pd
|
450
|
-
RandomPortfolios = pd.DataFrame(random_p)
|
451
|
-
# 设置数据框RandomPortfolios每一列的名称
|
452
|
-
RandomPortfolios.columns = [ticker + "_weight" for ticker in tickerlist] \
|
453
|
-
+ ['Returns', 'Volatility']
|
454
|
-
|
455
|
-
# 绘制散点图
|
456
|
-
RandomPortfolios.plot('Volatility','Returns',kind='scatter',color='y',edgecolors='k')
|
457
|
-
"""
|
458
|
-
plt.style.use('seaborn-dark')
|
459
|
-
RandomPortfolios.plot.scatter(x='Volatility', y='Returns', c='Returns',
|
460
|
-
cmap='RdYlGn', edgecolors='black')
|
461
|
-
"""
|
462
|
-
plt.title("投资组合: 马科维茨可行集")
|
463
|
-
plt.ylabel("预期收益")
|
464
|
-
|
465
|
-
import datetime as dt; stoday=dt.date.today()
|
466
|
-
footnote1="预期风险-->"
|
467
|
-
footnote2="\n基于"+pname+"之成分股构造,共"+str(simulation)+"个"
|
468
|
-
footnote3="\n数据来源: 新浪/EM/stooq, "+str(stoday)
|
469
|
-
plt.xlabel(footnote1+footnote2+footnote3)
|
470
|
-
plt.show()
|
471
|
-
|
472
|
-
return [pf_info,RandomPortfolios]
|
473
|
-
|
474
|
-
if __name__=='__main__':
|
475
|
-
Market={'Market':('US','^GSPC','我的组合001')}
|
476
|
-
Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
|
477
|
-
Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
|
478
|
-
portfolio=dict(Market,**Stocks1,**Stocks2)
|
479
|
-
pf_info=portfolio_expret(portfolio,'2019-12-31')
|
480
|
-
|
481
|
-
es_info=portfolio_es(pf_info,simulation=50000)
|
482
|
-
#==============================================================================
|
483
|
-
def portfolio_GMV(es_info):
|
484
|
-
"""
|
485
|
-
功能:计算投资组合的最小风险组合
|
486
|
-
GMV=Global Minimium Variance
|
487
|
-
"""
|
488
|
-
[[tickerlist,sharelist,StockReturns,stock_return,today, \
|
489
|
-
[portfolio_weights,portfolio_weights_ew,mcap_weights]], \
|
490
|
-
RandomPortfolios]=es_info
|
491
|
-
pname=StockReturns['Portfolio_name'].values[0]
|
492
|
-
numstocks=len(tickerlist)
|
493
|
-
|
494
|
-
#取出观察期
|
495
|
-
hstart0=StockReturns.index[0]
|
496
|
-
hstart=str(hstart0.date())
|
497
|
-
hend0=StockReturns.index[-1]
|
498
|
-
hend=str(hend0.date())
|
499
|
-
|
500
|
-
# 绘制散点图
|
501
|
-
RandomPortfolios.plot('Volatility','Returns',kind='scatter',color='y',edgecolors='k')
|
502
|
-
|
503
|
-
# 找到标准差最小数据的索引值
|
504
|
-
min_index = RandomPortfolios.Volatility.idxmin()
|
505
|
-
# 在收益-风险散点图中突出风险最小的点
|
506
|
-
x = RandomPortfolios.loc[min_index,'Volatility']
|
507
|
-
y = RandomPortfolios.loc[min_index,'Returns']
|
508
|
-
plt.scatter(x, y, color='m',marker='8',s=100,label="GMV Point",edgecolors='r')
|
509
|
-
|
510
|
-
plt.title("投资组合: GMV点的位置")
|
511
|
-
plt.ylabel("预期收益")
|
512
|
-
plt.xlabel("预期风险-->\nGMV表示风险最低的组合方式(Global Minimium Variance). \
|
513
|
-
\n数据来源: 新浪/stooq, "+str(today))
|
514
|
-
plt.legend()
|
515
|
-
plt.show()
|
516
|
-
|
517
|
-
# 提取最小波动组合对应的权重, 并转换成Numpy数组
|
518
|
-
import numpy as np
|
519
|
-
GMV_weights = np.array(RandomPortfolios.iloc[min_index, 0:numstocks])
|
520
|
-
# 计算GMV投资组合收益
|
521
|
-
StockReturns['Portfolio_GMV'] = stock_return.mul(GMV_weights, axis=1).sum(axis=1)
|
522
|
-
# 绘制累积收益曲线
|
523
|
-
namelist=['Portfolio_GMV', 'Portfolio']
|
524
|
-
titletxt="投资组合: 累计收益率的比较"
|
525
|
-
ylabeltxt="累计收益率"
|
526
|
-
xlabeltxt="数据来源: 新浪/stooq, "+str(today)
|
527
|
-
cumulative_returns_plot(StockReturns,namelist,titletxt,ylabeltxt,xlabeltxt)
|
528
|
-
|
529
|
-
#输出GMV投资组合构成比例
|
530
|
-
print("\n===== GMV组合方式: 构造风险最低的投资组合 =====")
|
531
|
-
print("成分股:",codetranslate(tickerlist))
|
532
|
-
GMV_weights_new=ratiolist_round(GMV_weights,num=3)
|
533
|
-
print("权重:",GMV_weights_new)
|
534
|
-
print("观察期:",hstart+'至'+hend)
|
535
|
-
print("预期收益:",round(y,4))
|
536
|
-
print("预期风险:",round(x,4))
|
537
|
-
|
538
|
-
import datetime as dt; today=dt.date.today()
|
539
|
-
print("*数据来源:新浪/stooq,"+str(today))
|
540
|
-
|
541
|
-
return
|
542
|
-
|
543
|
-
#==============================================================================
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
def portfolio_MSR_GMV(es_info,RF=0):
|
549
|
-
"""
|
550
|
-
功能:计算投资组合的最高夏普比率组合
|
551
|
-
MSR: Maximium Sharpe Rate, 最高夏普指数方案
|
552
|
-
GMV: Global Minimum Volatility, 全局最小波动方案
|
553
|
-
"""
|
554
|
-
[[tickerlist,sharelist,StockReturns,stock_return,today, \
|
555
|
-
[portfolio_weights,portfolio_weights_ew,mcap_weights]], \
|
556
|
-
RandomPortfolios]=es_info
|
557
|
-
numstocks=len(tickerlist)
|
558
|
-
pname=StockReturns['Portfolio_name'].values[0]
|
559
|
-
|
560
|
-
#取出观察期
|
561
|
-
hstart0=StockReturns.index[0]
|
562
|
-
hstart=str(hstart0.date())
|
563
|
-
hend0=StockReturns.index[-1]
|
564
|
-
hend=str(hend0.date())
|
565
|
-
|
566
|
-
# 设置无风险回报率
|
567
|
-
risk_free = RF
|
568
|
-
# 计算每项资产的夏普比率
|
569
|
-
RandomPortfolios['Sharpe'] = (RandomPortfolios.Returns - risk_free) \
|
570
|
-
/ RandomPortfolios.Volatility
|
571
|
-
|
572
|
-
# 绘制收益-标准差的散点图,并用颜色描绘夏普比率
|
573
|
-
# 绘制散点图
|
574
|
-
RandomPortfolios.plot('Volatility','Returns',kind='scatter',alpha=0.3)
|
575
|
-
plt.scatter(RandomPortfolios.Volatility, RandomPortfolios.Returns,
|
576
|
-
c=RandomPortfolios.Sharpe)
|
577
|
-
plt.colorbar(label='Colored in Sharpe Ratio')
|
578
|
-
plt.title("投资组合: 马科维茨可行集的夏普比率分布")
|
579
|
-
plt.ylabel("预期收益")
|
580
|
-
plt.xlabel("预期风险-->"+ \
|
581
|
-
"\n观察期:"+hstart+"至"+hend+ \
|
582
|
-
"\n数据来源: 新浪/stooq, "+str(today))
|
583
|
-
plt.show()
|
584
|
-
|
585
|
-
#绘制有效集
|
586
|
-
RandomPortfolios.plot('Volatility','Returns',kind='scatter',color='y',edgecolors='k')
|
587
|
-
|
588
|
-
# 找到夏普比率最大数据对应的索引值
|
589
|
-
max_index = RandomPortfolios.Sharpe.idxmax()
|
590
|
-
# 在收益-风险散点图中突出夏普比率最大的点
|
591
|
-
MSR_x = RandomPortfolios.loc[max_index,'Volatility']
|
592
|
-
MSR_y = RandomPortfolios.loc[max_index,'Returns']
|
593
|
-
plt.scatter(MSR_x, MSR_y, color='red',marker='*',s=150,label="MSR Point")
|
594
|
-
# 提取最大夏普比率组合对应的权重,并转化为numpy数组
|
595
|
-
import numpy as np
|
596
|
-
MSR_weights = np.array(RandomPortfolios.iloc[max_index, 0:numstocks])
|
597
|
-
# 计算MSR组合的收益
|
598
|
-
StockReturns['Portfolio_MSR'] = stock_return.mul(MSR_weights, axis=1).sum(axis=1)
|
599
|
-
|
600
|
-
# 找到标准差最小数据的索引值
|
601
|
-
min_index = RandomPortfolios.Volatility.idxmin()
|
602
|
-
# 提取最小波动组合对应的权重, 并转换成Numpy数组
|
603
|
-
# 在收益-风险散点图中突出风险最小的点
|
604
|
-
GMV_x = RandomPortfolios.loc[min_index,'Volatility']
|
605
|
-
GMV_y = RandomPortfolios.loc[min_index,'Returns']
|
606
|
-
plt.scatter(GMV_x, GMV_y, color='m',marker='8',s=100,label="GMV Point")
|
607
|
-
# 提取最小风险组合对应的权重,并转化为numpy数组
|
608
|
-
GMV_weights = np.array(RandomPortfolios.iloc[min_index, 0:numstocks])
|
609
|
-
# 计算GMV投资组合收益
|
610
|
-
StockReturns['Portfolio_GMV'] = stock_return.mul(GMV_weights, axis=1).sum(axis=1)
|
611
|
-
|
612
|
-
plt.title("投资组合: MSR点和GMV点的位置")
|
613
|
-
plt.ylabel("预期收益")
|
614
|
-
plt.xlabel("预期风险-->"+ \
|
615
|
-
"\n观察期:"+hstart+"至"+hend+ \
|
616
|
-
"\n数据来源: 新浪/stooq, "+str(today))
|
617
|
-
plt.legend()
|
618
|
-
plt.show()
|
619
|
-
|
620
|
-
# 绘制累积收益曲线
|
621
|
-
namelist=['Portfolio_MSR','Portfolio_GMV','Portfolio']
|
622
|
-
titletxt="投资组合: 累计收益率的比较"
|
623
|
-
ylabeltxt="累计收益率"
|
624
|
-
xlabeltxt="观察期:"+hstart+"至"+hend+ \
|
625
|
-
"\n数据来源: 新浪/stooq, "+str(today)
|
626
|
-
cumulative_returns_plot(StockReturns,namelist,titletxt,ylabeltxt,xlabeltxt)
|
627
|
-
|
628
|
-
#输出MSR投资组合构成比例
|
629
|
-
print("\n===== 投资组合的构造方式: 最高夏普比率(MSR) =====")
|
630
|
-
print("成分股:",codetranslate(tickerlist))
|
631
|
-
MSR_weights_new=ratiolist_round(MSR_weights,3)
|
632
|
-
print("权重:",MSR_weights_new)
|
633
|
-
print("观察期:",hstart+"至"+hend)
|
634
|
-
print("预期收益:",round(MSR_y*100.0,2),'\b%')
|
635
|
-
print("预期风险:",round(MSR_x*100.0,2),'\b%')
|
636
|
-
|
637
|
-
#import datetime as dt; today=dt.date.today()
|
638
|
-
print("*数据来源:新浪/stooq,"+str(today))
|
639
|
-
|
640
|
-
#输出GMV投资组合构成比例
|
641
|
-
print("\n===== 投资组合的构造方式: 风险最小化(GMV) =====")
|
642
|
-
print("成分股:",codetranslate(tickerlist))
|
643
|
-
GMV_weights_new=ratiolist_round(GMV_weights,3)
|
644
|
-
print("权重:",GMV_weights_new)
|
645
|
-
print("观察期:",hstart+"至"+hend)
|
646
|
-
print("预期收益:",round(GMV_y*100.0,2),'\b%')
|
647
|
-
print("预期风险:",round(GMV_x*100.0,2),'\b%')
|
648
|
-
print("*数据来源:新浪/stooq,"+str(today))
|
649
|
-
|
650
|
-
return StockReturns
|
651
|
-
|
652
|
-
#==============================================================================
|
653
|
-
def translate_tickerlist(tickerlist):
|
654
|
-
newlist=[]
|
655
|
-
for t in tickerlist:
|
656
|
-
name=codetranslate(t)
|
657
|
-
newlist=newlist+[name]
|
658
|
-
|
659
|
-
return newlist
|
660
|
-
#==============================================================================
|
661
|
-
# 绘制马科维茨有效边界
|
662
|
-
#==============================================================================
|
663
|
-
def ret_monthly(ticker,prices):
|
664
|
-
"""
|
665
|
-
功能:
|
666
|
-
"""
|
667
|
-
price=prices['Adj Close'][ticker]
|
668
|
-
|
669
|
-
import numpy as np
|
670
|
-
div=price.pct_change()+1
|
671
|
-
logret=np.log(div)
|
672
|
-
import pandas as pd
|
673
|
-
lrdf=pd.DataFrame(logret)
|
674
|
-
lrdf['ymd']=lrdf.index.astype("str")
|
675
|
-
lrdf['ym']=lrdf['ymd'].apply(lambda x:x[0:7])
|
676
|
-
lrdf.dropna(inplace=True)
|
677
|
-
|
678
|
-
mret=lrdf.groupby(by=['ym'])[ticker].sum()
|
679
|
-
|
680
|
-
return mret
|
681
|
-
|
682
|
-
if __name__=='__main__':
|
683
|
-
ticker='MSFT'
|
684
|
-
fromdate,todate='2019-1-1','2020-8-1'
|
685
|
-
|
686
|
-
#==============================================================================
|
687
|
-
def objFunction(W,R,target_ret):
|
688
|
-
|
689
|
-
import numpy as np
|
690
|
-
stock_mean=np.mean(R,axis=0)
|
691
|
-
port_mean=np.dot(W,stock_mean) # portfolio mean
|
692
|
-
|
693
|
-
cov=np.cov(R.T) # var-cov matrix
|
694
|
-
port_var=np.dot(np.dot(W,cov),W.T) # portfolio variance
|
695
|
-
penalty = 2000*abs(port_mean-target_ret)# penalty 4 deviation
|
696
|
-
|
697
|
-
objfunc=np.sqrt(port_var) + penalty # objective function
|
698
|
-
|
699
|
-
return objfunc
|
700
|
-
|
701
|
-
#==============================================================================
|
702
|
-
def portfolio_ef_0(stocks,fromdate,todate):
|
703
|
-
"""
|
704
|
-
功能:绘制马科维茨有效前沿,不区分上半沿和下半沿
|
705
|
-
"""
|
706
|
-
#Code for getting stock prices
|
707
|
-
prices=get_prices(stocks,fromdate,todate)
|
708
|
-
|
709
|
-
#Code for generating a return matrix R
|
710
|
-
R0=ret_monthly(stocks[0],prices) # starting from 1st stock
|
711
|
-
n_stock=len(stocks) # number of stocks
|
712
|
-
import pandas as pd
|
713
|
-
import numpy as np
|
714
|
-
for i in range(1,n_stock): # merge with other stocks
|
715
|
-
x=ret_monthly(stocks[i],prices)
|
716
|
-
R0=pd.merge(R0,x,left_index=True,right_index=True)
|
717
|
-
R=np.array(R0)
|
718
|
-
|
719
|
-
#Code for estimating optimal portfolios for a given return
|
720
|
-
out_mean,out_std,out_weight=[],[],[]
|
721
|
-
import numpy as np
|
722
|
-
stockMean=np.mean(R,axis=0)
|
723
|
-
|
724
|
-
from scipy.optimize import minimize
|
725
|
-
for r in np.linspace(np.min(stockMean),np.max(stockMean),num=100):
|
726
|
-
W = np.ones([n_stock])/n_stock # starting from equal weights
|
727
|
-
b_ = [(0,1) for i in range(n_stock)] # bounds, here no short
|
728
|
-
c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. }) #constraint
|
729
|
-
result=minimize(objFunction,W,(R,r),method='SLSQP'
|
730
|
-
,constraints=c_, bounds=b_)
|
731
|
-
if not result.success: # handle error raise
|
732
|
-
BaseException(result.message)
|
733
|
-
|
734
|
-
out_mean.append(round(r,4)) # 4 decimal places
|
735
|
-
std_=round(np.std(np.sum(R*result.x,axis=1)),6)
|
736
|
-
out_std.append(std_)
|
737
|
-
out_weight.append(result.x)
|
738
|
-
|
739
|
-
#Code for plotting the efficient frontier
|
740
|
-
|
741
|
-
plt.title('Efficient Frontier of Portfolio')
|
742
|
-
plt.xlabel('Standard Deviation of portfolio (Risk))')
|
743
|
-
plt.ylabel('Return of portfolio')
|
744
|
-
|
745
|
-
out_std_min=min(out_std)
|
746
|
-
pos=out_std.index(out_std_min)
|
747
|
-
out_mean_min=out_mean[pos]
|
748
|
-
x_left=out_std_min+0.25
|
749
|
-
y_left=out_mean_min+0.5
|
750
|
-
|
751
|
-
#plt.figtext(x_left,y_left,str(n_stock)+' stock are used: ')
|
752
|
-
plt.figtext(x_left,y_left,"投资组合由"+str(n_stock)+'种证券构成: ')
|
753
|
-
plt.figtext(x_left,y_left-0.05,' '+str(stocks))
|
754
|
-
plt.figtext(x_left,y_left-0.1,'观察期间:'+str(fromdate)+'至'+str(todate))
|
755
|
-
plt.plot(out_std,out_mean,color='r',ls=':',lw=4)
|
756
|
-
plt.show()
|
757
|
-
|
758
|
-
return
|
759
|
-
|
760
|
-
if __name__=='__main__':
|
761
|
-
stocks=['IBM','WMT','AAPL','C','MSFT']
|
762
|
-
fromdate,todate='2019-1-1','2020-8-1'
|
763
|
-
|
764
|
-
#==============================================================================
|
765
|
-
def portfolio_ef(stocks,fromdate,todate):
|
766
|
-
"""
|
767
|
-
功能:多只股票的马科维茨有效边界,区分上半沿和下半沿,标记风险极小点
|
768
|
-
"""
|
769
|
-
print("\n...Searching for portfolio information, please wait...")
|
770
|
-
#Code for getting stock prices
|
771
|
-
prices=get_prices(stocks,fromdate,todate)
|
772
|
-
|
773
|
-
#Code for generating a return matrix R
|
774
|
-
R0=ret_monthly(stocks[0],prices) # starting from 1st stock
|
775
|
-
n_stock=len(stocks) # number of stocks
|
776
|
-
|
777
|
-
import pandas as pd
|
778
|
-
import numpy as np
|
779
|
-
for i in range(1,n_stock): # merge with other stocks
|
780
|
-
x=ret_monthly(stocks[i],prices)
|
781
|
-
R0=pd.merge(R0,x,left_index=True,right_index=True)
|
782
|
-
R=np.array(R0)
|
783
|
-
|
784
|
-
#Code for estimating optimal portfolios for a given return
|
785
|
-
out_mean,out_std,out_weight=[],[],[]
|
786
|
-
stockMean=np.mean(R,axis=0)
|
787
|
-
|
788
|
-
from scipy.optimize import minimize
|
789
|
-
for r in np.linspace(np.min(stockMean),np.max(stockMean),num=100):
|
790
|
-
W = np.ones([n_stock])/n_stock # starting from equal weights
|
791
|
-
b_ = [(0,1) for i in range(n_stock)] # bounds, here no short
|
792
|
-
c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. }) #constraint
|
793
|
-
result=minimize(objFunction,W,(R,r),method='SLSQP'
|
794
|
-
,constraints=c_, bounds=b_)
|
795
|
-
if not result.success: # handle error raise
|
796
|
-
BaseException(result.message)
|
797
|
-
|
798
|
-
out_mean.append(round(r,4)) # 4 decimal places
|
799
|
-
std_=round(np.std(np.sum(R*result.x,axis=1)),6)
|
800
|
-
out_std.append(std_)
|
801
|
-
out_weight.append(result.x)
|
802
|
-
|
803
|
-
#Code for positioning
|
804
|
-
out_std_min=min(out_std)
|
805
|
-
pos=out_std.index(out_std_min)
|
806
|
-
out_mean_min=out_mean[pos]
|
807
|
-
x_left=out_std_min+0.25
|
808
|
-
y_left=out_mean_min+0.5
|
809
|
-
|
810
|
-
import pandas as pd
|
811
|
-
out_df=pd.DataFrame(out_mean,out_std,columns=['mean'])
|
812
|
-
out_df_ef=out_df[out_df['mean']>=out_mean_min]
|
813
|
-
out_df_ief=out_df[out_df['mean']<out_mean_min]
|
814
|
-
|
815
|
-
#Code for plotting the efficient frontier
|
816
|
-
|
817
|
-
plt.title('投资组合:马科维茨有效边界')
|
818
|
-
|
819
|
-
import datetime as dt; today=dt.date.today()
|
820
|
-
plt.xlabel('预期风险-->'+"\n数据来源:新浪/stooq, "+str(today))
|
821
|
-
plt.ylabel('预期收益')
|
822
|
-
|
823
|
-
plt.figtext(x_left,y_left,"投资组合由"+str(n_stock)+'种证券构成: ')
|
824
|
-
plt.figtext(x_left,y_left-0.05,' '+str(stocks))
|
825
|
-
plt.figtext(x_left,y_left-0.1,'观察期间:'+str(fromdate)+'至'+str(todate))
|
826
|
-
plt.plot(out_df_ef.index,out_df_ef['mean'],color='r',ls='--',lw=2,label='有效边界')
|
827
|
-
plt.plot(out_df_ief.index,out_df_ief['mean'],color='k',ls=':',lw=2,label='无效边界')
|
828
|
-
plt.plot(out_std_min,out_mean_min,'g*-',markersize=16,label='风险最低点')
|
829
|
-
|
830
|
-
plt.legend(loc='best')
|
831
|
-
plt.show()
|
832
|
-
|
833
|
-
return out_df
|
834
|
-
|
835
|
-
if __name__=='__main__':
|
836
|
-
stocks=['IBM','WMT','AAPL','C','MSFT']
|
837
|
-
fromdate,todate='2019-1-1','2020-8-1'
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|