siat 3.10.131__py3-none-any.whl → 3.10.133__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/markowitz2.py CHANGED
@@ -21,6 +21,7 @@ from siat.common import *
21
21
  from siat.translate import *
22
22
  from siat.security_prices import *
23
23
  from siat.security_price2 import *
24
+ from siat.grafix import *
24
25
  #from siat.fama_french import *
25
26
 
26
27
  import pandas as pd
@@ -172,6 +173,7 @@ def cumulative_returns_plot(retgroup,name_list="",titletxt="投资组合策略
172
173
  xlabeltxt=footnote1+footnote2
173
174
 
174
175
  # 持有收益曲线绘制函数
176
+ """
175
177
  lslist=['-','--',':','-.']
176
178
  markerlist=['.','h','+','x','4','3','2','1']
177
179
  for name in name_list:
@@ -196,6 +198,22 @@ def cumulative_returns_plot(retgroup,name_list="",titletxt="投资组合策略
196
198
 
197
199
  plt.gca().set_facecolor(facecolor)
198
200
  plt.show()
201
+ """
202
+ import pandas as pd
203
+ df=pd.DataFrame()
204
+ for name in name_list:
205
+ # 计算持有收益率
206
+ CumulativeReturns = ((1+retgroup[name]).cumprod()-1)
207
+
208
+ df[name]=CumulativeReturns
209
+
210
+ draw_lines(df,y_label=ylabeltxt,x_label=xlabeltxt, \
211
+ axhline_value=0,axhline_label='', \
212
+ title_txt=titletxt, \
213
+ annotate=True, \
214
+ annotate_value=False, \
215
+ facecolor=facecolor, \
216
+ )
199
217
 
200
218
  return
201
219
 
@@ -300,8 +318,8 @@ def portfolio_build(portfolio,thedate='default',pastyears=3, \
300
318
  获取股价时的信息
301
319
  是否显示原始组合、等权重组合和交易金额加权组合的成分股构成
302
320
  是否显示原始组合、等权重组合和交易金额加权组合的收益风险排名
303
- 3. pastyears=3更有可能生成斜向上的椭圆形可行集,需要与不同行业的证券搭配。
304
- 同行业证券相关性较强,不易生成斜向上的椭圆形可行集。
321
+ 3. pastyears=3更有可能生成斜向上的椭圆形可行集,短于3形状不佳,长于3改善形状有限。
322
+ 需要与不同行业的证券搭配。同行业证券相关性较强,不易生成斜向上的椭圆形可行集。
305
323
  4. 若ticker_type='fund'可能导致无法处理股票的复权价!
306
324
  5. 若要指定特定的证券为债券,则需要使用列表逐一指定证券的类型(股票,债券,基金)
307
325
  6. 默认采用前复权计算收益率,更加平稳
@@ -328,7 +346,7 @@ def portfolio_build(portfolio,thedate='default',pastyears=3, \
328
346
  print(" #Warning(portfolio_build): invalid date",thedate)
329
347
  return None
330
348
 
331
- print(" Searching for portfolio info, which may take time ...")
349
+ print(f" Searching portfolio info for recent {pastyears} years ...")
332
350
  # 解构投资组合
333
351
  scope,_,tickerlist,sharelist0,ticker_type=decompose_portfolio(portfolio)
334
352
  pname=portfolio_name(portfolio)
@@ -1150,7 +1168,7 @@ if __name__=='__main__':
1150
1168
 
1151
1169
  portfolio_eset(pf_info,simulation=50000)
1152
1170
 
1153
- def portfolio_feset(pf_info,simulation=50000,convex_hull=True,frontier="both",facecolor='papayawhip'):
1171
+ def portfolio_feset(pf_info,simulation=10000,convex_hull=True,frontier="both",facecolor='papayawhip'):
1154
1172
  """
1155
1173
  功能:套壳函数portfolio_eset
1156
1174
  当frontier不在列表['efficient','inefficient','both']中时,绘制可行集
@@ -1158,6 +1176,7 @@ def portfolio_feset(pf_info,simulation=50000,convex_hull=True,frontier="both",fa
1158
1176
  当frontier == 'inefficient'时绘制无效边界
1159
1177
  当frontier == 'both'时同时绘制有效边界和无效边界
1160
1178
  当绘制有效/无效边界时,默认使用凸包绘制(convex_hull=True)
1179
+ 注:若可行集形状不佳,首先尝试pastyears=3,再尝试增加simulation数量
1161
1180
  """
1162
1181
  if frontier is None:
1163
1182
  convex_hull=False
@@ -1211,7 +1230,7 @@ def portfolio_eset(pf_info,simulation=50000,convex_hull=False,frontier="both",fa
1211
1230
  np.random.seed(RANDOM_SEED)
1212
1231
 
1213
1232
  # 循环模拟n次随机的投资组合
1214
- print(" Calculating portfolio feasible/efficient set, please wait ...")
1233
+ print(f" Simulating {simulation} feasible sets of portfolios ...")
1215
1234
  for i in range(simulation):
1216
1235
  # 生成numstocks个随机数,并归一化,得到一组随机的权重数据
1217
1236
  random9 = np.random.random(numstocks)
@@ -1267,6 +1286,8 @@ def portfolio_eset(pf_info,simulation=50000,convex_hull=False,frontier="both",fa
1267
1286
  pf_returns_list=list(pf_returns)
1268
1287
  points=[]
1269
1288
  for x in pf_volatilities_list:
1289
+ print_progress_percent2(x,pf_volatilities_list,steps=5,leading_blanks=4)
1290
+
1270
1291
  pos=pf_volatilities_list.index(x)
1271
1292
  y=pf_returns_list[pos]
1272
1293
  points=points+[[x,y]]
@@ -1348,7 +1369,10 @@ def portfolio_eset(pf_info,simulation=50000,convex_hull=False,frontier="both",fa
1348
1369
  else:
1349
1370
  pass
1350
1371
  else:
1351
- print('')
1372
+ pass
1373
+
1374
+ # 空一行
1375
+ print('')
1352
1376
 
1353
1377
 
1354
1378
  import datetime as dt; stoday=dt.date.today()
@@ -1393,11 +1417,11 @@ def portfolio_eset(pf_info,simulation=50000,convex_hull=False,frontier="both",fa
1393
1417
  titletxt0=": Markowitz Feasible Set"
1394
1418
 
1395
1419
  plt.colorbar(label='Return/Std')
1396
- plt.title(pname+": Feasible/Efficient Set",fontsize=title_txt_size)
1420
+ plt.title(pname+titletxt0,fontsize=title_txt_size)
1397
1421
  plt.ylabel("Annualized Return",fontsize=ylabel_txt_size)
1398
1422
 
1399
1423
  footnote1="Annualized Std -->\n\n"
1400
- footnote2="Based on given securities, constructed "+str(simulation)+" portfolios\n"
1424
+ footnote2="Built "+str(simulation)+" portfolios of given securities\n"
1401
1425
  footnote3="Period of sample: "+hstart+" to "+hend
1402
1426
  footnote4="\nData source: Sina/EM/Stooq/Yahoo, "+str(stoday)
1403
1427
 
@@ -1429,7 +1453,7 @@ def portfolio_es_sharpe(pf_info,simulation=50000,RF=0):
1429
1453
  """
1430
1454
  功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年均风险溢价及其标准差,绘制投资组合的可行集
1431
1455
  """
1432
- print(" Calculating possible portfolio combinations, please wait ...")
1456
+ print(f" Calculating {simulation} portfolio combinations ...")
1433
1457
 
1434
1458
  [[portfolio,thedate,stock_return0,rf_df,_],_]=pf_info
1435
1459
  pname=portfolio_name(portfolio)
@@ -1523,7 +1547,7 @@ def portfolio_es_sortino(pf_info,simulation=50000,RF=0):
1523
1547
  """
1524
1548
  功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年均风险溢价及其下偏标准差,绘制投资组合的可行集
1525
1549
  """
1526
- print(" Calculating possible portfolio combinations, please wait ...")
1550
+ print(f" Calculating {simulation} portfolio combinations ...")
1527
1551
 
1528
1552
  [[portfolio,thedate,stock_return0,rf_df,_],_]=pf_info
1529
1553
  pname=portfolio_name(portfolio)
@@ -1621,9 +1645,10 @@ if __name__=='__main__':
1621
1645
 
1622
1646
  def portfolio_es_alpha(pf_info,simulation=50000,RF=0):
1623
1647
  """
1624
- 功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年化标准差和阿尔法指数,绘制投资组合的可行集
1648
+ 功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年化标准差和阿尔法指数,
1649
+ 绘制投资组合的可行集
1625
1650
  """
1626
- print(" Calculating possible portfolio combinations, please wait ...")
1651
+ print(f" Calculating {simulation} portfolio combinations ...")
1627
1652
 
1628
1653
  [[portfolio,thedate,stock_return0,rf_df,_],_]=pf_info
1629
1654
  pname=portfolio_name(portfolio)
@@ -1636,7 +1661,18 @@ def portfolio_es_alpha(pf_info,simulation=50000,RF=0):
1636
1661
  #计算市场指数的收益率
1637
1662
  import pandas as pd
1638
1663
  start1=date_adjust(hstart,adjust=-30)
1639
- mkt=get_prices(mktidx,start1,hend)
1664
+
1665
+ import os, sys
1666
+ class HiddenPrints:
1667
+ def __enter__(self):
1668
+ self._original_stdout = sys.stdout
1669
+ sys.stdout = open(os.devnull, 'w')
1670
+
1671
+ def __exit__(self, exc_type, exc_val, exc_tb):
1672
+ sys.stdout.close()
1673
+ sys.stdout = self._original_stdout
1674
+ with HiddenPrints():
1675
+ mkt=get_prices(mktidx,start1,hend)
1640
1676
  mkt['Mkt']=mkt['Close'].pct_change()
1641
1677
  mkt.dropna(inplace=True)
1642
1678
  mkt1=pd.DataFrame(mkt['Mkt'])
@@ -1741,7 +1777,7 @@ def portfolio_es_treynor(pf_info,simulation=50000,RF=0):
1741
1777
  """
1742
1778
  功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的风险溢价和贝塔系数,绘制投资组合的可行集
1743
1779
  """
1744
- print(" Calculating possible portfolio combinations, please wait ...")
1780
+ print(f" Calculating {simulation} portfolio combinations ...")
1745
1781
 
1746
1782
  [[portfolio,_,stock_return0,rf_df,_],_]=pf_info
1747
1783
  pname=portfolio_name(portfolio)
@@ -1754,7 +1790,18 @@ def portfolio_es_treynor(pf_info,simulation=50000,RF=0):
1754
1790
  #计算市场指数的收益率
1755
1791
  import pandas as pd
1756
1792
  start1=date_adjust(hstart,adjust=-30)
1757
- mkt=get_prices(mktidx,start1,hend)
1793
+
1794
+ import os, sys
1795
+ class HiddenPrints:
1796
+ def __enter__(self):
1797
+ self._original_stdout = sys.stdout
1798
+ sys.stdout = open(os.devnull, 'w')
1799
+
1800
+ def __exit__(self, exc_type, exc_val, exc_tb):
1801
+ sys.stdout.close()
1802
+ sys.stdout = self._original_stdout
1803
+ with HiddenPrints():
1804
+ mkt=get_prices(mktidx,start1,hend)
1758
1805
  mkt['Mkt']=mkt['Close'].pct_change()
1759
1806
  mkt.dropna(inplace=True)
1760
1807
  mkt1=pd.DataFrame(mkt['Mkt'])
@@ -1833,6 +1880,8 @@ def portfolio_es_treynor(pf_info,simulation=50000,RF=0):
1833
1880
  # 设置数据框RandomPortfolios每一列的名称
1834
1881
  RandomPortfolios.columns = [ticker + "_weight" for ticker in tickerlist] \
1835
1882
  + ['Risk premium', 'beta']
1883
+ # 新增
1884
+ RandomPortfolios['treynor']=RandomPortfolios['Risk premium']/RandomPortfolios['beta']
1836
1885
 
1837
1886
  return [pf_info,RandomPortfolios]
1838
1887
 
@@ -1898,6 +1947,8 @@ def RandomPortfolios_plot(RandomPortfolios,col_x,col_y,colorbartxt,title_ext, \
1898
1947
  pf_returns_list=list(pf_returns)
1899
1948
  points=[]
1900
1949
  for x in pf_volatilities_list:
1950
+ print_progress_percent2(x,pf_volatilities_list,steps=5,leading_blanks=4)
1951
+
1901
1952
  pos=pf_volatilities_list.index(x)
1902
1953
  y=pf_returns_list[pos]
1903
1954
  points=points+[[x,y]]
@@ -1960,7 +2011,10 @@ def RandomPortfolios_plot(RandomPortfolios,col_x,col_y,colorbartxt,title_ext, \
1960
2011
  pass
1961
2012
  else:
1962
2013
  #无convex hull
1963
- print('')
2014
+ pass
2015
+
2016
+ # 空一行
2017
+ print('')
1964
2018
 
1965
2019
  lang = check_language()
1966
2020
  if lang == 'Chinese':
@@ -1982,7 +2036,7 @@ def RandomPortfolios_plot(RandomPortfolios,col_x,col_y,colorbartxt,title_ext, \
1982
2036
 
1983
2037
  import datetime as dt; stoday=dt.date.today()
1984
2038
  footnote1=x_axis_name+" -->\n\n"
1985
- footnote2="Based on given securities, constructed "+str(simulation)+" portfolios"
2039
+ footnote2="Built "+str(simulation)+" portfolios of given securities"
1986
2040
  footnote3="\nPeriod of sample: "+hstart+" to "+hend
1987
2041
  footnote4="\nData source: Sina/EM/Stooq/Yahoo, "+str(stoday)
1988
2042
 
@@ -2213,8 +2267,9 @@ def portfolio_optimize_alpha(es_info,graph=True,convex_hull=False,frontier='effi
2213
2267
  """
2214
2268
 
2215
2269
  #需要定制:定义名称变量......................................................
2216
- col_ratio='Sharpe' #指数名称
2217
- col_y='alpha' #指数分子
2270
+ col_ratio='Alpha' #指数名称
2271
+ col_y='alpha' #指数分子
2272
+ #col_y='Risk premium' #指数分子
2218
2273
  col_x='beta' #指数分母
2219
2274
 
2220
2275
  name_hiret='MAR' #Maximum Alpha Ratio,指数最高点
@@ -2261,15 +2316,16 @@ def portfolio_optimize_treynor(es_info,graph=True,convex_hull=False,frontier='ef
2261
2316
 
2262
2317
  #需要定制:定义名称变量......................................................
2263
2318
  col_ratio='Treynor' #指数名称
2264
- col_y='Risk premium' #指数分子
2265
- col_x='beta' #指数分母
2319
+ col_y='treynor' #指数:直接做纵轴
2320
+ col_x='beta' #做横轴
2266
2321
 
2267
2322
  name_hiret='MTR' #Maximum Treynor Ratio,指数最高点
2268
2323
  name_lorisk='GMBT' #Global Minimum Beta in Treynor,风险最低点
2269
2324
 
2270
2325
  title_ext=text_lang("特雷诺比率","Treynor Ratio") #用于标题区别
2271
2326
  colorbartxt=text_lang("特雷诺比率","Treynor Ratio") #用于彩色棒标签
2272
- ylabeltxt=text_lang("年化风险溢价","Annualized Risk Premium") #用于纵轴名称
2327
+ #ylabeltxt=text_lang("年化风险溢价","Annualized Risk Premium") #用于纵轴名称
2328
+ ylabeltxt=text_lang("特雷诺比率·","Treynor Ratio")
2273
2329
  x_axis_name=text_lang("贝塔系数","Beta") #用于横轴名称
2274
2330
  #定制部分结束...............................................................
2275
2331
 
@@ -2320,9 +2376,9 @@ def portfolio_optimize_rar(es_info,col_ratio,col_y,col_x,name_hiret,name_lorisk,
2320
2376
  hend0=StockReturns.index[-1]; hend=str(hend0.strftime("%Y-%m-%d"))
2321
2377
 
2322
2378
  #识别并计算指数..........................................................
2323
- if col_ratio in ['Alpha']:
2379
+ if col_ratio.title() in ['Treynor','Alpha']:
2324
2380
  RandomPortfolios[col_ratio] = RandomPortfolios[col_y]
2325
- elif col_ratio in ['Treynor','Sharpe','Sortino']:
2381
+ elif col_ratio.title() in ['Sharpe','Sortino']:
2326
2382
  RandomPortfolios[col_ratio] = RandomPortfolios[col_y] / RandomPortfolios[col_x]
2327
2383
  else:
2328
2384
  print(" #Error(portfolio_optimize_rar): invalid rar",col_ratio)
@@ -2399,27 +2455,39 @@ if __name__=='__main__':
2399
2455
  simulation=1000
2400
2456
  simulation=50000
2401
2457
 
2402
- RF=0
2403
- graph=True;hirar_return=False;lorisk=True
2404
- convex_hull=False
2458
+ pf_info0=pf_info
2459
+ ratio='treynor'
2460
+
2461
+ simulation=10000
2462
+ RF=0.046
2463
+ graph=True
2464
+ hirar_return=True; lorisk=True
2465
+ convex_hull=True; frontier='efficient'; facecolor='papayawhip'
2466
+
2467
+
2405
2468
 
2406
- def portfolio_optimize(pf_info,ratio='sharpe',simulation=50000,RF=0, \
2469
+ def portfolio_optimize(pf_info0,ratio='sharpe',simulation=10000,RF=0, \
2407
2470
  graph=True,hirar_return=False,lorisk=True, \
2408
2471
  convex_hull=True,frontier='efficient',facecolor='papayawhip'):
2409
2472
  """
2410
2473
  功能:集成式投资组合优化策略
2411
2474
  注意:实验发现RF较小时对于结果的影响极其微小难以观察,默认设为不使用无风险利率调整收益
2412
- 但RF较大时对于结果的影响明显变大,已经不能忽略!
2475
+ 但RF较大时对于结果的影响明显变大,已经不能忽略!
2476
+ 若可行集形状不佳,优先尝试pastyears=3,再尝试增加simulation次数。
2477
+ simulation数值过大时将导致速度太慢。
2413
2478
  """
2414
- ratio=ratio.lower()
2479
+ # 防止原始数据被修改
2480
+ import copy
2481
+ pf_info=copy.deepcopy(pf_info0)
2415
2482
 
2416
2483
  ratio_list=['treynor','sharpe','sortino','alpha']
2484
+ ratio=ratio.lower()
2417
2485
  if not (ratio in ratio_list):
2418
2486
  print(" #Error(portfolio_optimize_strategy): invalid strategy ratio",ratio)
2419
2487
  print(" Supported strategy ratios",ratio_list)
2420
2488
  return
2421
2489
 
2422
- print(" Optimizing portfolio configuration based on",ratio,"ratio ...")
2490
+ print(" Optimizing portfolio configuration by",ratio,"ratio ...")
2423
2491
 
2424
2492
  [[portfolio,_,_,_,_],_]=pf_info
2425
2493
  pname=portfolio_name(portfolio)
@@ -2555,7 +2623,7 @@ def objFunction(W,R,target_ret):
2555
2623
  def portfolio_ef_0(stocks,fromdate,todate):
2556
2624
  """
2557
2625
  功能:绘制马科维茨有效前沿,不区分上半沿和下半沿
2558
- 问题:很可能出现上下边界折叠的情况,难以解释,弃用
2626
+ 问题:很可能出现上下边界折叠的情况,难以解释,弃用!!!
2559
2627
  """
2560
2628
  #Code for getting stock prices
2561
2629
  prices=get_prices(stocks,fromdate,todate)
@@ -2630,7 +2698,7 @@ if __name__=='__main__':
2630
2698
  def portfolio_ef(stocks,fromdate,todate):
2631
2699
  """
2632
2700
  功能:多只股票的马科维茨有效边界,区分上半沿和下半沿,标记风险极小点
2633
- 问题:很可能出现上下边界折叠的情况,难以解释,弃用
2701
+ 问题:很可能出现上下边界折叠的情况,难以解释,弃用!!!
2634
2702
  """
2635
2703
  print("\n Searching for portfolio information, please wait...")
2636
2704
  #Code for getting stock prices
@@ -2877,6 +2945,10 @@ def security_correlation(tickers,start='L5Y',end='today',info_type='Close', \
2877
2945
  if __name__ =="__main__":
2878
2946
  portfolio={'Market':('US','^GSPC','Test 1'),'EDU':0.4,'TAL':0.3,'TEDU':0.2}
2879
2947
 
2948
+ def portfolio_describe(portfolio):
2949
+ describe_portfolio(portfolio)
2950
+ return
2951
+
2880
2952
  def describe_portfolio(portfolio):
2881
2953
  """
2882
2954
  功能:描述投资组合的信息
@@ -2887,27 +2959,34 @@ def describe_portfolio(portfolio):
2887
2959
  scope,mktidx,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
2888
2960
  pname=portfolio_name(portfolio)
2889
2961
 
2890
- print("*** 投资组合信息:",pname)
2891
- print("\n所在市场:",ectranslate(scope))
2892
- print("市场指数:",ticker_name(mktidx,'bond')+'('+mktidx+')')
2893
- print("成分股及其份额:")
2962
+ print(text_lang("*** 投资组合名称:","*** Portfolio name:"),pname)
2963
+ print(text_lang("所在市场:","Market:"),ectranslate(scope))
2964
+ print(text_lang("市场指数:","Market index:"),ticker_name(mktidx,'bond')+'('+mktidx+')')
2965
+ print(text_lang("\n*** 成分股及其份额:","\n*** Members and shares:"))
2894
2966
 
2895
2967
  num=len(tickerlist)
2896
2968
  #seqlist=[]
2897
2969
  tickerlist1=[]
2898
2970
  sharelist1=[]
2971
+ totalshares=0
2899
2972
  for t in range(num):
2900
2973
  #seqlist=seqlist+[t+1]
2901
2974
  tickerlist1=tickerlist1+[ticker_name(tickerlist[t],'bond')+'('+tickerlist[t]+')']
2902
- sharelist1=sharelist1+[str(round(sharelist[t],2))+'%']
2975
+ sharelist1=sharelist1+[str(round(sharelist[t]*100,2))+'%']
2976
+
2977
+ totalshares=totalshares+sharelist[t]
2903
2978
 
2904
2979
  import pandas as pd
2905
2980
  #df=pd.DataFrame({'序号':seqlist,'成分股':tickerlist1,'份额':sharelist1})
2906
- df=pd.DataFrame({'成分股':tickerlist1,'份额':sharelist1})
2981
+ df=pd.DataFrame({text_lang('成分股','Members'):tickerlist1,text_lang('份额','Shares'):sharelist1})
2907
2982
  df.index=df.index+1
2908
2983
 
2909
2984
  alignlist=['center','left','right']
2910
2985
  print(df.to_markdown(index=True,tablefmt='plain',colalign=alignlist))
2986
+
2987
+ print("*** "+text_lang("成分股份额总和:","Total shares: ")+str(totalshares*100)+'%')
2988
+ if totalshares != 1:
2989
+ print(" #Warning: total shares is expecting to be 100%")
2911
2990
 
2912
2991
  return
2913
2992
 
@@ -2941,6 +3020,128 @@ def portfolio_drop(portfolio,last=0,droplist=[],new_name=''):
2941
3020
 
2942
3021
  return portfolio_new
2943
3022
 
3023
+ #==============================================================================
3024
+ def portfolio_define(name='My Portfolio', \
3025
+ market='CN', \
3026
+ market_index='000001.SS', \
3027
+ members={}, \
3028
+ check=False):
3029
+ """
3030
+ 功能:定义一个投资组合
3031
+ 参数:
3032
+ name: 投资组合的名字
3033
+ economy_entity: 投资组合的成分股所在的经济体
3034
+ market_index: 经济体的代表性市场指数
3035
+ members: 数据字典,投资组合的各个成分股代码及其所占的股数份额
3036
+
3037
+ 返回值:投资组合的字典型描述
3038
+ """
3039
+
3040
+ # 检查市场名称
3041
+ market=market.upper()
3042
+ if len(market) != 2:
3043
+ print(" #Warning(portfolio_define): need a country code of 2 letters")
3044
+ return None
3045
+
3046
+ #屏蔽函数内print信息输出的类
3047
+ import os, sys
3048
+ class HiddenPrints:
3049
+ def __enter__(self):
3050
+ self._original_stdout = sys.stdout
3051
+ sys.stdout = open(os.devnull, 'w')
3052
+
3053
+ def __exit__(self, exc_type, exc_val, exc_tb):
3054
+ sys.stdout.close()
3055
+ sys.stdout = self._original_stdout
3056
+
3057
+ error_flag=False
3058
+ govt_bond='1Y'+market+'Y.B'
3059
+ with HiddenPrints():
3060
+ rf_df=get_price_stooq(govt_bond,start='MRW')
3061
+ if not(rf_df is None):
3062
+ RF=round(rf_df['Close'].mean() / 100.0,6)
3063
+ print(f" Notice: recent annualized RF for {market} market is {RF} (or {round(RF*100,4)}%)")
3064
+ else:
3065
+ error_flag=True
3066
+ print(f" #Warning(portfolio_define): no RF info found for market {market}")
3067
+ print(" Solution: manually define annualized RF value without %")
3068
+
3069
+ # 检查是否存在成分股
3070
+ if not isinstance(members,dict):
3071
+ print(" #Warning(portfolio_define): invalid structure for portfolio members")
3072
+ return None
3073
+
3074
+ if len(members) == 0:
3075
+ print(" #Warning(portfolio_define): no members found in the portfolio")
3076
+ return None
3077
+
3078
+ try:
3079
+ keys=members.keys()
3080
+ values=members.values()
3081
+ except:
3082
+ print(" #Warning(portfolio_define): invalid dict for portfolio members")
3083
+ return None
3084
+ if len(keys) != len(values):
3085
+ print(" #Warning(portfolio_define): number of members and their portions mismatch")
3086
+ return None
3087
+
3088
+ marketdict={'Market':(market,market_index,name)}
3089
+ portfolio=dict(marketdict,**members)
3090
+
3091
+ if check:
3092
+ print(" Checking portfolio information ...")
3093
+ df=None
3094
+ with HiddenPrints():
3095
+ df=security_indicator(market_index,fromdate='MRW',graph=False)
3096
+ if df is None:
3097
+ error_flag=True
3098
+ print(f" #Warning(portfolio_define): market index {market_index} not found")
3099
+
3100
+ for t in keys:
3101
+ with HiddenPrints():
3102
+ df=security_indicator(t,fromdate='MRW',graph=False)
3103
+ if df is None:
3104
+ error_flag=True
3105
+ print(f" #Warning(portfolio_define): portfolio member {t} not found")
3106
+
3107
+ if not check:
3108
+ print(f" Notice: portfolio information not fully checked")
3109
+ else:
3110
+ if not error_flag:
3111
+ print(f" Congratulations! Portfolio is ready to go")
3112
+ else:
3113
+ print(f" #Warning(portfolio_define): there are issues in portfolio definition")
3114
+
3115
+ return portfolio,RF
3116
+
3117
+
3118
+ #==============================================================================
3119
+
3120
+ def portfolio_feasible(pf_info,simulation=10000,facecolor='papayawhip'):
3121
+ """
3122
+ 功能:绘制投资组合的可行集散点图,仅供教学演示,无实际用途
3123
+ """
3124
+ fset=portfolio_feset(pf_info,frontier=None, \
3125
+ simulation=simulation,facecolor=facecolor)
3126
+ return
3127
+ #==============================================================================
3128
+
3129
+ def portfolio_efficient(pf_info,frontier='Both',simulation=10000,facecolor='papayawhip'):
3130
+ """
3131
+ 功能:绘制投资组合的有效边界散点图,仅供教学演示,无实际用途
3132
+ """
3133
+ frontier=frontier.title()
3134
+ frontier_list=['Efficient','Inefficient','Both']
3135
+ if not (frontier in frontier_list):
3136
+ print(f" #Warning: invalid frontier {frontier}")
3137
+ print(f" Valid options for frontier: {frontier_list}")
3138
+ return
3139
+
3140
+ eset=portfolio_feset(pf_info,frontier=frontier, \
3141
+ simulation=simulation,facecolor=facecolor)
3142
+
3143
+ return
3144
+
2944
3145
  #==============================================================================
2945
3146
  #==============================================================================
2946
3147
  #==============================================================================