siat 2.14.2__py3-none-any.whl → 3.0.0__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/allin.py +1 -0
- siat/assets_liquidity.py +16 -16
- siat/beta_adjustment.py +6 -6
- siat/beta_adjustment_china.py +9 -9
- siat/bond.py +71 -67
- siat/capm_beta.py +11 -11
- siat/capm_beta2.py +49 -23
- siat/common.py +427 -60
- siat/compare_cross.py +15 -82
- siat/exchange_bond_china.pickle +0 -0
- siat/fama_french.py +3 -3
- siat/financials.py +15 -15
- siat/financials2.py +8 -8
- siat/financials_china.py +20 -20
- siat/financials_china2.py +25 -25
- siat/fund_china.pickle +0 -0
- siat/fund_china.py +5 -4
- siat/grafix.py +195 -132
- siat/markowitz.py +6 -5
- siat/option_china.py +1 -1
- siat/option_pricing.py +6 -6
- siat/risk_adjusted_return.py +14 -14
- siat/risk_adjusted_return2.py +64 -42
- siat/risk_evaluation.py +32 -32
- siat/risk_free_rate.py +0 -0
- siat/sector_china.py +3 -195
- siat/security_price2.py +616 -0
- siat/security_prices.py +935 -308
- siat/security_trend2.py +28 -47
- siat/stock.py +225 -437
- siat/stock_china.py +19 -19
- siat/stock_info.pickle +0 -0
- siat/stock_technical.py +547 -144
- siat/transaction.py +3 -3
- siat/translate.py +781 -24
- siat/valuation.py +6 -6
- siat/var_model_validation.py +2 -2
- {siat-2.14.2.dist-info → siat-3.0.0.dist-info}/METADATA +1 -1
- {siat-2.14.2.dist-info → siat-3.0.0.dist-info}/RECORD +41 -40
- {siat-2.14.2.dist-info → siat-3.0.0.dist-info}/WHEEL +0 -0
- {siat-2.14.2.dist-info → siat-3.0.0.dist-info}/top_level.txt +0 -0
siat/security_prices.py
CHANGED
@@ -19,200 +19,238 @@ import pandas as pd
|
|
19
19
|
|
20
20
|
#==============================================================================
|
21
21
|
#==============================================================================
|
22
|
-
def get_price(ticker,fromdate,todate,adj=False,source='auto'):
|
23
|
-
"""
|
24
|
-
套壳函数get_prices,为保持兼容
|
25
|
-
"""
|
26
|
-
df=get_prices(ticker,fromdate,todate,adj=adj,source=source)
|
27
|
-
|
28
|
-
df2=remove_timezone(df)
|
29
|
-
return df2
|
30
|
-
#==============================================================================
|
31
22
|
if __name__=='__main__':
|
32
|
-
ticker=
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
23
|
+
ticker="430047.BJ"
|
24
|
+
ticker="430047.BJ"
|
25
|
+
ticker="600519.SS"
|
26
|
+
ticker="000858.SZ"
|
27
|
+
ticker_type='auto'
|
37
28
|
|
38
|
-
ticker=
|
29
|
+
ticker="sz169107" #LOF基金
|
30
|
+
ticker="sh510050" #ETF基金
|
39
31
|
|
40
|
-
ticker=['AAPL','MSFT']
|
41
|
-
ticker=['AAPL','MSFT','ABCD']
|
42
32
|
|
43
|
-
ticker=
|
44
|
-
|
45
|
-
|
33
|
+
ticker="sh010504" #国债
|
34
|
+
ticker_type='bond'
|
35
|
+
|
36
|
+
ticker='801002.SW'
|
37
|
+
ticker_type='auto'
|
38
|
+
|
39
|
+
ticker={'Market':('China','000001.SS','白酒组合'),'600519':0.4,'000858':0.6}
|
40
|
+
|
41
|
+
fromdate="2024-1-1"
|
42
|
+
todate="2024-4-1"
|
43
|
+
adj=False
|
44
|
+
retry_count=3
|
45
|
+
pause=1
|
46
|
+
source='auto'
|
47
|
+
|
48
|
+
prices=get_prices_all(ticker,fromdate,todate,ticker_type=ticker_type)
|
46
49
|
|
47
|
-
def
|
50
|
+
def get_prices_all(ticker,fromdate,todate,adj=False,source='auto',ticker_type='auto'):
|
48
51
|
"""
|
49
|
-
|
52
|
+
功能:多个证券(股票,指数,基金,债券),或投资组合(可含股票和/或债券)
|
53
|
+
ticker_type:若为'auto'则基金优先于债券(代码重合时),亦可为列表分别指定优先抓取类型。
|
54
|
+
'stock', 'fund', 'bond','swindex','portfolio',不足部分自动补充为'auto'
|
55
|
+
其中,'auto'/'stock'/'fund'优先抓取指数、股票和基金;'bond'优先抓取债券;
|
56
|
+
'swindex'优先抓取申万行业指数
|
57
|
+
|
58
|
+
注意:未经充分测试!!!
|
50
59
|
"""
|
51
|
-
if isinstance(ticker,str):
|
52
|
-
return ticker.upper()
|
53
|
-
elif isinstance(ticker,list):
|
54
|
-
tlist=[]
|
55
|
-
for t in ticker:
|
56
|
-
try:
|
57
|
-
tupper=t.upper()
|
58
|
-
except:
|
59
|
-
tupper=t
|
60
|
-
tlist=tlist+[tupper]
|
61
|
-
return tlist
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
#补足ticker_type
|
62
|
+
if isinstance(ticker_type,str):
|
63
|
+
ticker_type_list=[ticker_type]
|
64
|
+
|
65
|
+
if isinstance(ticker,str) or isinstance(ticker,dict):
|
66
|
+
ticker_list=[ticker]
|
67
|
+
|
68
|
+
ticker_num=len(ticker_list)
|
69
|
+
ticker_type_len=len(ticker_type_list)
|
70
|
+
if ticker_num > ticker_type_len:
|
71
|
+
ticker_type_list=ticker_type_list + ['auto'*(ticker_type_len - ticker_num)]
|
72
|
+
|
73
|
+
#单个证券的特殊处理
|
74
|
+
if ticker_num == 1:
|
75
|
+
#普通证券
|
76
|
+
if isinstance(ticker_list[0],str):
|
77
|
+
df=get_prices(ticker_list[0],fromdate,todate,adj=adj,source=source,ticker_type=ticker_type_list[0])
|
78
|
+
|
79
|
+
#投资组合
|
80
|
+
if isinstance(ticker_list[0],dict):
|
81
|
+
_,_,tickerlist,sharelist=decompose_portfolio(ticker_list[0])
|
82
|
+
df=get_price_portfolio(tickerlist,sharelist,fromdate,todate,adj=adj, \
|
83
|
+
source=source,ticker_type='bond')
|
84
|
+
return df
|
66
85
|
|
67
|
-
|
68
|
-
|
86
|
+
#多个证券
|
87
|
+
df=pd.DataFrame()
|
88
|
+
for t in ticker_list:
|
89
|
+
pos=ticker_list.index(t)
|
90
|
+
tt=ticker_type_list[pos]
|
91
|
+
|
92
|
+
#普通证券
|
93
|
+
if isinstance(t,str):
|
94
|
+
dft=get_prices(t,fromdate,todate,adj=adj,source=source,ticker_type=tt)
|
95
|
+
|
96
|
+
#投资组合
|
97
|
+
if isinstance(t,dict):
|
98
|
+
_,_,tickerlist,sharelist=decompose_portfolio(t)
|
99
|
+
dft=get_price_portfolio(tickerlist,sharelist,fromdate,todate,adj=adj, \
|
100
|
+
source=source,ticker_type='bond')
|
101
|
+
t=portfolio_name(t)
|
102
|
+
|
103
|
+
columns=create_tuple_for_columns(dft,t)
|
104
|
+
dft.columns=pd.MultiIndex.from_tuples(columns)
|
105
|
+
|
106
|
+
if len(df)==0:
|
107
|
+
df=dft
|
108
|
+
else:
|
109
|
+
#合并
|
110
|
+
df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
|
69
111
|
|
112
|
+
return df
|
113
|
+
|
114
|
+
#==============================================================================
|
115
|
+
if __name__=='__main__':
|
116
|
+
ticker="430047.BJ"
|
70
117
|
ticker="430047.BJ"
|
71
|
-
|
72
|
-
|
118
|
+
ticker="600519.SS"
|
119
|
+
ticker="000858.SZ"
|
120
|
+
ticker_type='auto'
|
121
|
+
|
122
|
+
ticker="sz169107" #LOF基金
|
123
|
+
ticker="sh510050" #ETF基金
|
124
|
+
|
125
|
+
ticker="sh010504" #国债
|
126
|
+
ticker_type='bond'
|
127
|
+
|
128
|
+
ticker='801002.SW'
|
129
|
+
ticker_type='auto'
|
130
|
+
|
131
|
+
ticker=['600519','000858']
|
132
|
+
ticker_type='bond'
|
133
|
+
|
134
|
+
fromdate="2024-1-1"
|
135
|
+
todate="2024-4-1"
|
73
136
|
adj=False
|
74
137
|
retry_count=3
|
75
138
|
pause=1
|
76
|
-
|
77
|
-
prices=get_prices(ticker,fromdate,todate)
|
139
|
+
source='auto'
|
78
140
|
|
79
|
-
|
80
|
-
北交所股票以43开头或83、87、88开头,因为新三板(基础层、创新层)股票代码一般为43、83、87开头。
|
81
|
-
沪市普通A股股票代码是以60开头,沪市科创板股票代码是以688开头。
|
82
|
-
深市普通A股票代码是以00开头,深市创业板股票代码以300开头。
|
83
|
-
"""
|
141
|
+
prices=get_prices(ticker,fromdate,todate,ticker_type=ticker_type)
|
84
142
|
|
85
143
|
def get_prices(ticker,fromdate,todate,adj=False,source='auto', \
|
86
|
-
retry_count=3,pause=1):
|
144
|
+
retry_count=3,pause=1,ticker_type='auto'):
|
87
145
|
"""
|
88
146
|
功能:抓取证券价格,pandas_datareader + yfinance + akshare
|
89
147
|
输出:指定收盘价格序列,日期升序排列
|
90
|
-
ticker:
|
148
|
+
ticker: 证券代码或其列表。大陆证券代码加上后缀.SZ或.SS或.BJ,港股代码去掉前导0加后缀.HK
|
91
149
|
start: 样本开始日期,yyyy-mm-dd
|
92
150
|
end: 样本结束日期,既可以是今天日期,也可以是一个历史日期
|
93
|
-
retry_count
|
94
|
-
pause
|
151
|
+
retry_count:网络失败时的重试次数,仅用于雅虎
|
152
|
+
pause:每次重试前的间隔秒数,仅用于雅虎
|
95
153
|
"""
|
96
|
-
print(" Searching prices for corresponding securities, please wait ...")
|
97
|
-
ticker=upper_ticker(ticker)
|
98
154
|
|
99
155
|
#检查日期期间的合理性
|
100
156
|
result,start,end=check_period(fromdate,todate)
|
101
157
|
if not result:
|
102
158
|
print(" #Error(get_prices): invalid date period from",fromdate,'to',todate)
|
103
159
|
return None
|
160
|
+
|
161
|
+
print(" Searching prices of security, please wait ...")
|
162
|
+
ticker=tickers_cvt2yahoo(ticker)
|
104
163
|
|
105
|
-
"""
|
106
|
-
#尝试pandas_datareader+FRED(仅对部分国外市场指数有效)
|
107
|
-
if ticker[0]=='^':
|
108
|
-
print(" Trying to capture info from fred for",ticker)
|
109
|
-
prices=get_index_fred(ticker,start,end)
|
110
|
-
if prices is None:
|
111
|
-
print(" #Warning(get_prices): info retrieving failed from fred for",ticker)
|
112
|
-
else:
|
113
|
-
if len(prices)==0:
|
114
|
-
print(" #Warning(get_prices): zero record found in fred for",ticker)
|
115
|
-
else:
|
116
|
-
return prices
|
117
|
-
"""
|
118
164
|
if source in ['auto']:
|
119
165
|
#尝试AkShare+Sina+EM(新浪,对中国内地股票、港股和美股有效,但不包括国外市场指数)
|
120
|
-
|
121
|
-
print(" Trying to capture prices from sina/EM for",ticker)
|
166
|
+
print(" Trying to capture prices from sina/EM ... ...")
|
122
167
|
try:
|
123
|
-
|
124
|
-
prices=get_prices_ak(ticker,fromdate,todate)
|
168
|
+
prices=get_prices_ak(ticker,fromdate,todate,ticker_type=ticker_type) #支持多个证券
|
125
169
|
except:
|
126
|
-
print("
|
127
|
-
#return None
|
170
|
+
print(" #Warning(get_prices): info retrieving failed from sina/EM")
|
128
171
|
else:
|
129
172
|
if prices is None:
|
130
|
-
print("
|
173
|
+
print(" #Warning(get_prices): info not found from sina/EM")
|
131
174
|
else:
|
132
175
|
num=len(prices)
|
133
176
|
if num==0:
|
134
|
-
print("
|
177
|
+
print(" #Warning(get_prices): security may suspend trading or already expired")
|
178
|
+
return prices
|
135
179
|
else:
|
136
|
-
#print(" Successfully retrieved",num,"records for",ticker)
|
137
180
|
prices2=remove_timezone(prices)
|
138
|
-
|
181
|
+
return prices2 #找到有效数据就返回,否则继续
|
139
182
|
|
140
183
|
if source in ['auto','stooq']:
|
141
184
|
#尝试pandas_datareader+stooq(对美股、港股、欧股、国外市场指数有效,但对深交所股票无效)
|
142
185
|
#注意stooq代码与新浪/stooq的不同
|
143
186
|
print(" Trying to capture info from stooq for",ticker)
|
144
187
|
try:
|
145
|
-
prices=get_price_stooq(ticker,fromdate,todate)
|
188
|
+
prices=get_price_stooq(ticker,fromdate,todate) #仅支持单只证券
|
189
|
+
#prices=get_prices_stooq(ticker,fromdate,todate)?
|
146
190
|
except:
|
147
|
-
print("
|
148
|
-
#return None
|
191
|
+
print(" #Warning(get_prices): info retrieving failed from stooq")
|
149
192
|
else:
|
150
193
|
if prices is None:
|
151
|
-
print("
|
194
|
+
print(" #Warning(get_prices): info not found from stooq")
|
152
195
|
else:
|
153
196
|
num=len(prices)
|
154
197
|
if num==0:
|
155
|
-
print("
|
198
|
+
print(" #Warning(get_prices): zero record found")
|
156
199
|
else:
|
157
|
-
#print(" Successfully retrieved",num,"records for",ticker)
|
158
200
|
prices2=remove_timezone(prices)
|
159
|
-
return prices2
|
201
|
+
return prices2 #找到有效数据就返回,否则继续
|
160
202
|
|
161
203
|
if source in ['auto','yahoo']:
|
162
204
|
#使用yahoo+yfinance抓取数据
|
163
205
|
#由于雅虎无法访问,建议暂时关闭,2021-10-24
|
164
206
|
#抓取证券(列表)价格,需要调整收盘价:yfinance优先,线程极易出错,先尝试关闭线程
|
165
207
|
try:
|
166
|
-
print(" Trying to capture info from Yahoo Finance using non-threads
|
167
|
-
prices=get_prices_yf(ticker,start,end,threads=False)
|
208
|
+
print(" Trying to capture info from Yahoo Finance using non-threads")
|
209
|
+
prices=get_prices_yf(ticker,start,end,threads=False) #支持多个证券
|
168
210
|
except:
|
169
|
-
print("
|
211
|
+
print(" #Warning(get_prices): retrieving using non-threads failed from yahoo")
|
170
212
|
else:
|
171
213
|
if prices is None:
|
172
|
-
print("
|
214
|
+
print(" #Warning(get_prices): info not found using non-threads failed from yahoo")
|
173
215
|
else:
|
174
216
|
num=len(prices)
|
175
217
|
if num==0:
|
176
|
-
print("
|
218
|
+
print(" #Warning(get_prices): zero record found")
|
177
219
|
else:
|
178
|
-
#print(" Successfully retrieved",num,"records for",ticker)
|
179
220
|
prices2=remove_timezone(prices)
|
180
|
-
return prices2
|
221
|
+
return prices2 #找到有效数据就返回,否则继续
|
181
222
|
|
182
223
|
#抓取证券(列表)价格,需要调整收盘价:yfinance优先,尝试打开线程
|
183
224
|
try:
|
184
|
-
print(" Trying to capture info from Yahoo Finance using threads
|
185
|
-
prices=get_prices_yf(ticker,start,end,threads=True)
|
225
|
+
print(" Trying to capture info from Yahoo Finance using threads")
|
226
|
+
prices=get_prices_yf(ticker,start,end,threads=True) #支持多个证券
|
186
227
|
except:
|
187
|
-
print("
|
228
|
+
print(" #Warning(get_prices): retrieving using threads failed from yahoo")
|
188
229
|
else:
|
189
230
|
if prices is None:
|
190
|
-
print("
|
231
|
+
print(" #Warning(get_prices): info not found using non-threads failed from yahoo")
|
191
232
|
else:
|
192
233
|
num=len(prices)
|
193
234
|
if num==0:
|
194
|
-
print("
|
235
|
+
print(" #Warning(get_prices): zero record found")
|
195
236
|
else:
|
196
|
-
#print(" Successfully retrieved",num,"records for",ticker)
|
197
237
|
prices2=remove_timezone(prices)
|
198
|
-
return prices2
|
238
|
+
return prices2 #找到有效数据就返回,否则继续
|
199
239
|
|
200
|
-
#抓取证券(列表)价格,不考虑是否需要调整收盘价:pandas_datareader
|
201
|
-
#由于雅虎财经当前无法访问,建议本段停用
|
202
|
-
#由于雅虎无法访问,暂时关闭,2021-10-24
|
240
|
+
#抓取证券(列表)价格,不考虑是否需要调整收盘价:pandas_datareader,使用雅虎
|
203
241
|
try:
|
204
|
-
print(" Trying to capture info from Yahoo Finance
|
242
|
+
print(" Trying to capture info from Yahoo Finance traditionally")
|
205
243
|
prices=get_prices_yahoo(ticker,start,end,retry_count=retry_count,pause=pause)
|
206
244
|
except:
|
207
|
-
print("
|
245
|
+
print(" #Warning(get_prices): info retrieving failed from Yahoo traditionally")
|
208
246
|
return None
|
209
247
|
else:
|
210
248
|
if prices is None:
|
211
|
-
print("
|
249
|
+
print(" #Warning(get_prices): info not found from Yahoo traditionally")
|
212
250
|
else:
|
213
251
|
num=len(prices)
|
214
252
|
if num==0:
|
215
|
-
print("
|
253
|
+
print(" #Warning(get_prices): zero record found from Yahoo traditionally")
|
216
254
|
else:
|
217
255
|
#print(" Successfully retrieved",num,"records for",ticker)
|
218
256
|
prices2=remove_timezone(prices)
|
@@ -220,9 +258,8 @@ def get_prices(ticker,fromdate,todate,adj=False,source='auto', \
|
|
220
258
|
|
221
259
|
#若能够抓取到数据均已提前返回,到达此处时表面未能抓取到任何数据
|
222
260
|
print(" #Warning(get_prices): tried everything but nothing found for",ticker)
|
223
|
-
return None
|
224
|
-
|
225
261
|
|
262
|
+
return None
|
226
263
|
|
227
264
|
if __name__=='__main__':
|
228
265
|
get_prices('INTC','2021-11-1','2021-11-5')
|
@@ -234,6 +271,18 @@ if __name__=='__main__':
|
|
234
271
|
df6=get_prices(['00988.HK','000858.SZ'],'2021-11-1','2021-11-5')
|
235
272
|
df7=get_prices(['INTL','MSFT','00988.HK','000858.SZ'],'2021-11-1','2021-11-5')
|
236
273
|
|
274
|
+
#==============================================================================
|
275
|
+
|
276
|
+
def get_price(ticker,fromdate,todate,adj=False,source='auto',ticker_type='auto'):
|
277
|
+
"""
|
278
|
+
套壳函数get_prices,为保持兼容
|
279
|
+
"""
|
280
|
+
df=get_prices(ticker,fromdate,todate,adj=adj,source=source,ticker_type=ticker_type)
|
281
|
+
|
282
|
+
df2=remove_timezone(df)
|
283
|
+
|
284
|
+
return df2
|
285
|
+
|
237
286
|
#==============================================================================
|
238
287
|
if __name__ =="__main__":
|
239
288
|
ticker="BMW.DE"
|
@@ -293,8 +342,7 @@ if __name__=='__main__':
|
|
293
342
|
todate='2022-12-15'
|
294
343
|
adjust=''
|
295
344
|
|
296
|
-
#在common中定义
|
297
|
-
#SUFFIX_LIST_CN=['SS','SZ','BJ','NQ']
|
345
|
+
#在common中定义SUFFIX_LIST_CN
|
298
346
|
|
299
347
|
def get_price_ak_em(ticker,fromdate,todate,adjust=''):
|
300
348
|
"""
|
@@ -368,9 +416,9 @@ def get_price_ak_em(ticker,fromdate,todate,adjust=''):
|
|
368
416
|
|
369
417
|
num=len(df1)
|
370
418
|
if num > 0:
|
371
|
-
print(" Successfully retrieved",num,"records for",ticker)
|
419
|
+
print(" Successfully retrieved",num,"records for",ticker,ticker_name(ticker,ticker_type))
|
372
420
|
else:
|
373
|
-
print(" Sorry, no records retrieved for",ticker)
|
421
|
+
print(" Sorry, no records retrieved for",ticker,ticker_name(ticker,ticker_type))
|
374
422
|
|
375
423
|
return df1
|
376
424
|
|
@@ -393,7 +441,7 @@ def cvt_stooq_suffix(symbol):
|
|
393
441
|
"""
|
394
442
|
import pandas as pd
|
395
443
|
suffix=pd.DataFrame([
|
396
|
-
['SS','CN'], ['SZ','CN'], ['T','JP'],
|
444
|
+
['SS','CN'], ['SH','CN'], ['SZ','CN'], ['BJ','CN'], ['T','JP'],
|
397
445
|
|
398
446
|
], columns=['yahoo','stooq'])
|
399
447
|
|
@@ -414,10 +462,11 @@ def cvt_stooq_symbol(symbol):
|
|
414
462
|
"""
|
415
463
|
映射雅虎指数符号至stooq指数符号
|
416
464
|
输入:雅虎指数符号。输出:stooq指数符号
|
465
|
+
注意:^IXIC/^NDQ是纳斯达克综合指数,^NDX是纳斯达克100指数
|
417
466
|
"""
|
418
467
|
import pandas as pd
|
419
468
|
suffix=pd.DataFrame([
|
420
|
-
['^GSPC','^SPX'], ['^IXIC','^NDQ'],
|
469
|
+
['^GSPC','^SPX'], ['^IXIC','^NDQ'],
|
421
470
|
['^RUT','QR.F'],
|
422
471
|
['000001.SS','^SHC'],
|
423
472
|
['^N225','^NKX'], ['^TWII','^TWSE'], ['^KS11','^KOSPI'],
|
@@ -448,7 +497,7 @@ def cvt_stooq_ticker(ticker):
|
|
448
497
|
"""
|
449
498
|
映射雅虎证券符号至stooq证券符号
|
450
499
|
输入:雅虎证券符号。输出:stooq证券符号
|
451
|
-
|
500
|
+
局限:无法处理深交所股票代码!stooq里没有深交所股票
|
452
501
|
"""
|
453
502
|
#直接转换
|
454
503
|
result,ticker_stooq=cvt_stooq_symbol(ticker)
|
@@ -496,6 +545,8 @@ if __name__=='__main__':
|
|
496
545
|
ticker='TRBNCN.M'
|
497
546
|
ticker='RSAYCN.M'
|
498
547
|
|
548
|
+
ticker=['AAPL','MSFT']
|
549
|
+
|
499
550
|
start='2023-1-1'
|
500
551
|
end='2024-2-19'
|
501
552
|
|
@@ -503,7 +554,7 @@ if __name__=='__main__':
|
|
503
554
|
|
504
555
|
def get_price_stooq(ticker,start,end):
|
505
556
|
"""
|
506
|
-
|
557
|
+
从stooq抓取单个股价
|
507
558
|
"""
|
508
559
|
#转换证券代码
|
509
560
|
ticker2=cvt_stooq_ticker(ticker)
|
@@ -535,7 +586,7 @@ def get_price_stooq(ticker,start,end):
|
|
535
586
|
ticker2 = ".".join([ticker2, 'US']) #若为空尝试当作美股代码处理,挽救第二次
|
536
587
|
prices=web.DataReader(ticker2,start=start,end=end,data_source='stooq')
|
537
588
|
else:
|
538
|
-
print(" Sorry, zero records found from stooq for",ticker,"from",start,'to',end)
|
589
|
+
#print(" Sorry, zero records found from stooq for",ticker,"from",start,'to',end)
|
539
590
|
return None
|
540
591
|
|
541
592
|
prices.sort_index(axis=0, ascending=True, inplace=True)
|
@@ -546,14 +597,17 @@ def get_price_stooq(ticker,start,end):
|
|
546
597
|
prices['ticker']=str(ticker)
|
547
598
|
prices['footnote']=''
|
548
599
|
|
600
|
+
if 'Volume' not in list(prices):
|
601
|
+
prices['Volume']=0
|
602
|
+
|
549
603
|
_,start1,end1=check_period(start,end)
|
550
604
|
prices2=prices[(prices.index >= start1) & (prices.index <= end1)]
|
551
605
|
num=len(prices2)
|
552
606
|
if num > 0:
|
553
|
-
print(" Successfully retrieved",num,"records for",ticker)
|
607
|
+
print(" Successfully retrieved",num,"records for",ticker,ticker_name(ticker,ticker_type))
|
554
608
|
return prices2
|
555
609
|
else:
|
556
|
-
print(" Sorry, no records found from stooq for",ticker,"from",start,'to',end)
|
610
|
+
print(" Sorry, no records found from stooq for",ticker,ticker_name(ticker,ticker_type),"from",start,'to',end)
|
557
611
|
return None
|
558
612
|
else:
|
559
613
|
return None
|
@@ -569,17 +623,22 @@ if __name__=='__main__':
|
|
569
623
|
#==============================================================================
|
570
624
|
if __name__=='__main__':
|
571
625
|
ticker='600340.SS'
|
572
|
-
|
573
|
-
|
626
|
+
ticker='000338.SZ'
|
627
|
+
ticker='600519.SS'
|
628
|
+
ticker_type='auto'
|
629
|
+
|
630
|
+
ticker='859811.SW'
|
631
|
+
ticker_type='auto'
|
632
|
+
|
633
|
+
fromdate='2024-1-1'
|
634
|
+
todate='2024-4-1'
|
574
635
|
adjust='none'
|
575
636
|
|
576
|
-
ticker=
|
577
|
-
|
578
|
-
#在common中定义
|
579
|
-
#SUFFIX_LIST_CN=['SS','SZ','BJ','NQ']
|
637
|
+
df=get_price_ak(ticker,fromdate,todate,ticker_type=ticker_type)
|
580
638
|
|
639
|
+
#在common中定义SUFFIX_LIST_CN
|
581
640
|
|
582
|
-
def get_price_ak(ticker,fromdate,todate,adjust='none'):
|
641
|
+
def get_price_ak(ticker,fromdate,todate,adjust='none',ticker_type='auto'):
|
583
642
|
"""
|
584
643
|
功能:基于akshare抓取A股、港股和美股单只股价
|
585
644
|
若抓取A股,调用get_price_ak_cn
|
@@ -592,60 +651,85 @@ def get_price_ak(ticker,fromdate,todate,adjust='none'):
|
|
592
651
|
ticker1=ticker.upper()
|
593
652
|
result,prefix,suffix=split_prefix_suffix(ticker1)
|
594
653
|
|
595
|
-
|
654
|
+
df=pd.DataFrame()
|
655
|
+
# A股股票、指数、基金、债券,申万行业指数
|
596
656
|
if suffix in SUFFIX_LIST_CN:
|
597
657
|
try:
|
598
|
-
|
599
|
-
df=get_price_ak_cn(
|
658
|
+
#抓取单个中国的证券
|
659
|
+
df=get_price_ak_cn(ticker1,fromdate,todate,ticker_type=ticker_type)
|
600
660
|
except:
|
601
|
-
df=None
|
602
|
-
#df=get_price_ak_cn(ticker,fromdate,todate,adjust=adjust)
|
603
|
-
if df is None:
|
604
|
-
#df=get_price_ak_cn(ticker,fromdate,todate)
|
605
661
|
#抓取东方财富,处理股指有时出错,所以要放在后面做planB
|
606
|
-
df=get_price_ak_em(
|
607
|
-
#if not (df is None): return df
|
662
|
+
df=get_price_ak_em(ticker1,fromdate,todate)
|
608
663
|
|
609
|
-
|
610
|
-
|
664
|
+
if df is None:
|
665
|
+
print(" #Error(get_price_ak): no info found for",ticker1)
|
666
|
+
return df
|
667
|
+
|
668
|
+
if len(df) ==0:
|
669
|
+
print(" #Warning(get_price_ak): no record found for",ticker1,'between',fromdate,todate)
|
670
|
+
return df
|
671
|
+
|
611
672
|
return df
|
612
673
|
|
613
674
|
if adjust=='none':
|
614
675
|
adjust=''
|
676
|
+
|
615
677
|
#抓取新浪港股,不能处理股指
|
616
678
|
if suffix in ['HK']:
|
617
679
|
#df=get_price_ak_hk(ticker,fromdate,todate,adjust=adjust)
|
618
|
-
df=get_price_ak_hk(
|
619
|
-
return df
|
680
|
+
df=get_price_ak_hk(ticker1,fromdate,todate)
|
681
|
+
return df
|
682
|
+
|
620
683
|
# 美股,不能处理股指
|
621
684
|
#df=get_price_ak_us(ticker,fromdate,todate,adjust=adjust)
|
622
|
-
df=get_price_ak_us(
|
685
|
+
df=get_price_ak_us(ticker1,fromdate,todate)
|
623
686
|
|
624
687
|
return df
|
625
688
|
|
626
|
-
|
689
|
+
#==============================================================================
|
690
|
+
if __name__=='__main__':
|
691
|
+
ticker='600340.SS' #股票
|
692
|
+
ticker='159990.SZ' #ETF基金
|
693
|
+
ticker='169201.SZ' #LOF基金
|
694
|
+
ticker='180801.SZ' #封闭式基金
|
695
|
+
ticker_type='auto'
|
696
|
+
|
697
|
+
ticker='sh019319' #国债
|
698
|
+
ticker='sh018084' #政策性金融债
|
699
|
+
ticker='sz149996' #公司债
|
700
|
+
ticker='sh018003' #政策性金融债
|
701
|
+
ticker_type='bond'
|
702
|
+
|
703
|
+
ticker='801002.SW'
|
704
|
+
ticker='sz100303'
|
705
|
+
ticker='100303.SZ'
|
706
|
+
ticker_type='auto'
|
707
|
+
|
708
|
+
fromdate='2024-1-1'; todate='2024-3-31'
|
709
|
+
adjust=''
|
710
|
+
|
711
|
+
prices=get_price_ak_cn(ticker,fromdate,todate)
|
712
|
+
|
713
|
+
#def get_price_ak_cn(ticker,fromdate,todate,adjust='none',ticker_type='auto'):
|
714
|
+
def get_price_ak_cn(ticker,fromdate,todate,adjust='',ticker_type='auto'):
|
627
715
|
"""
|
628
|
-
功能:从akshare
|
629
|
-
ticker
|
716
|
+
功能:从akshare获得中国国内的股票、交易所基金、指数和债券历史行情,只能处理单个证券
|
717
|
+
ticker:雅虎格式,其他的不处理,直接返回None
|
630
718
|
fromdate:格式为YYYY-m-d,需要改造为YYYYMMDD
|
631
719
|
todate:格式为YYYY-m-d,需要改造为YYYYMMDD
|
632
|
-
adjust
|
720
|
+
adjust:不复权为'',后复权为'hfq',前复权为'qfq'
|
721
|
+
ticker_type:抓取数据的优先顺序,'auto'/'stock'/'fund'为指数、股票和基金优先,'bond'为债券优先
|
722
|
+
其目的是解决基金和债券代码部分重合的问题
|
633
723
|
返回结果:雅虎格式,日期升序,列明首字母大写等
|
634
724
|
"""
|
725
|
+
import akshare as ak
|
726
|
+
import pandas as pd
|
727
|
+
import datetime as dt
|
728
|
+
|
729
|
+
df=None; found='None'
|
730
|
+
|
635
731
|
#变换代码格式
|
636
|
-
|
637
|
-
last3=ticker1[-3:]
|
638
|
-
headcode=ticker1[:-3]
|
639
|
-
if last3 == '.SS':
|
640
|
-
ticker2='sh'+headcode
|
641
|
-
if last3 == '.SZ':
|
642
|
-
ticker2='sz'+headcode
|
643
|
-
if last3 == '.BJ':
|
644
|
-
ticker2='bj'+headcode
|
645
|
-
|
646
|
-
if last3 not in ['.SS','.SZ','.BJ']:
|
647
|
-
print(" #Warning(get_price_ak_cn): not eligible for security",ticker)
|
648
|
-
return None
|
732
|
+
ticker2=tickers_cvt2ak(ticker)
|
649
733
|
|
650
734
|
#变换日期格式
|
651
735
|
result,start,end=check_period(fromdate,todate)
|
@@ -655,66 +739,160 @@ def get_price_ak_cn(ticker,fromdate,todate,adjust='none'):
|
|
655
739
|
start1=start.strftime('%Y%m%d')
|
656
740
|
end1=end.strftime('%Y%m%d')
|
657
741
|
|
658
|
-
adjustlist=['none','hfq','qfq']
|
742
|
+
#adjustlist=['none','hfq','qfq']
|
743
|
+
adjustlist=['','qfq','hfq','qfq-factor','hfq-factor']
|
659
744
|
if adjust not in adjustlist:
|
660
745
|
print(" #Warning(get_price_ak_cn): adjust only supports",adjustlist)
|
661
746
|
return None
|
662
747
|
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
#printmsg=str(ticker)+" from "+fromdate+" to "+todate
|
668
|
-
#不考虑复权情形
|
669
|
-
if adjust == 'none':
|
670
|
-
try:
|
671
|
-
#抓取指数行情,实际上亦可抓取股票行情
|
672
|
-
df = ak.stock_zh_index_daily(symbol=ticker2)
|
673
|
-
df['Date']=df.index
|
674
|
-
df['Date']=df['Date'].dt.tz_localize(None)
|
675
|
-
except:
|
748
|
+
_,prefix,suffix=split_prefix_suffix(ticker2)
|
749
|
+
#考虑股票复权情形:指数/基金/债券无复权
|
750
|
+
if adjust != '':
|
751
|
+
if ticker_type in ['auto','stock'] and suffix not in ['SW']:
|
676
752
|
try:
|
677
|
-
|
678
|
-
df=ak.
|
679
|
-
df['Date']=
|
753
|
+
#仅用于股票的历史行情数据(考虑复权)
|
754
|
+
df=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
|
755
|
+
df['Date']=df['date']
|
680
756
|
except:
|
681
|
-
|
682
|
-
|
757
|
+
df=None
|
758
|
+
found=df_have_data(df)
|
759
|
+
|
760
|
+
#不是股票:指数/基金/债券
|
761
|
+
if found != 'Found':
|
762
|
+
if ticker_type in ['auto','stock'] and suffix not in ['SW']:
|
763
|
+
try:
|
764
|
+
#指数/股票/基金
|
765
|
+
df = ak.stock_zh_index_daily(symbol=ticker2)
|
766
|
+
df['Date']=df['date'].apply(lambda x: pd.to_datetime(x))
|
767
|
+
except: pass
|
768
|
+
found=df_have_data(df)
|
769
|
+
|
770
|
+
if found != 'Found':
|
771
|
+
try:
|
772
|
+
#特殊函数(不考虑复权)
|
773
|
+
df=ak.stock_zh_a_cdr_daily(ticker2,start1,end1)
|
774
|
+
df['Date']=pd.to_datetime(df['date'])
|
775
|
+
except: pass
|
776
|
+
found=df_have_data(df)
|
777
|
+
|
778
|
+
if found != 'Found':
|
779
|
+
try:
|
780
|
+
#最后抓取交易所债券行情
|
781
|
+
df = exchange_bond_price(ticker2,fromdate,todate,graph=False,data_crop=False)
|
782
|
+
df['Date']=df.index
|
783
|
+
except:
|
784
|
+
#print(" #Error(get_price_ak_cn): failed to find prices for",ticker)
|
785
|
+
return None
|
786
|
+
found=df_have_data(df)
|
787
|
+
|
788
|
+
#已找到证券信息,但在规定时段无数据
|
789
|
+
if found=='Empty': return df
|
683
790
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
791
|
+
#债券优先,然后查找指数、股票和基金。因部分债券代码(特别是国债)与基金代码重合,需要甄别!
|
792
|
+
#例如;sh010504既是"05国债⑷"也是"招商稳兴混合C"基金的代码:-(
|
793
|
+
if ticker_type in ['bond'] and suffix not in ['SW']:
|
794
|
+
try:
|
795
|
+
#优先抓取交易所债券行情
|
796
|
+
df = exchange_bond_price(ticker2,fromdate,todate,graph=False,data_crop=False)
|
797
|
+
df['Date']=df.index
|
798
|
+
except: pass
|
799
|
+
found=df_have_data(df)
|
800
|
+
|
801
|
+
#已找到证券信息,但在规定时段无数据
|
802
|
+
if found=='Empty': return df
|
803
|
+
|
804
|
+
if found != 'Found':
|
805
|
+
try:
|
806
|
+
#其次仅抓取股票行情
|
807
|
+
df=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
|
808
|
+
df['Date']=df['date']
|
809
|
+
df['Date']=df['Date'].dt.tz_localize(None)
|
810
|
+
except: pass
|
811
|
+
found=df_have_data(df)
|
812
|
+
|
813
|
+
if found != 'Found':
|
814
|
+
try:
|
815
|
+
#接着查找指数
|
816
|
+
df = ak.stock_zh_index_daily(symbol=ticker2)
|
817
|
+
df['Date']=df['date'].apply(lambda x: pd.to_datetime(x))
|
818
|
+
except: pass
|
819
|
+
found=df_have_data(df)
|
820
|
+
|
821
|
+
if found != 'Found':
|
822
|
+
try:
|
823
|
+
#最后查找开放式基金
|
824
|
+
df =get_price_oef_china(ticker2,fromdate,todate)
|
825
|
+
df['Date']=df.index
|
826
|
+
except: pass
|
827
|
+
found=df_have_data(df)
|
828
|
+
|
829
|
+
#基金。因部分债券代码(特别是国债)与基金代码重合,需要甄别!
|
830
|
+
if ticker_type in ['fund'] and suffix not in ['SW']:
|
831
|
+
try:
|
832
|
+
#优先抓取开放式基金单位净值
|
833
|
+
df =get_price_oef_china(ticker2,fromdate,todate)
|
834
|
+
df['Date']=df.index
|
835
|
+
except: pass
|
836
|
+
found=df_have_data(df)
|
837
|
+
|
838
|
+
#已找到证券信息,但在规定时段无数据
|
839
|
+
if found=='Empty': return df
|
840
|
+
|
841
|
+
if found != 'Found': #未找到,其次抓取股票行情
|
842
|
+
try:
|
843
|
+
df=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
|
844
|
+
df['Date']=df['date']
|
845
|
+
df['Date']=df['Date'].dt.tz_localize(None)
|
846
|
+
except: pass
|
847
|
+
found=df_have_data(df)
|
848
|
+
|
849
|
+
if found != 'Found':
|
850
|
+
try:
|
851
|
+
#再次查找股票指数
|
852
|
+
df = ak.stock_zh_index_daily(symbol=ticker2)
|
853
|
+
df['Date']=df['date'].apply(lambda x: pd.to_datetime(x))
|
854
|
+
except: pass
|
855
|
+
found=df_have_data(df)
|
856
|
+
|
857
|
+
if found != 'Found':
|
858
|
+
try:
|
859
|
+
#最后查找债券
|
860
|
+
df = exchange_bond_price(ticker2,fromdate,todate,graph=False,data_crop=False)
|
861
|
+
df['Date']=df.index
|
862
|
+
except: pass
|
863
|
+
found=df_have_data(df)
|
864
|
+
|
865
|
+
|
866
|
+
if suffix in ['SW']:
|
867
|
+
try:
|
868
|
+
df = fetch_price_swindex(prefix,fromdate,todate)
|
869
|
+
df['Date']=df.index
|
870
|
+
except: pass
|
871
|
+
#print(" #Error(get_price_ak_cn): failed to retrieve prices for",ticker)
|
872
|
+
found=df_have_data(df)
|
693
873
|
|
694
|
-
if
|
695
|
-
return None
|
696
|
-
else:
|
874
|
+
if found in ['Found','Empty']:
|
697
875
|
#设置新的索引
|
698
876
|
df.set_index(['Date'],inplace=True)
|
699
877
|
df.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
|
700
878
|
df['Adj Close']=df['Close']
|
701
879
|
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
if
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
880
|
+
try:
|
881
|
+
df1=df[df.index >= start]
|
882
|
+
df2=df1[df1.index <= end]
|
883
|
+
except:
|
884
|
+
df2=df
|
885
|
+
found=df_have_data(df2)
|
886
|
+
|
887
|
+
if found in ['Found','Empty']:
|
888
|
+
df2['source']=text_lang('新浪','sina')
|
889
|
+
df2['ticker']=str(ticker)
|
890
|
+
if 'Adj Close' not in list(df2):
|
891
|
+
df2['Adj Close']=df2['Close']
|
892
|
+
df2['footnote']=adjust
|
893
|
+
|
894
|
+
if len(df2) > 0:
|
895
|
+
print(" Successfully retrieved",len(df2),"records for",ticker,ticker_name(ticker,ticker_type))
|
718
896
|
|
719
897
|
return df2
|
720
898
|
|
@@ -747,11 +925,11 @@ def get_price_ak_us(symbol, fromdate, todate, adjust=""):
|
|
747
925
|
#printmsg=str(symbol)+" from "+fromdate+" to "+todate
|
748
926
|
|
749
927
|
import akshare as ak
|
750
|
-
print(" Searching info in Sina for",symbol,"... ...")
|
928
|
+
#print(" Searching info in Sina for",symbol,"... ...")
|
751
929
|
try:
|
752
930
|
df=ak.stock_us_daily(symbol=symbol, adjust=adjust)
|
753
931
|
except:
|
754
|
-
print(" #Error(get_price_ak_us): no info found for",symbol)
|
932
|
+
#print(" #Error(get_price_ak_us): no info found for",symbol)
|
755
933
|
return None
|
756
934
|
|
757
935
|
#去掉可能出现的时区信息,必须使用datetime中的tz_localize
|
@@ -778,7 +956,7 @@ def get_price_ak_us(symbol, fromdate, todate, adjust=""):
|
|
778
956
|
df2['Adj Close']=df2['Close']
|
779
957
|
df2['source']='新浪'
|
780
958
|
df2['footnote']=adjust
|
781
|
-
print(" Successfully retrieved",num,"records for",symbol)
|
959
|
+
print(" Successfully retrieved",num,"records for",symbol,ticker_name(symbol,ticker_type))
|
782
960
|
|
783
961
|
return df2
|
784
962
|
|
@@ -795,7 +973,7 @@ if __name__=='__main__':
|
|
795
973
|
|
796
974
|
def get_price_ak_hk(symbol, fromdate, todate, adjust=""):
|
797
975
|
"""
|
798
|
-
|
976
|
+
抓取单个港股股价,不能处理股指,股指无.HK后缀
|
799
977
|
"""
|
800
978
|
|
801
979
|
#检查日期期间
|
@@ -807,7 +985,7 @@ def get_price_ak_hk(symbol, fromdate, todate, adjust=""):
|
|
807
985
|
#printmsg=str(symbol)+" from "+fromdate+" to "+todate
|
808
986
|
|
809
987
|
import akshare as ak
|
810
|
-
print(" Searching info in Sina for",symbol,"... ...")
|
988
|
+
#print(" Searching info in Sina for",symbol,"... ...")
|
811
989
|
symbol1=symbol.upper()
|
812
990
|
symbol2 = symbol1.strip('.HK')
|
813
991
|
if len(symbol2)==4:
|
@@ -840,14 +1018,14 @@ def get_price_ak_hk(symbol, fromdate, todate, adjust=""):
|
|
840
1018
|
return None
|
841
1019
|
num=len(df2)
|
842
1020
|
if num==0:
|
843
|
-
print(" #Error(get_price_ak_hk): found zero record for",
|
1021
|
+
print(" #Error(get_price_ak_hk): found zero record for",symbol)
|
844
1022
|
return None
|
845
1023
|
|
846
1024
|
df2.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
|
847
1025
|
df2['ticker']=symbol
|
848
1026
|
df2['Adj Close']=df2['Close']
|
849
1027
|
df2['source']='新浪'
|
850
|
-
print(" Successfully retrieved",num,"records for",symbol)
|
1028
|
+
print(" Successfully retrieved",num,"records for",symbol,ticker_name(symbol,ticker_type))
|
851
1029
|
|
852
1030
|
return df2
|
853
1031
|
|
@@ -860,56 +1038,52 @@ if __name__=='__main__':
|
|
860
1038
|
ticker=['600519.SS','000858.SZ']
|
861
1039
|
fromdate='2020-12-1'
|
862
1040
|
todate='2021-1-31'
|
863
|
-
adjust='none'
|
1041
|
+
adjust='none'
|
1042
|
+
|
1043
|
+
prices=get_prices_ak(ticker,fromdate,todate,adjust,ticker_type)
|
864
1044
|
|
865
|
-
def get_prices_ak(ticker,fromdate,todate,adjust='none'):
|
1045
|
+
def get_prices_ak(ticker,fromdate,todate,adjust='none',ticker_type='auto'):
|
866
1046
|
"""
|
867
1047
|
功能:获取中国国内股票或指数的历史行情,多个股票
|
868
1048
|
"""
|
869
|
-
|
1049
|
+
#检查是否为多个证券:单个证券代码
|
870
1050
|
if isinstance(ticker,str):
|
871
|
-
df=get_price_ak(ticker,fromdate,todate,adjust=adjust)
|
1051
|
+
df=get_price_ak(ticker,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
|
872
1052
|
return df
|
873
1053
|
|
874
|
-
|
1054
|
+
#检查是否为多个证券:空的列表
|
875
1055
|
if isinstance(ticker,list) and len(ticker) == 0:
|
876
1056
|
pass
|
877
1057
|
return None
|
878
1058
|
|
879
|
-
|
1059
|
+
#检查是否为多个证券:列表中只有一个代码
|
880
1060
|
if isinstance(ticker,list) and len(ticker) == 1:
|
881
1061
|
ticker1=ticker[0]
|
882
|
-
|
1062
|
+
#抓取单个证券
|
1063
|
+
df=get_price_ak(ticker1,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
|
883
1064
|
return df
|
884
1065
|
|
885
|
-
"""
|
886
|
-
#检查是否均为中国国内的股票或指数
|
887
|
-
cncode=True
|
888
|
-
for t in ticker:
|
889
|
-
last3=t[-3:]
|
890
|
-
if last3 not in ['.SS','.SZ']:
|
891
|
-
cncode=False
|
892
|
-
return None
|
893
|
-
"""
|
894
1066
|
import pandas as pd
|
895
|
-
|
1067
|
+
#处理列表中的第一个证券
|
896
1068
|
i=0
|
897
1069
|
df=None
|
898
1070
|
while df is None:
|
899
1071
|
t=ticker[i]
|
900
|
-
|
1072
|
+
#抓取单个证券
|
1073
|
+
df=get_price_ak(t,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
|
901
1074
|
if not (df is None):
|
902
1075
|
columns=create_tuple_for_columns(df,t)
|
903
1076
|
df.columns=pd.MultiIndex.from_tuples(columns)
|
904
1077
|
else:
|
905
1078
|
i=i+1
|
906
1079
|
if (i+1) == len(ticker):
|
907
|
-
|
1080
|
+
#已经到达代码列表末尾
|
908
1081
|
return df
|
909
1082
|
|
910
|
-
|
1083
|
+
#处理列表中的其余证券
|
911
1084
|
for t in ticker[(i+1):]:
|
912
|
-
|
1085
|
+
#抓取单个证券
|
1086
|
+
dft=get_price_ak(t,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
|
913
1087
|
if not (dft is None):
|
914
1088
|
columns=create_tuple_for_columns(dft,t)
|
915
1089
|
dft.columns=pd.MultiIndex.from_tuples(columns)
|
@@ -994,7 +1168,7 @@ if __name__=='__main__':
|
|
994
1168
|
ticker='AAPL'
|
995
1169
|
ticker='^JN0U.JO'
|
996
1170
|
|
997
|
-
start='
|
1171
|
+
start='2024-3-1'
|
998
1172
|
end='2024-3-31'
|
999
1173
|
retry_count=3
|
1000
1174
|
pause=1
|
@@ -1003,12 +1177,14 @@ if __name__=='__main__':
|
|
1003
1177
|
|
1004
1178
|
ticker=['AAPL','MSFT']
|
1005
1179
|
ticker=['AAPL','MSFT','ABCD']
|
1180
|
+
|
1181
|
+
df=get_prices_yahoo(ticker,start,end)
|
1006
1182
|
|
1007
1183
|
def get_prices_yahoo(ticker,start,end,retry_count=3,pause=1):
|
1008
1184
|
"""
|
1009
1185
|
功能:抓取股价,使用pandas_datareader
|
1010
1186
|
输出:指定收盘价格序列,最新日期的股价排列在前
|
1011
|
-
ticker: 股票代码。大陆股票代码加上后缀.SZ或.SS,港股代码去掉前导0加后缀.HK
|
1187
|
+
ticker: 股票代码。大陆股票代码加上后缀.SZ或.SS或.BJ,港股代码去掉前导0加后缀.HK
|
1012
1188
|
start: 样本开始日期,尽量远的日期,以便取得足够多的原始样本,yyyy-mm-dd
|
1013
1189
|
end: 样本结束日期,既可以是今天日期,也可以是一个历史日期
|
1014
1190
|
retry_count:网络失败时的重试次数
|
@@ -1024,26 +1200,24 @@ def get_prices_yahoo(ticker,start,end,retry_count=3,pause=1):
|
|
1024
1200
|
import yfinance as yfin
|
1025
1201
|
yfin.pdr_override()
|
1026
1202
|
"""
|
1203
|
+
p=None
|
1204
|
+
|
1027
1205
|
try:
|
1028
1206
|
#p=data.DataReader(ticker,'yahoo',start,end,retry_count=retry_count,pause=pause)
|
1029
1207
|
p=pdr.get_data_yahoo(ticker,start=start,end=end)
|
1030
|
-
except:
|
1031
|
-
|
1032
|
-
return None
|
1208
|
+
except: pass
|
1209
|
+
found=df_have_data(p)
|
1033
1210
|
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
p['ticker']=ticker
|
1039
|
-
#p['Adj Close']=p['Close']
|
1040
|
-
p['source']='雅虎'
|
1211
|
+
if found in ['Found']:
|
1212
|
+
cols=list(p)
|
1213
|
+
if 'Adj Close' not in cols:
|
1214
|
+
p['Adj Close']=p['Close']
|
1041
1215
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
print("
|
1216
|
+
p['ticker']=ticker
|
1217
|
+
#p['Adj Close']=p['Close']
|
1218
|
+
p['source']='雅虎'
|
1219
|
+
|
1220
|
+
print(" Successfully retrieved",len(p),"records for",ticker,ticker_name(ticker,ticker_type))
|
1047
1221
|
|
1048
1222
|
return p
|
1049
1223
|
|
@@ -1062,16 +1236,6 @@ def get_price_yf(ticker,start,end,threads=False):
|
|
1062
1236
|
"""
|
1063
1237
|
df=get_prices_yf(ticker,start,end,threads=threads)
|
1064
1238
|
|
1065
|
-
df['ticker']=ticker
|
1066
|
-
df['Adj Close']=df['Close']
|
1067
|
-
df['source']='雅虎'
|
1068
|
-
|
1069
|
-
num=len(df)
|
1070
|
-
if num > 0:
|
1071
|
-
print(" Successfully retrieved",num,"records for",ticker)
|
1072
|
-
else:
|
1073
|
-
print(" Sorry, no records retrieved for",ticker)
|
1074
|
-
|
1075
1239
|
return df
|
1076
1240
|
|
1077
1241
|
|
@@ -1085,21 +1249,25 @@ if __name__=='__main__':
|
|
1085
1249
|
ticker=['AAPL','MSFT','0700.HK','600519.SS']
|
1086
1250
|
|
1087
1251
|
threads=False
|
1252
|
+
threads=True
|
1253
|
+
|
1254
|
+
df=get_price_yf(ticker,start,end,threads)
|
1088
1255
|
|
1089
1256
|
|
1090
1257
|
def get_prices_yf(ticker,start,end,threads=False):
|
1091
1258
|
"""
|
1092
1259
|
功能:从新浪/stooq抓取股价,使用yfinance(对非美股抓取速度快,但有时不太稳定)
|
1093
1260
|
输入:股票代码或股票代码列表,开始日期,结束日期
|
1094
|
-
ticker: 股票代码或股票代码列表。大陆股票代码加上后缀.SZ或.SS,港股代码去掉前导0加后缀.HK
|
1261
|
+
ticker: 股票代码或股票代码列表。大陆股票代码加上后缀.SZ或.SS或.BJ,港股代码去掉前导0加后缀.HK
|
1095
1262
|
start: 样本开始日期,尽量远的日期,以便取得足够多的原始样本,yyyy-mm-dd
|
1096
1263
|
end: 样本结束日期,既可以是今天日期,也可以是一个历史日期
|
1097
1264
|
|
1098
1265
|
输出:指定收盘价格序列,最新日期的股价排列在前
|
1099
1266
|
特别注意:yfinance中的收盘价Close其实是Yahoo Finance中的调整收盘价Adj Close。
|
1100
1267
|
"""
|
1101
|
-
|
1102
|
-
|
1268
|
+
p=None
|
1269
|
+
|
1270
|
+
#支持多个证券
|
1103
1271
|
import yfinance as yf
|
1104
1272
|
ticker1,islist=cvt_yftickerlist(ticker)
|
1105
1273
|
if not islist:
|
@@ -1108,46 +1276,33 @@ def get_prices_yf(ticker,start,end,threads=False):
|
|
1108
1276
|
try:
|
1109
1277
|
#p=stock.history(start=start,end=end,threads=threads)
|
1110
1278
|
p=stock.history(start=start,end=end)
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
key1='WSAETIMEDOUT'
|
1117
|
-
if emsg.find(key1) != -1:
|
1118
|
-
print(" #Error(get_prices_yf): data source unreachable, try later")
|
1119
|
-
return None
|
1120
|
-
|
1121
|
-
#单个代码:是否未找到
|
1122
|
-
key2='Date'
|
1123
|
-
if emsg.find(key2):
|
1124
|
-
#单个ticker,未找到代码
|
1125
|
-
print(" #Error(get_prices_yf): ticker info inaccessible now for",ticker)
|
1126
|
-
return None
|
1279
|
+
#仅针对雅虎情况
|
1280
|
+
if p is not None:
|
1281
|
+
if len(p)==0: p=None
|
1282
|
+
except:
|
1283
|
+
p=None
|
1127
1284
|
else:
|
1128
1285
|
#下载股票列表的股价
|
1129
1286
|
try:
|
1130
1287
|
p=yf.download(ticker1,start=start,end=end,progress=False,threads=threads)
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
num=len(p)
|
1147
|
-
if num > 0:
|
1148
|
-
print(" Successfully retrieved",num,"records for",ticker)
|
1288
|
+
#仅针对雅虎情况
|
1289
|
+
if p is not None:
|
1290
|
+
if len(p)==0: p=None
|
1291
|
+
except:
|
1292
|
+
p=None
|
1293
|
+
|
1294
|
+
found=df_have_data(p)
|
1295
|
+
if found in ['Found','Empty']:
|
1296
|
+
if 'Adj Close' not in list(p):
|
1297
|
+
p['Adj Close']=p['Close']
|
1298
|
+
p['ticker']=ticker
|
1299
|
+
p['source']='雅虎'
|
1300
|
+
|
1301
|
+
if len(p) > 0:
|
1302
|
+
print(" Successfully retrieved",len(p),"records for",ticker1,ticker_name(ticker1,ticker_type))
|
1149
1303
|
else:
|
1150
|
-
|
1304
|
+
pass
|
1305
|
+
#print(" #Error(get_prices_yf):",ticker1,"not found or no prices in the period or inaccessible to yahoo")
|
1151
1306
|
|
1152
1307
|
return p
|
1153
1308
|
|
@@ -1207,7 +1362,7 @@ def get_index_fred(ticker,start,end):
|
|
1207
1362
|
|
1208
1363
|
num=len(df)
|
1209
1364
|
if num > 0:
|
1210
|
-
print(" Successfully retrieved",num,"records for",ticker)
|
1365
|
+
print(" Successfully retrieved",num,"records for",ticker,ticker_name(ticker,ticker_type))
|
1211
1366
|
else:
|
1212
1367
|
print(" Sorry, no records retrieved for",ticker)
|
1213
1368
|
|
@@ -1241,12 +1396,15 @@ def create_tuple_for_columns(df_a, multi_level_col):
|
|
1241
1396
|
#==============================================================================
|
1242
1397
|
#==============================================================================
|
1243
1398
|
#==============================================================================
|
1244
|
-
def get_price_portfolio(tickerlist,sharelist,fromdate,todate,adj=False,
|
1399
|
+
def get_price_portfolio(tickerlist,sharelist,fromdate,todate,adj=False, \
|
1400
|
+
source='auto',ticker_type='bond'):
|
1245
1401
|
"""
|
1246
1402
|
套壳函数get_prices_portfolio
|
1247
1403
|
经测试,已经能够支持capm_beta2
|
1404
|
+
ticker_type='bond':抓取债券优先,因投资组合中配置债券的可能性远高于基金和指数
|
1248
1405
|
"""
|
1249
|
-
df=get_prices_portfolio(tickerlist,sharelist,fromdate,todate,adj=adj,
|
1406
|
+
df=get_prices_portfolio(tickerlist,sharelist,fromdate,todate,adj=adj, \
|
1407
|
+
source=source,ticker_type=ticker_type)
|
1250
1408
|
return df
|
1251
1409
|
|
1252
1410
|
if __name__=='__main__':
|
@@ -1263,20 +1421,22 @@ if __name__=='__main__':
|
|
1263
1421
|
sharelist=[1000]
|
1264
1422
|
|
1265
1423
|
fromdate='2024-1-1'
|
1266
|
-
todate='2024-
|
1424
|
+
todate='2024-4-1'
|
1267
1425
|
adj=False
|
1268
1426
|
source='auto'
|
1427
|
+
ticker_type='auto'
|
1269
1428
|
|
1270
|
-
|
1271
|
-
_,_,tickerlist,sharelist=decompose_portfolio(
|
1429
|
+
ticker={'Market':('China','000001.SS','白酒组合'),'600519.SS':0.4,'000858.SZ':0.6}
|
1430
|
+
_,_,tickerlist,sharelist=decompose_portfolio(ticker)
|
1272
1431
|
|
1273
1432
|
p=get_prices_portfolio(tickerlist,sharelist,fromdate,todate,source='auto')
|
1274
1433
|
|
1275
|
-
def get_prices_portfolio(tickerlist,sharelist,fromdate,todate,adj=False,
|
1434
|
+
def get_prices_portfolio(tickerlist,sharelist,fromdate,todate,adj=False, \
|
1435
|
+
source='auto',ticker_type='bond'):
|
1276
1436
|
"""
|
1277
1437
|
功能:抓取投资组合的每日价值
|
1278
|
-
|
1279
|
-
tickerlist:
|
1438
|
+
输入:证券代码列表,份额列表,开始日期,结束日期
|
1439
|
+
tickerlist: 证券代码列表
|
1280
1440
|
sharelist:持有份额列表,与股票代码列表一一对应
|
1281
1441
|
fromdate: 样本开始日期。格式:'YYYY-MM-DD'
|
1282
1442
|
todate: 样本结束日期。既可以是今天日期,也可以是一个历史日期
|
@@ -1285,13 +1445,13 @@ def get_prices_portfolio(tickerlist,sharelist,fromdate,todate,adj=False,source='
|
|
1285
1445
|
"""
|
1286
1446
|
import pandas as pd
|
1287
1447
|
|
1288
|
-
|
1448
|
+
#检查证券列表个数与份额列表个数是否一致
|
1289
1449
|
if len(tickerlist) != len(sharelist):
|
1290
1450
|
print(" #Error(get_prices_portfolio): numbers of stocks and shares mismatch.")
|
1291
1451
|
return None
|
1292
1452
|
|
1293
|
-
|
1294
|
-
p=get_prices(tickerlist,fromdate,todate,adj=adj,source=source)
|
1453
|
+
#抓取证券价格:如何只抓取股票和债券???
|
1454
|
+
p=get_prices(tickerlist,fromdate,todate,adj=adj,source=source,ticker_type=ticker_type)
|
1295
1455
|
if p is None: return None
|
1296
1456
|
|
1297
1457
|
#删除无用的空列preclose,避免引起后续程序误判
|
@@ -1537,7 +1697,7 @@ def calc_rolling_return(drdf, period="Weekly"):
|
|
1537
1697
|
#检查period类型
|
1538
1698
|
periodlist = ["Weekly","Monthly","Quarterly","Annual"]
|
1539
1699
|
if not (period in periodlist):
|
1540
|
-
print("
|
1700
|
+
print(" #Error(calc_rolling_return), periodic type only support:",periodlist)
|
1541
1701
|
return None
|
1542
1702
|
|
1543
1703
|
#换算期间对应的实际交易天数
|
@@ -2173,6 +2333,473 @@ def get_price_security(security,start,end,source='auto'):
|
|
2173
2333
|
|
2174
2334
|
return prices
|
2175
2335
|
|
2336
|
+
#==============================================================================
|
2337
|
+
if __name__=='__main__':
|
2338
|
+
ticker='600519.SS'
|
2339
|
+
ticker='000858.SZ'
|
2340
|
+
|
2341
|
+
ticker='SH600519'
|
2342
|
+
ticker='sh600519'
|
2343
|
+
ticker='sz000858'
|
2344
|
+
|
2345
|
+
ticker='sz600519'
|
2346
|
+
ticker='sh000858'
|
2347
|
+
|
2348
|
+
ticker='600519.SH'
|
2349
|
+
ticker='600519.sh'
|
2350
|
+
ticker='000858.sz'
|
2351
|
+
|
2352
|
+
ticker='000858.sh'
|
2353
|
+
ticker='600519.sz'
|
2354
|
+
|
2355
|
+
ticker='600519'
|
2356
|
+
ticker='000858'
|
2357
|
+
ticker='600519.CN'
|
2358
|
+
ticker='000858.CN'
|
2359
|
+
ticker='801010.SW'
|
2360
|
+
ticker='880410.ZZ'
|
2361
|
+
|
2362
|
+
ticker='01210.HK'
|
2363
|
+
ticker='AAPL'
|
2364
|
+
ticker='6758.T'
|
2365
|
+
ticker='SONA.F'
|
2366
|
+
|
2367
|
+
ticker1_cvt2yahoo(ticker)
|
2368
|
+
|
2369
|
+
def ticker1_cvt2yahoo(ticker):
|
2370
|
+
"""
|
2371
|
+
功能:将一只股票、基金、债券代码转换为siat内部默认的yahoo格式
|
2372
|
+
情形:后缀,前缀,无后缀和前缀
|
2373
|
+
注意:中证行业代码若为沪深交易所收藏的,仍以SS/SZ为后缀,不可用ZZ后缀
|
2374
|
+
"""
|
2375
|
+
ticker1=ticker.upper() #转为大写
|
2376
|
+
|
2377
|
+
#后缀
|
2378
|
+
result,prefix,suffix=split_prefix_suffix(ticker1)
|
2379
|
+
if suffix in ['SS','SH','SZ','BJ','CN','SW','ZZ'] and len(prefix)==6:
|
2380
|
+
if suffix in ['SH']:
|
2381
|
+
suffix1='SS'
|
2382
|
+
elif suffix in ['CN']:
|
2383
|
+
suffix1,_=china_security_identify(prefix)
|
2384
|
+
else:
|
2385
|
+
suffix1=suffix
|
2386
|
+
|
2387
|
+
"""
|
2388
|
+
#检查是否搞错SS/SZ/BJ
|
2389
|
+
if suffix1 in ['SS','SZ','BJ']:
|
2390
|
+
suffix1,_=china_security_identify(prefix)
|
2391
|
+
"""
|
2392
|
+
ticker2=prefix+'.'+suffix1
|
2393
|
+
return ticker2
|
2394
|
+
|
2395
|
+
#前缀
|
2396
|
+
head2=ticker1[:2]
|
2397
|
+
rest2=ticker1[2:]
|
2398
|
+
if head2 in ['SH','SZ','BJ','SW','ZZ'] and len(rest2)==6:
|
2399
|
+
#suffix1,_=china_security_identify(rest2)
|
2400
|
+
if head2 in ['SH']:
|
2401
|
+
suffix1='SS'
|
2402
|
+
else:
|
2403
|
+
suffix1=head2
|
2404
|
+
"""
|
2405
|
+
#检查是否搞错SS/SZ/BJ
|
2406
|
+
if suffix1 in ['SS','SZ','BJ']:
|
2407
|
+
suffix1,_=china_security_identify(rest2)
|
2408
|
+
"""
|
2409
|
+
ticker2=rest2+'.'+suffix1
|
2410
|
+
return ticker2
|
2411
|
+
|
2412
|
+
#无前后缀,6位数字,默认为A股
|
2413
|
+
if is_all_digits(ticker1) and len(ticker1) == 6:
|
2414
|
+
suffix1,_=china_security_identify(ticker1)
|
2415
|
+
ticker2=ticker1+'.'+suffix1
|
2416
|
+
return ticker2
|
2417
|
+
|
2418
|
+
#其他:直接返回
|
2419
|
+
return ticker1
|
2420
|
+
|
2421
|
+
#==============================================================================
|
2422
|
+
if __name__=='__main__':
|
2423
|
+
ticker=['600519.SS','sz000858','002594.sz','aapl']
|
2424
|
+
|
2425
|
+
tickers_cvt2yahoo(ticker)
|
2426
|
+
|
2427
|
+
def tickers_cvt2yahoo(ticker):
|
2428
|
+
"""
|
2429
|
+
功能:将多只股票、基金、债券代码转换为siat内部默认的yahoo格式
|
2430
|
+
"""
|
2431
|
+
#单个字符串:返回字符串
|
2432
|
+
if isinstance(ticker,str):
|
2433
|
+
result=ticker1_cvt2yahoo(ticker)
|
2434
|
+
return result
|
2435
|
+
|
2436
|
+
#列表:返回列表
|
2437
|
+
if isinstance(ticker,list): #避免下面的循环出错
|
2438
|
+
tickerlist=[]
|
2439
|
+
for t in ticker:
|
2440
|
+
t2=ticker1_cvt2yahoo(t)
|
2441
|
+
tickerlist=tickerlist+[t2]
|
2442
|
+
|
2443
|
+
result=tickerlist
|
2444
|
+
return result
|
2445
|
+
|
2446
|
+
#其他:直接返回
|
2447
|
+
return ticker
|
2448
|
+
|
2449
|
+
#==============================================================================
|
2450
|
+
if __name__=='__main__':
|
2451
|
+
ticker='SH600519'
|
2452
|
+
ticker='sh600519'
|
2453
|
+
ticker='sz000858'
|
2454
|
+
|
2455
|
+
ticker='sz600519'
|
2456
|
+
ticker='sh000858'
|
2457
|
+
|
2458
|
+
ticker='600519.SH'
|
2459
|
+
ticker='600519.sh'
|
2460
|
+
ticker='000858.sz'
|
2461
|
+
|
2462
|
+
ticker='000858.sh'
|
2463
|
+
ticker='600519.sz'
|
2464
|
+
|
2465
|
+
ticker='600519'
|
2466
|
+
ticker='000858'
|
2467
|
+
ticker='600519.CN'
|
2468
|
+
ticker='000858.CN'
|
2469
|
+
ticker='801010.SW'
|
2470
|
+
ticker='880410.ZZ'
|
2471
|
+
|
2472
|
+
ticker='sh149996'
|
2473
|
+
|
2474
|
+
ticker='01210.HK'
|
2475
|
+
ticker='AAPL'
|
2476
|
+
ticker='6758.T'
|
2477
|
+
ticker='SONA.F'
|
2478
|
+
|
2479
|
+
ticker1_cvt2ak(ticker)
|
2480
|
+
|
2481
|
+
def ticker1_cvt2ak(ticker):
|
2482
|
+
"""
|
2483
|
+
功能:将一只股票、基金、债券代码转换为akshare格式
|
2484
|
+
情形:后缀,前缀,无后缀和前缀
|
2485
|
+
注意:中证行业代码若为沪深交易所收藏的,仍以SS/SZ为后缀,不可用ZZ后缀
|
2486
|
+
"""
|
2487
|
+
ticker1=ticker.upper() #转为大写
|
2488
|
+
|
2489
|
+
#后缀
|
2490
|
+
result,prefix,suffix=split_prefix_suffix(ticker1)
|
2491
|
+
if suffix in ['SS','SH','SZ','BJ','CN'] and len(prefix)==6:
|
2492
|
+
if suffix in ['SH','SS']: prefix1='sh'
|
2493
|
+
if suffix in ['SZ']: prefix1='sz'
|
2494
|
+
if suffix in ['BJ']: prefix1='bj'
|
2495
|
+
if suffix in ['CN']:
|
2496
|
+
suffix1,_=china_security_identify(prefix)
|
2497
|
+
prefix1='sh'
|
2498
|
+
if suffix1 in ['SS']: prefix1='sh'
|
2499
|
+
if suffix1 in ['SZ']: prefix1='sz'
|
2500
|
+
if suffix1 in ['BJ']: prefix1='bj'
|
2501
|
+
"""
|
2502
|
+
#检查是否搞错SS/SZ/BJ
|
2503
|
+
if suffix in ['SS','SH','SZ','BJ']:
|
2504
|
+
suffix1,_=china_security_identify(prefix)
|
2505
|
+
if suffix1 in ['SS','SH']: prefix1='sh'
|
2506
|
+
if suffix1 == 'SZ': prefix1='sz'
|
2507
|
+
if suffix1 == 'BJ': prefix1='bj'
|
2508
|
+
"""
|
2509
|
+
ticker2=prefix1+prefix
|
2510
|
+
return ticker2
|
2511
|
+
|
2512
|
+
#前缀
|
2513
|
+
head2=ticker1[:2]
|
2514
|
+
rest2=ticker1[2:]
|
2515
|
+
if head2 in ['SH','SS','SZ','BJ'] and len(rest2)==6:
|
2516
|
+
if head2 in ['SH','SS']: prefix1='sh'
|
2517
|
+
if head2 in ['SZ']: prefix1='sz'
|
2518
|
+
if head2 in ['BJ']: prefix1='bj'
|
2519
|
+
|
2520
|
+
"""
|
2521
|
+
#检查是否搞错SS/SZ/BJ
|
2522
|
+
if head2 in ['SH','SS','SZ','BJ']:
|
2523
|
+
suffix1,_=china_security_identify(rest2)
|
2524
|
+
if suffix1 == 'SS': prefix1='sh'
|
2525
|
+
if suffix1 == 'SZ': prefix1='sz'
|
2526
|
+
if suffix1 == 'BJ': prefix1='bj'
|
2527
|
+
"""
|
2528
|
+
ticker2=prefix1+rest2
|
2529
|
+
return ticker2
|
2530
|
+
|
2531
|
+
#无前后缀,6位数字,默认为A股
|
2532
|
+
if is_all_digits(ticker1) and len(ticker1) == 6:
|
2533
|
+
suffix1,_=china_security_identify(ticker1)
|
2534
|
+
prefix1='sh'
|
2535
|
+
if head2 in ['SH','SS']: prefix1='sh'
|
2536
|
+
if head2 in ['SZ']: prefix1='sz'
|
2537
|
+
if head2 in ['BJ']: prefix1='bj'
|
2538
|
+
|
2539
|
+
ticker2=prefix1+ticker1
|
2540
|
+
return ticker2
|
2541
|
+
|
2542
|
+
#其他:直接返回
|
2543
|
+
return ticker1
|
2544
|
+
|
2545
|
+
#==============================================================================
|
2546
|
+
if __name__=='__main__':
|
2547
|
+
ticker=['600519.SS','sz000858','002594.sz','aapl']
|
2548
|
+
|
2549
|
+
tickers_cvt2ak(ticker)
|
2550
|
+
|
2551
|
+
def tickers_cvt2ak(ticker):
|
2552
|
+
"""
|
2553
|
+
功能:将多只股票、基金、债券代码转换为akshare格式
|
2554
|
+
"""
|
2555
|
+
#单个字符串:返回字符串
|
2556
|
+
if isinstance(ticker,str):
|
2557
|
+
result=ticker1_cvt2ak(ticker)
|
2558
|
+
return result
|
2559
|
+
|
2560
|
+
#列表:返回列表
|
2561
|
+
if isinstance(ticker,list): #避免下面的循环出错
|
2562
|
+
tickerlist=[]
|
2563
|
+
for t in ticker:
|
2564
|
+
t2=ticker1_cvt2ak(t)
|
2565
|
+
tickerlist=tickerlist+[t2]
|
2566
|
+
|
2567
|
+
result=tickerlist
|
2568
|
+
return result
|
2569
|
+
|
2570
|
+
#其他:直接返回
|
2571
|
+
return ticker
|
2572
|
+
|
2573
|
+
|
2574
|
+
#==============================================================================
|
2575
|
+
if __name__=='__main__':
|
2576
|
+
s='123456'
|
2577
|
+
s='123456.'
|
2578
|
+
s='123456a'
|
2579
|
+
|
2580
|
+
is_all_digits(s)
|
2581
|
+
|
2582
|
+
def is_all_digits(s):
|
2583
|
+
"""
|
2584
|
+
功能:检查字符串s是否为全数字构成
|
2585
|
+
"""
|
2586
|
+
import re
|
2587
|
+
return bool(re.match(r'^\d+$', s))
|
2588
|
+
|
2589
|
+
#==============================================================================
|
2590
|
+
if __name__=='__main__':
|
2591
|
+
ticker6='AAPL'
|
2592
|
+
ticker6='01211'
|
2593
|
+
ticker6='600519'
|
2594
|
+
ticker6='149996'
|
2595
|
+
|
2596
|
+
def china_security_identify(ticker6):
|
2597
|
+
"""
|
2598
|
+
功能:区分中国内地证券代码前缀,返回后缀SS/SZ/BJ
|
2599
|
+
情形:股票,基金,债券,指数
|
2600
|
+
注意:ticker6需为6位数字字符,目前仅限沪深京交易所,未包括期货期权交易所
|
2601
|
+
"""
|
2602
|
+
suffix='SS'
|
2603
|
+
stype='stock'
|
2604
|
+
|
2605
|
+
#检查是否为6位数字字符
|
2606
|
+
if not is_all_digits(ticker6) or len(ticker6) != 6:
|
2607
|
+
suffix=''
|
2608
|
+
stype=''
|
2609
|
+
return suffix,stype
|
2610
|
+
|
2611
|
+
head1=ticker6[:1]
|
2612
|
+
head2=ticker6[:2]
|
2613
|
+
head3=ticker6[:3]
|
2614
|
+
|
2615
|
+
#股票代码
|
2616
|
+
if head2 in ['60','68']: #上交所:60-主板,68-科创板
|
2617
|
+
suffix='SS'
|
2618
|
+
stype='stock'
|
2619
|
+
return suffix,stype
|
2620
|
+
if head2 in ['00','30']: #深交所:00-主板,30-创业板
|
2621
|
+
suffix='SZ'
|
2622
|
+
stype='stock'
|
2623
|
+
return suffix,stype
|
2624
|
+
if head1 in ['8']: #北交所
|
2625
|
+
suffix='BJ'
|
2626
|
+
stype='stock'
|
2627
|
+
return suffix,stype
|
2628
|
+
|
2629
|
+
#沪深基金
|
2630
|
+
if head2 in ['50','51']: #上交所:50-封闭式,51-ETF
|
2631
|
+
suffix='SS'
|
2632
|
+
stype='fund'
|
2633
|
+
return suffix,stype
|
2634
|
+
if head2 in ['15','16','18']: #深交所:15-ETF,16-LOF,18-封闭式
|
2635
|
+
suffix='SZ'
|
2636
|
+
stype='fund'
|
2637
|
+
return suffix,stype
|
2638
|
+
|
2639
|
+
#沪深债券
|
2640
|
+
if head3 in ['271','270','240','188','185','184','175','163','155','152', \
|
2641
|
+
'143','138','137','136','127','124','122','118','115','113', \
|
2642
|
+
'100','020','019','018','010']:
|
2643
|
+
suffix='SS'
|
2644
|
+
stype='bond'
|
2645
|
+
return suffix,stype
|
2646
|
+
|
2647
|
+
#有重复
|
2648
|
+
if head3 in ['149','148','133','128','127','123','114','112','111','110', \
|
2649
|
+
'108','102','101','100']:
|
2650
|
+
suffix='SZ'
|
2651
|
+
stype='bond'
|
2652
|
+
return suffix,stype
|
2653
|
+
|
2654
|
+
#沪深B股
|
2655
|
+
if head3 in ['900']:
|
2656
|
+
suffix='SS'
|
2657
|
+
stype='stockb'
|
2658
|
+
return suffix,stype
|
2659
|
+
if head3 in ['200']:
|
2660
|
+
suffix='SZ'
|
2661
|
+
stype='stockb'
|
2662
|
+
return suffix,stype
|
2663
|
+
|
2664
|
+
#其他
|
2665
|
+
return '',''
|
2666
|
+
|
2667
|
+
#==============================================================================
|
2668
|
+
if __name__=='__main__':
|
2669
|
+
ticker='850831'
|
2670
|
+
|
2671
|
+
start='2023-1-1'
|
2672
|
+
end='2023-4-4'
|
2673
|
+
info_types=['Close','Volume']
|
2674
|
+
|
2675
|
+
df3=fetch_price_swindex(ticker,start,end)
|
2676
|
+
|
2677
|
+
def fetch_price_swindex(ticker,start,end,info_types=['Close','Volume'],adjust=-2*365):
|
2678
|
+
"""
|
2679
|
+
功能:获取申万行业指数的信息
|
2680
|
+
ticker:申万行业指数
|
2681
|
+
start,end:日期期间
|
2682
|
+
info_types:信息测度,默认['Close'],还可以为['Close','Open','High','Low',
|
2683
|
+
'Volume','Adj Close']
|
2684
|
+
特点:为compare_indicator使用,包括指数名称
|
2685
|
+
"""
|
2686
|
+
df=None
|
2687
|
+
|
2688
|
+
# 检查日期期间的合理性
|
2689
|
+
result,startpd,endpd=check_period(start,end)
|
2690
|
+
if not result:
|
2691
|
+
#print(" #Error(fetch_price_swindex): invalid date period between",start,"and",end)
|
2692
|
+
return None
|
2693
|
+
|
2694
|
+
start1=date_adjust(start,adjust=adjust)
|
2695
|
+
_,start1pd,_=check_period(start1,end)
|
2696
|
+
|
2697
|
+
import akshare as ak
|
2698
|
+
try:
|
2699
|
+
prices= ak.index_hist_sw(symbol=ticker,period="day")
|
2700
|
+
except: pass
|
2701
|
+
|
2702
|
+
found=df_have_data(prices)
|
2703
|
+
if found not in ['Found','Empty']: return df
|
2704
|
+
|
2705
|
+
prices.columns=['Code','Date','Close','Open','High','Low','Volume','Amount']
|
2706
|
+
million=1000000
|
2707
|
+
prices['Volume']=prices['Volume']*million
|
2708
|
+
prices['Amount']=prices['Amount']*million
|
2709
|
+
|
2710
|
+
import pandas as pd
|
2711
|
+
prices['date']=pd.to_datetime(prices['Date'])
|
2712
|
+
prices.set_index('date',inplace=True)
|
2713
|
+
|
2714
|
+
prices2=prices[(prices.index >= start1pd) & (prices.index <= endpd)]
|
2715
|
+
|
2716
|
+
|
2717
|
+
if isinstance(info_types,str):
|
2718
|
+
typelist=[info_types]
|
2719
|
+
else:
|
2720
|
+
typelist=info_types
|
2721
|
+
|
2722
|
+
import pandas as pd
|
2723
|
+
df=pd.DataFrame()
|
2724
|
+
|
2725
|
+
for t in typelist:
|
2726
|
+
try:
|
2727
|
+
df[t]=prices2[t]
|
2728
|
+
except:
|
2729
|
+
continue
|
2730
|
+
|
2731
|
+
collist=list(df)
|
2732
|
+
pcollist=['Open','High','Low','Adj Close']
|
2733
|
+
for p in pcollist:
|
2734
|
+
if p not in collist:
|
2735
|
+
df[p]=df['Close']
|
2736
|
+
|
2737
|
+
df['Code']=ticker
|
2738
|
+
df['Type']='swindex'
|
2739
|
+
df['Name']=ticker_name(ticker)
|
2740
|
+
|
2741
|
+
#print(" Successfully retrieved",len(df),"records for sw index",ticker)
|
2742
|
+
|
2743
|
+
return df
|
2744
|
+
|
2745
|
+
#==============================================================================
|
2746
|
+
if __name__=='__main__':
|
2747
|
+
ticker='sh018003'
|
2748
|
+
fromdate='2024-1-1'
|
2749
|
+
todate='2024-4-17'
|
2750
|
+
trend_type='净值'
|
2751
|
+
|
2752
|
+
f=get_price_oef_china(ticker,fromdate,todate)
|
2753
|
+
|
2754
|
+
def get_price_oef_china(ticker,fromdate,todate):
|
2755
|
+
"""
|
2756
|
+
功能:单纯获取中国开放式基金的单位净值趋势
|
2757
|
+
"""
|
2758
|
+
#检查日期
|
2759
|
+
result,start,end=check_period(fromdate,todate)
|
2760
|
+
if not result:
|
2761
|
+
print(" #Error(get_price_oef_china): invalid date period:",fromdate,todate)
|
2762
|
+
return None
|
2763
|
+
|
2764
|
+
#print("Searching for open-ended fund (OEF) trend info in China ...")
|
2765
|
+
import akshare as ak
|
2766
|
+
|
2767
|
+
fund1=ticker[:6]; fund2=ticker[-6:]
|
2768
|
+
if fund1.isdigit():
|
2769
|
+
fund=fund1
|
2770
|
+
elif fund2.isdigit():
|
2771
|
+
fund=fund2
|
2772
|
+
else:
|
2773
|
+
fund=ticker
|
2774
|
+
|
2775
|
+
fund_name=ticker_name(fund,'fund')
|
2776
|
+
|
2777
|
+
#单位净值
|
2778
|
+
found='None'; df2=None
|
2779
|
+
try:
|
2780
|
+
df1 = ak.fund_open_fund_info_em(fund, indicator="单位净值走势")
|
2781
|
+
df1.rename(columns={'净值日期':'date','单位净值':'Close'}, inplace=True)
|
2782
|
+
df1['Open']=df1['High']=df1['Low']=df1['Close']
|
2783
|
+
df1['Volume']=0
|
2784
|
+
df1['name']=ticker_name(fund,'fund')
|
2785
|
+
|
2786
|
+
df1['date']=df1['date'].apply(lambda x: pd.to_datetime(x))
|
2787
|
+
df1.set_index(['date'],inplace=True)
|
2788
|
+
|
2789
|
+
df2=df1[['Open','Close','High','Low','Volume','name']]
|
2790
|
+
df2=df2[(df2.index >= start) & (df2.index <= end)]
|
2791
|
+
|
2792
|
+
if len(df2)==0:
|
2793
|
+
found='Empty'
|
2794
|
+
else:
|
2795
|
+
found='Found'
|
2796
|
+
except:
|
2797
|
+
pass
|
2798
|
+
|
2799
|
+
return df2
|
2800
|
+
|
2801
|
+
#==============================================================================
|
2802
|
+
|
2176
2803
|
#==============================================================================
|
2177
2804
|
#==============================================================================
|
2178
2805
|
#==============================================================================
|