siat 3.1.23__py3-none-any.whl → 3.2.1__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
@@ -252,6 +252,23 @@ if __name__=='__main__':
252
252
  printout=True
253
253
  graph=False
254
254
 
255
+ #测试3
256
+ Market={'Market':('China','000300.SS','锂电池1号')}
257
+ Stocks={'300750.SZ':0.4,#宁德时代
258
+ '002594.SZ':0.3,#比亚迪
259
+ '300014.SZ':0.2,#亿纬锂能
260
+ '300207.SZ':0.1,#欣旺达
261
+ }
262
+ portfolio=dict(Market,**Stocks)
263
+
264
+ indicator='Adj Close'
265
+ adjust='qfq'; source='auto'; ticker_type='bond'
266
+ thedate='2024-6-19'
267
+ pastyears=2
268
+ printout=True
269
+ graph=False
270
+
271
+
255
272
  pf_info=portfolio_build(portfolio,thedate,pastyears,printout,graph)
256
273
 
257
274
  """
@@ -260,12 +277,30 @@ def portfolio_cumret(portfolio,thedate,pastyears=1, \
260
277
  printout=True,graph=True):
261
278
  """
262
279
  def portfolio_build(portfolio,thedate='default',pastyears=1, \
280
+ indicator='Adj Close', \
281
+ adjust='qfq',source='auto',ticker_type='auto', \
263
282
  printout=True,graph=False):
264
283
  """
265
284
  功能:收集投资组合成份股数据,绘制收益率趋势图,并与等权和期间内交易额加权策略组合比较
266
285
  注意:此处无需RF,待到优化策略时再指定
267
286
  printout=True控制获取股价时是否逐个显示
287
+
288
+ 特别注意:若ticker_type='fund'可能导致无法处理股票的复权价!
289
+ 若要指定特定的证券为债券,则需要使用列表逐一指定证券的类型(股票,债券,基金)
268
290
  """
291
+ #判断复权标志
292
+ indicator_list=['Close','Adj Close']
293
+ if indicator not in indicator_list:
294
+ print(" Warning(portfolio_build): invalid indicator",indicator)
295
+ print(" Supported indicator:",indicator_list)
296
+ indicator='Adj Close'
297
+
298
+ adjust_list=['','qfq','hfq']
299
+ if adjust not in adjust_list:
300
+ print(" Warning(portfolio_build): invalid indicator",adjust)
301
+ print(" Supported adjust:",adjust_list)
302
+ adjust='qfq'
303
+
269
304
  import datetime
270
305
  stoday = datetime.date.today()
271
306
  if thedate=='default':
@@ -329,18 +364,23 @@ def portfolio_build(portfolio,thedate='default',pastyears=1, \
329
364
 
330
365
  # 抓取投资组合股价
331
366
  #prices=get_prices(tickerlist,start,thedate)
332
-
367
+ #判断是否赚取复权价
368
+ if indicator == 'Adj Close' and adjust == '':
369
+ adjust='qfq'
370
+ if indicator == 'Close' and adjust != '':
371
+ indicator = 'Adj Close'
372
+
333
373
  if printout:
334
374
  #prices=get_prices_simple(tickerlist,start,thedate) #有待改造?
335
375
  #债券优先
336
376
  prices,found=get_price_mticker(tickerlist,start,thedate, \
337
- adjust='',source='auto',ticker_type='bond',fill=False)
377
+ adjust=adjust,source=source,ticker_type=ticker_type,fill=False)
338
378
  else:
339
379
  with HiddenPrints():
340
380
  #prices=get_prices_simple(tickerlist,start,thedate) #有待改造?
341
381
  prices,found=get_price_mticker(tickerlist,start,thedate, \
342
- adjust='',source='auto',ticker_type='bond',fill=False)
343
-
382
+ adjust=adjust,source=source,ticker_type=ticker_type,fill=False)
383
+
344
384
  if found == 'Found':
345
385
  ntickers=len(list(prices['Close']))
346
386
  nrecords=len(prices)
@@ -360,6 +400,14 @@ def portfolio_build(portfolio,thedate='default',pastyears=1, \
360
400
  print(" #Error(portfolio_build): retrieved empty prices for",pname)
361
401
  return None
362
402
  #..........................................................................
403
+ #判断是否使用复权价:若是,使用Adj Close直接覆盖Close。方法最简单,且兼容后续处理!
404
+ if (indicator =='Adj Close') or (adjust !=''):
405
+ prices_collist=list(prices)
406
+ for pc in prices_collist:
407
+ pc1=pc[0]; pc2=pc[1]
408
+ if pc1=='Close':
409
+ pc_adj=('Adj Close',pc2)
410
+ prices[pc]=prices[pc_adj]
363
411
 
364
412
  # 取各个成份股的收盘价
365
413
  aclose=prices['Close']
@@ -81,11 +81,18 @@ def get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe", \
81
81
 
82
82
  start1=date_adjust(start,adjust=-dateahead)
83
83
 
84
+ #判断复权价
85
+ if ('adj' in ret_type_lower):
86
+ adjust='qfq'
87
+ else:
88
+ adjust=''
89
+
84
90
  #抓取股价
85
91
  #pricedf=get_price(ticker,start1,end,source=source)
86
92
  #pricedf=get_price_security(ticker,start1,end,source=source)
87
- pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1, \
88
- todate=end,source=source,ticker_type=ticker_type)
93
+ pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1,todate=end, \
94
+ adjust=adjust, \
95
+ source=source,ticker_type=ticker_type)
89
96
 
90
97
  if found !='Found':
91
98
  print(" #Error(get_rolling_sharpe_sortino): no records found for",ticker)
@@ -115,6 +122,12 @@ def get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe", \
115
122
  #开始日期富余一段时间,有助于绘图时显示出期望的开始日期
116
123
  startpd=pd.to_datetime(date_adjust(start,adjust=-7))
117
124
  endpd=pd.to_datetime(end)
125
+
126
+ rardf4['index_tmp']=rardf4.index
127
+ rardf4['index_tmp']=rardf4['index_tmp'].apply(lambda x: pd.to_datetime(x))
128
+ rardf4.set_index(['index_tmp'],inplace=True)
129
+ #rardf4.drop(['index_tmp'],inplace=True)
130
+
118
131
  rardf5=rardf4[(rardf4.index >=startpd) & (rardf4.index <=endpd)]
119
132
 
120
133
  #确定风险字段名
@@ -169,11 +182,19 @@ def get_expanding_sharpe_sortino(ticker,start,end,rar_name="sharpe", \
169
182
  dateahead=7
170
183
  start1=date_adjust(start,adjust=-dateahead)
171
184
 
185
+ #判断复权价
186
+ ret_type=ret_type.title()
187
+ if ('Adj' in ret_type):
188
+ adjust='qfq'
189
+ else:
190
+ adjust=''
191
+
172
192
  #抓取股价
173
193
  #pricedf=get_price(ticker,start1,end,source=source)
174
194
  #pricedf=get_price_security(ticker,start1,end,source=source)
175
- pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1, \
176
- todate=end,source=source,ticker_type=ticker_type)
195
+ pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1,todate=end, \
196
+ adjust=adjust, \
197
+ source=source,ticker_type=ticker_type)
177
198
 
178
199
  #计算收益率和收益率标准差
179
200
  rardf2=calc_expanding_return(pricedf,start)
@@ -285,8 +306,17 @@ def get_rolling_treynor_alpha(ticker,start,end,rar_name="alpha", \
285
306
  #计算CAPM需要的日期提前量
286
307
  start2=date_adjust(start1,adjust=-regression_period-7*2)
287
308
 
309
+ #判断复权价
310
+ ret_type=ret_type.title()
311
+ if ('Adj' in ret_type):
312
+ adjust='qfq'
313
+ else:
314
+ adjust=''
315
+
288
316
  #CAPM回归,计算贝塔系数
289
- reg_result,dretdf3=regression_capm(ticker,start2,end,RF=RF, \
317
+ reg_result,dretdf3=regression_capm(ticker,start2,end, \
318
+ adjust=adjust, \
319
+ RF=RF, \
290
320
  regtrddays=regtrddays,mktidx=mktidx, \
291
321
  source=source,ticker_type=ticker_type)
292
322
 
@@ -371,8 +401,17 @@ def get_expanding_treynor_alpha(ticker,start,end,rar_name="alpha", \
371
401
  #计算CAPM需要的日期提前量
372
402
  start2=date_adjust(start1,adjust=-regression_period-7*2)
373
403
 
404
+ #判断复权价
405
+ ret_type=ret_type.title()
406
+ if ('Adj' in ret_type):
407
+ adjust='qfq'
408
+ else:
409
+ adjust=''
410
+
374
411
  #CAPM回归,计算贝塔系数
375
- reg_result,dretdf3=regression_capm(ticker,start2,end,RF=RF, \
412
+ reg_result,dretdf3=regression_capm(ticker,start2,end, \
413
+ adjust=adjust, \
414
+ RF=RF, \
376
415
  regtrddays=regtrddays,mktidx=mktidx, \
377
416
  source=source,ticker_type=ticker_type)
378
417
 
siat/sector_china.py CHANGED
@@ -1161,7 +1161,7 @@ if __name__=='__main__':
1161
1161
  period="day"
1162
1162
  industry_list='all'
1163
1163
 
1164
- def get_industry_sw(itype='1',period="day",industry_list='all'):
1164
+ def get_industry_sw(itype='1',period="day",industry_list='all',max_sleep=8):
1165
1165
  """
1166
1166
  功能:遍历某类申万指数,下载数据
1167
1167
  itype: F表征指数,n=1/2/3行业指数,S风格指数,B大类风格,C金创类
@@ -1174,7 +1174,7 @@ def get_industry_sw(itype='1',period="day",industry_list='all'):
1174
1174
  if not (itype in typelist):
1175
1175
  print(" #Error(get_industry_sw): unsupported industry category",itype)
1176
1176
  print(" Supported industry category",typelist)
1177
- print(" F: Featured, n-Level n Industry, S-Styled, B- Big Styled, C- Financial Innovation, A-All (more time))")
1177
+ print(" F: Featured, n-Level n industry, S-Styled, B- Big Styled, C- Financial Innovation, A-All (more time))")
1178
1178
  return None
1179
1179
 
1180
1180
  #获得指数代码
@@ -1192,10 +1192,10 @@ def get_industry_sw(itype='1',period="day",industry_list='all'):
1192
1192
  #循环获取指标
1193
1193
  import pandas as pd
1194
1194
  import akshare as ak
1195
- import datetime
1195
+ import datetime; import random; import time
1196
1196
  df=pd.DataFrame()
1197
1197
 
1198
- print(" Searching industry information, please wait ...")
1198
+ print(" Start searching industry data, it takes time, please wait ...")
1199
1199
  num=len(ilist)
1200
1200
  if num <= 10:
1201
1201
  steps=5
@@ -1205,7 +1205,7 @@ def get_industry_sw(itype='1',period="day",industry_list='all'):
1205
1205
  total=len(ilist)
1206
1206
  fail_list=[]
1207
1207
  for i in ilist:
1208
-
1208
+ print_progress_percent2(i,ilist,steps=5,leading_blanks=4)
1209
1209
  #print(" Retrieving information for industry",i)
1210
1210
 
1211
1211
  #抓取指数价格
@@ -1235,7 +1235,11 @@ def get_industry_sw(itype='1',period="day",industry_list='all'):
1235
1235
  df=df._append(dft2)
1236
1236
 
1237
1237
  current=ilist.index(i)
1238
- print_progress_percent(current,total,steps=steps,leading_blanks=2)
1238
+ #print_progress_percent(current,total,steps=steps,leading_blanks=2)
1239
+
1240
+ #生成随机数睡眠,试图防止被反爬虫,不知是否管用!
1241
+ random_int=random.randint(1,max_sleep)
1242
+ time.sleep(random_int)
1239
1243
 
1240
1244
  #num=list(set(list(df['ticker'])))
1241
1245
  if len(fail_list)==0:
@@ -1266,7 +1270,7 @@ if __name__=='__main__':
1266
1270
  industry_list=['850831.SW','801785.SW','801737.SW','801194.SW',
1267
1271
  '801784.SW','801783.SW','801782.SW']
1268
1272
 
1269
- def get_industry_sw2(industry_list,period="day"):
1273
+ def get_industry_sw2(industry_list,period="day",max_sleep=8):
1270
1274
  """
1271
1275
  功能:遍历指定的申万指数列表,下载数据
1272
1276
  period="day"; choice of {"day", "week", "month"}
@@ -1280,10 +1284,10 @@ def get_industry_sw2(industry_list,period="day"):
1280
1284
  #循环获取指标
1281
1285
  import pandas as pd
1282
1286
  import akshare as ak
1283
- import datetime
1287
+ import datetime; import random; import time
1284
1288
  df=pd.DataFrame()
1285
1289
 
1286
- print("Searching industry information, please wait ...")
1290
+ print(" Searching industry information, it takes time, please wait ...")
1287
1291
  ilist=industry_list
1288
1292
  num=len(ilist)
1289
1293
  if num <= 10:
@@ -1323,6 +1327,10 @@ def get_industry_sw2(industry_list,period="day"):
1323
1327
 
1324
1328
  current=ilist.index(i)
1325
1329
  print_progress_percent(current,total,steps=steps,leading_blanks=2)
1330
+
1331
+ #生成随机数睡眠,试图防止被反爬虫,不知是否管用!
1332
+ random_int=random.randint(1,max_sleep)
1333
+ time.sleep(random_int)
1326
1334
 
1327
1335
  #num=list(set(list(df['ticker'])))
1328
1336
  if len(fail_list) > 0:
siat/security_price2.py CHANGED
@@ -89,7 +89,7 @@ def get_price_1ticker(ticker,fromdate,todate, \
89
89
  #检查复权选项合理性
90
90
  ak_fq_list=['','qfq','hfq','qfq-factor','hfq-factor']
91
91
  if adjust not in ak_fq_list:
92
- adjust=''
92
+ adjust='qfq'
93
93
 
94
94
  #变换ticker为内部格式(yahoo格式)
95
95
  ticker1=ticker1_cvt2yahoo(ticker)
@@ -231,6 +231,17 @@ if __name__=='__main__':
231
231
  source='auto'
232
232
  fill=True
233
233
 
234
+ #测试复权价
235
+ ticker=['300750.SZ','300014.SZ']
236
+ fromdate="2023-4-20"
237
+ todate="2023-4-30"
238
+ ticker_type='fund'
239
+
240
+ adjust='qfq'
241
+ source='auto'
242
+ fill=False
243
+
244
+
234
245
  prices,found=get_price_mticker(ticker,fromdate,todate,adjust,source,ticker_type,fill)
235
246
 
236
247
  def get_price_mticker(ticker,fromdate,todate, \
siat/security_prices.py CHANGED
@@ -766,7 +766,11 @@ def get_price_ak_cn(ticker,fromdate,todate,adjust='',ticker_type='auto'):
766
766
  if ticker_type in ['auto','stock'] and suffix not in ['SW']:
767
767
  try:
768
768
  #仅用于股票的历史行情数据(考虑复权)
769
- df=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
769
+ dffqno=ak.stock_zh_a_daily(ticker2,start1,end1,adjust='')
770
+ dffq=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
771
+ dffq.rename(columns={'close':'Adj Close'},inplace=True)
772
+
773
+ df=pd.merge(dffqno,dffq[['date','Adj Close']],on=['date'])
770
774
  df['Date']=df['date']
771
775
  except:
772
776
  df=None
@@ -901,7 +905,6 @@ def get_price_ak_cn(ticker,fromdate,todate,adjust='',ticker_type='auto'):
901
905
  #设置新的索引
902
906
  df.set_index(['Date'],inplace=True)
903
907
  df.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
904
- df['Adj Close']=df['Close']
905
908
 
906
909
  try:
907
910
  df1=df[df.index >= start]
@@ -956,7 +959,15 @@ def get_price_ak_us(symbol, fromdate, todate, adjust=""):
956
959
  import akshare as ak
957
960
  #print(" Searching info in Sina for",symbol,"... ...")
958
961
  try:
959
- df=ak.stock_us_daily(symbol=symbol, adjust=adjust)
962
+ if adjust=='':
963
+ df=ak.stock_us_daily(symbol=symbol,adjust=adjust)
964
+ else:
965
+ dffqno=ak.stock_us_daily(symbol=symbol,adjust='')
966
+ dffq=ak.stock_us_daily(symbol=symbol,adjust='qfq')
967
+ dffq.rename(columns={'close':'Adj Close'},inplace=True)
968
+
969
+ df=pd.merge(dffqno,dffq[['date','Adj Close']],on=['date'])
970
+
960
971
  except:
961
972
  #print(" #Error(get_price_ak_us): no info found for",symbol)
962
973
  return None
@@ -982,7 +993,8 @@ def get_price_ak_us(symbol, fromdate, todate, adjust=""):
982
993
 
983
994
  df2.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
984
995
  df2['ticker']=symbol
985
- df2['Adj Close']=df2['Close']
996
+ if 'Adj Close' not in list(df2):
997
+ df2['Adj Close']=df2['Close']
986
998
  df2['source']='新浪'
987
999
  df2['footnote']=adjust
988
1000
 
@@ -1000,14 +1012,21 @@ if __name__=='__main__':
1000
1012
  if __name__=='__main__':
1001
1013
  symbol='0700.HK'
1002
1014
  symbol='0700.hk'
1015
+
1003
1016
  symbol='00700.HK'
1004
- fromdate='2020-12-1'
1005
- todate='2021-1-31'
1017
+ fromdate='2014-5-1'
1018
+ todate ='2014-5-31'
1019
+ adjust="qfq"
1020
+
1021
+ tx=get_price_ak_hk(symbol='00700.HK',fromdate='2014-5-1',todate='2014-5-30',adjust="qfq")
1006
1022
 
1007
- def get_price_ak_hk(symbol, fromdate, todate, adjust=""):
1023
+ def get_price_ak_hk(symbol,fromdate,todate,adjust=""):
1008
1024
  """
1009
1025
  抓取单个港股股价,不能处理股指,股指无.HK后缀
1010
1026
  """
1027
+ DEBUG=False
1028
+ if DEBUG:
1029
+ print("Start searching HK stock prices for",symbol,"...")
1011
1030
 
1012
1031
  #检查日期期间
1013
1032
  result,start,end=check_period(fromdate,todate)
@@ -1027,19 +1046,20 @@ def get_price_ak_hk(symbol, fromdate, todate, adjust=""):
1027
1046
  symbol3=symbol2
1028
1047
 
1029
1048
  try:
1030
- df=ak.stock_hk_daily(symbol=symbol3, adjust="")
1049
+ if adjust == '':
1050
+ df=ak.stock_hk_daily(symbol=symbol3, adjust=adjust)
1051
+ else:
1052
+ dffqno=ak.stock_hk_daily(symbol=symbol3, adjust='')
1053
+ dffq =ak.stock_hk_daily(symbol=symbol3,adjust='qfq')
1054
+ dffq.rename(columns={'close':'Adj Close'},inplace=True)
1055
+
1056
+ df=pd.merge(dffqno,dffq[['date','Adj Close']],on=['date'])
1031
1057
  except:
1032
1058
  print(" #Error(get_price_ak_hk): no info found for",symbol)
1033
1059
  return None
1034
1060
 
1035
- df['Date']=df.index
1036
- #去掉可能出现的时区信息,必须使用datetime中的tz_localize
1037
- import datetime as dt
1038
- try:
1039
- df['Date']=df['Date'].dt.tz_localize(None)
1040
- except:
1041
- import pandas as pd
1042
- df['Date']=pd.to_datetime(df['date'])
1061
+ #去掉可能出现的时区信息
1062
+ df['Date']=pd.to_datetime(df['date'])
1043
1063
  #设置新的索引
1044
1064
  df.set_index(['Date'],inplace=True)
1045
1065
 
@@ -1056,7 +1076,8 @@ def get_price_ak_hk(symbol, fromdate, todate, adjust=""):
1056
1076
 
1057
1077
  df2.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
1058
1078
  df2['ticker']=symbol
1059
- df2['Adj Close']=df2['Close']
1079
+ if 'Adj Close' not in list(df2):
1080
+ df2['Adj Close']=df2['Close']
1060
1081
  df2['source']='新浪'
1061
1082
 
1062
1083
  ptname=ticker_name(symbol,'stock')