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.
- siat/__init__.py +0 -0
- siat/allin.py +0 -0
- siat/assets_liquidity.py +0 -0
- siat/beta_adjustment.py +0 -0
- siat/beta_adjustment_china.py +0 -0
- siat/blockchain.py +0 -0
- siat/bond.py +0 -0
- siat/bond_base.py +0 -0
- siat/bond_china.py +0 -0
- siat/bond_zh_sina.py +0 -0
- siat/capm_beta.py +0 -0
- siat/capm_beta2.py +0 -0
- siat/compare_cross.py +0 -0
- siat/copyrights.py +0 -0
- siat/cryptocurrency.py +0 -0
- siat/economy.py +0 -0
- siat/economy2.py +0 -0
- siat/esg.py +0 -0
- siat/event_study.py +0 -0
- siat/exchange_bond_china.pickle +0 -0
- siat/fama_french.py +0 -0
- siat/fin_stmt2_yahoo.py +0 -0
- siat/financial_base.py +0 -0
- siat/financial_statements.py +0 -0
- siat/financials.py +0 -0
- siat/financials2.py +0 -0
- siat/financials_china.py +0 -0
- siat/financials_china2.py +0 -0
- siat/fund.py +0 -0
- siat/fund_china.pickle +0 -0
- siat/fund_china.py +0 -0
- siat/future_china.py +0 -0
- siat/google_authenticator.py +0 -0
- siat/grafix.py +0 -0
- siat/holding_risk.py +0 -0
- siat/luchy_draw.py +0 -0
- siat/market_china.py +0 -0
- siat/markowitz.py +0 -0
- siat/markowitz2.py +0 -0
- siat/markowitz2_20250704.py +0 -0
- siat/markowitz2_20250705.py +0 -0
- siat/markowitz_simple.py +0 -0
- siat/ml_cases.py +0 -0
- siat/ml_cases_example.py +0 -0
- siat/option_china.py +0 -0
- siat/option_pricing.py +0 -0
- siat/other_indexes.py +0 -0
- siat/risk_adjusted_return.py +0 -0
- siat/risk_adjusted_return2.py +0 -0
- siat/risk_evaluation.py +0 -0
- siat/risk_free_rate.py +0 -0
- siat/sector_china.py +0 -0
- siat/security_price2.py +0 -0
- siat/security_prices.py +40 -2
- siat/security_trend.py +0 -0
- siat/security_trend2.py +0 -0
- siat/stock.py +0 -0
- siat/stock_advice_linear.py +0 -0
- siat/stock_base.py +0 -0
- siat/stock_china.py +0 -0
- siat/stock_info.pickle +0 -0
- siat/stock_prices_kneighbors.py +0 -0
- siat/stock_prices_linear.py +0 -0
- siat/stock_profile.py +0 -0
- siat/stock_technical.py +0 -0
- siat/stooq.py +0 -0
- siat/transaction.py +0 -0
- siat/translate.py +0 -0
- siat/valuation.py +0 -0
- siat/valuation_china.py +0 -0
- siat/var_model_validation.py +0 -0
- siat/yf_name.py +0 -0
- {siat-3.10.132.dist-info/licenses → siat-3.10.133.dist-info}/LICENSE +0 -0
- {siat-3.10.132.dist-info → siat-3.10.133.dist-info}/METADATA +232 -235
- siat-3.10.133.dist-info/RECORD +78 -0
- {siat-3.10.132.dist-info → siat-3.10.133.dist-info}/WHEEL +1 -1
- {siat-3.10.132.dist-info → siat-3.10.133.dist-info}/top_level.txt +0 -1
- build/lib/build/lib/siat/__init__.py +0 -75
- build/lib/build/lib/siat/allin.py +0 -137
- build/lib/build/lib/siat/assets_liquidity.py +0 -915
- build/lib/build/lib/siat/beta_adjustment.py +0 -1058
- build/lib/build/lib/siat/beta_adjustment_china.py +0 -548
- build/lib/build/lib/siat/blockchain.py +0 -143
- build/lib/build/lib/siat/bond.py +0 -2900
- build/lib/build/lib/siat/bond_base.py +0 -992
- build/lib/build/lib/siat/bond_china.py +0 -100
- build/lib/build/lib/siat/bond_zh_sina.py +0 -143
- build/lib/build/lib/siat/capm_beta.py +0 -783
- build/lib/build/lib/siat/capm_beta2.py +0 -887
- build/lib/build/lib/siat/common.py +0 -5360
- build/lib/build/lib/siat/compare_cross.py +0 -642
- build/lib/build/lib/siat/copyrights.py +0 -18
- build/lib/build/lib/siat/cryptocurrency.py +0 -667
- build/lib/build/lib/siat/economy.py +0 -1471
- build/lib/build/lib/siat/economy2.py +0 -1853
- build/lib/build/lib/siat/esg.py +0 -536
- build/lib/build/lib/siat/event_study.py +0 -815
- build/lib/build/lib/siat/fama_french.py +0 -1521
- build/lib/build/lib/siat/fin_stmt2_yahoo.py +0 -982
- build/lib/build/lib/siat/financial_base.py +0 -1160
- build/lib/build/lib/siat/financial_statements.py +0 -598
- build/lib/build/lib/siat/financials.py +0 -2339
- build/lib/build/lib/siat/financials2.py +0 -1278
- build/lib/build/lib/siat/financials_china.py +0 -4433
- build/lib/build/lib/siat/financials_china2.py +0 -2212
- build/lib/build/lib/siat/fund.py +0 -629
- build/lib/build/lib/siat/fund_china.py +0 -3307
- build/lib/build/lib/siat/future_china.py +0 -551
- build/lib/build/lib/siat/google_authenticator.py +0 -47
- build/lib/build/lib/siat/grafix.py +0 -3636
- build/lib/build/lib/siat/holding_risk.py +0 -867
- build/lib/build/lib/siat/luchy_draw.py +0 -638
- build/lib/build/lib/siat/market_china.py +0 -1168
- build/lib/build/lib/siat/markowitz.py +0 -2363
- build/lib/build/lib/siat/markowitz2.py +0 -3150
- build/lib/build/lib/siat/markowitz2_20250704.py +0 -2969
- build/lib/build/lib/siat/markowitz2_20250705.py +0 -3158
- build/lib/build/lib/siat/markowitz_simple.py +0 -373
- build/lib/build/lib/siat/ml_cases.py +0 -2291
- build/lib/build/lib/siat/ml_cases_example.py +0 -60
- build/lib/build/lib/siat/option_china.py +0 -3069
- build/lib/build/lib/siat/option_pricing.py +0 -1925
- build/lib/build/lib/siat/other_indexes.py +0 -409
- build/lib/build/lib/siat/risk_adjusted_return.py +0 -1576
- build/lib/build/lib/siat/risk_adjusted_return2.py +0 -1900
- build/lib/build/lib/siat/risk_evaluation.py +0 -2218
- build/lib/build/lib/siat/risk_free_rate.py +0 -351
- build/lib/build/lib/siat/sector_china.py +0 -4140
- build/lib/build/lib/siat/security_price2.py +0 -727
- build/lib/build/lib/siat/security_prices.py +0 -3408
- build/lib/build/lib/siat/security_trend.py +0 -402
- build/lib/build/lib/siat/security_trend2.py +0 -646
- build/lib/build/lib/siat/stock.py +0 -4284
- build/lib/build/lib/siat/stock_advice_linear.py +0 -934
- build/lib/build/lib/siat/stock_base.py +0 -26
- build/lib/build/lib/siat/stock_china.py +0 -2095
- build/lib/build/lib/siat/stock_prices_kneighbors.py +0 -910
- build/lib/build/lib/siat/stock_prices_linear.py +0 -386
- build/lib/build/lib/siat/stock_profile.py +0 -707
- build/lib/build/lib/siat/stock_technical.py +0 -3305
- build/lib/build/lib/siat/stooq.py +0 -74
- build/lib/build/lib/siat/transaction.py +0 -347
- build/lib/build/lib/siat/translate.py +0 -5183
- build/lib/build/lib/siat/valuation.py +0 -1378
- build/lib/build/lib/siat/valuation_china.py +0 -2076
- build/lib/build/lib/siat/var_model_validation.py +0 -444
- build/lib/build/lib/siat/yf_name.py +0 -811
- build/lib/siat/__init__.py +0 -75
- build/lib/siat/allin.py +0 -137
- build/lib/siat/assets_liquidity.py +0 -915
- build/lib/siat/beta_adjustment.py +0 -1058
- build/lib/siat/beta_adjustment_china.py +0 -548
- build/lib/siat/blockchain.py +0 -143
- build/lib/siat/bond.py +0 -2900
- build/lib/siat/bond_base.py +0 -992
- build/lib/siat/bond_china.py +0 -100
- build/lib/siat/bond_zh_sina.py +0 -143
- build/lib/siat/capm_beta.py +0 -783
- build/lib/siat/capm_beta2.py +0 -887
- build/lib/siat/common.py +0 -5360
- build/lib/siat/compare_cross.py +0 -642
- build/lib/siat/copyrights.py +0 -18
- build/lib/siat/cryptocurrency.py +0 -667
- build/lib/siat/economy.py +0 -1471
- build/lib/siat/economy2.py +0 -1853
- build/lib/siat/esg.py +0 -536
- build/lib/siat/event_study.py +0 -815
- build/lib/siat/fama_french.py +0 -1521
- build/lib/siat/fin_stmt2_yahoo.py +0 -982
- build/lib/siat/financial_base.py +0 -1160
- build/lib/siat/financial_statements.py +0 -598
- build/lib/siat/financials.py +0 -2339
- build/lib/siat/financials2.py +0 -1278
- build/lib/siat/financials_china.py +0 -4433
- build/lib/siat/financials_china2.py +0 -2212
- build/lib/siat/fund.py +0 -629
- build/lib/siat/fund_china.py +0 -3307
- build/lib/siat/future_china.py +0 -551
- build/lib/siat/google_authenticator.py +0 -47
- build/lib/siat/grafix.py +0 -3636
- build/lib/siat/holding_risk.py +0 -867
- build/lib/siat/luchy_draw.py +0 -638
- build/lib/siat/market_china.py +0 -1168
- build/lib/siat/markowitz.py +0 -2363
- build/lib/siat/markowitz2.py +0 -3150
- build/lib/siat/markowitz2_20250704.py +0 -2969
- build/lib/siat/markowitz2_20250705.py +0 -3158
- build/lib/siat/markowitz_simple.py +0 -373
- build/lib/siat/ml_cases.py +0 -2291
- build/lib/siat/ml_cases_example.py +0 -60
- build/lib/siat/option_china.py +0 -3069
- build/lib/siat/option_pricing.py +0 -1925
- build/lib/siat/other_indexes.py +0 -409
- build/lib/siat/risk_adjusted_return.py +0 -1576
- build/lib/siat/risk_adjusted_return2.py +0 -1900
- build/lib/siat/risk_evaluation.py +0 -2218
- build/lib/siat/risk_free_rate.py +0 -351
- build/lib/siat/sector_china.py +0 -4140
- build/lib/siat/security_price2.py +0 -727
- build/lib/siat/security_prices.py +0 -3408
- build/lib/siat/security_trend.py +0 -402
- build/lib/siat/security_trend2.py +0 -646
- build/lib/siat/stock.py +0 -4284
- build/lib/siat/stock_advice_linear.py +0 -934
- build/lib/siat/stock_base.py +0 -26
- build/lib/siat/stock_china.py +0 -2095
- build/lib/siat/stock_prices_kneighbors.py +0 -910
- build/lib/siat/stock_prices_linear.py +0 -386
- build/lib/siat/stock_profile.py +0 -707
- build/lib/siat/stock_technical.py +0 -3305
- build/lib/siat/stooq.py +0 -74
- build/lib/siat/transaction.py +0 -347
- build/lib/siat/translate.py +0 -5183
- build/lib/siat/valuation.py +0 -1378
- build/lib/siat/valuation_china.py +0 -2076
- build/lib/siat/var_model_validation.py +0 -444
- build/lib/siat/yf_name.py +0 -811
- 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
|
-
#==============================================================================
|