siat 3.10.132__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.
Files changed (218) hide show
  1. siat/__init__.py +0 -0
  2. siat/allin.py +0 -0
  3. siat/assets_liquidity.py +0 -0
  4. siat/beta_adjustment.py +0 -0
  5. siat/beta_adjustment_china.py +0 -0
  6. siat/blockchain.py +0 -0
  7. siat/bond.py +0 -0
  8. siat/bond_base.py +0 -0
  9. siat/bond_china.py +0 -0
  10. siat/bond_zh_sina.py +0 -0
  11. siat/capm_beta.py +0 -0
  12. siat/capm_beta2.py +0 -0
  13. siat/compare_cross.py +0 -0
  14. siat/copyrights.py +0 -0
  15. siat/cryptocurrency.py +0 -0
  16. siat/economy.py +0 -0
  17. siat/economy2.py +0 -0
  18. siat/esg.py +0 -0
  19. siat/event_study.py +0 -0
  20. siat/exchange_bond_china.pickle +0 -0
  21. siat/fama_french.py +0 -0
  22. siat/fin_stmt2_yahoo.py +0 -0
  23. siat/financial_base.py +0 -0
  24. siat/financial_statements.py +0 -0
  25. siat/financials.py +0 -0
  26. siat/financials2.py +0 -0
  27. siat/financials_china.py +0 -0
  28. siat/financials_china2.py +0 -0
  29. siat/fund.py +0 -0
  30. siat/fund_china.pickle +0 -0
  31. siat/fund_china.py +0 -0
  32. siat/future_china.py +0 -0
  33. siat/google_authenticator.py +0 -0
  34. siat/grafix.py +0 -0
  35. siat/holding_risk.py +0 -0
  36. siat/luchy_draw.py +0 -0
  37. siat/market_china.py +0 -0
  38. siat/markowitz.py +0 -0
  39. siat/markowitz2.py +0 -0
  40. siat/markowitz2_20250704.py +0 -0
  41. siat/markowitz2_20250705.py +0 -0
  42. siat/markowitz_simple.py +0 -0
  43. siat/ml_cases.py +0 -0
  44. siat/ml_cases_example.py +0 -0
  45. siat/option_china.py +0 -0
  46. siat/option_pricing.py +0 -0
  47. siat/other_indexes.py +0 -0
  48. siat/risk_adjusted_return.py +0 -0
  49. siat/risk_adjusted_return2.py +0 -0
  50. siat/risk_evaluation.py +0 -0
  51. siat/risk_free_rate.py +0 -0
  52. siat/sector_china.py +0 -0
  53. siat/security_price2.py +0 -0
  54. siat/security_prices.py +40 -2
  55. siat/security_trend.py +0 -0
  56. siat/security_trend2.py +0 -0
  57. siat/stock.py +0 -0
  58. siat/stock_advice_linear.py +0 -0
  59. siat/stock_base.py +0 -0
  60. siat/stock_china.py +0 -0
  61. siat/stock_info.pickle +0 -0
  62. siat/stock_prices_kneighbors.py +0 -0
  63. siat/stock_prices_linear.py +0 -0
  64. siat/stock_profile.py +0 -0
  65. siat/stock_technical.py +0 -0
  66. siat/stooq.py +0 -0
  67. siat/transaction.py +0 -0
  68. siat/translate.py +0 -0
  69. siat/valuation.py +0 -0
  70. siat/valuation_china.py +0 -0
  71. siat/var_model_validation.py +0 -0
  72. siat/yf_name.py +0 -0
  73. {siat-3.10.132.dist-info/licenses → siat-3.10.133.dist-info}/LICENSE +0 -0
  74. {siat-3.10.132.dist-info → siat-3.10.133.dist-info}/METADATA +232 -235
  75. siat-3.10.133.dist-info/RECORD +78 -0
  76. {siat-3.10.132.dist-info → siat-3.10.133.dist-info}/WHEEL +1 -1
  77. {siat-3.10.132.dist-info → siat-3.10.133.dist-info}/top_level.txt +0 -1
  78. build/lib/build/lib/siat/__init__.py +0 -75
  79. build/lib/build/lib/siat/allin.py +0 -137
  80. build/lib/build/lib/siat/assets_liquidity.py +0 -915
  81. build/lib/build/lib/siat/beta_adjustment.py +0 -1058
  82. build/lib/build/lib/siat/beta_adjustment_china.py +0 -548
  83. build/lib/build/lib/siat/blockchain.py +0 -143
  84. build/lib/build/lib/siat/bond.py +0 -2900
  85. build/lib/build/lib/siat/bond_base.py +0 -992
  86. build/lib/build/lib/siat/bond_china.py +0 -100
  87. build/lib/build/lib/siat/bond_zh_sina.py +0 -143
  88. build/lib/build/lib/siat/capm_beta.py +0 -783
  89. build/lib/build/lib/siat/capm_beta2.py +0 -887
  90. build/lib/build/lib/siat/common.py +0 -5360
  91. build/lib/build/lib/siat/compare_cross.py +0 -642
  92. build/lib/build/lib/siat/copyrights.py +0 -18
  93. build/lib/build/lib/siat/cryptocurrency.py +0 -667
  94. build/lib/build/lib/siat/economy.py +0 -1471
  95. build/lib/build/lib/siat/economy2.py +0 -1853
  96. build/lib/build/lib/siat/esg.py +0 -536
  97. build/lib/build/lib/siat/event_study.py +0 -815
  98. build/lib/build/lib/siat/fama_french.py +0 -1521
  99. build/lib/build/lib/siat/fin_stmt2_yahoo.py +0 -982
  100. build/lib/build/lib/siat/financial_base.py +0 -1160
  101. build/lib/build/lib/siat/financial_statements.py +0 -598
  102. build/lib/build/lib/siat/financials.py +0 -2339
  103. build/lib/build/lib/siat/financials2.py +0 -1278
  104. build/lib/build/lib/siat/financials_china.py +0 -4433
  105. build/lib/build/lib/siat/financials_china2.py +0 -2212
  106. build/lib/build/lib/siat/fund.py +0 -629
  107. build/lib/build/lib/siat/fund_china.py +0 -3307
  108. build/lib/build/lib/siat/future_china.py +0 -551
  109. build/lib/build/lib/siat/google_authenticator.py +0 -47
  110. build/lib/build/lib/siat/grafix.py +0 -3636
  111. build/lib/build/lib/siat/holding_risk.py +0 -867
  112. build/lib/build/lib/siat/luchy_draw.py +0 -638
  113. build/lib/build/lib/siat/market_china.py +0 -1168
  114. build/lib/build/lib/siat/markowitz.py +0 -2363
  115. build/lib/build/lib/siat/markowitz2.py +0 -3150
  116. build/lib/build/lib/siat/markowitz2_20250704.py +0 -2969
  117. build/lib/build/lib/siat/markowitz2_20250705.py +0 -3158
  118. build/lib/build/lib/siat/markowitz_simple.py +0 -373
  119. build/lib/build/lib/siat/ml_cases.py +0 -2291
  120. build/lib/build/lib/siat/ml_cases_example.py +0 -60
  121. build/lib/build/lib/siat/option_china.py +0 -3069
  122. build/lib/build/lib/siat/option_pricing.py +0 -1925
  123. build/lib/build/lib/siat/other_indexes.py +0 -409
  124. build/lib/build/lib/siat/risk_adjusted_return.py +0 -1576
  125. build/lib/build/lib/siat/risk_adjusted_return2.py +0 -1900
  126. build/lib/build/lib/siat/risk_evaluation.py +0 -2218
  127. build/lib/build/lib/siat/risk_free_rate.py +0 -351
  128. build/lib/build/lib/siat/sector_china.py +0 -4140
  129. build/lib/build/lib/siat/security_price2.py +0 -727
  130. build/lib/build/lib/siat/security_prices.py +0 -3408
  131. build/lib/build/lib/siat/security_trend.py +0 -402
  132. build/lib/build/lib/siat/security_trend2.py +0 -646
  133. build/lib/build/lib/siat/stock.py +0 -4284
  134. build/lib/build/lib/siat/stock_advice_linear.py +0 -934
  135. build/lib/build/lib/siat/stock_base.py +0 -26
  136. build/lib/build/lib/siat/stock_china.py +0 -2095
  137. build/lib/build/lib/siat/stock_prices_kneighbors.py +0 -910
  138. build/lib/build/lib/siat/stock_prices_linear.py +0 -386
  139. build/lib/build/lib/siat/stock_profile.py +0 -707
  140. build/lib/build/lib/siat/stock_technical.py +0 -3305
  141. build/lib/build/lib/siat/stooq.py +0 -74
  142. build/lib/build/lib/siat/transaction.py +0 -347
  143. build/lib/build/lib/siat/translate.py +0 -5183
  144. build/lib/build/lib/siat/valuation.py +0 -1378
  145. build/lib/build/lib/siat/valuation_china.py +0 -2076
  146. build/lib/build/lib/siat/var_model_validation.py +0 -444
  147. build/lib/build/lib/siat/yf_name.py +0 -811
  148. build/lib/siat/__init__.py +0 -75
  149. build/lib/siat/allin.py +0 -137
  150. build/lib/siat/assets_liquidity.py +0 -915
  151. build/lib/siat/beta_adjustment.py +0 -1058
  152. build/lib/siat/beta_adjustment_china.py +0 -548
  153. build/lib/siat/blockchain.py +0 -143
  154. build/lib/siat/bond.py +0 -2900
  155. build/lib/siat/bond_base.py +0 -992
  156. build/lib/siat/bond_china.py +0 -100
  157. build/lib/siat/bond_zh_sina.py +0 -143
  158. build/lib/siat/capm_beta.py +0 -783
  159. build/lib/siat/capm_beta2.py +0 -887
  160. build/lib/siat/common.py +0 -5360
  161. build/lib/siat/compare_cross.py +0 -642
  162. build/lib/siat/copyrights.py +0 -18
  163. build/lib/siat/cryptocurrency.py +0 -667
  164. build/lib/siat/economy.py +0 -1471
  165. build/lib/siat/economy2.py +0 -1853
  166. build/lib/siat/esg.py +0 -536
  167. build/lib/siat/event_study.py +0 -815
  168. build/lib/siat/fama_french.py +0 -1521
  169. build/lib/siat/fin_stmt2_yahoo.py +0 -982
  170. build/lib/siat/financial_base.py +0 -1160
  171. build/lib/siat/financial_statements.py +0 -598
  172. build/lib/siat/financials.py +0 -2339
  173. build/lib/siat/financials2.py +0 -1278
  174. build/lib/siat/financials_china.py +0 -4433
  175. build/lib/siat/financials_china2.py +0 -2212
  176. build/lib/siat/fund.py +0 -629
  177. build/lib/siat/fund_china.py +0 -3307
  178. build/lib/siat/future_china.py +0 -551
  179. build/lib/siat/google_authenticator.py +0 -47
  180. build/lib/siat/grafix.py +0 -3636
  181. build/lib/siat/holding_risk.py +0 -867
  182. build/lib/siat/luchy_draw.py +0 -638
  183. build/lib/siat/market_china.py +0 -1168
  184. build/lib/siat/markowitz.py +0 -2363
  185. build/lib/siat/markowitz2.py +0 -3150
  186. build/lib/siat/markowitz2_20250704.py +0 -2969
  187. build/lib/siat/markowitz2_20250705.py +0 -3158
  188. build/lib/siat/markowitz_simple.py +0 -373
  189. build/lib/siat/ml_cases.py +0 -2291
  190. build/lib/siat/ml_cases_example.py +0 -60
  191. build/lib/siat/option_china.py +0 -3069
  192. build/lib/siat/option_pricing.py +0 -1925
  193. build/lib/siat/other_indexes.py +0 -409
  194. build/lib/siat/risk_adjusted_return.py +0 -1576
  195. build/lib/siat/risk_adjusted_return2.py +0 -1900
  196. build/lib/siat/risk_evaluation.py +0 -2218
  197. build/lib/siat/risk_free_rate.py +0 -351
  198. build/lib/siat/sector_china.py +0 -4140
  199. build/lib/siat/security_price2.py +0 -727
  200. build/lib/siat/security_prices.py +0 -3408
  201. build/lib/siat/security_trend.py +0 -402
  202. build/lib/siat/security_trend2.py +0 -646
  203. build/lib/siat/stock.py +0 -4284
  204. build/lib/siat/stock_advice_linear.py +0 -934
  205. build/lib/siat/stock_base.py +0 -26
  206. build/lib/siat/stock_china.py +0 -2095
  207. build/lib/siat/stock_prices_kneighbors.py +0 -910
  208. build/lib/siat/stock_prices_linear.py +0 -386
  209. build/lib/siat/stock_profile.py +0 -707
  210. build/lib/siat/stock_technical.py +0 -3305
  211. build/lib/siat/stooq.py +0 -74
  212. build/lib/siat/transaction.py +0 -347
  213. build/lib/siat/translate.py +0 -5183
  214. build/lib/siat/valuation.py +0 -1378
  215. build/lib/siat/valuation_china.py +0 -2076
  216. build/lib/siat/var_model_validation.py +0 -444
  217. build/lib/siat/yf_name.py +0 -811
  218. siat-3.10.132.dist-info/RECORD +0 -218
@@ -1,3408 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- 版权:王德宏,北京外国语大学国际商学院
4
- 功能:
5
- 1、获取证券价格,多种方法,解决不稳定网络超时问题
6
- 2、既可获取单一证券的价格,也可获取证券组合的价格
7
- 3、与爬虫过程有关的错误信息尽可能都在本过程中处理
8
- 版本:1.0,2021-1-31
9
- """
10
-
11
- #==============================================================================
12
- #关闭所有警告
13
- import warnings; warnings.filterwarnings('ignore')
14
- from siat.common import *
15
- from siat.translate import *
16
-
17
- #==============================================================================
18
- import pandas as pd
19
-
20
- #==============================================================================
21
- #==============================================================================
22
- if __name__=='__main__':
23
- ticker="430047.BJ"
24
- ticker="430047.BJ"
25
- ticker="600519.SS"
26
- ticker="000858.SZ"
27
- ticker_type='auto'
28
-
29
- ticker="sz169107" #LOF基金
30
- ticker="sh510050" #ETF基金
31
-
32
-
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)
49
-
50
- def get_prices_all(ticker,fromdate,todate,adj=False,source='auto',ticker_type='auto'):
51
- """
52
- 功能:多个证券(股票,指数,基金,债券),或投资组合(可含股票和/或债券)
53
- ticker_type:若为'auto'则基金优先于债券(代码重合时),亦可为列表分别指定优先抓取类型。
54
- 'stock', 'fund', 'bond','swindex','portfolio',不足部分自动补充为'auto'
55
- 其中,'auto'/'stock'/'fund'优先抓取指数、股票和基金;'bond'优先抓取债券;
56
- 'swindex'优先抓取申万行业指数
57
-
58
- 注意:未经充分测试!!!
59
- """
60
-
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,ticker_type=decompose_portfolio(ticker_list[0])
82
- df=get_price_portfolio(tickerlist,sharelist,fromdate,todate,adj=adj, \
83
- source=source,ticker_type=ticker_type)
84
- return df
85
-
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,ticker_type=decompose_portfolio(t)
99
- dft=get_price_portfolio(tickerlist,sharelist,fromdate,todate,adj=adj, \
100
- source=source,ticker_type=ticker_type)
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)
111
-
112
- return df
113
-
114
- #==============================================================================
115
- if __name__=='__main__':
116
- ticker="430047.BJ"
117
- ticker="430047.BJ"
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
- ticker='GEM25.CME'
135
-
136
- ticker=['^SPX']
137
-
138
- fromdate="2025-1-1"
139
- todate="2025-6-1"
140
- adj=False
141
- retry_count=3
142
- pause=1
143
- source='auto'
144
- ticker_type='auto'
145
-
146
- prices=get_prices(ticker,fromdate,todate,ticker_type=ticker_type)
147
-
148
- def get_prices(ticker,fromdate,todate,adj=False,source='auto', \
149
- retry_count=3,pause=1,ticker_type='auto'):
150
- """
151
- 功能:抓取证券价格,pandas_datareader + yfinance + akshare
152
- 输出:指定收盘价格序列,日期升序排列
153
- ticker: 证券代码或其列表。大陆证券代码加上后缀.SZ或.SS或.BJ,港股代码去掉前导0加后缀.HK
154
- start: 样本开始日期,yyyy-mm-dd
155
- end: 样本结束日期,既可以是今天日期,也可以是一个历史日期
156
- retry_count:网络失败时的重试次数,仅用于雅虎
157
- pause:每次重试前的间隔秒数,仅用于雅虎
158
- """
159
- # yfinance可用性
160
- YF=False
161
- # pandas_datareader对yahoo可用性
162
- PDR_yahoo=False
163
-
164
- prices=None
165
-
166
- #检查日期期间的合理性
167
- result,start,end=check_period(fromdate,todate)
168
- if not result:
169
- print(" #Error(get_prices): invalid date period from",fromdate,'to',todate)
170
- return None
171
-
172
- print(" Searching prices of security, please wait ...")
173
- ticker=tickers_cvt2yahoo(ticker)
174
-
175
- if source in ['auto']:
176
- #尝试AkShare+Sina+EM(新浪,对中国内地股票、港股和美股有效,但不包括国外市场指数)
177
- print(" Trying to capture prices from sina/EM for",ticker,"...")
178
- try:
179
- prices=get_prices_ak(ticker,fromdate,todate,ticker_type=ticker_type) #支持多个证券
180
- except:
181
- print(" #Warning(get_prices): info retrieving failed from sina/EM for",ticker)
182
- else:
183
- if prices is None:
184
- print(" #Warning(get_prices): info not found from sina/EM for",ticker)
185
- else:
186
- num=len(prices)
187
- if num==0:
188
- print(" #Warning(get_prices):",ticker,"may be suspended or delisted")
189
- return prices
190
- else:
191
- prices2=remove_timezone(prices)
192
- #prices2=remove_df_index_timezone(prices)
193
- return prices2 #找到有效数据就返回,否则继续
194
-
195
- if source in ['auto','stooq']:
196
- #尝试pandas_datareader+stooq(对美股、港股、欧股、国外市场指数有效,但对深交所股票无效)
197
- #注意stooq代码与新浪/stooq的不同
198
- print(" Trying to capture info from stooq for",ticker)
199
- try:
200
- prices=get_prices_stooq(ticker,fromdate,todate) #仅支持单只证券
201
- #prices=get_prices_stooq(ticker,fromdate,todate)?
202
- except:
203
- print(" #Warning(get_prices): info retrieving failed from stooq for",ticker)
204
- else:
205
- if prices is None:
206
- print(" #Warning(get_prices): info not found from stooq for",ticker)
207
- else:
208
- num=len(prices)
209
- if num==0:
210
- print(" #Warning(get_prices): zero record found for",ticker)
211
- else:
212
- prices2=remove_timezone(prices)
213
- #prices2=remove_df_index_timezone(prices)
214
- return prices2 #找到有效数据就返回,否则继续
215
-
216
- if source in ['auto','yahoo']:
217
- #使用yahoo+yfinance抓取数据
218
- #由于雅虎无法访问,建议暂时关闭,2021-10-24
219
- #抓取证券(列表)价格,需要调整收盘价:yfinance优先,线程极易出错,先尝试关闭线程
220
- try:
221
- if YF:
222
- print(" Trying to capture info from Yahoo Finance using non-threads")
223
- prices=get_prices_yf(ticker,start,end,threads=False) #支持多个证券
224
- else:
225
- print(" Trying to capture info from Yahoo Finance ...")
226
- prices=get_prices_yq(ticker,start,end)
227
-
228
- except:
229
- print(" #Warning(get_prices): retrieving using non-threads failed from yahoo")
230
- else:
231
- if prices is None:
232
- print(" #Warning(get_prices): info not found using non-threads failed from yahoo")
233
- else:
234
- num=len(prices)
235
- if num==0:
236
- print(" #Warning(get_prices): zero record found")
237
- else:
238
- prices2=remove_timezone(prices)
239
- #prices2=remove_df_index_timezone(prices)
240
- return prices2 #找到有效数据就返回,否则继续
241
-
242
- #抓取证券(列表)价格,需要调整收盘价:yfinance优先,尝试打开线程
243
- try:
244
- if YF:
245
- print(" Trying to capture info from Yahoo Finance using threads")
246
- prices=get_prices_yf(ticker,start,end,threads=True) #支持多个证券
247
- except:
248
- print(" #Warning(get_prices): retrieving using threads failed from yahoo")
249
- else:
250
- if prices is None:
251
- print(" #Warning(get_prices): info not found using non-threads failed from yahoo")
252
- else:
253
- num=len(prices)
254
- if num==0:
255
- print(" #Warning(get_prices): zero record found")
256
- else:
257
- prices2=remove_timezone(prices)
258
- #prices2=remove_df_index_timezone(prices)
259
- return prices2 #找到有效数据就返回,否则继续
260
-
261
- #抓取证券(列表)价格,不考虑是否需要调整收盘价:pandas_datareader,使用雅虎
262
- try:
263
- print(" Trying to capture info from Yahoo Finance traditionally")
264
- if PDR_yahoo:
265
- prices=get_prices_yahoo(ticker,start,end,retry_count=retry_count,pause=pause)
266
- except:
267
- print(" #Warning(get_prices): info retrieving failed from Yahoo traditionally")
268
- return None
269
- else:
270
- if prices is None:
271
- print(" #Warning(get_prices): info not found from Yahoo traditionally")
272
- else:
273
- num=len(prices)
274
- if num==0:
275
- print(" #Warning(get_prices): zero record found from Yahoo traditionally")
276
- else:
277
- #print(" Successfully retrieved",num,"records for",ticker)
278
- prices2=remove_timezone(prices)
279
- #prices2=remove_df_index_timezone(prices)
280
- return prices2
281
-
282
- #若能够抓取到数据均已提前返回,到达此处时表面未能抓取到任何数据
283
- print(" #Warning(get_prices): tried everything but nothing found for",ticker)
284
-
285
- return None
286
-
287
- if __name__=='__main__':
288
- get_prices('INTC','2021-11-1','2021-11-5')
289
- get_prices('BMW.DE','2021-11-1','2021-11-5')
290
- get_prices(['INTC'],'2021-11-1','2021-11-5')
291
- get_prices(['XYZ'],'2021-11-1','2021-11-5')
292
- df4=get_prices(['INTC','MSFT'],'2021-11-1','2021-11-5')
293
- df5=get_prices(['INTC','UVW'],'2021-11-1','2021-11-5')
294
- df6=get_prices(['00988.HK','000858.SZ'],'2021-11-1','2021-11-5')
295
- df7=get_prices(['INTL','MSFT','00988.HK','000858.SZ'],'2021-11-1','2021-11-5')
296
-
297
- #==============================================================================
298
-
299
- def get_price(ticker,fromdate,todate,adj=False,source='auto',ticker_type='auto'):
300
- """
301
- 套壳函数get_prices,为保持兼容
302
- """
303
- df=get_prices(ticker,fromdate,todate,adj=adj,source=source,ticker_type=ticker_type)
304
-
305
- df2=remove_timezone(df)
306
- #df2=remove_df_index_timezone(df)
307
-
308
- return df2
309
-
310
- #==============================================================================
311
- if __name__ =="__main__":
312
- ticker="BMW.DE"
313
- fromdate="2023-1-1"
314
- todate="2023-5-20"
315
-
316
- ticker=["600519.SS",'000858.SZ']
317
- pricedf=get_prices(ticker,fromdate,todate)
318
-
319
- def remove_timezone(pricedf):
320
- """
321
- 功能:去掉df索引中可能存在的时区信息,避免时区错误
322
- """
323
- if pricedf is None:
324
- return None
325
-
326
- import datetime as dt
327
- import pandas as pd
328
-
329
- pricedf.index=pd.Series(pd.to_datetime(pricedf.index)).dt.tz_localize(None)
330
-
331
- return pricedf
332
-
333
- def remove_timezone_tmp(pricedf):
334
- """
335
- 功能:去掉df索引中可能存在的时区信息,避免时区错误
336
- 注意:有问题,应该改用common中的df_index_timezone_remove函数
337
- """
338
- #去掉时区
339
- pricedf2=df_index_timezone_remove(pricedf)
340
- return pricedf2
341
-
342
- """
343
- if pricedf is None:
344
- return pricedf
345
-
346
- pricedf['date_tz']=pricedf.index
347
- pricedf['date_y4m2d2']=pricedf['date_tz'].astype(str)
348
-
349
- import pandas as pd
350
- pricedf['date']=pricedf['date_y4m2d2'].apply(lambda x: pd.to_datetime(x))
351
- pricedf2=pricedf.reset_index(drop=True)
352
- try:
353
- pricedf2=pricedf2.set_index('Date',drop=True)
354
- except:
355
- pricedf2=pricedf2.set_index('date',drop=True)
356
-
357
- pricedf2.drop(['date_tz','date_y4m2d2'],axis=1,inplace=True)
358
-
359
- return pricedf2
360
- """
361
-
362
- #==============================================================================
363
- if __name__=='__main__':
364
- ticker='430047.BJ'
365
- ticker='600519.SS'
366
- ticker='000001.SZ'
367
-
368
- ticker='GEM25.CME'
369
-
370
- fromdate='2025-1-1'
371
- todate='2025-6-15'
372
-
373
- adjust=''; ticker_type='auto'
374
-
375
- get_price_ak_em(ticker,fromdate,todate)
376
-
377
- #在common中定义SUFFIX_LIST_CN
378
-
379
- def get_price_ak_em(ticker,fromdate,todate,adjust='',ticker_type='auto'):
380
- """
381
- 功能:基于东方财富从akshare获得中国国内的股票和指数历史行情,只能处理单个股票,处理指数有时出错
382
- ticker:雅虎格式,沪市股票为.SS,深市为.SZ,北交所为.BJ,其他的不处理,直接返回None
383
- fromdate:格式为YYYY-m-d,需要改造为YYYYMMDD
384
- todate:格式为YYYY-m-d,需要改造为YYYYMMDD
385
- adjust:不考虑复权为'',后复权为'hfq',前复权为'qfq'
386
- 返回结果:雅虎格式,日期升序,列明首字母大写等
387
-
388
- 缺陷:处理指数容易出错或返回错误数据!!!
389
- """
390
- #变换代码格式
391
- ticker1=ticker.upper()
392
- result,prefix,suffix=split_prefix_suffix(ticker1)
393
-
394
- #若不是A股则返回
395
- if not (suffix in SUFFIX_LIST_CN):
396
- print(" #Warning(get_price_ak_em): function not suitable for",ticker)
397
- return None
398
- else:
399
- ticker2=prefix
400
-
401
- #变换日期格式
402
- result,start,end=check_period(fromdate,todate)
403
- if not result:
404
- print(" #Warning(get_price_ak_em): invalid date period from",fromdate,'to',todate)
405
- return None
406
- start1=start.strftime('%Y%m%d')
407
- end1=end.strftime('%Y%m%d')
408
-
409
- #检查复权选项
410
- adjustlist=['','none','hfq','qfq']
411
- if adjust not in adjustlist:
412
- print(" #Warning(get_price_ak_em): invalid close adjustment",adjust)
413
- return None
414
- if adjust=='none': adjust=''
415
-
416
- #抓取股价,含复权选项
417
- import akshare as ak
418
- try:
419
- #bug: 股票代码为399xxx时出错
420
- df=ak.stock_zh_a_hist(symbol=ticker2,period="daily",start_date=start1,end_date=end1,adjust=adjust)
421
- except:
422
- print(" #Warning(get_price_ak_em): failed to find prices from EM for",ticker)
423
- return None
424
-
425
- #检查抓取到的结果
426
- if df is None:
427
- print(" #Warning(get_price_ak_em): no record found from EM for",ticker)
428
- return None
429
- if len(df)==0:
430
- print(" #Warning(get_price_ak_em): zero record found from EM for",ticker)
431
- return None
432
-
433
- #升序排序
434
- df.sort_values(by=['日期'],ascending=[True],inplace=True)
435
-
436
- #调整数据格式
437
- df['Date']=pd.to_datetime(df['日期'])
438
- df.set_index(['Date'],inplace=True)
439
-
440
- df.rename(columns={'开盘':'Open','收盘':'Close','最高':'High','最低':'Low', \
441
- '成交量':'Volume','成交额':'Amount','换手率':'Turnover'},inplace=True)
442
- df1=df[['Open','Close','High','Low','Volume','Amount','Turnover']]
443
-
444
- df1['source']=text_lang('东方财富','EM')
445
- df1['ticker']=str(ticker)
446
- df1['Adj Close']=df1['Close']
447
- df1['footnote']=adjust
448
-
449
- num=len(df1)
450
- """
451
- ptname=ticker_name(ticker,ticker_type)
452
- if ptname == ticker: ptname=''
453
- """
454
- if num > 0:
455
- #print(" Successfully retrieved",num,"records for",ticker,ptname)
456
- print(" Successfully retrieved",num,"records for",ticker)
457
- else:
458
- #print(" Sorry, no records retrieved for",ticker,ptname)
459
- print(" Sorry, no records retrieved for",ticker)
460
-
461
- return df1
462
-
463
-
464
- if __name__=='__main__':
465
- df1=get_price_ak_em('600519.SS','2020-12-1','2020-12-5',adjust='none')
466
- df2=get_price_ak_em('600519.SS','2020-12-1','2021-2-5',adjust='hfq')
467
- df3=get_price_ak_em('399001.SZ','2020-12-1','2021-2-5') #出错
468
- df4=get_price_ak_em('000688.SS','2020-12-1','2021-2-5')
469
- df5=get_price_ak_em('AAPL','2020-12-1','2021-2-5')
470
- df6=get_price_ak_em('000001.SS','2020-12-1','2021-2-5')
471
- df7=get_price_ak_em('000002.SS','2020-12-1','2021-2-5')
472
- df7=get_price_ak_em('000300.SS','2020-12-1','2021-2-5')
473
-
474
- #==============================================================================
475
- def cvt_stooq_suffix(symbol):
476
- """
477
- 映射雅虎后缀符号至stooq后缀符号
478
- 输入:雅虎后缀符号。输出:stooq后缀符号
479
- """
480
- import pandas as pd
481
- suffix=pd.DataFrame([
482
- ['SS','CN'], ['SH','CN'], ['SZ','CN'], ['BJ','CN'],
483
- ['T','JP'],['L','UK'],
484
-
485
- ], columns=['yahoo','stooq'])
486
-
487
- try:
488
- stooq=suffix[suffix['yahoo']==symbol]['stooq'].values[0]
489
- except:
490
- #未查到翻译词汇,返回原词
491
- stooq=symbol
492
-
493
- return stooq
494
-
495
- if __name__=='__main__':
496
- cvt_stooq_suffix('SS')
497
- cvt_stooq_suffix('SZ')
498
- cvt_stooq_suffix('T')
499
- #==================================================================================
500
- def cvt_stooq_symbol(symbol):
501
- """
502
- 映射雅虎指数符号至stooq指数符号
503
- 输入:雅虎指数符号。输出:stooq指数符号
504
- 注意:^IXIC/^NDQ是纳斯达克综合指数,^NDX是纳斯达克100指数
505
- """
506
- import pandas as pd
507
- suffix=pd.DataFrame([
508
- ['^GSPC','^SPX'], ['^IXIC','^NDQ'],
509
- ['^RUT','QR.F'],
510
- ['000001.SS','^SHC'],
511
- ['^N225','^NKX'], ['^TWII','^TWSE'], ['^KS11','^KOSPI'],
512
- ['^BSESN','^SNX'],['^FTSE','^FTM'], ['^GDAXI','^DAX'],
513
- ['^FCHI','^CAC'], ['IMOEX.ME','^MOEX'],
514
-
515
- ], columns=['yahoo','stooq'])
516
-
517
- result=True
518
- try:
519
- stooq=suffix[suffix['yahoo']==symbol]['stooq'].values[0]
520
- except:
521
- #未查到翻译词汇,返回原词
522
- stooq=symbol
523
-
524
- return result,stooq
525
-
526
- if __name__=='__main__':
527
- cvt_stooq_symbol('^GSPC')
528
- cvt_stooq_symbol('^IXIC')
529
- cvt_stooq_symbol('000001.SS')
530
- cvt_stooq_symbol('600619.SS')
531
-
532
- #==================================================================================
533
- if __name__=='__main__':
534
- ticker='600519.SS'
535
- ticker='0LNG.L'
536
-
537
- cvt_stooq_ticker(ticker)
538
-
539
- def cvt_stooq_ticker(ticker):
540
- """
541
- 映射雅虎证券符号至stooq证券符号
542
- 输入:雅虎证券符号。输出:stooq证券符号
543
- 局限:无法处理深交所股票代码!stooq里没有深交所股票
544
- """
545
- #直接转换
546
- result,ticker_stooq=cvt_stooq_symbol(ticker)
547
- if not result:
548
- return ticker_stooq
549
-
550
- #拆分前缀后缀
551
- result,prefix,suffix=split_prefix_suffix(ticker)
552
-
553
- #去掉前导0
554
- prefix2=prefix.lstrip('0')
555
-
556
- #无后缀
557
- if not result:
558
- _,ticker_stooq=cvt_stooq_symbol(prefix2)
559
-
560
- #有后缀
561
- if result:
562
- _,prefix3=cvt_stooq_symbol(prefix2)
563
- ticker_stooq=prefix3+'.'+cvt_stooq_suffix(suffix)
564
-
565
- return ticker_stooq
566
-
567
- if __name__=='__main__':
568
- cvt_stooq_ticker('^GSPC')
569
- cvt_stooq_ticker('000001.SS')
570
- cvt_stooq_ticker('0700.HK')
571
-
572
- #有问题
573
- cvt_stooq_ticker('002504.SZ')
574
- #==================================================================================
575
-
576
- if __name__=='__main__':
577
- ticker='AAPL'
578
- ticker='^HSI'
579
- ticker='^GSPC'
580
- ticker='^DJI'
581
- ticker='000001.SS'
582
- ticker='00700.HK'
583
- ticker='IBM'
584
- ticker='0LNG.UK'
585
- ticker='CNYUSD'
586
- ticker='CPIYCN.M'
587
- ticker='INPYCN.M'
588
- ticker='TRBNCN.M'
589
- ticker='RSAYCN.M'
590
- ticker='600519.SS'
591
-
592
- ticker='GC.F' #无法下载
593
- ticker='XAUCNY' #一盎司黄金的现货人民币价格
594
- ticker='XAUUSD' #一盎司黄金的现货美元价格
595
-
596
- ticker=['AAPL','MSFT']
597
- start='2025-6-1'; end='2025-6-1'
598
-
599
- ticker='BMW.DE'
600
- start='2022-6-1'; end='2025-6-15'
601
-
602
- ticker='GEM25.CME'
603
- start='2024-6-1'; end='2025-6-15'
604
-
605
- p=get_price_stooq(ticker,start,end)
606
-
607
- def get_price_stooq(ticker,start,end='today'):
608
- """
609
- 从stooq抓取单个股价
610
- """
611
- start,end=start_end_preprocess(start,end)
612
-
613
- #转换证券代码
614
- ticker2=cvt_stooq_ticker(ticker)
615
-
616
- #从stooq抓取每日价格
617
- import pandas_datareader.data as web
618
- """
619
- #尝试重指向pandas_datareader中的stooq.py为siat中的stooq.py
620
- import importlib
621
- import siat
622
- importlib.reload(siat.stooq)
623
- """
624
- try:
625
- prices=web.DataReader(ticker2,start=start,end=end,data_source='stooq')
626
- except:
627
- symbol_parts = ticker2.split(".")
628
- if len(symbol_parts) == 1:
629
- ticker2 = ".".join([ticker2, 'US']) #若出错尝试当作美股代码处理,挽救第一次
630
- prices=web.DataReader(ticker2,start=start,end=end,data_source='stooq')
631
- else:
632
- print(" #Warning(get_price_stooq): inaccessible from stooq for",ticker)
633
- return None
634
-
635
- #添加附注
636
- if not (prices is None):
637
- if len(prices)==0:
638
- symbol_parts = ticker2.split(".")
639
- if len(symbol_parts) == 1:
640
- ticker2 = ".".join([ticker2, 'US']) #若为空尝试当作美股代码处理,挽救第二次
641
- prices=web.DataReader(ticker2,start=start,end=end,data_source='stooq')
642
- else:
643
- #print(" Sorry, zero records found from stooq for",ticker,"from",start,'to',end)
644
- return None
645
-
646
- #仍然无记录
647
- if len(prices)==0:return None
648
-
649
- prices.sort_index(axis=0, ascending=True, inplace=True)
650
- #prices.dropna(inplace=True)
651
-
652
- prices['Adj Close']=prices['Close']
653
- prices['source']='Stooq'
654
- prices['ticker']=str(ticker)
655
- prices['footnote']=''
656
-
657
- if 'Volume' not in list(prices):
658
- prices['Volume']=0
659
-
660
- _,start1,end1=check_period(start,end)
661
- prices2=prices[(prices.index >= start1) & (prices.index <= end1)]
662
-
663
- num=len(prices2)
664
- """
665
- ptname=ticker_name(ticker,'stock')
666
- if ptname == ticker: ptname=''
667
- """
668
- if num > 0:
669
- print(" Successfully retrieved",num,"records for",ticker)
670
- return prices2
671
- else:
672
- print(" Sorry, no records found from stooq for",ticker,"from",start,'to',end)
673
- return None
674
- else:
675
- return None
676
-
677
- if __name__=='__main__':
678
- get_price_stooq('AAPL','2021-11-1','2021-11-5')
679
- get_price_stooq('BMW.DE','2021-11-1','2021-11-5')
680
- hsi=get_price_stooq('^HSI','2021-11-1','2021-11-5')
681
- get_price_stooq('0700.HK','2021-11-1','2021-11-5')
682
- get_price_stooq('^N225','2021-11-1','2021-11-5')
683
- get_price_stooq('^DJI','2021-11-1','2021-11-5')
684
-
685
- #==============================================================================
686
- if __name__=='__main__':
687
- ticker=['AAPL','MSFT']
688
- ticker=['^SPX']
689
- fromdate,todate='2025-1-1','2025-1-31'
690
-
691
- prices=get_prices_stooq(ticker,fromdate,todate)
692
-
693
- def get_prices_stooq(ticker,fromdate,todate):
694
- """
695
- 功能:获取stooq股票或指数的历史行情,多个股票
696
- 注意:stooq不能抓取深交所和北交所的股票
697
- """
698
- #检查是否为多个证券:单个证券代码
699
- if isinstance(ticker,str):
700
- if security_in_China(ticker):
701
- df=get_price_ak(ticker,fromdate,todate)
702
- else:
703
- df=get_price_stooq(ticker,fromdate,todate)
704
- return df
705
-
706
- #检查是否为多个证券:空的列表
707
- if isinstance(ticker,list) and len(ticker) == 0:
708
- pass
709
- return None
710
-
711
- #检查是否为多个证券:列表中只有一个代码
712
- if isinstance(ticker,list) and len(ticker) == 1:
713
- ticker1=ticker[0]
714
- #抓取单个证券
715
- if security_in_China(ticker1):
716
- df=get_price_ak(ticker1,fromdate,todate)
717
- else:
718
- df=get_price_stooq(ticker1,fromdate,todate)
719
- return df
720
-
721
- import pandas as pd
722
- #处理列表中的第一个证券
723
- i=0
724
- df=None
725
- while df is None:
726
- #注意列表序号超界
727
- if i <= len(ticker)-1:
728
- t=ticker[i]
729
- else:
730
- return df
731
-
732
- #抓取单个证券
733
- if security_in_China(t):
734
- df=get_price_ak(t,fromdate,todate)
735
- else:
736
- df=get_price_stooq(t,fromdate,todate)
737
-
738
- if not (df is None):
739
- columns=create_tuple_for_columns(df,t)
740
- df.columns=pd.MultiIndex.from_tuples(columns)
741
- else:
742
- i=i+1
743
-
744
- if (i+1) == len(ticker):
745
- pass
746
- #已经到达代码列表末尾
747
- return df
748
-
749
- #处理列表中的其余证券
750
- if i+1 <= len(ticker)-1:
751
- for t in ticker[(i+1):]:
752
- #抓取单个证券
753
- if security_in_China(t):
754
- df=get_price_ak(t,fromdate,todate)
755
- else:
756
- df=get_price_stooq(t,fromdate,todate)
757
-
758
- if not (dft is None):
759
- columns=create_tuple_for_columns(dft,t)
760
- dft.columns=pd.MultiIndex.from_tuples(columns)
761
-
762
- df=pd.merge(df,dft,how='inner',left_index=True,right_index=True)
763
-
764
- return df
765
-
766
-
767
- #==============================================================================
768
- if __name__=='__main__':
769
- ticker='600340.SS'
770
- ticker='000338.SZ'
771
- ticker='600519.SS'
772
- ticker_type='auto'
773
-
774
- ticker='859811.SW'
775
- ticker_type='auto'
776
-
777
- fromdate='2024-1-1'
778
- todate='2024-4-1'
779
- adjust='none'
780
-
781
- df=get_price_ak(ticker,fromdate,todate,ticker_type=ticker_type)
782
-
783
- #在common中定义SUFFIX_LIST_CN
784
-
785
- def get_price_ak(ticker,fromdate,todate,adjust='none',ticker_type='auto'):
786
- """
787
- 功能:基于akshare抓取A股、港股和美股单只股价
788
- 若抓取A股,调用get_price_ak_cn
789
- 若抓取港股,调用get_price_ak_hk
790
- 若抓取美股,调用get_price_ak_us
791
-
792
- 注意:忽略了复权价格
793
- """
794
- #提取交易所后缀
795
- ticker1=ticker.upper()
796
- result,prefix,suffix=split_prefix_suffix(ticker1)
797
-
798
- df=pd.DataFrame()
799
- # A股股票、指数、基金、债券,申万行业指数
800
- if suffix in SUFFIX_LIST_CN:
801
- try:
802
- #抓取单个中国的证券
803
- df=get_price_ak_cn(ticker1,fromdate,todate,ticker_type=ticker_type)
804
- except:
805
- #抓取东方财富,处理股指有时出错,所以要放在后面做planB
806
- df=get_price_ak_em(ticker1,fromdate,todate)
807
-
808
- if df is None:
809
- print(" #Error(get_price_ak): no info found for",ticker1)
810
- return df
811
-
812
- if len(df) ==0:
813
- print(" #Warning(get_price_ak): no record found for",ticker1,'between',fromdate,todate)
814
- return df
815
-
816
- return df
817
-
818
- if adjust=='none':
819
- adjust=''
820
-
821
- #抓取新浪港股,不能处理股指
822
- if suffix in ['HK']:
823
- #df=get_price_ak_hk(ticker,fromdate,todate,adjust=adjust)
824
- df=get_price_ak_hk(ticker1,fromdate,todate)
825
- return df
826
-
827
- # 美股,不能处理股指
828
- #df=get_price_ak_us(ticker,fromdate,todate,adjust=adjust)
829
- df=get_price_ak_us(ticker1,fromdate,todate)
830
-
831
- return df
832
-
833
- #==============================================================================
834
- if __name__=='__main__':
835
- ticker='600340.SS' #股票
836
- ticker='159990.SZ' #ETF基金
837
- ticker='169201.SZ' #LOF基金
838
- ticker='180801.SZ' #封闭式基金
839
-
840
- ticker="006257"
841
-
842
- ticker_type='auto'
843
-
844
- ticker='sh019319' #国债
845
- ticker='sh018084' #政策性金融债
846
- ticker='sz149996' #公司债
847
- ticker='sh018003' #政策性金融债
848
- ticker_type='bond'
849
-
850
- ticker='801002.SW'
851
- ticker='807110.SW'
852
-
853
- ticker='sz100303'
854
- ticker='100303.SZ'
855
- ticker_type='auto'
856
-
857
- ticker='000418'
858
- ticker='180202.SZ'
859
- ticker_type='fund'
860
-
861
- fromdate='2024-1-1'; todate='2024-3-31'
862
- adjust=''
863
-
864
- prices=get_price_ak_cn(ticker,fromdate,todate)
865
-
866
- #def get_price_ak_cn(ticker,fromdate,todate,adjust='none',ticker_type='auto'):
867
- def get_price_ak_cn(ticker,fromdate,todate,adjust='',ticker_type='auto'):
868
- """
869
- 功能:从akshare获得中国国内的股票、交易所基金、指数和债券历史行情,只能处理单个证券
870
- ticker:雅虎格式,其他的不处理,直接返回None
871
- fromdate:格式为YYYY-m-d,需要改造为YYYYMMDD
872
- todate:格式为YYYY-m-d,需要改造为YYYYMMDD
873
- adjust:不复权为'',后复权为'hfq',前复权为'qfq'
874
- ticker_type:抓取数据的优先顺序,'auto'/'stock'/'fund'为指数、股票和基金优先,'bond'为债券优先
875
- 其目的是解决基金和债券代码部分重合的问题
876
- 返回结果:雅虎格式,日期升序,列明首字母大写等
877
- """
878
- import akshare as ak
879
- import pandas as pd
880
- import datetime as dt
881
-
882
- df=None; found='None'
883
-
884
- #变换代码格式
885
- ticker2=tickers_cvt2ak(ticker)
886
-
887
- #变换日期格式
888
- result,start,end=check_period(fromdate,todate)
889
- if not result:
890
- print(" #Warning(get_price_ak_cn): invalid date period from",fromdate,'to',todate)
891
- return None
892
- start1=start.strftime('%Y%m%d')
893
- end1=end.strftime('%Y%m%d')
894
-
895
- #adjustlist=['none','hfq','qfq']
896
- adjustlist=['','qfq','hfq','qfq-factor','hfq-factor','adj_only']
897
- if adjust not in adjustlist:
898
- print(" #Warning(get_price_ak_cn): adjust only supports",adjustlist)
899
- return None
900
-
901
- _,prefix,suffix=split_prefix_suffix(ticker2)
902
- #考虑股票复权情形:仅收盘价为复权价,指数/基金/债券无复权
903
- if adjust not in ['','adj_only']:
904
- if ticker_type in ['auto','stock'] and suffix not in ['SW']:
905
- try:
906
- #仅用于股票的历史行情数据(考虑复权)
907
- dffqno=ak.stock_zh_a_daily(ticker2,start1,end1,adjust='')
908
- dffq=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
909
- dffq.rename(columns={'close':'Adj Close'},inplace=True)
910
-
911
- df=pd.merge(dffqno,dffq[['date','Adj Close']],on=['date'])
912
- df['Date']=df['date']
913
- except:
914
- df=None
915
- found=df_have_data(df)
916
-
917
- #考虑股票复权情形:所有价格均为复权价,指数/基金/债券无复权
918
- if adjust == 'adj_only':
919
- if ticker_type in ['auto','stock'] and suffix not in ['SW']:
920
- try:
921
- #仅用于股票的历史行情数据(考虑复权)
922
- df=ak.stock_zh_a_daily(ticker2,start1,end1,adjust='qfq')
923
- df['Adj Close']=df['close']
924
- df['Date']=df['date']
925
- except:
926
- df=None
927
- found=df_have_data(df)
928
-
929
- #股票(无复权)指数/基金/债券
930
- if found != 'Found':
931
- if ticker_type in ['auto','stock'] and suffix not in ['SW']:
932
- try:
933
- #指数/股票/基金
934
- df = ak.stock_zh_index_daily(symbol=ticker2)
935
- df['Date']=df['date'].apply(lambda x: pd.to_datetime(x))
936
- except:
937
- df=None
938
- found=df_have_data(df)
939
-
940
- if found != 'Found':
941
- try:
942
- #特殊函数(不考虑复权)
943
- df=ak.stock_zh_a_cdr_daily(ticker2,start1,end1)
944
- df['Date']=pd.to_datetime(df['date'])
945
- except:
946
- df=None
947
- found=df_have_data(df)
948
-
949
- if found != 'Found':
950
- try:
951
- #最后抓取交易所债券行情
952
- df = exchange_bond_price(ticker2,fromdate,todate,graph=False,data_crop=False)
953
- df['Date']=df.index
954
- except:
955
- try:
956
- #再次尝试抓取开放式基金单位净值
957
- df =get_price_oef_china(ticker2,fromdate,todate)
958
- df['Date']=df.index
959
-
960
- df['ticker']=ticker
961
- df['Adj Close']=df['Close']
962
- df['source']='Sina'
963
- except:
964
- df=None
965
- #print(" #Error(get_price_ak_cn): failed to find prices for",ticker)
966
- return None
967
- found=df_have_data(df)
968
-
969
- #已找到证券信息,或在规定时段无数据
970
- if found in ['Empty','Found']: return df
971
-
972
- #债券优先,然后查找指数、股票和基金。因部分债券代码(特别是国债)与基金代码重合,需要甄别!
973
- #例如;sh010504既是"05国债⑷"也是"招商稳兴混合C"基金的代码:-(
974
- if ticker_type in ['bond'] and suffix not in ['SW']:
975
- try:
976
- #优先抓取交易所债券行情
977
- df = exchange_bond_price(ticker2,fromdate,todate,graph=False,data_crop=False)
978
- df['Date']=df.index
979
- except:
980
- df=None
981
- found=df_have_data(df)
982
-
983
- #已找到证券信息,但在规定时段无数据
984
- if found=='Empty': return df
985
-
986
- if found != 'Found':
987
- try:
988
- #其次仅抓取股票行情
989
- df=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
990
- df['Date']=df['date']
991
- df['Date']=df['Date'].dt.tz_localize(None)
992
- except:
993
- df=None
994
- found=df_have_data(df)
995
-
996
- if found != 'Found':
997
- try:
998
- #接着查找指数
999
- df = ak.stock_zh_index_daily(symbol=ticker2)
1000
- df['Date']=df['date'].apply(lambda x: pd.to_datetime(x))
1001
- except:
1002
- df=None
1003
- found=df_have_data(df)
1004
-
1005
- if found != 'Found':
1006
- try:
1007
- #最后查找开放式基金
1008
- df =get_price_oef_china(ticker2,fromdate,todate)
1009
- df['Date']=df.index
1010
- except:
1011
- df=None
1012
- found=df_have_data(df)
1013
-
1014
- #基金。因部分债券代码(特别是国债)与基金代码重合,需要甄别!
1015
- if ticker_type in ['fund'] and suffix not in ['SW']:
1016
- try:
1017
- #优先抓取开放式基金单位净值
1018
- df =get_price_oef_china(ticker2,fromdate,todate)
1019
- df['Date']=df.index
1020
- except:
1021
- df=None
1022
- found=df_have_data(df)
1023
-
1024
- #已找到证券信息,但在规定时段无数据
1025
- #if found=='Empty': return df
1026
-
1027
- if found != 'Found': #未找到,其次从股票爬虫抓取基金行情
1028
- try:
1029
- df=ak.stock_zh_a_daily(ticker2,start1,end1,adjust=adjust)
1030
- df['Date']=df['date']
1031
- df['Date']=df['Date'].dt.tz_localize(None)
1032
- except:
1033
- df=None
1034
- found=df_have_data(df)
1035
-
1036
- if found != 'Found':
1037
- try:
1038
- #再次查找股票指数
1039
- df = ak.stock_zh_index_daily(symbol=ticker2)
1040
- df['Date']=df['date'].apply(lambda x: pd.to_datetime(x))
1041
- except:
1042
- df=None
1043
- found=df_have_data(df)
1044
-
1045
- if found != 'Found':
1046
- try:
1047
- #最后从债券爬虫查找基金信息
1048
- df = exchange_bond_price(ticker2,fromdate,todate,graph=False,data_crop=False)
1049
- df['Date']=df.index
1050
- except:
1051
- df=None
1052
- found=df_have_data(df)
1053
-
1054
- #申万指数
1055
- if suffix in ['SW']:
1056
- try:
1057
- df = fetch_price_swindex(prefix,fromdate,todate)
1058
- df['Date']=df.index
1059
- except:
1060
- df=None
1061
- #print(" #Error(get_price_ak_cn): failed to retrieve prices for",ticker)
1062
- found=df_have_data(df)
1063
-
1064
- if found in ['Found','Empty']:
1065
- #设置新的索引
1066
- df.set_index(['Date'],inplace=True)
1067
- df.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
1068
-
1069
- try:
1070
- df1=df[df.index >= start]
1071
- df2=df1[df1.index <= end]
1072
- except:
1073
- df2=df
1074
- found=df_have_data(df2)
1075
-
1076
- if found in ['Found','Empty']:
1077
- df2['source']=text_lang('新浪','sina')
1078
- df2['ticker']=str(ticker)
1079
- if 'Adj Close' not in list(df2):
1080
- df2['Adj Close']=df2['Close']
1081
- df2['footnote']=adjust
1082
-
1083
- """
1084
- ptname=ticker_name(ticker,ticker_type)
1085
- if ptname == ticker: ptname=''
1086
- """
1087
-
1088
- if len(df2) > 0:
1089
- print(" Successfully retrieved",len(df2),"records for",ticker)
1090
-
1091
- return df2
1092
-
1093
- if __name__=='__main__':
1094
- dfx=get_price_ak_cn('600519.SS','2020-12-1','2020-12-5',adjust='none')
1095
- dfy=get_price_ak_cn('600519.SS','2020-12-1','2021-2-5',adjust='hfq')
1096
- df399001=get_price_ak_cn('399001.SZ','2020-12-1','2021-2-5')
1097
- df000688=get_price_ak('000688.SS','2020-12-1','2021-2-5')
1098
- dfz=get_price_ak_cn('AAPL','2020-12-1','2021-2-5')
1099
-
1100
- #==============================================================================
1101
- if __name__=='__main__':
1102
- symbol='AAPL'
1103
- symbol='GEM25.CME'
1104
- fromdate='2024-5-1'
1105
- todate='2025-5-20'
1106
- adjust="qfq"
1107
-
1108
- get_price_ak_us(symbol, fromdate, todate, adjust)
1109
-
1110
- def get_price_ak_us(symbol, fromdate, todate, adjust=""):
1111
- """
1112
- 抓取单个美股股价,不能处理股指
1113
- """
1114
- import pandas as pd #此处需要,去掉会出错!
1115
- DEBUG=False
1116
-
1117
- #检查日期期间
1118
- result,start,end=check_period(fromdate,todate)
1119
- if not result:
1120
- print(" #Warning(get_price_ak_us): invalid date period from",fromdate,'to',todate)
1121
- return None
1122
-
1123
- symbol=symbol.upper()
1124
- #printmsg=str(symbol)+" from "+fromdate+" to "+todate
1125
-
1126
- import akshare as ak
1127
- if DEBUG:
1128
- print(" Searching info in Sina for",symbol,"... ...")
1129
- try:
1130
- if adjust=='':
1131
- df=ak.stock_us_daily(symbol=symbol,adjust=adjust)
1132
- elif adjust=='Adj_only':
1133
- df=ak.stock_us_daily(symbol=symbol,adjust='qfq')
1134
- df['Adj Close']=df['close']
1135
-
1136
- else:
1137
- #分别获取收盘价和复权价,并合成
1138
- dffqno=ak.stock_us_daily(symbol=symbol,adjust='')
1139
- dffq=ak.stock_us_daily(symbol=symbol,adjust='qfq')
1140
- dffq.rename(columns={'close':'Adj Close'},inplace=True)
1141
-
1142
- df=pd.merge(dffqno,dffq[['date','Adj Close']],on=['date'])
1143
- except:
1144
- if DEBUG:
1145
- print(" #Error(get_price_ak_us): no info found for",symbol)
1146
- return None
1147
-
1148
- #去掉可能出现的时区信息,必须使用datetime中的tz_localize
1149
- df['date']=pd.to_datetime(df['date'])
1150
- #df['date']=df['date'].tz_localize(None)
1151
-
1152
- #设置新的索引
1153
- df.set_index(['date'],inplace=True)
1154
-
1155
- #选取时间范围
1156
- df1=df[df.index >=start]
1157
- df2=df1[df1.index <=end]
1158
- if df2 is None:
1159
- print(" #Error(get_price_ak_us): failed to find prices for",symbol)
1160
- return None
1161
- num=len(df2)
1162
- if num==0:
1163
- print(" #Error(get_price_ak_us): found zero record for",symbol)
1164
- return None
1165
-
1166
- df2.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
1167
- df2['ticker']=symbol
1168
- if 'Adj Close' not in list(df2):
1169
- df2['Adj Close']=df2['Close']
1170
- df2['source']=text_lang('新浪','Sina')
1171
- df2['footnote']=adjust
1172
-
1173
- """
1174
- ptname=ticker_name(symbol,'stock')
1175
- if ptname == symbol: ptname=''
1176
- """
1177
-
1178
- print(" Successfully retrieved",num,"records for",symbol)
1179
-
1180
- return df2
1181
-
1182
- if __name__=='__main__':
1183
- get_price_ak_us('AAPL', '2021-11-1', '2021-11-5')
1184
- get_price_ak_us('^DJI', '2021-11-1', '2021-11-5')
1185
- #==============================================================================
1186
- if __name__=='__main__':
1187
- symbol='0700.HK'
1188
- symbol='0700.hk'
1189
-
1190
- symbol='00700.HK'
1191
- fromdate='2014-5-1'
1192
- todate ='2014-5-31'
1193
- adjust="qfq"
1194
-
1195
- tx=get_price_ak_hk(symbol='00700.HK',fromdate='2014-5-1',todate='2014-5-30',adjust="qfq")
1196
-
1197
- def get_price_ak_hk(symbol,fromdate,todate,adjust=""):
1198
- """
1199
- 抓取单个港股股价,不能处理股指,股指无.HK后缀
1200
- """
1201
- import pandas as pd
1202
-
1203
- DEBUG=False
1204
- if DEBUG:
1205
- print("Start searching HK stock prices for",symbol,"...")
1206
-
1207
- #检查日期期间
1208
- result,start,end=check_period(fromdate,todate)
1209
- if not result:
1210
- print(" #Warning(get_price_ak_hk): invalid date period from",fromdate,'to',todate)
1211
- return None
1212
-
1213
- #printmsg=str(symbol)+" from "+fromdate+" to "+todate
1214
-
1215
- import akshare as ak
1216
- #print(" Searching info in Sina for",symbol,"... ...")
1217
- symbol1=symbol.upper()
1218
- symbol2 = symbol1.strip('.HK')
1219
- if len(symbol2)==4:
1220
- symbol3='0'+symbol2
1221
- else:
1222
- symbol3=symbol2
1223
-
1224
- try:
1225
- if adjust == '':
1226
- df=ak.stock_hk_daily(symbol=symbol3, adjust=adjust)
1227
- elif adjust == 'Adj_only':
1228
- df=ak.stock_hk_daily(symbol=symbol3, adjust='qfq')
1229
- df['Adj Close']=df['close']
1230
- else:
1231
- dffqno=ak.stock_hk_daily(symbol=symbol3, adjust='')
1232
- dffq =ak.stock_hk_daily(symbol=symbol3,adjust='qfq')
1233
- dffq.rename(columns={'close':'Adj Close'},inplace=True)
1234
-
1235
- df=pd.merge(dffqno,dffq[['date','Adj Close']],on=['date'])
1236
- except:
1237
- print(" #Error(get_price_ak_hk): no info found for",symbol)
1238
- return None
1239
-
1240
- #去掉可能出现的时区信息
1241
- df['Date']=pd.to_datetime(df['date'])
1242
- #设置新的索引
1243
- df.set_index(['Date'],inplace=True)
1244
-
1245
- #选取时间范围
1246
- df1=df[df.index >=start]
1247
- df2=df1[df1.index <=end]
1248
- if df2 is None:
1249
- print(" #Error(get_price_ak_hk): failed to find prices for",symbol)
1250
- return None
1251
- num=len(df2)
1252
- if num==0:
1253
- print(" #Error(get_price_ak_hk): found zero record for",symbol)
1254
- return None
1255
-
1256
- df2.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close','volume':'Volume'},inplace=True)
1257
- df2['ticker']=symbol
1258
- if 'Adj Close' not in list(df2):
1259
- df2['Adj Close']=df2['Close']
1260
- df2['source']=text_lang('新浪','Sina')
1261
-
1262
- """
1263
- ptname=ticker_name(symbol,'stock')
1264
- if ptname == symbol: ptname=''
1265
- """
1266
-
1267
- print(" Successfully retrieved",num,"records for",symbol)
1268
-
1269
- return df2
1270
-
1271
- if __name__=='__main__':
1272
- df=get_price_ak_hk('0700.hk', '2021-11-1', '2021-11-5')
1273
- df=get_price_ak_hk('0700.HK', '2021-11-1', '2021-11-5')
1274
- df=get_price_ak_hk('00700.hk', '2021-11-1', '2021-11-5')
1275
- #==============================================================================
1276
- if __name__=='__main__':
1277
- ticker=['600519.SS','000858.SZ']
1278
- fromdate='2020-12-1'
1279
- todate='2021-1-31'
1280
- adjust='none'
1281
-
1282
- prices=get_prices_ak(ticker,fromdate,todate,adjust,ticker_type)
1283
-
1284
- def get_prices_ak(ticker,fromdate,todate,adjust='none',ticker_type='auto'):
1285
- """
1286
- 功能:获取中国国内股票或指数的历史行情,多个股票
1287
- """
1288
- #检查是否为多个证券:单个证券代码
1289
- if isinstance(ticker,str):
1290
- df=get_price_ak(ticker,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
1291
- return df
1292
-
1293
- #检查是否为多个证券:空的列表
1294
- if isinstance(ticker,list) and len(ticker) == 0:
1295
- pass
1296
- return None
1297
-
1298
- #检查是否为多个证券:列表中只有一个代码
1299
- if isinstance(ticker,list) and len(ticker) == 1:
1300
- ticker1=ticker[0]
1301
- #抓取单个证券
1302
- df=get_price_ak(ticker1,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
1303
- return df
1304
-
1305
- import pandas as pd
1306
- #处理列表中的第一个证券
1307
- i=0
1308
- df=None
1309
- while df is None:
1310
- if i <= len(ticker)-1:
1311
- t=ticker[i]
1312
- else:
1313
- return df
1314
-
1315
- #抓取单个证券
1316
- df=get_price_ak(t,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
1317
- if not (df is None):
1318
- columns=create_tuple_for_columns(df,t)
1319
- df.columns=pd.MultiIndex.from_tuples(columns)
1320
- else:
1321
- i=i+1
1322
- if (i+1) == len(ticker):
1323
- #已经到达代码列表末尾
1324
- return df
1325
-
1326
- #处理列表中的其余证券
1327
- if i+1 <= len(ticker)-1:
1328
- for t in ticker[(i+1):]:
1329
- #抓取单个证券
1330
- dft=get_price_ak(t,fromdate,todate,adjust=adjust,ticker_type=ticker_type)
1331
- if not (dft is None):
1332
- columns=create_tuple_for_columns(dft,t)
1333
- dft.columns=pd.MultiIndex.from_tuples(columns)
1334
-
1335
- df=pd.merge(df,dft,how='inner',left_index=True,right_index=True)
1336
-
1337
- return df
1338
-
1339
- if __name__=='__main__':
1340
- dfm=get_prices_ak(['600519.SS','000858.SZ'],'2020-12-1','2021-1-31')
1341
- dfm2=get_prices_ak(['600519.SS','AAPL'],'2020-12-1','2021-1-31')
1342
-
1343
- #==============================================================================
1344
- if __name__=='__main__':
1345
- ticker=['600519.SS','000858.SZ']
1346
- fromdate='2020-12-1'
1347
- todate='2021-1-31'
1348
- adjust='none'
1349
-
1350
- def get_prices_simple(ticker,fromdate,todate,adjust='none'):
1351
- """
1352
- 功能:直接循环获取股票或指数的历史行情,多个股票
1353
- """
1354
- #检查是否为多个股票:单个股票代码
1355
- if isinstance(ticker,str):
1356
- df=get_prices(ticker,fromdate,todate,adjust=adjust)
1357
- return df
1358
-
1359
- #检查是否为多个股票:空的列表
1360
- if isinstance(ticker,list) and len(ticker) == 0:
1361
- pass
1362
- return None
1363
-
1364
- #检查是否为多个股票:列表中只有一个代码
1365
- if isinstance(ticker,list) and len(ticker) == 1:
1366
- ticker1=ticker[0]
1367
- df=get_prices(ticker1,fromdate,todate,adjust=adjust)
1368
- return df
1369
-
1370
- import pandas as pd
1371
- #处理列表中的第一个股票
1372
- i=0
1373
- df=None
1374
- while df is None:
1375
- t=ticker[i]
1376
- #df=get_prices(t,fromdate,todate,adjust=adjust)
1377
- df=get_prices(t,fromdate,todate)
1378
- if not (df is None):
1379
- columns=create_tuple_for_columns(df,t)
1380
- df.columns=pd.MultiIndex.from_tuples(columns)
1381
- else:
1382
- i=i+1
1383
- if (i+1) == len(ticker):
1384
- #已经到达股票代码列表末尾
1385
- return df
1386
-
1387
- #对抗时区不匹配问题
1388
- df.index=pd.to_datetime(df.index)
1389
- #处理列表中的其余股票
1390
- for t in ticker[(i+1):]:
1391
- #dft=get_prices(t,fromdate,todate,adjust=adjust)
1392
- dft=get_prices(t,fromdate,todate)
1393
- if dft is None: continue
1394
- if len(dft)==0: continue
1395
-
1396
- if not (dft is None):
1397
- columns=create_tuple_for_columns(dft,t)
1398
- dft.columns=pd.MultiIndex.from_tuples(columns)
1399
-
1400
- dft.index=pd.to_datetime(dft.index)
1401
- df=pd.merge(df,dft,how='inner',left_index=True,right_index=True)
1402
-
1403
- return df
1404
-
1405
- if __name__=='__main__':
1406
- dfm=get_prices_simple(['600519.SS','000858.SZ'],'2020-12-1','2021-1-31')
1407
- dfm2=get_prices_simple(['600519.SS','AAPL'],'2020-12-1','2021-1-31')
1408
-
1409
- #==============================================================================
1410
-
1411
- if __name__=='__main__':
1412
- ticker='AAPL'
1413
- ticker='^JN0U.JO'
1414
-
1415
- start='2024-3-1'
1416
- end='2024-3-31'
1417
- retry_count=3
1418
- pause=1
1419
-
1420
- ticker='^RUT'
1421
-
1422
- ticker=['AAPL','MSFT']
1423
- ticker=['AAPL','MSFT','ABCD']
1424
-
1425
- df=get_prices_yahoo(ticker,start,end)
1426
-
1427
- def get_prices_yahoo(ticker,start,end,retry_count=3,pause=1):
1428
- """
1429
- 功能:抓取股价,使用pandas_datareader
1430
- 输出:指定收盘价格序列,最新日期的股价排列在前
1431
- ticker: 股票代码。大陆股票代码加上后缀.SZ或.SS或.BJ,港股代码去掉前导0加后缀.HK
1432
- start: 样本开始日期,尽量远的日期,以便取得足够多的原始样本,yyyy-mm-dd
1433
- end: 样本结束日期,既可以是今天日期,也可以是一个历史日期
1434
- retry_count:网络失败时的重试次数
1435
- pause:每次重试前的间隔秒数
1436
- """
1437
-
1438
- #抓取新浪/stooq股票价格
1439
- from pandas_datareader import data as pdr
1440
-
1441
- """
1442
- #临时修正新浪/stooq网站问题: 2021-7-14
1443
- #yfinance极易出现线程失败,不再覆盖pdr,2021-10-24
1444
- import yfinance as yfin
1445
- yfin.pdr_override()
1446
- """
1447
- p=None
1448
-
1449
- try:
1450
- #p=data.DataReader(ticker,'yahoo',start,end,retry_count=retry_count,pause=pause)
1451
- p=pdr.get_data_yahoo(ticker,start=start,end=end)
1452
- except: pass
1453
- found=df_have_data(p)
1454
-
1455
- if found in ['Found']:
1456
- cols=list(p)
1457
- if 'Adj Close' not in cols:
1458
- p['Adj Close']=p['Close']
1459
-
1460
- p['ticker']=ticker
1461
- #p['Adj Close']=p['Close']
1462
- p['source']=text_lang('雅虎','Yahoo')
1463
-
1464
- """
1465
- ptname=ticker_name(ticker,'stock')
1466
- if ptname == ticker: ptname=''
1467
- """
1468
-
1469
- print(" Successfully retrieved",len(p),"records for",ticker)
1470
-
1471
- #去掉时区
1472
- p=df_index_timezone_remove(p)
1473
-
1474
- return p
1475
-
1476
- if __name__=='__main__':
1477
- df1=get_prices_yahoo('AAPL','2020-12-1','2021-1-31')
1478
- df2=get_prices_yahoo('ABCD','2020-12-1','2021-1-31')
1479
- df3=get_prices_yahoo(['AAPL','MSFT'],'2020-12-1','2021-1-31')
1480
- df4=get_prices_yahoo(['AAPL','EFGH','MSFT','ABCD'],'2020-12-1','2021-1-31')
1481
- df5=get_prices_yahoo(['0700.HK','600519.SS'],'2020-12-1','2021-1-31')
1482
- df6=get_prices_yahoo(['AAPL','MSFT','0700.HK','600519.SS'],'2020-12-1','2021-1-31')
1483
-
1484
- #==============================================================================
1485
- def get_price_yf(ticker,start,end,threads=False):
1486
- """
1487
- 套壳函数get_prices_yf,保持兼容
1488
- """
1489
- df=get_prices_yf(ticker,start,end,threads=threads)
1490
-
1491
- return df
1492
-
1493
-
1494
- if __name__=='__main__':
1495
- start='2024-12-1'
1496
- end='2025-1-31'
1497
-
1498
- ticker='AAPL'
1499
- ticker='GC=F'
1500
-
1501
- ticker='XAUUSD'
1502
-
1503
- ticker=['AAPL','MSFT']
1504
- ticker=['0700.HK','600519.SS']
1505
- ticker=['AAPL','MSFT','0700.HK','600519.SS']
1506
-
1507
- threads=False
1508
- threads=True
1509
-
1510
- df=get_price_yf(ticker,start,end,threads)
1511
-
1512
-
1513
- def get_prices_yf(ticker,start,end,threads=False):
1514
- """
1515
- 功能:抓取股价,使用yfinance(对非美股抓取速度快,但有时不太稳定)
1516
- 输入:股票代码或股票代码列表,开始日期,结束日期
1517
- ticker: 股票代码或股票代码列表。大陆股票代码加上后缀.SZ或.SS或.BJ,港股代码去掉前导0加后缀.HK
1518
- start: 样本开始日期,尽量远的日期,以便取得足够多的原始样本,yyyy-mm-dd
1519
- end: 样本结束日期,既可以是今天日期,也可以是一个历史日期
1520
-
1521
- 输出:指定收盘价格序列,最新日期的股价排列在前
1522
- 特别注意:yfinance中的收盘价Close其实是Yahoo Finance中的调整收盘价Adj Close。
1523
- """
1524
- p=None
1525
-
1526
- #支持多个证券
1527
- import yfinance as yf
1528
- ticker1,islist=cvt_yftickerlist(ticker)
1529
- if not islist:
1530
- #下载单一股票的股价
1531
- stock=yf.Ticker(ticker1)
1532
- try:
1533
- #p=stock.history(start=start,end=end,threads=threads)
1534
- p=stock.history(start=start,end=end)
1535
- #仅针对雅虎情况
1536
- if p is not None:
1537
- if len(p)==0: p=None
1538
- except:
1539
- p=None
1540
- else:
1541
- #下载股票列表的股价
1542
- try:
1543
- p=yf.download(ticker1,start=start,end=end,progress=False,threads=threads)
1544
- #仅针对雅虎情况
1545
- if p is not None:
1546
- if len(p)==0: p=None
1547
- except:
1548
- p=None
1549
-
1550
- found=df_have_data(p)
1551
- if found in ['Found','Empty']:
1552
- if 'Adj Close' not in list(p):
1553
- p['Adj Close']=p['Close']
1554
- p['ticker']=ticker
1555
- p['source']=text_lang('雅虎','Yahoo')
1556
-
1557
- if len(p) > 0:
1558
- """
1559
- ptname=ticker_name(ticker1,'stock')
1560
- if ptname == ticker: ptname=''
1561
- """
1562
-
1563
- print(" Successfully retrieved",len(p),"records for",ticker1)
1564
-
1565
- #去掉时区
1566
- p=df_index_timezone_remove(p)
1567
- else:
1568
- pass
1569
- #print(" #Error(get_prices_yf):",ticker1,"not found or no prices in the period or inaccessible to yahoo")
1570
-
1571
- return p
1572
-
1573
- if __name__=='__main__':
1574
- df1=get_prices_yf('AAPL','2020-12-1','2021-1-31')
1575
- df1b=get_prices_yf('EFGH','2020-12-1','2021-1-31')
1576
- df2=get_prices_yf(['AAPL'],'2020-12-1','2021-1-31')
1577
- df3=get_prices_yf(['AAPL','MSFT'],'2020-12-1','2021-1-31')
1578
- df3b=get_prices_yf(['AAPL','MSFS'],'2020-12-1','2021-1-31')
1579
- df4=get_prices_yf(['0700.HK','600519.SS'],'2020-12-1','2021-1-31')
1580
- df5=get_prices_yf(['AAPL','MSFT','0700.HK','600519.SS'],'2020-12-1','2021-1-31')
1581
- df6=get_prices_yf(['ABCD','EFGH','0700.HK','600519.SS'],'2020-12-1','2021-1-31')
1582
-
1583
- #==============================================================================
1584
- if __name__=='__main__':
1585
- ticker='^TYX'
1586
- ticker='AMZN'
1587
- ticker='AAPL'
1588
- start='2020-12-1'; end='2025-1-31'
1589
-
1590
- ticker='GEM25.CME'
1591
- start='2025-1-1'; end='2025-5-30'
1592
-
1593
- p=get_price_yq(ticker,start,end)
1594
-
1595
- def get_price_yq(ticker,start,end):
1596
- """
1597
- 功能:从雅虎财经抓取股价,使用yahooquery(注意插件版本问题)
1598
- 输入:股票代码或股票代码列表,开始日期,结束日期
1599
- ticker: 股票代码或股票代码列表。大陆股票代码加上后缀.SZ或.SS或.BJ,港股代码去掉前导0加后缀.HK
1600
- start: 样本开始日期,尽量远的日期,以便取得足够多的原始样本,yyyy-mm-dd
1601
- end: 样本结束日期,既可以是今天日期,也可以是一个历史日期
1602
-
1603
- 输出:指定收盘价格序列,最新日期的股价排列在前
1604
- """
1605
- DEBUG=False
1606
-
1607
- p=None
1608
-
1609
- #支持多个证券
1610
- import yahooquery as yq
1611
- ticker1,islist=cvt_yftickerlist(ticker)
1612
-
1613
- try:
1614
- #下载单一股票的股价;下载股票列表的股价,与单股票情况相同,但需注意MultiInex结构
1615
- stock=yq.Ticker(ticker1, asynchronous=True)
1616
- except:
1617
- if DEBUG:
1618
- print(" Yahoo api is tentatively inaccessible, recovering ...")
1619
- sleep_random(max_sleep=60)
1620
- try:
1621
- stock=yq.Ticker(ticker1, asynchronous=True)
1622
- except:
1623
- if DEBUG:
1624
- print(f" Sorry, failed to retrieve info from Yahoo for {ticker}")
1625
- p=None
1626
- return p
1627
-
1628
- if not islist:
1629
- try:
1630
- p=stock.history(start=start,end=end)
1631
- #仅针对雅虎情况
1632
- if p is not None:
1633
- if len(p)==0: p=None
1634
- except:
1635
- p=None
1636
- else:
1637
- try:
1638
- p=stock.history(start=start,end=end)
1639
- #仅针对雅虎情况
1640
- if p is not None:
1641
- if len(p)==0: p=None
1642
- except:
1643
- p=None
1644
-
1645
- found=df_have_data(p)
1646
- if found in ['Found','Empty']:
1647
- p.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close', \
1648
- 'adjclose':'Adj Close','volume':'Volume'},inplace=True)
1649
-
1650
- if 'Adj Close' not in list(p):
1651
- p['Adj Close']=p['Close']
1652
- p['ticker']=ticker
1653
- p['source']=text_lang('雅虎','Yahoo')
1654
-
1655
- # 去掉一级Index
1656
- p=p.droplevel('symbol')
1657
- p['date']=p.index
1658
- if len(p) > 0:
1659
- print(" Successfully retrieved",len(p),"records for",ticker1)
1660
-
1661
- #去掉时区
1662
- p=df_index_timezone_remove(p)
1663
- else:
1664
- pass
1665
- #print(" #Error(get_prices_yf):",ticker1,"not found or no prices in the period or inaccessible to yahoo")
1666
-
1667
- return p
1668
-
1669
- #==============================================================================
1670
- if __name__=='__main__':
1671
- ticker=['600519.SS','000858.SZ']
1672
- fromdate='2020-12-1'
1673
- todate='2021-1-31'
1674
-
1675
- ticker=['ICBC','SNP','HNP']
1676
- fromdate,todate='2025-6-1','2025-6-20'
1677
-
1678
-
1679
- prices=get_prices_yq(ticker,fromdate,todate)
1680
-
1681
- def get_prices_yq(ticker,fromdate,todate):
1682
- """
1683
- 功能:获取yahooquery股票或指数的历史行情,多个股票
1684
- """
1685
- #检查是否为多个证券:单个证券代码
1686
- if isinstance(ticker,str):
1687
- df=get_price_yq(ticker,fromdate,todate)
1688
- return df
1689
-
1690
- #检查是否为多个证券:空的列表
1691
- if isinstance(ticker,list) and len(ticker) == 0:
1692
- pass
1693
- return None
1694
-
1695
- #检查是否为多个证券:列表中只有一个代码
1696
- if isinstance(ticker,list) and len(ticker) == 1:
1697
- ticker1=ticker[0]
1698
- #抓取单个证券
1699
- df=get_price_yq(ticker1,fromdate,todate)
1700
- return df
1701
-
1702
- import pandas as pd
1703
- #处理列表中的第一个证券
1704
- i=0
1705
- df=None
1706
- while df is None:
1707
- if i <= len(ticker)-1:
1708
- t=ticker[i]
1709
- else:
1710
- return df
1711
-
1712
- #抓取单个证券
1713
- df=get_price_yq(t,fromdate,todate)
1714
- if not (df is None):
1715
- columns=create_tuple_for_columns(df,t)
1716
- df.columns=pd.MultiIndex.from_tuples(columns)
1717
- else:
1718
- i=i+1
1719
-
1720
- if (i+1) == len(ticker):
1721
- pass
1722
- #已经到达代码列表末尾
1723
- return df
1724
-
1725
- #处理列表中的其余证券
1726
- if i+1 <= len(ticker)-1:
1727
- for t in ticker[(i+1):]:
1728
- #抓取单个证券
1729
- dft=get_price_yq(t,fromdate,todate)
1730
- if not (dft is None):
1731
- columns=create_tuple_for_columns(dft,t)
1732
- dft.columns=pd.MultiIndex.from_tuples(columns)
1733
-
1734
- df=pd.merge(df,dft,how='inner',left_index=True,right_index=True)
1735
-
1736
- return df
1737
-
1738
- if __name__=='__main__':
1739
- dfm=get_prices_yq(['600519.SS','000858.SZ'],'2020-12-1','2021-1-31')
1740
- dfm2=get_prices_yq(['600519.SS','AAPL'],'2020-12-1','2021-1-31')
1741
- #==============================================================================
1742
- if __name__=='__main__':
1743
- ticker='AMZN'
1744
- ticker='AAPL'
1745
- start='2020-12-1'; end='2025-1-31'
1746
-
1747
- def get_dividend_yq(ticker,start,end,facecolor="papayawhip"):
1748
- """
1749
- 功能:获得股票分红历史数据
1750
- """
1751
-
1752
- print(f" Looking for dividend info for {ticker} from Yahoo ...")
1753
- try:
1754
- p=get_price_yq(ticker,start,end)
1755
- except:
1756
- print(f" #Error(get_dividend_yq): crump problem. If {ticker} is correct, try again later")
1757
- return None
1758
- if p is None:
1759
- print(f" #Error(get_dividend_yq): failed to get dividend info for {ticker}, may try again later")
1760
- return None
1761
-
1762
- pcols=list(p)
1763
- if not ('dividends' in pcols):
1764
- print(f" No dividend info found for {ticker} from {start} to {end}")
1765
- return None
1766
-
1767
- div1=p[['date','dividends','Close','Adj Close']]
1768
- div2=div1[div1['dividends'] != 0]
1769
-
1770
- if len(div2) == 0:
1771
- print(f" No dividend info found for {ticker} during {start} to {end}")
1772
- return None
1773
-
1774
- div2['dividends']=div2['dividends'].apply(lambda x: str(round(x,5)))
1775
- div2['Close']=div2['Close'].apply(lambda x: round(x,2))
1776
- div2['Adj Close']=div2['Adj Close'].apply(lambda x: round(x,2))
1777
- div2.rename(columns={"date":text_lang("除息日期","Ex-Dividend"), \
1778
- "dividends":text_lang("每股分红(本币,税前)","Div per Share (Pre-tax)"), \
1779
- "Close":text_lang("收盘价","Close Price"), \
1780
- "Adj Close":text_lang("调整收盘价(前复权价)","Adjusted Close Price")}, \
1781
- inplace=True)
1782
-
1783
- titletxt=ticker_name(ticker,"stock")+": "+text_lang("股票分红历史","Stock Dividend History")
1784
- import datetime
1785
- todaydt = datetime.date.today()
1786
- footnote_cn=f"【注】期间:{start}至{end}, 数据来源:雅虎, {todaydt}"
1787
- footnote_en=f"Period:{start} to {end}. Data source:Yahoo, {todaydt}"
1788
- footnote=text_lang(footnote_cn,footnote_en)
1789
- df_display_CSS(div2,titletxt=titletxt,footnote=footnote,facecolor=facecolor,decimals=2, \
1790
- first_col_align='center',second_col_align='center', \
1791
- last_col_align='center',other_col_align='center')
1792
-
1793
-
1794
- return div2
1795
-
1796
- def get_split_yq(ticker,start,end,facecolor="papayawhip"):
1797
- """
1798
- 功能:获得股票分拆历史数据
1799
- """
1800
-
1801
- print(f" Looking for split info for {ticker} from Yahoo ...")
1802
- try:
1803
- p=get_price_yq(ticker,start,end)
1804
- except:
1805
- print(f" #Error(get_split_yq): crump problem. If {ticker} is correct, try again later")
1806
- return None
1807
- if p is None:
1808
- print(f" #Error(get_split_yq): split info not found for {ticker}, may try again later")
1809
- return None
1810
-
1811
- pcols=list(p)
1812
- if not ('splits' in pcols):
1813
- print(f" No split info found for {ticker} from {start} to {end}")
1814
- return None
1815
-
1816
- div1=p[['date','splits','Close','Adj Close']]
1817
- div2=div1[div1['splits'] != 0]
1818
-
1819
- if len(div2) == 0:
1820
- print(f" No split info found for {ticker} during {start} to {end}")
1821
- return None
1822
-
1823
- div2['Close']=div2['Close']*div2['splits']
1824
- div2['splits']=div2['splits'].apply(lambda x: str(int(x)) if x.is_integer() else str(round(x,1)))
1825
- div2['Close']=div2['Close'].apply(lambda x: round(x,2))
1826
- div2['Adj Close']=div2['Adj Close'].apply(lambda x: round(x,2))
1827
- div2.rename(columns={"date":text_lang("分拆日期","Split Date"), \
1828
- "splits":text_lang("分拆比例","Split Ratio"), \
1829
- "Close":text_lang("收盘价","Close Price"), \
1830
- "Adj Close":text_lang("调整收盘价(前复权价)","Adjusted Close Price")}, \
1831
- inplace=True)
1832
-
1833
- titletxt=ticker_name(ticker,"stock")+": "+text_lang("股票分拆历史","Stock Split History")
1834
- import datetime
1835
- todaydt = datetime.date.today()
1836
- footnote_cn=f"【注】期间:{start}至{end}, 数据来源:雅虎, {todaydt}"
1837
- footnote_en=f"Period:{start} to {end}. Data source:Yahoo, {todaydt}"
1838
- footnote=text_lang(footnote_cn,footnote_en)
1839
- df_display_CSS(div2,titletxt=titletxt,footnote=footnote,facecolor=facecolor,decimals=2, \
1840
- first_col_align='center',second_col_align='center', \
1841
- last_col_align='right',other_col_align='center')
1842
-
1843
-
1844
- return div2
1845
-
1846
- #==============================================================================
1847
- if __name__=='__main__':
1848
- ticker='^GSPC'
1849
- start='1991-1-1'
1850
- end='2000-12-31'
1851
-
1852
- def get_index_fred(ticker,start,end):
1853
- """
1854
- 功能:临时解决方案,获取标普500、道琼斯等国外市场指数
1855
- """
1856
- yahoolist=['^GSPC','^DJI','^VIX','^IXIC','^N225','^NDX']
1857
- fredlist=['sp500','djia','vixcls','nasdaqcom','nikkei225','nasdaq100']
1858
-
1859
- if not (ticker in yahoolist):
1860
- return None
1861
-
1862
- import pandas as pd
1863
- import pandas_datareader.data as web
1864
- if ticker in yahoolist:
1865
- pos=yahoolist.index(ticker)
1866
- id=fredlist[pos]
1867
-
1868
- try:
1869
- df = web.DataReader([id], start=start, end=end, data_source='fred')
1870
- except:
1871
- print(" #Warning(get_index_fred): connection failed, trying to recover ...")
1872
- import time
1873
- time.sleep(5) # 暂停 5秒
1874
- try:
1875
- df = web.DataReader([id], start=start, end=end, data_source='fred')
1876
- except:
1877
- pass
1878
- return None
1879
- if len(df)==0:
1880
- return None
1881
- df.rename(columns={id:'Close'},inplace=True)
1882
-
1883
- #删除空值记录
1884
- #df.dropna(inplace=True)
1885
-
1886
- df['ticker']=ticker
1887
- df['Adj Close']=df['Close']
1888
- df['source']='FRED'
1889
-
1890
- num=len(df)
1891
- if num > 0:
1892
- """
1893
- ptname=ticker_name(ticker,'stock')
1894
- if ptname == ticker: ptname=''
1895
- """
1896
-
1897
- print(" Successfully retrieved",num,"records for",ticker)
1898
- else:
1899
- print(" Sorry, no records retrieved for",ticker)
1900
-
1901
- #去掉时区
1902
- df=df_index_timezone_remove(df)
1903
-
1904
- return df
1905
-
1906
- if __name__=='__main__':
1907
- df1=get_index_fred('^VIX','1991-1-1','1991-12-31')
1908
- df2=get_index_fred('^DJI','1991-1-1','2020-12-31') #始于2011-11-25
1909
- df3=get_index_fred('^GSPC','1991-1-1','2020-12-31') #始于2011-11-25
1910
- df4=get_index_fred('^IXIC','1991-1-1','2020-12-31')
1911
- df5=get_index_fred('^N225','1991-1-1','2020-12-31')
1912
- df6=get_index_fred('^NDX','1991-1-1','2020-12-31')
1913
- #==============================================================================
1914
- def create_tuple_for_columns(df_a, multi_level_col):
1915
- """
1916
- Create a columns tuple that can be pandas MultiIndex to create multi level column
1917
-
1918
- :param df_a: pandas dataframe containing the columns that must form the first level of the multi index
1919
- :param multi_level_col: name of second level column
1920
- :return: tuple containing (first_level_cols,second_level_col)
1921
- """
1922
- temp_columns = []
1923
- for item in df_a.columns:
1924
- try:
1925
- temp_columns.append((item, multi_level_col))
1926
- except:
1927
- temp_columns._append((item, multi_level_col))
1928
-
1929
- return temp_columns
1930
- #==============================================================================
1931
- #==============================================================================
1932
- #==============================================================================
1933
- #==============================================================================
1934
- def get_price_portfolio(tickerlist,sharelist,fromdate,todate,adj=False, \
1935
- source='auto',ticker_type='bond'):
1936
- """
1937
- 套壳函数get_prices_portfolio
1938
- 经测试,已经能够支持capm_beta2
1939
- ticker_type='bond':抓取债券优先,因投资组合中配置债券的可能性远高于基金和指数
1940
- """
1941
- df=get_prices_portfolio(tickerlist,sharelist,fromdate,todate,adj=adj, \
1942
- source=source,ticker_type=ticker_type)
1943
- return df
1944
-
1945
- if __name__=='__main__':
1946
- tickerlist=['INTC','MSFT']
1947
- sharelist=[0.6,0.4]
1948
-
1949
- tickerlist=['600519.SS', '000858.SZ', '600809.SS']
1950
- sharelist=[0.4,0.3,0.3]
1951
-
1952
- tickerlist=['JD']
1953
- sharelist=[1000]
1954
-
1955
- tickerlist=['601988.SS']
1956
- sharelist=[1000]
1957
-
1958
- fromdate='2024-1-1'
1959
- todate='2024-4-1'
1960
- adj=False
1961
- source='auto'
1962
- ticker_type='auto'
1963
-
1964
- ticker={'Market':('China','000001.SS','白酒组合'),'600519.SS':0.4,'000858.SZ':0.6}
1965
- _,_,tickerlist,sharelist,ticker_type=decompose_portfolio(ticker)
1966
-
1967
- p=get_prices_portfolio(tickerlist,sharelist,fromdate,todate,source='auto')
1968
-
1969
- def get_prices_portfolio(tickerlist,sharelist,fromdate,todate,adj=False, \
1970
- source='auto',ticker_type='bond'):
1971
- """
1972
- 功能:抓取投资组合的每日价值
1973
- 输入:证券代码列表,份额列表,开始日期,结束日期
1974
- tickerlist: 证券代码列表
1975
- sharelist:持有份额列表,与股票代码列表一一对应
1976
- fromdate: 样本开始日期。格式:'YYYY-MM-DD'
1977
- todate: 样本结束日期。既可以是今天日期,也可以是一个历史日期
1978
-
1979
- 输出:投资组合的价格序列,按照日期升序排列
1980
- """
1981
- import pandas as pd
1982
-
1983
- #检查证券列表个数与份额列表个数是否一致
1984
- if len(tickerlist) != len(sharelist):
1985
- print(" #Error(get_prices_portfolio): numbers of stocks and shares mismatch.")
1986
- return None
1987
-
1988
- #抓取证券价格:如何只抓取股票和债券???
1989
- p=get_prices(tickerlist,fromdate,todate,adj=adj,source=source,ticker_type=ticker_type)
1990
- if p is None: return None
1991
-
1992
- #删除无用的空列preclose,避免引起后续程序误判
1993
- try:
1994
- del p['prevclose']
1995
- except: pass
1996
-
1997
- #结果非空时,检查整列为空的证券代码
1998
- nancollist=[]
1999
- collist=list(p)
2000
- for c in collist:
2001
- if p[c].isnull().all():
2002
- nancollist=nancollist+[c]
2003
- #查找错误的ticker
2004
- wrongtickers=[]
2005
- for w in tickerlist:
2006
- nancolstr=str(nancollist)
2007
- if nancolstr.find(w.upper()) != -1: #找到
2008
- wrongtickers=wrongtickers+[w]
2009
-
2010
- if len(wrongtickers) > 0:
2011
- print(" #Warning(get_prices_portfolio): price info not found for",wrongtickers)
2012
- print(" #Warning(get_prices_portfolio): dropping all the rows related to",wrongtickers)
2013
- p.dropna(axis=1,how="all",inplace=True) # 丢弃全为缺失值的那些列
2014
-
2015
- #删除投资组合中相关的权重
2016
- for w in wrongtickers:
2017
- pos=tickerlist.index(w)
2018
- try:
2019
- del tickerlist[pos]
2020
- del sharelist[pos]
2021
- except: pass
2022
-
2023
- if len(sharelist) > 1:
2024
- #计算投资者的开盘价
2025
- op=p['Open']
2026
- #计算投资组合的价值
2027
- oprice=pd.DataFrame(op.dot(sharelist))
2028
- oprice.rename(columns={0: 'Open'}, inplace=True)
2029
-
2030
- #计算投资者的收盘价
2031
- cp=p['Close']
2032
- #计算投资组合的价值
2033
- cprice=pd.DataFrame(cp.dot(sharelist))
2034
- cprice.rename(columns={0: 'Close'}, inplace=True)
2035
-
2036
- #计算投资者的调整收盘价
2037
- acp=p['Adj Close']
2038
- #计算投资组合的价值
2039
- acprice=pd.DataFrame(acp.dot(sharelist))
2040
- acprice.rename(columns={0: 'Adj Close'}, inplace=True)
2041
-
2042
- #合成开盘价、收盘价和调整收盘价
2043
- ocprice=pd.merge(oprice,cprice,how='inner',left_index=True,right_index=True)
2044
- prices=pd.merge(ocprice,acprice,how='inner',left_index=True,right_index=True)
2045
- else:
2046
- #prices=p*sharelist[0]
2047
- prices=p
2048
- pcols=list(prices)
2049
- import pandas as pd
2050
- for pc in pcols:
2051
- #判断某列的数据类型
2052
- if pd.api.types.is_float_dtype(prices[pc]):
2053
- prices[pc]=prices[pc]*sharelist[0]
2054
- else:
2055
- continue
2056
-
2057
- #提取日期和星期几
2058
- prices['Date']=prices.index.strftime("%Y-%m-%d")
2059
- prices['Weekday']=prices.index.weekday+1
2060
-
2061
- prices['Portfolio']=str(tickerlist)
2062
- prices['Shares']=str(sharelist)
2063
- try:
2064
- prices['Adjustment']=prices.apply(lambda x: \
2065
- False if x['Close']==x['Adj Close'] else True, axis=1)
2066
-
2067
- stockdf=prices[['Portfolio','Shares','Date','Weekday', \
2068
- 'Open','Close','Adj Close','Adjustment']]
2069
- except:
2070
- return None
2071
-
2072
- return stockdf
2073
-
2074
- if __name__=='__main__':
2075
- tickerlist=['INTC','MSFT']
2076
- sharelist=[0.6,0.4]
2077
- fromdate='2020-11-1'
2078
- todate='2021-1-31'
2079
- dfp=get_prices_portfolio(tickerlist,sharelist,fromdate,todate)
2080
-
2081
- #==============================================================================
2082
- #==============================================================================
2083
- if __name__=='__main__':
2084
- ticker='AAPL'
2085
-
2086
- ticker=['AAPL','MSFT','0700.HK','600519.SS']
2087
-
2088
- def cvt_yftickerlist(ticker):
2089
- """
2090
- 功能:转换pandas_datareader的tickerlist为yfinance的格式
2091
- 输入参数:单一股票代码或pandas_datareader的股票代码列表
2092
-
2093
- 输出参数:yfinance格式的股票代码列表
2094
- """
2095
- #如果不是股票代码列表,直接返回股票代码
2096
- if not isinstance(ticker,list): return ticker,False
2097
-
2098
- #如果是股票代码列表,但只有一个元素
2099
- if len(ticker)==1: return ticker[0],False
2100
-
2101
- #如果是股票代码列表,有两个及以上元素
2102
- yftickerlist=ticker[0]
2103
- for t in ticker[1:]:
2104
- yftickerlist=yftickerlist+' '+t.upper()
2105
-
2106
- return yftickerlist,True
2107
-
2108
-
2109
- if __name__=='__main__':
2110
- cvt_yftickerlist('AAPL')
2111
- cvt_yftickerlist(['AAPL'])
2112
- cvt_yftickerlist(['AAPL','MSFT'])
2113
- cvt_yftickerlist(['AAPL','MSFT','0700.hk'])
2114
-
2115
- #==============================================================================
2116
- if __name__=='__main__':
2117
- url='https://finance.yahoo.com'
2118
-
2119
- def test_website(url='https://finance.yahoo.com'):
2120
- """
2121
- 功能:测试网站的联通性和反应时间
2122
- 优点:真实
2123
- 缺点:运行过程非常慢
2124
- """
2125
- print(" Testing internet connection to",url,"...")
2126
- import pycurl
2127
- from io import BytesIO
2128
-
2129
- #进行网络测试
2130
- c = pycurl.Curl()
2131
- buffer = BytesIO() # 创建缓存对象
2132
- c.setopt(c.WRITEDATA, buffer) # 设置资源数据写入到缓存对象
2133
- c.setopt(c.URL, url) # 指定请求的URL
2134
- c.setopt(c.MAXREDIRS, 3) # 指定HTTP重定向的最大数
2135
-
2136
- test_result=True
2137
- test_msg=""
2138
- try:
2139
- c.perform() # 测试目标网站
2140
- except Exception as e:
2141
- c.close()
2142
-
2143
- #print(e)
2144
- print(" #Error(test_website2):",e)
2145
-
2146
- test_result=False
2147
- test_msg="UNREACHABLE"
2148
-
2149
- return test_result,test_msg
2150
-
2151
- #获得网络测试结果阐述
2152
- http_code = c.getinfo(pycurl.HTTP_CODE) # 返回的HTTP状态码
2153
- dns_resolve = c.getinfo(pycurl.NAMELOOKUP_TIME) # DNS解析所消耗的时间
2154
- http_conn_time = c.getinfo(pycurl.CONNECT_TIME) # 建立连接所消耗的时间
2155
- http_pre_trans = c.getinfo(pycurl.PRETRANSFER_TIME) # 从建立连接到准备传输所消耗的时间
2156
- http_start_trans = c.getinfo(pycurl.STARTTRANSFER_TIME) # 从建立连接到传输开始消耗的时间
2157
- http_total_time = c.getinfo(pycurl.TOTAL_TIME) # 传输结束所消耗的总时间
2158
- http_size_download = c.getinfo(pycurl.SIZE_DOWNLOAD) # 下载数据包大小
2159
- http_size_upload = c.getinfo(pycurl.SIZE_UPLOAD) # 上传数据包大小
2160
- http_header_size = c.getinfo(pycurl.HEADER_SIZE) # HTTP头部大小
2161
- http_speed_downlaod = c.getinfo(pycurl.SPEED_DOWNLOAD) # 平均下载速度
2162
- http_speed_upload = c.getinfo(pycurl.SPEED_UPLOAD) # 平均上传速度
2163
- http_redirect_time = c.getinfo(pycurl.REDIRECT_TIME) # 重定向所消耗的时间
2164
-
2165
- """
2166
- print('HTTP响应状态: %d' % http_code)
2167
- print('DNS解析时间:%.2f ms' % (dns_resolve * 1000))
2168
- print('建立连接时间: %.2f ms' % (http_conn_time * 1000))
2169
- print('准备传输时间: %.2f ms' % (http_pre_trans * 1000))
2170
- print("传输开始时间: %.2f ms" % (http_start_trans * 1000))
2171
- print("传输结束时间: %.2f ms" % (http_total_time * 1000))
2172
- print("重定向时间: %.2f ms" % (http_redirect_time * 1000))
2173
- print("上传数据包大小: %d bytes/s" % http_size_upload)
2174
- print("下载数据包大小: %d bytes/s" % http_size_download)
2175
- print("HTTP头大小: %d bytes/s" % http_header_size)
2176
- print("平均上传速度: %d k/s" % (http_speed_upload / 1024))
2177
- print("平均下载速度: %d k/s" % (http_speed_downlaod / 1024))
2178
- """
2179
- c.close()
2180
-
2181
- if http_speed_downlaod >= 100*1024: test_msg="FAST"
2182
- if http_speed_downlaod < 100*1024: test_msg="GOOD"
2183
- if http_speed_downlaod < 50*1024: test_msg="GOOD"
2184
- if http_speed_downlaod < 10*1024: test_msg="VERY SLOW"
2185
- if http_speed_downlaod < 1*1024: test_msg="UNSTABLE"
2186
-
2187
- return test_result,test_msg
2188
-
2189
- if __name__=='__main__':
2190
- test_website()
2191
-
2192
- #==============================================================================
2193
- def calc_daily_return(pricedf):
2194
- """
2195
- 功能:基于从新浪/stooq抓取的单个证券价格数据集计算其日收益率
2196
- 输入:从新浪/stooq抓取的单个证券价格数据集pricedf,基于收盘价或调整收盘价进行计算
2197
- 输出:证券日收益率序列,按照日期升序排列。
2198
- """
2199
- import numpy as np
2200
- #计算算术日收益率:基于收盘价
2201
- pricedf["Daily Ret"]=pricedf['Close'].pct_change()
2202
- pricedf["Daily Ret%"]=pricedf["Daily Ret"]*100.0
2203
-
2204
- #计算算术日收益率:基于调整收盘价
2205
- pricedf["Daily Adj Ret"]=pricedf['Adj Close'].pct_change()
2206
- pricedf["Daily Adj Ret%"]=pricedf["Daily Adj Ret"]*100.0
2207
-
2208
- #计算对数日收益率
2209
- pricedf["log(Daily Ret)"]=np.log(pricedf["Daily Ret"]+1)
2210
- pricedf["log(Daily Adj Ret)"]=np.log(pricedf["Daily Adj Ret"]+1)
2211
-
2212
- return pricedf
2213
-
2214
-
2215
- if __name__ =="__main__":
2216
- ticker='AAPL'
2217
- fromdate='2018-1-1'
2218
- todate='2020-3-16'
2219
- pricedf=get_price(ticker, fromdate, todate)
2220
- drdf=calc_daily_return(pricedf)
2221
-
2222
- eu7=['GSK','ASML','NVS','NVO','AZN','SAP','SNY']
2223
- for ticker in eu7:
2224
- print("Processing",ticker,"...")
2225
- pricedf,found=get_price_1ticker_mixed(ticker,fromdate='2022-1-1',todate='2024-6-16',source='yahoo')
2226
- dret=calc_daily_return(pricedf)
2227
-
2228
-
2229
- #==============================================================================
2230
- def calc_rolling_return(drdf, period="Weekly"):
2231
- """
2232
- 功能:基于单个证券的日收益率数据集, 计算其滚动期间收益率
2233
- 输入:
2234
- 单个证券的日收益率数据集drdf。
2235
- 期间类型period,默认为每周。
2236
- 输出:期间滚动收益率序列,按照日期升序排列。
2237
- """
2238
- #检查period类型
2239
- periodlist = ["Weekly","Monthly","Quarterly","Annual"]
2240
- if not (period in periodlist):
2241
- print(" #Error(calc_rolling_return), periodic type only support:",periodlist)
2242
- return None
2243
-
2244
- #换算期间对应的实际交易天数
2245
- perioddays=[5,21,63,252]
2246
- rollingnum=perioddays[periodlist.index(period)]
2247
-
2248
- #计算滚动收益率:基于收盘价
2249
- retname1=period+" Ret"
2250
- retname2=period+" Ret%"
2251
- import numpy as np
2252
- drdf[retname1]=np.exp(drdf["log(Daily Ret)"].rolling(rollingnum,min_periods=1).sum())-1.0
2253
- drdf[retname2]=drdf[retname1]*100.0
2254
-
2255
- #计算滚动收益率:基于调整收盘价
2256
- retname3=period+" Adj Ret"
2257
- retname4=period+" Adj Ret%"
2258
- drdf[retname3]=np.exp(drdf["log(Daily Adj Ret)"].rolling(rollingnum,min_periods=1).sum())-1.0
2259
- drdf[retname4]=drdf[retname3]*100.0
2260
-
2261
- return drdf
2262
-
2263
- if __name__ =="__main__":
2264
- ticker='000002.SZ'
2265
- period="Weekly"
2266
- prdf=calc_rolling_return(drdf, period)
2267
- prdf=calc_rolling_return(drdf, "Monthly")
2268
- prdf=calc_rolling_return(drdf, "Quarterly")
2269
- prdf=calc_rolling_return(drdf, "Annual")
2270
-
2271
- #==============================================================================
2272
- def calc_expanding_return(drdf0,basedate):
2273
- """
2274
- 功能:基于日收益率数据集,从起始日期开始到结束日期的扩展窗口收益率序列。
2275
- 输入:
2276
- 日收益率数据集drdf。
2277
- 输出:期间累计收益率序列,按照日期升序排列。
2278
- """
2279
- #去掉时区
2280
- drdf0=df_index_timezone_remove(drdf0)
2281
-
2282
- import pandas as pd
2283
- basedate_pd=pd.to_datetime(basedate)
2284
- drdf=drdf0[drdf0.index >= basedate_pd]
2285
- if len(drdf)==0:
2286
- ticker=drdf0['ticker'].values[0]
2287
- lastdate=drdf0.index.values[-1]
2288
- print("\n #Warning(calc_expanding_return): no records in",ticker,'after',basedate)
2289
- """
2290
- print(" basedate_pd=",basedate_pd)
2291
- print(" drdf0=",drdf0)
2292
- print(" drdf=",drdf)
2293
- """
2294
- return None
2295
- """
2296
- drdf0['date_tmp']=drdf0.index
2297
- drdf0['date_tmp']=drdf0['date_tmp'].apply(lambda x: x.strftime('%Y-%m-%d'))
2298
- basedate2=basedate_pd.strftime('%Y-%m-%d')
2299
- drdf=drdf0[drdf0['date_tmp'] >= basedate2]
2300
- """
2301
-
2302
- #计算累计收益率:基于收盘价
2303
- retname1="Exp Ret"
2304
- retname2="Exp Ret%"
2305
- import numpy as np
2306
- #drdf[retname1]=np.exp(drdf["log(Daily Ret)"].expanding(min_periods=1).sum())-1.0
2307
- #drdf[retname1]=np.exp(drdf["log(Daily Ret)"].expanding(min_periods=5).sum())-1.0
2308
- first_close=drdf.head(1)['Close'].values[0]
2309
- drdf[retname1]=drdf['Close']/first_close-1
2310
- drdf[retname2]=drdf[retname1]*100.0
2311
-
2312
- #计算累计收益率:基于调整收盘价
2313
- retname3="Exp Adj Ret"
2314
- retname4="Exp Adj Ret%"
2315
- #drdf[retname3]=np.exp(drdf["log(Daily Adj Ret)"].expanding(min_periods=1).sum())-1.0
2316
- #drdf[retname3]=np.exp(drdf["log(Daily Adj Ret)"].expanding(min_periods=5).sum())-1.0
2317
- first_aclose=drdf.head(1)['Adj Close'].values[0]
2318
- drdf[retname3]=drdf['Adj Close']/first_aclose-1
2319
- drdf[retname4]=drdf[retname3]*100.0
2320
-
2321
- return drdf
2322
-
2323
- if __name__ =="__main__":
2324
- ticker='000002.SZ'
2325
- basedate="2019-1-1"
2326
- erdf=calc_expanding_return(prdf,basedate)
2327
-
2328
- #==============================================================================
2329
- def rolling_price_volatility(df, period="Weekly"):
2330
- """
2331
- 功能:基于单个证券价格的期间调整标准差, 计算其滚动期间价格风险
2332
- 输入:
2333
- 单个证券的日价格数据集df。
2334
- 期间类型period,默认为每周。
2335
- 输出:期间滚动价格风险序列,按照日期升序排列。
2336
- """
2337
- #检查period类型
2338
- periodlist = ["Weekly","Monthly","Quarterly","Annual"]
2339
- if not (period in periodlist):
2340
- print("*** 错误#1(calc_rolling_volatility),仅支持期间类型:",periodlist)
2341
- return None
2342
-
2343
- #换算期间对应的实际交易天数
2344
- perioddays=[5,21,63,252]
2345
- rollingnum=perioddays[periodlist.index(period)]
2346
-
2347
- #计算滚动期间的调整标准差价格风险:基于收盘价
2348
- retname1=period+" Price Volatility"
2349
- import numpy as np
2350
- #df[retname1]=df["Close"].rolling(rollingnum).apply(lambda x: np.std(x,ddof=1)/np.mean(x)*np.sqrt(len(x)))
2351
- df[retname1]=df["Close"].rolling(rollingnum,min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
2352
-
2353
- #计算滚动期间的调整标准差价格风险:基于调整收盘价
2354
- retname3=period+" Adj Price Volatility"
2355
- #df[retname3]=df["Adj Close"].rolling(rollingnum).apply(lambda x: np.std(x,ddof=1)/np.mean(x)*np.sqrt(len(x)))
2356
- df[retname3]=df["Adj Close"].rolling(rollingnum,min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
2357
-
2358
- return df
2359
-
2360
- if __name__ =="__main__":
2361
- period="Weekly"
2362
- df=get_price('000002.SZ','2018-1-1','2020-3-16')
2363
- vdf=rolling_price_volatility(df, period)
2364
-
2365
- #==============================================================================
2366
- def expanding_price_volatility(df0,basedate):
2367
- """
2368
- 功能:基于日价格数据集,从起始日期开始到结束日期调整价格风险的扩展窗口序列。
2369
- 输入:
2370
- 日价格数据集df。
2371
- 输出:期间扩展调整价格风险序列,按照日期升序排列。
2372
- """
2373
- import pandas as pd
2374
- basedate_pd=pd.to_datetime(basedate)
2375
- df=df0[df0.index >= basedate_pd]
2376
-
2377
- #计算扩展窗口调整价格风险:基于收盘价
2378
- retname1="Exp Price Volatility"
2379
- import numpy as np
2380
- #df[retname1]=df["Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x)*np.sqrt(len(x)))
2381
- df[retname1]=df["Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
2382
- #df[retname1]=df["Close"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
2383
-
2384
- #计算扩展窗口调整价格风险:基于调整收盘价
2385
- retname3="Exp Adj Price Volatility"
2386
- #df[retname3]=df["Adj Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x)*np.sqrt(len(x)))
2387
- df[retname3]=df["Adj Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
2388
- #df[retname3]=df["Adj Close"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
2389
-
2390
- return df
2391
-
2392
- if __name__ =="__main__":
2393
- df=get_price('000002.SZ','2018-1-1','2020-3-16')
2394
- evdf=expanding_price_volatility(df)
2395
-
2396
-
2397
- #==============================================================================
2398
- if __name__ =="__main__":
2399
- ticker='301161.SZ';
2400
- ticker='600519.SS';
2401
- start='2022-1-1'; end='2024-9-30'
2402
- pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start,todate=end)
2403
- rardf1=calc_daily_return(pricedf)
2404
- rardf2=calc_rolling_return(rardf1,period=ret_period)
2405
- df=rardf2
2406
- period="Annual"
2407
-
2408
- def rolling_ret_volatility(df, period="Weekly"):
2409
- """
2410
- 功能:基于单个证券的期间收益率, 计算其滚动收益率波动风险
2411
- 输入:
2412
- 单个证券的期间收益率数据集df。
2413
- 期间类型period,默认为每周。
2414
- 输出:滚动收益率波动风险序列,按照日期升序排列。
2415
- """
2416
- #检查period类型
2417
- periodlist = ["Weekly","Monthly","Quarterly","Annual"]
2418
- if not (period in periodlist):
2419
- print(" #Warning(rolling_ret_volatility), only support",periodlist)
2420
- return None
2421
-
2422
- #换算期间对应的实际交易天数
2423
- perioddays=[5,21,63,252]
2424
- rollingnum=perioddays[periodlist.index(period)]
2425
-
2426
- #计算滚动标准差:基于普通收益率
2427
- periodret=period+" Ret"
2428
- retname1=period+" Ret Volatility"
2429
- retname2=retname1+'%'
2430
- import numpy as np
2431
- #min_periods=1: 一些股票上市期间短,可能出现数据量不足,进而导致年度波动率全为空
2432
- df[retname1]=df[periodret].rolling(window=rollingnum,min_periods=1).apply(lambda x: np.std(x,ddof=1))
2433
- if df[retname1].isnull().all():
2434
- print(" #Warning(rolling_ret_volatility): "+retname1+" is all nan becos of insufficient data for period "+period)
2435
-
2436
- df[retname2]=df[retname1]*100.0
2437
-
2438
- #计算滚动标准差:基于调整收益率
2439
- periodadjret=period+" Adj Ret"
2440
- retname3=period+" Adj Ret Volatility"
2441
- retname4=retname3+'%'
2442
- df[retname3]=df[periodadjret].rolling(window=rollingnum,min_periods=1).apply(lambda x: np.std(x,ddof=1))
2443
- df[retname4]=df[retname3]*100.0
2444
-
2445
- return df
2446
-
2447
- if __name__ =="__main__":
2448
- period="Weekly"
2449
- pricedf=get_price('000002.SZ','2018-1-1','2020-3-16')
2450
- retdf=calc_daily_return(pricedf)
2451
- vdf=rolling_ret_volatility(retdf, period)
2452
-
2453
- #==============================================================================
2454
- def expanding_ret_volatility(df0,basedate):
2455
- """
2456
- 功能:基于日收益率数据集,从起始日期basedate开始的收益率波动风险扩展窗口序列。
2457
- 输入:
2458
- 日收益率数据集df。
2459
- 输出:扩展调整收益率波动风险序列,按照日期升序排列。
2460
- """
2461
- df0["Daily Ret"]=df0['Close'].pct_change()
2462
- df0["Daily Adj Ret"]=df0['Adj Close'].pct_change()
2463
-
2464
- import pandas as pd
2465
- basedate_pd=pd.to_datetime(basedate)
2466
- df=df0[df0.index >= basedate_pd]
2467
-
2468
- #计算扩展窗口调整收益率波动风险:基于普通收益率
2469
- retname1="Exp Ret Volatility"
2470
- retname2="Exp Ret Volatility%"
2471
- import numpy as np
2472
-
2473
- #df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)*np.sqrt(len(x)))
2474
- df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1))
2475
- #df[retname1]=df["Daily Ret"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1))
2476
- df[retname2]=df[retname1]*100.0
2477
-
2478
- #计算扩展窗口调整收益率风险:基于调整收益率
2479
- retname3="Exp Adj Ret Volatility"
2480
- retname4="Exp Adj Ret Volatility%"
2481
- #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)*np.sqrt(len(x)))
2482
- df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1))
2483
- #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1))
2484
- df[retname4]=df[retname3]*100.0
2485
-
2486
- return df
2487
-
2488
- if __name__ =="__main__":
2489
- basedate='2019-1-1'
2490
- pricedf=get_price('000002.SZ','2018-1-1','2020-3-16')
2491
- retdf=calc_daily_return(pricedf)
2492
- evdf=expanding_ret_volatility(retdf,'2019-1-1')
2493
-
2494
- #==============================================================================
2495
- def lpsd(ds):
2496
- """
2497
- 功能:基于给定数据序列计算其下偏标准差。
2498
- 输入:
2499
- 数据序列ds。
2500
- 输出:序列的下偏标准差。
2501
- """
2502
- import numpy as np
2503
- #若序列长度为0则直接返回数值型空值
2504
- if len(ds) == 0: return np.nan
2505
-
2506
- #求均值
2507
- import numpy as np
2508
- miu=np.mean(ds)
2509
-
2510
- #计算根号内的下偏平方和
2511
- sum=0; ctr=0
2512
- for s in list(ds):
2513
- if s < miu:
2514
- sum=sum+pow((s-miu),2)
2515
- ctr=ctr+1
2516
-
2517
- #下偏标准差
2518
- if ctr > 1:
2519
- result=np.sqrt(sum/(ctr-1))
2520
- elif ctr == 1: result=np.nan
2521
- else: result=np.nan
2522
-
2523
- return result
2524
-
2525
- if __name__ =="__main__":
2526
- df=get_price("000002.SZ","2020-1-1","2020-3-16")
2527
- print(lpsd(df['Close']))
2528
-
2529
- #==============================================================================
2530
- def rolling_ret_lpsd(df, period="Weekly"):
2531
- """
2532
- 功能:基于单个证券期间收益率, 计算其滚动收益率损失风险。
2533
- 输入:
2534
- 单个证券的期间收益率数据集df。
2535
- 期间类型period,默认为每周。
2536
- 输出:滚动收益率的下偏标准差序列,按照日期升序排列。
2537
- """
2538
- #检查period类型
2539
- periodlist = ["Weekly","Monthly","Quarterly","Annual"]
2540
- if not (period in periodlist):
2541
- print("*** 错误#1(rolling_ret_lpsd),仅支持期间类型:",periodlist)
2542
- return None
2543
-
2544
- #换算期间对应的实际交易天数
2545
- perioddays=[5,21,63,252]
2546
- rollingnum=perioddays[periodlist.index(period)]
2547
-
2548
- #计算滚动下偏标准差:基于普通收益率
2549
- periodret=period+" Ret"
2550
- retname1=period+" Ret LPSD"
2551
- retname2=retname1+'%'
2552
- #import numpy as np
2553
- df[retname1]=df[periodret].rolling(rollingnum,min_periods=1).apply(lambda x: lpsd(x))
2554
- df[retname2]=df[retname1]*100.0
2555
-
2556
- #计算滚动下偏标准差:基于调整收益率
2557
- periodadjret=period+" Adj Ret"
2558
- retname3=period+" Adj Ret LPSD"
2559
- retname4=retname3+'%'
2560
- df[retname3]=df[periodadjret].rolling(rollingnum,min_periods=1).apply(lambda x: lpsd(x))
2561
- df[retname4]=df[retname3]*100.0
2562
-
2563
- return df
2564
-
2565
- if __name__ =="__main__":
2566
- period="Weekly"
2567
- pricedf=get_price('000002.SZ','2018-1-1','2020-3-16')
2568
- retdf=calc_daily_return(pricedf)
2569
- vdf=rolling_ret_lpsd(retdf, period)
2570
-
2571
- #==============================================================================
2572
- def expanding_ret_lpsd(df0,basedate):
2573
- """
2574
- 功能:基于日收益率数据集,从起始日期basedate开始的收益率损失风险扩展窗口序列。
2575
- 输入:
2576
- 日收益率数据集df。
2577
- 输出:扩展调整收益率波动风险序列,按照日期升序排列。
2578
- """
2579
- df0["Daily Ret"]=df0['Close'].pct_change()
2580
- df0["Daily Adj Ret"]=df0['Adj Close'].pct_change()
2581
-
2582
- import pandas as pd
2583
- basedate_pd=pd.to_datetime(basedate)
2584
- df=df0[df0.index >= basedate_pd]
2585
-
2586
- #计算扩展窗口调整收益率下偏标准差:基于普通收益率
2587
- retname1="Exp Ret LPSD"
2588
- retname2=retname1+'%'
2589
- import numpy as np
2590
- #df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x)*np.sqrt(len(x)))
2591
- df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x))
2592
- #df[retname1]=df["Daily Ret"].expanding(min_periods=5).apply(lambda x: lpsd(x))
2593
- df[retname2]=df[retname1]*100.0
2594
-
2595
- #计算扩展窗口调整下偏标准差:基于调整收益率
2596
- retname3="Exp Adj Ret LPSD"
2597
- retname4=retname3+'%'
2598
- #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x)*np.sqrt(len(x)))
2599
- df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x))
2600
- #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=5).apply(lambda x: lpsd(x))
2601
- df[retname4]=df[retname3]*100.0
2602
-
2603
- return df
2604
-
2605
- if __name__ =="__main__":
2606
- basedate='2019-1-1'
2607
- pricedf=get_price('000002.SZ','2018-1-1','2020-3-16')
2608
- retdf=calc_daily_return(pricedf)
2609
- evdf=expanding_ret_lpsd(retdf,'2019-1-1')
2610
- #==============================================================================
2611
- if __name__ =="__main__":
2612
- portfolio={'Market':('US','^GSPC'),'AAPL':1}
2613
- portfolio={'Market':('China','^HSI'),'0823.HK':1.0}
2614
- portfolio={'Market':('China','000001.SS'),'000661.SZ':2,'603392.SS':3,'300601.SZ':4}
2615
- portfolio={'Market':('US','^SPX'),'^SPX':1}
2616
-
2617
- fromdate='2019-7-19'
2618
- todate='2020-7-20'
2619
-
2620
- market={"Market":("China","000300.SS","我的地产组合")}
2621
- stocks1={"600048.SS":.4,"001979":.3}
2622
- stocks2={"600515.SS":.2,"600895":.1}
2623
- portfolio=dict(market,**stocks1,**stocks2)
2624
-
2625
- fromdate="2024-1-1"; todate="2024-11-25"
2626
- adj=False
2627
- source='auto'
2628
-
2629
- df=get_portfolio_prices(portfolio,fromdate,todate,adj=False,source='auto')
2630
-
2631
- def get_portfolio_prices(portfolio,fromdate,todate,adj=True,source='auto',RF=0):
2632
- """
2633
- 功能:抓取投资组合portfolio的每日价值和FF3各个因子
2634
- 输入:投资组合portfolio,开始日期,结束日期
2635
- fromdate: 样本开始日期。格式:'YYYY-MM-DD'
2636
- todate: 样本结束日期。既可以是今天日期,也可以是一个历史日期
2637
-
2638
- 输出:投资组合的价格序列,按照日期升序排列
2639
- """
2640
-
2641
- #解构投资组合
2642
- _,mktidx,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
2643
-
2644
- #检查股票列表个数与份额列表个数是否一致
2645
- if len(tickerlist) != len(sharelist):
2646
- print(" #Error(get_portfolio_prices): numbers of stocks and shares mismatch.")
2647
- return None
2648
-
2649
- #抓取股票价格
2650
- #print(" Searching portfolio prices for",tickerlist,'from',fromdate,'to',todate)
2651
- p=get_prices(tickerlist,fromdate,todate,adj=adj,source=source)
2652
- if p is None:
2653
- print(" #Error(get_portfolio_prices): information inaccessible for",tickerlist)
2654
- return None
2655
-
2656
- #print(" Retrieved",len(p),'records of portfolio records')
2657
- import pandas as pd
2658
- if len(sharelist) > 0:
2659
- #计算投资组合的开盘价
2660
- op=pd.DataFrame(p['Open'])
2661
- #计算投资组合的价值
2662
- try:
2663
- oprice=pd.DataFrame(op.dot(sharelist))
2664
- except:
2665
- print(" #Error(get_portfolio_prices): Dot product shape mismatch for open price",tickerlist)
2666
- return None
2667
- oprice.rename(columns={0: 'Open'}, inplace=True)
2668
-
2669
- #计算投资组合的收盘价
2670
- cp=pd.DataFrame(p['Close'])
2671
- #计算投资组合的价值
2672
- cprice=pd.DataFrame(cp.dot(sharelist))
2673
- cprice.rename(columns={0: 'Close'}, inplace=True)
2674
-
2675
- #计算投资组合的调整收盘价
2676
- acp=pd.DataFrame(p['Adj Close'])
2677
- #计算投资组合的价值
2678
- acprice=pd.DataFrame(acp.dot(sharelist))
2679
- acprice.rename(columns={0: 'Adj Close'}, inplace=True)
2680
-
2681
- #计算投资组合的交易量
2682
- vol=pd.DataFrame(p['Volume'])
2683
- #计算投资组合的价值
2684
- pfvol=pd.DataFrame(vol.dot(sharelist))
2685
- pfvol.rename(columns={0: 'Volume'}, inplace=True)
2686
-
2687
- #计算投资组合的交易金额
2688
- if len(sharelist) > 1:
2689
- for t in tickerlist:
2690
- p['Amount',t]=p['Close',t]*p['Volume',t]
2691
- elif len(sharelist) == 1:
2692
- p['Amount']=p['Close']*p['Volume']
2693
- amt=pd.DataFrame(p['Amount'])
2694
-
2695
- #计算投资组合的价值
2696
- pfamt=pd.DataFrame(amt.dot(sharelist))
2697
- pfamt.rename(columns={0: 'Amount'}, inplace=True)
2698
-
2699
- #合成开盘价、收盘价、调整收盘价、交易量和交易金额
2700
- pf1=pd.merge(oprice,cprice,how='inner',left_index=True,right_index=True)
2701
- pf2=pd.merge(pf1,acprice,how='inner',left_index=True,right_index=True)
2702
- pf3=pd.merge(pf2,pfvol,how='inner',left_index=True,right_index=True)
2703
- pf4=pd.merge(pf3,pfamt,how='inner',left_index=True,right_index=True)
2704
- """
2705
- else:
2706
- p['Amount']=p['Close']*p['Volume']
2707
- pf4=p
2708
- """
2709
- pf4['Ret%']=pf4['Close'].pct_change()*100.0
2710
- pf4['Ret-RF']=pf4['Ret%'] - RF*100/365
2711
-
2712
- #获得期间的市场收益率
2713
- try:
2714
- m=get_prices(mktidx,fromdate,todate)
2715
- except:
2716
- print(" #Error(get_portfolio_prices): info inaccesible for market index",mktidx)
2717
- return None
2718
-
2719
- m['Mkt-RF']=m['Close'].pct_change()*100.0 - RF*100/365
2720
- m['RF']=RF*100/365
2721
- rf_df=m[['Mkt-RF','RF']]
2722
-
2723
- #合并pf4与rf_df
2724
- prices=pd.merge(pf4,rf_df,how='left',left_index=True,right_index=True)
2725
-
2726
- #提取日期和星期几
2727
- #prices['Date']=(prices.index).strftime("%Y-%m-%d")
2728
- prices['Date']=prices.index
2729
- prices['Date']=prices['Date'].apply(lambda x: x.strftime("%Y-%m-%d"))
2730
-
2731
- prices['Weekday']=prices.index.weekday+1
2732
-
2733
- prices['Portfolio']=str(tickerlist)
2734
- prices['Shares']=str(sharelist)
2735
-
2736
- prices['Adjustment']=adj
2737
- try:
2738
- prices['Adjustment']=prices.apply(lambda x: \
2739
- False if x['Close']==x['Adj Close'] else True, axis=1)
2740
- except: pass
2741
-
2742
- pfdf=prices[['Portfolio','Shares','Date','Weekday', \
2743
- 'Open','Close','Adj Close','Adjustment', \
2744
- 'Volume','Amount','Ret%','Ret-RF','Mkt-RF','RF']]
2745
-
2746
- return pfdf
2747
-
2748
-
2749
- #==============================================================================
2750
- if __name__ =="__main__":
2751
- ticker='AAPL'
2752
-
2753
- def recent_stock_split(ticker):
2754
- """
2755
- 功能:显示股票最近一年的分拆历史
2756
- 输入:单一股票代码
2757
- 输出:最近一年的分拆历史
2758
- """
2759
- #获取今日日期
2760
- import datetime
2761
- today = datetime.date.today()
2762
- fromdate = date_adjust(today,-365)
2763
-
2764
- import yfinance as yf
2765
- stock = yf.Ticker(ticker)
2766
- try:
2767
- div=stock.splits
2768
- except:
2769
- print("#Error(recent_stock_split): no split info found for",ticker)
2770
- return None
2771
- if len(div)==0:
2772
- print("#Warning(recent_stock_split): no split info found for",ticker)
2773
- return None
2774
-
2775
- #过滤期间
2776
- div2=div[div.index >= fromdate]
2777
- if len(div2)==0:
2778
- print("#Warning(stock_split): no split info from",fromdate,'to',today)
2779
- return None
2780
-
2781
- #对齐打印
2782
- import pandas as pd
2783
- pd.set_option('display.max_columns', 1000)
2784
- pd.set_option('display.width', 1000)
2785
- pd.set_option('display.max_colwidth', 1000)
2786
-
2787
- divdf=pd.DataFrame(div2)
2788
- divdf['Index Date']=divdf.index
2789
- datefmt=lambda x : x.strftime('%Y-%m-%d')
2790
- divdf['Split Date']= divdf['Index Date'].apply(datefmt)
2791
-
2792
- #增加星期
2793
- from datetime import datetime
2794
- weekdayfmt=lambda x : x.isoweekday()
2795
- divdf['Weekdayiso']= divdf['Index Date'].apply(weekdayfmt)
2796
- wdlist=['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
2797
- wdfmt=lambda x : wdlist[x-1]
2798
- divdf['Weekday']= divdf['Weekdayiso'].apply(wdfmt)
2799
-
2800
- #增加序号
2801
- divdf['Seq']=divdf['Split Date'].rank(ascending=1)
2802
- divdf['Seq']=divdf['Seq'].astype('int')
2803
-
2804
- divdf['Splitint']=divdf['Stock Splits'].astype('int')
2805
- splitfmt=lambda x: "1:"+str(x)
2806
- divdf['Splits']=divdf['Splitint'].apply(splitfmt)
2807
-
2808
- divprt=divdf[['Seq','Split Date','Weekday','Splits']]
2809
-
2810
- print(text_lang("\n=== 近期股票分拆历史 ===","\n=== Recent Stock Split ==="))
2811
- print(text_lang("股票:","Stock:"),ticker,'\b,',ticker)
2812
- print(text_lang("期间:","Period:"),fromdate,"to",today)
2813
- divprt.columns=[text_lang('序号','No.'),text_lang('日期','Date'),text_lang('星期','Weekday'),text_lang('分拆比例','Split Ratio')]
2814
- print(divprt.to_string(index=False))
2815
-
2816
- import datetime
2817
- today = datetime.date.today()
2818
- print(text_lang("数据来源: 综合新浪/yahoo,","Data source: Yahoo Finance,"),today)
2819
-
2820
- return divdf
2821
-
2822
-
2823
- if __name__ =="__main__":
2824
- df=recent_stock_split('AAPL')
2825
-
2826
- #==============================================================================
2827
- if __name__ =="__main__":
2828
- ticker='AAPL'
2829
- get_last_close(ticker)
2830
-
2831
- def get_last_close(ticker):
2832
- """
2833
- 功能:从新浪/stooq抓取股票股价或指数价格或投资组合价值,使用pandas_datareader
2834
- 输入:股票代码或股票代码列表,开始日期,结束日期
2835
- ticker: 股票代码或者股票代码列表。
2836
- 大陆股票代码加上后缀.SZ或.SS,港股代码去掉前导0加后缀.HK
2837
- 输出:最新的收盘价和日期
2838
- """
2839
- #获取今日日期
2840
- import datetime
2841
- stoday = datetime.date.today()
2842
- fromdate = date_adjust(stoday,-30)
2843
- todate=str(stoday)
2844
-
2845
- #抓取新浪/stooq股票价格
2846
- try:
2847
- #price,found=get_price_1ticker_mixed(ticker,fromdate=fromdate,todate=todate,source='yahoo')
2848
- price,found=get_price_1ticker_mixed(ticker,fromdate=fromdate,todate=todate)
2849
- except:
2850
- print("\n #Error(get_last_close): failed in retrieving prices for",ticker)
2851
- return None,None
2852
- if price is None:
2853
- print("\n #Error(get_last_close): retrieved none info for",ticker)
2854
- return None,None
2855
- if len(price)==0:
2856
- print("\n #Error(get_last_close): retrieved empty info for",ticker)
2857
- return None,None
2858
-
2859
- price['date']=price.index
2860
- datecvt=lambda x:x.strftime("%Y-%m-%d")
2861
- price['date']=price['date'].apply(datecvt)
2862
- price.sort_values("date",inplace=True)
2863
-
2864
- #提取最新的日期和收盘价
2865
- lasttradedate=list(price['date'])[-1]
2866
- lasttradeclose=round(list(price['Close'])[-1],2)
2867
-
2868
- return lasttradedate,lasttradeclose
2869
-
2870
- if __name__ =="__main__":
2871
- get_last_close('AAPL')
2872
-
2873
- #==============================================================================
2874
-
2875
- if __name__=='__main__':
2876
- security={'Market':('US','^SPX','中概教培组合'),'EDU':0.4,'TAL':0.3,'TCTM':0.2}
2877
- security={'Market':('US','^SPX','China Edtraining'),'X01':0.4,'X02':0.3,'X03':0.2}
2878
- security={'Market':('China','000300.SS','China Edtraining'),'600519.SS':0.4,'000858.SZ':0.3,'600809.SS':0.2}
2879
- security={'Market':('China','auto','China Edtraining'),'600519.SS':0.4,'000858.SZ':0.3,'600809.SS':0.2}
2880
- security='600519.SS'
2881
-
2882
- start='2024-1-1'; end='2024-3-23'
2883
- source='auto'
2884
-
2885
- prices=get_price_security(security,start,end)
2886
-
2887
- def get_price_security(security,start,end,source='auto'):
2888
- """
2889
- 功能:获取股票或投资组合的价格
2890
- 经测试已经可以支持capm_beta2,risk_adjusted_return待测试?
2891
- """
2892
-
2893
- if isinstance(security,dict): #投资组合
2894
- scope,mktidx,tickerlist,sharelist,ticker_type=decompose_portfolio(security)
2895
- prices=get_price_portfolio(tickerlist,sharelist,start,end,source=source)
2896
-
2897
- pname=portfolio_name(security)
2898
- if prices is None:
2899
- print(" #Error(get_price_security): no price info retrieved for portfolio",pname)
2900
- return None
2901
- if len(prices) ==0:
2902
- print(" #Error(get_price_security): zero info retrieved for portfolio",pname)
2903
- return None
2904
- else: #股票或股票列表
2905
- prices=get_price(security,start,end,source=source)
2906
- if prices is None:
2907
- print(" #Error(get_price_security): no price info retrieved for",security)
2908
- return None
2909
- if len(prices) ==0:
2910
- print(" #Error(get_price_security): zero info retrieved for",security)
2911
- return None
2912
-
2913
- return prices
2914
-
2915
- #==============================================================================
2916
- if __name__=='__main__':
2917
- ticker='600519.SS'
2918
- ticker='000858.SZ'
2919
-
2920
- ticker='SH600519'
2921
- ticker='sh600519'
2922
- ticker='sz000858'
2923
-
2924
- ticker='sz600519'
2925
- ticker='sh000858'
2926
-
2927
- ticker='600519.SH'
2928
- ticker='600519.sh'
2929
- ticker='000858.sz'
2930
-
2931
- ticker='000858.sh'
2932
- ticker='600519.sz'
2933
-
2934
- ticker='600519'
2935
- ticker='000858'
2936
- ticker='600519.CN'
2937
- ticker='000858.CN'
2938
- ticker='801010.SW'
2939
- ticker='880410.ZZ'
2940
-
2941
- ticker='01210.HK'
2942
- ticker='AAPL'
2943
- ticker='6758.T'
2944
- ticker='SONA.F'
2945
-
2946
- ticker1_cvt2yahoo(ticker)
2947
-
2948
- def ticker1_cvt2yahoo(ticker):
2949
- """
2950
- 功能:将一只股票、基金、债券代码转换为siat内部默认的yahoo格式
2951
- 情形:后缀,前缀,无后缀和前缀
2952
- 注意:中证行业代码若为沪深交易所收藏的,仍以SS/SZ为后缀,不可用ZZ后缀
2953
- """
2954
- ticker1=ticker.upper() #转为大写
2955
-
2956
- #后缀
2957
- result,prefix,suffix=split_prefix_suffix(ticker1)
2958
- if suffix in ['SS','SH','SZ','BJ','CN','SW','ZZ'] and len(prefix)==6:
2959
- if suffix in ['SH']:
2960
- suffix1='SS'
2961
- elif suffix in ['CN']:
2962
- suffix1,_=china_security_identify(prefix)
2963
- else:
2964
- suffix1=suffix
2965
-
2966
- """
2967
- #检查是否搞错SS/SZ/BJ
2968
- if suffix1 in ['SS','SZ','BJ']:
2969
- suffix1,_=china_security_identify(prefix)
2970
- """
2971
- ticker2=prefix+'.'+suffix1
2972
- return ticker2
2973
-
2974
- #前缀
2975
- head2=ticker1[:2]
2976
- rest2=ticker1[2:]
2977
- if head2 in ['SH','SZ','BJ','SW','ZZ'] and len(rest2)==6:
2978
- #suffix1,_=china_security_identify(rest2)
2979
- if head2 in ['SH']:
2980
- suffix1='SS'
2981
- else:
2982
- suffix1=head2
2983
- """
2984
- #检查是否搞错SS/SZ/BJ
2985
- if suffix1 in ['SS','SZ','BJ']:
2986
- suffix1,_=china_security_identify(rest2)
2987
- """
2988
- ticker2=rest2+'.'+suffix1
2989
- return ticker2
2990
-
2991
- #无前后缀,6位数字,默认为A股
2992
- if is_all_digits(ticker1) and len(ticker1) == 6:
2993
- suffix1,_=china_security_identify(ticker1)
2994
- ticker2=ticker1+'.'+suffix1
2995
- return ticker2
2996
-
2997
- #其他:直接返回
2998
- return ticker1
2999
-
3000
- #==============================================================================
3001
- if __name__=='__main__':
3002
- ticker=['600519.SS','sz000858','002594.sz','aapl']
3003
-
3004
- tickers_cvt2yahoo(ticker)
3005
-
3006
- def tickers_cvt2yahoo(ticker):
3007
- """
3008
- 功能:将多只股票、基金、债券代码转换为siat内部默认的yahoo格式
3009
- """
3010
- #单个字符串:返回字符串
3011
- if isinstance(ticker,str):
3012
- result=ticker1_cvt2yahoo(ticker)
3013
- return result
3014
-
3015
- #列表:返回列表
3016
- if isinstance(ticker,list): #避免下面的循环出错
3017
- tickerlist=[]
3018
- for t in ticker:
3019
- t2=ticker1_cvt2yahoo(t)
3020
- tickerlist=tickerlist+[t2]
3021
-
3022
- result=tickerlist
3023
- return result
3024
-
3025
- #其他:直接返回
3026
- return ticker
3027
-
3028
- #==============================================================================
3029
- if __name__=='__main__':
3030
- ticker='SH600519'
3031
- ticker='sh600519'
3032
- ticker='sz000858'
3033
-
3034
- ticker='sz600519'
3035
- ticker='sh000858'
3036
-
3037
- ticker='600519.SH'
3038
- ticker='600519.sh'
3039
- ticker='000858.sz'
3040
-
3041
- ticker='000858.sh'
3042
- ticker='600519.sz'
3043
-
3044
- ticker='600519'
3045
- ticker='000858'
3046
- ticker='600519.CN'
3047
- ticker='000858.CN'
3048
- ticker='801010.SW'
3049
- ticker='880410.ZZ'
3050
-
3051
- ticker='sh149996'
3052
-
3053
- ticker='01210.HK'
3054
- ticker='AAPL'
3055
- ticker='6758.T'
3056
- ticker='SONA.F'
3057
-
3058
- ticker1_cvt2ak(ticker)
3059
-
3060
- def ticker1_cvt2ak(ticker):
3061
- """
3062
- 功能:将一只股票、基金、债券代码转换为akshare格式
3063
- 情形:后缀,前缀,无后缀和前缀
3064
- 注意:中证行业代码若为沪深交易所收藏的,仍以SS/SZ为后缀,不可用ZZ后缀
3065
- """
3066
- ticker1=ticker.upper() #转为大写
3067
-
3068
- #后缀
3069
- result,prefix,suffix=split_prefix_suffix(ticker1)
3070
- if suffix in ['SS','SH','SZ','BJ','CN'] and len(prefix)==6:
3071
- if suffix in ['SH','SS']: prefix1='sh'
3072
- if suffix in ['SZ']: prefix1='sz'
3073
- if suffix in ['BJ']: prefix1='bj'
3074
- if suffix in ['CN']:
3075
- suffix1,_=china_security_identify(prefix)
3076
- prefix1='sh'
3077
- if suffix1 in ['SS']: prefix1='sh'
3078
- if suffix1 in ['SZ']: prefix1='sz'
3079
- if suffix1 in ['BJ']: prefix1='bj'
3080
- """
3081
- #检查是否搞错SS/SZ/BJ
3082
- if suffix in ['SS','SH','SZ','BJ']:
3083
- suffix1,_=china_security_identify(prefix)
3084
- if suffix1 in ['SS','SH']: prefix1='sh'
3085
- if suffix1 == 'SZ': prefix1='sz'
3086
- if suffix1 == 'BJ': prefix1='bj'
3087
- """
3088
- ticker2=prefix1+prefix
3089
- return ticker2
3090
-
3091
- #前缀
3092
- head2=ticker1[:2]
3093
- rest2=ticker1[2:]
3094
- if head2 in ['SH','SS','SZ','BJ'] and len(rest2)==6:
3095
- if head2 in ['SH','SS']: prefix1='sh'
3096
- if head2 in ['SZ']: prefix1='sz'
3097
- if head2 in ['BJ']: prefix1='bj'
3098
-
3099
- """
3100
- #检查是否搞错SS/SZ/BJ
3101
- if head2 in ['SH','SS','SZ','BJ']:
3102
- suffix1,_=china_security_identify(rest2)
3103
- if suffix1 == 'SS': prefix1='sh'
3104
- if suffix1 == 'SZ': prefix1='sz'
3105
- if suffix1 == 'BJ': prefix1='bj'
3106
- """
3107
- ticker2=prefix1+rest2
3108
- return ticker2
3109
-
3110
- #无前后缀,6位数字,默认为A股
3111
- if is_all_digits(ticker1) and len(ticker1) == 6:
3112
- suffix1,_=china_security_identify(ticker1)
3113
- prefix1='sh'
3114
- if head2 in ['SH','SS']: prefix1='sh'
3115
- if head2 in ['SZ']: prefix1='sz'
3116
- if head2 in ['BJ']: prefix1='bj'
3117
-
3118
- ticker2=prefix1+ticker1
3119
- return ticker2
3120
-
3121
- #其他:直接返回
3122
- return ticker1
3123
-
3124
- #==============================================================================
3125
- if __name__=='__main__':
3126
- ticker=['600519.SS','sz000858','002594.sz','aapl']
3127
-
3128
- tickers_cvt2ak(ticker)
3129
-
3130
- def tickers_cvt2ak(ticker):
3131
- """
3132
- 功能:将多只股票、基金、债券代码转换为akshare格式
3133
- """
3134
- #单个字符串:返回字符串
3135
- if isinstance(ticker,str):
3136
- result=ticker1_cvt2ak(ticker)
3137
- return result
3138
-
3139
- #列表:返回列表
3140
- if isinstance(ticker,list): #避免下面的循环出错
3141
- tickerlist=[]
3142
- for t in ticker:
3143
- t2=ticker1_cvt2ak(t)
3144
- tickerlist=tickerlist+[t2]
3145
-
3146
- result=tickerlist
3147
- return result
3148
-
3149
- #其他:直接返回
3150
- return ticker
3151
-
3152
-
3153
- #==============================================================================
3154
- if __name__=='__main__':
3155
- s='123456'
3156
- s='123456.'
3157
- s='123456a'
3158
-
3159
- is_all_digits(s)
3160
-
3161
- def is_all_digits(s):
3162
- """
3163
- 功能:检查字符串s是否为全数字构成
3164
- """
3165
- import re
3166
- return bool(re.match(r'^\d+$', s))
3167
-
3168
- #==============================================================================
3169
- if __name__=='__main__':
3170
- ticker6='AAPL'
3171
- ticker6='01211'
3172
- ticker6='600519'
3173
- ticker6='149996'
3174
-
3175
- def china_security_identify(ticker6):
3176
- """
3177
- 功能:区分中国内地证券代码前缀,返回后缀SS/SZ/BJ
3178
- 情形:股票,基金,债券,指数
3179
- 注意:ticker6需为6位数字字符,目前仅限沪深京交易所,未包括期货期权交易所
3180
- """
3181
- suffix='SS'
3182
- stype='stock'
3183
-
3184
- #检查是否为6位数字字符
3185
- if not is_all_digits(ticker6) or len(ticker6) != 6:
3186
- suffix=''
3187
- stype=''
3188
- return suffix,stype
3189
-
3190
- head1=ticker6[:1]
3191
- head2=ticker6[:2]
3192
- head3=ticker6[:3]
3193
-
3194
- #股票代码
3195
- if head2 in ['60','68']: #上交所:60-主板,68-科创板
3196
- suffix='SS'
3197
- stype='stock'
3198
- return suffix,stype
3199
- if head2 in ['00','30']: #深交所:00-主板,30-创业板
3200
- suffix='SZ'
3201
- stype='stock'
3202
- return suffix,stype
3203
- if head1 in ['8']: #北交所
3204
- suffix='BJ'
3205
- stype='stock'
3206
- return suffix,stype
3207
-
3208
- #沪深基金
3209
- if head2 in ['50','51']: #上交所:50-封闭式,51-ETF
3210
- suffix='SS'
3211
- stype='fund'
3212
- return suffix,stype
3213
- if head2 in ['15','16','18']: #深交所:15-ETF,16-LOF,18-封闭式
3214
- suffix='SZ'
3215
- stype='fund'
3216
- return suffix,stype
3217
-
3218
- #沪深债券
3219
- if head3 in ['271','270','240','188','185','184','175','163','155','152', \
3220
- '143','138','137','136','127','124','122','118','115','113', \
3221
- '100','020','019','018','010']:
3222
- suffix='SS'
3223
- stype='bond'
3224
- return suffix,stype
3225
-
3226
- #有重复
3227
- if head3 in ['149','148','133','128','127','123','114','112','111','110', \
3228
- '108','102','101','100']:
3229
- suffix='SZ'
3230
- stype='bond'
3231
- return suffix,stype
3232
-
3233
- #沪深B股
3234
- if head3 in ['900']:
3235
- suffix='SS'
3236
- stype='stockb'
3237
- return suffix,stype
3238
- if head3 in ['200']:
3239
- suffix='SZ'
3240
- stype='stockb'
3241
- return suffix,stype
3242
-
3243
- #其他
3244
- return '',''
3245
-
3246
- #==============================================================================
3247
- if __name__=='__main__':
3248
- ticker='850831.SW'
3249
-
3250
- start='2024-1-1'
3251
- end='2024-4-4'
3252
- info_types=['Close','Volume']
3253
-
3254
- df3=fetch_price_swindex(ticker,start,end)
3255
-
3256
- def fetch_price_swindex(ticker,start,end,info_types=['Close','Volume'],adjust=-2*365):
3257
- """
3258
- 功能:获取申万行业指数的信息
3259
- ticker:申万行业指数
3260
- start,end:日期期间
3261
- info_types:信息测度,默认['Close'],还可以为['Close','Open','High','Low',
3262
- 'Volume','Adj Close']
3263
- 特点:为compare_indicator使用,包括指数名称
3264
-
3265
- """
3266
- df=None
3267
-
3268
- # 检查日期期间的合理性
3269
- result,startpd,endpd=check_period(start,end)
3270
- if not result:
3271
- #print(" #Error(fetch_price_swindex): invalid date period between",start,"and",end)
3272
- return None
3273
-
3274
- start1=date_adjust(start,adjust=adjust)
3275
- _,start1pd,_=check_period(start1,end)
3276
-
3277
- import akshare as ak
3278
- if len(ticker)==6:
3279
- ticker=ticker+'.SW'
3280
- ticker6=ticker[:6]
3281
- try:
3282
- # 注意:如果失败,尝试升级akshare
3283
- prices= ak.index_hist_sw(symbol=ticker6,period="day")
3284
- except:
3285
- try:
3286
- dft = ak.index_hist_fund_sw(symbol=ticker6,period="day")
3287
- dft['代码']=ticker6
3288
- dft['收盘']=dft['收盘指数']
3289
- dft['开盘']=dft['收盘指数']
3290
- dft['最高']=dft['收盘指数']
3291
- dft['最低']=dft['收盘指数']
3292
- dft['成交量']=0; dft['成交额']=0
3293
-
3294
- prices=dft
3295
- except:
3296
- print(" #Error(fetch_price_swindex): failed to fetch prices for",ticker)
3297
- return None
3298
-
3299
- found=df_have_data(prices)
3300
- if found not in ['Found','Empty']:
3301
- pass
3302
- return df
3303
-
3304
- #强制修改列名
3305
- #prices.columns=['Code','Date','Close','Open','High','Low','Volume','Amount']
3306
- prices.rename(columns={'代码':'Code','日期':'Date','收盘':'Close','开盘':'Open', \
3307
- '最高':'High','最低':'Low','成交量':'Volume','成交额':'Amount'}, inplace=True)
3308
-
3309
- million=1000000
3310
- prices['Volume']=prices['Volume']*million
3311
- prices['Amount']=prices['Amount']*million
3312
-
3313
- import pandas as pd
3314
- prices['date']=pd.to_datetime(prices['Date'])
3315
- prices.set_index('date',inplace=True)
3316
-
3317
- prices2=prices[(prices.index >= start1pd) & (prices.index <= endpd)]
3318
-
3319
-
3320
- if isinstance(info_types,str):
3321
- typelist=[info_types]
3322
- else:
3323
- typelist=info_types
3324
-
3325
- import pandas as pd
3326
- df=pd.DataFrame()
3327
-
3328
- for t in typelist:
3329
- try:
3330
- df[t]=prices2[t]
3331
- except:
3332
- continue
3333
-
3334
- collist=list(df)
3335
- pcollist=['Open','High','Low','Adj Close']
3336
- for p in pcollist:
3337
- if p not in collist:
3338
- df[p]=df['Close']
3339
-
3340
- df['Code']=ticker
3341
- df['Type']='swindex'
3342
- df['Name']=ticker_name(ticker)
3343
-
3344
- #print(" Successfully retrieved",len(df),"records for sw index",ticker)
3345
-
3346
- return df
3347
-
3348
- #==============================================================================
3349
- if __name__=='__main__':
3350
- ticker='sh018003'
3351
- fromdate='2024-1-1'
3352
- todate='2024-4-17'
3353
- trend_type='净值'
3354
-
3355
- f=get_price_oef_china(ticker,fromdate,todate)
3356
-
3357
- def get_price_oef_china(ticker,fromdate,todate):
3358
- """
3359
- 功能:单纯获取中国开放式基金的单位净值趋势
3360
- """
3361
- #检查日期
3362
- result,start,end=check_period(fromdate,todate)
3363
- if not result:
3364
- print(" #Error(get_price_oef_china): invalid date period:",fromdate,todate)
3365
- return None
3366
-
3367
- #print("Searching for open-ended fund (OEF) trend info in China ...")
3368
- import akshare as ak
3369
-
3370
- fund1=ticker[:6]; fund2=ticker[-6:]
3371
- if fund1.isdigit():
3372
- fund=fund1
3373
- elif fund2.isdigit():
3374
- fund=fund2
3375
- else:
3376
- fund=ticker
3377
-
3378
- fund_name=ticker_name(fund,'fund')
3379
-
3380
- #单位净值
3381
- found='None'; df2=None
3382
- try:
3383
- df1 = ak.fund_open_fund_info_em(fund, indicator="单位净值走势")
3384
- df1.rename(columns={'净值日期':'date','单位净值':'Close'}, inplace=True)
3385
- df1['Open']=df1['High']=df1['Low']=df1['Close']
3386
- df1['Volume']=0
3387
- df1['name']=ticker_name(fund,'fund')
3388
-
3389
- df1['date']=df1['date'].apply(lambda x: pd.to_datetime(x))
3390
- df1.set_index(['date'],inplace=True)
3391
-
3392
- df2=df1[['Open','Close','High','Low','Volume','name']]
3393
- df2=df2[(df2.index >= start) & (df2.index <= end)]
3394
-
3395
- if len(df2)==0:
3396
- found='Empty'
3397
- else:
3398
- found='Found'
3399
- except:
3400
- pass
3401
-
3402
- return df2
3403
-
3404
- #==============================================================================
3405
-
3406
- #==============================================================================
3407
- #==============================================================================
3408
- #==============================================================================