siat 3.10.132__py3-none-any.whl → 3.11.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- siat/__init__.py +0 -0
- siat/allin.py +8 -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 +4 -4
- siat/common.py +9 -6
- 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 +55 -4
- 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 +1 -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 +8 -4
- siat/risk_evaluation.py +0 -0
- siat/risk_free_rate.py +0 -0
- siat/save2docx.py +345 -0
- siat/save2pdf.py +145 -0
- siat/sector_china.py +0 -0
- siat/security_price2.py +0 -0
- siat/security_prices.py +168 -6
- siat/security_trend.py +0 -0
- siat/security_trend2.py +2 -2
- siat/stock.py +11 -1
- 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.11.1.dist-info}/LICENSE +0 -0
- {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/METADATA +234 -235
- siat-3.11.1.dist-info/RECORD +80 -0
- {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/WHEEL +1 -1
- {siat-3.10.132.dist-info → siat-3.11.1.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
build/lib/siat/stock.py
DELETED
@@ -1,4284 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
"""
|
3
|
-
本模块功能:提供全球证券信息,应用层,以股票为基础,兼容雅虎财经上的大多数其他证券产品
|
4
|
-
所属工具包:证券投资分析工具SIAT
|
5
|
-
SIAT:Security Investment Analysis Tool
|
6
|
-
创建日期:2018年6月16日
|
7
|
-
最新修订日期:2020年8月28日
|
8
|
-
作者:王德宏 (WANG Dehong, Peter)
|
9
|
-
作者单位:北京外国语大学国际商学院
|
10
|
-
作者邮件:wdehong2000@163.com
|
11
|
-
版权所有:王德宏
|
12
|
-
用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
|
13
|
-
特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
|
14
|
-
"""
|
15
|
-
#==============================================================================
|
16
|
-
#关闭所有警告
|
17
|
-
import warnings; warnings.filterwarnings('ignore')
|
18
|
-
|
19
|
-
from siat.common import *
|
20
|
-
from siat.translate import *
|
21
|
-
from siat.grafix import *
|
22
|
-
from siat.security_prices import *
|
23
|
-
from siat.security_price2 import *
|
24
|
-
|
25
|
-
#==============================================================================
|
26
|
-
import matplotlib.pyplot as plt
|
27
|
-
#plt.rcParams['figure.figsize']=(12.8,7.2)
|
28
|
-
plt.rcParams['figure.figsize']=(12.8,6.4)
|
29
|
-
plt.rcParams['figure.dpi']=300
|
30
|
-
plt.rcParams['font.size'] = 13
|
31
|
-
plt.rcParams['xtick.labelsize']=11 #横轴字体大小
|
32
|
-
plt.rcParams['ytick.labelsize']=11 #纵轴字体大小
|
33
|
-
|
34
|
-
title_txt_size=16
|
35
|
-
ylabel_txt_size=14
|
36
|
-
xlabel_txt_size=14
|
37
|
-
legend_txt_size=14
|
38
|
-
|
39
|
-
import mplfinance as mpf
|
40
|
-
|
41
|
-
#处理绘图汉字乱码问题
|
42
|
-
import sys; czxt=sys.platform
|
43
|
-
if czxt in ['win32','win64']:
|
44
|
-
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
|
45
|
-
mpfrc={'font.family': 'SimHei'}
|
46
|
-
|
47
|
-
if czxt in ['darwin']: #MacOSX
|
48
|
-
plt.rcParams['font.family']= ['Heiti TC']
|
49
|
-
mpfrc={'font.family': 'Heiti TC'}
|
50
|
-
|
51
|
-
if czxt in ['linux']: #website Jupyter
|
52
|
-
plt.rcParams['font.family']= ['Heiti TC']
|
53
|
-
mpfrc={'font.family':'Heiti TC'}
|
54
|
-
|
55
|
-
# 解决保存图像时'-'显示为方块的问题
|
56
|
-
plt.rcParams['axes.unicode_minus'] = False
|
57
|
-
|
58
|
-
#设置绘图风格:关闭网格虚线
|
59
|
-
plt.rcParams['axes.grid']=False
|
60
|
-
|
61
|
-
#==============================================================================
|
62
|
-
def reset_plt():
|
63
|
-
"""
|
64
|
-
功能:用于使用完mplfinance可能造成的绘图乱码问题,但不能恢复默认绘图尺寸
|
65
|
-
"""
|
66
|
-
import matplotlib.pyplot as plt
|
67
|
-
if czxt in ['win32','win64']:
|
68
|
-
plt.rcParams['font.sans-serif'] = ['SimHei']
|
69
|
-
if czxt in ['darwin']:
|
70
|
-
plt.rcParams['font.sans-serif']=['Arial Unicode MS']
|
71
|
-
plt.rcParams['axes.unicode_minus'] = False
|
72
|
-
|
73
|
-
#尝试恢复绘图尺寸
|
74
|
-
#统一设定绘制的图片大小:数值为英寸,1英寸=100像素
|
75
|
-
#plt.rcParams['figure.figsize']=(12.8,7.2)
|
76
|
-
plt.rcParams['figure.figsize']=(12.8,6.4)
|
77
|
-
plt.rcParams['figure.dpi']=300
|
78
|
-
plt.rcParams['font.size'] = 13
|
79
|
-
plt.rcParams['xtick.labelsize']=11 #横轴字体大小
|
80
|
-
plt.rcParams['ytick.labelsize']=11 #纵轴字体大小
|
81
|
-
|
82
|
-
title_txt_size=16
|
83
|
-
ylabel_txt_size=14
|
84
|
-
xlabel_txt_size=14
|
85
|
-
legend_txt_size=14
|
86
|
-
|
87
|
-
#设置绘图风格:网格虚线
|
88
|
-
plt.rcParams['axes.grid']=False
|
89
|
-
#plt.rcParams['grid.color']='steelblue'
|
90
|
-
#plt.rcParams['grid.linestyle']='dashed'
|
91
|
-
#plt.rcParams['grid.linewidth']=0.5
|
92
|
-
#plt.rcParams['axes.facecolor']='whitesmoke'
|
93
|
-
|
94
|
-
return
|
95
|
-
|
96
|
-
#==============================================================================
|
97
|
-
#以下使用新浪/stooq数据源
|
98
|
-
#==============================================================================
|
99
|
-
|
100
|
-
if __name__ =="__main__":
|
101
|
-
ticker='AAPL'
|
102
|
-
ticker='00700.HK'
|
103
|
-
|
104
|
-
def get_profile(ticker):
|
105
|
-
"""
|
106
|
-
功能:按照证券代码抓取证券基本信息。
|
107
|
-
输入:证券代码ticker。
|
108
|
-
返回:证券基本信息,数据框
|
109
|
-
注意:经常出现无规律失败,放弃!!!
|
110
|
-
"""
|
111
|
-
#引入插件
|
112
|
-
try:
|
113
|
-
import yfinance as yf
|
114
|
-
except:
|
115
|
-
print(" #Error(get_profile): need to install yfinance")
|
116
|
-
return None
|
117
|
-
|
118
|
-
ticker1=ticker
|
119
|
-
result,prefix,suffix=split_prefix_suffix(ticker)
|
120
|
-
if result & (suffix=='HK'):
|
121
|
-
if len(prefix)==5:
|
122
|
-
ticker1=ticker[1:]
|
123
|
-
|
124
|
-
#抓取证券信息,结果为字典
|
125
|
-
tp=yf.Ticker(ticker1)
|
126
|
-
try:
|
127
|
-
dic=tp.info
|
128
|
-
except:
|
129
|
-
print(f" #Error(get_profile): failed to retrieve info for {ticker}")
|
130
|
-
print(" Solution: try get_stock_profile instead")
|
131
|
-
return None
|
132
|
-
|
133
|
-
if dic is None:
|
134
|
-
print(f" #Error(get_profile): none retrieved for {ticker}")
|
135
|
-
print(" Solution: upgrade yfinance if already accessible to Yahoo")
|
136
|
-
return None
|
137
|
-
|
138
|
-
|
139
|
-
#将字典转换为数据框
|
140
|
-
import pandas as pd
|
141
|
-
df=pd.DataFrame([dic])
|
142
|
-
|
143
|
-
#转换特殊列的内容:10位时间戳-->日期
|
144
|
-
cols=list(df)
|
145
|
-
import time
|
146
|
-
if ('exDividendDate' in cols):
|
147
|
-
df['exDividendDate']=int10_to_date(df['exDividendDate'][0])
|
148
|
-
if ('lastSplitDate' in cols):
|
149
|
-
df['lastSplitDate']=int10_to_date(df['lastSplitDate'][0])
|
150
|
-
if ('sharesShortPreviousMonthDate' in cols):
|
151
|
-
df['sharesShortPreviousMonthDate']=int10_to_date(df['sharesShortPreviousMonthDate'][0])
|
152
|
-
if ('dateShortInterest' in cols):
|
153
|
-
df['dateShortInterest']=int10_to_date(df['dateShortInterest'][0])
|
154
|
-
if ('mostRecentQuarter' in cols):
|
155
|
-
df['mostRecentQuarter']=int10_to_date(df['mostRecentQuarter'][0])
|
156
|
-
if ('lastFiscalYearEnd' in cols):
|
157
|
-
df['lastFiscalYearEnd']=int10_to_date(df['lastFiscalYearEnd'][0])
|
158
|
-
if ('nextFiscalYearEnd' in cols):
|
159
|
-
df['nextFiscalYearEnd']=int10_to_date(df['nextFiscalYearEnd'][0])
|
160
|
-
|
161
|
-
#转换特殊列的内容:可交易标志
|
162
|
-
"""
|
163
|
-
if df['tradeable'][0]: df['tradeable']="Yes"
|
164
|
-
else: df['tradeable']="No"
|
165
|
-
"""
|
166
|
-
|
167
|
-
return df
|
168
|
-
|
169
|
-
if __name__ =="__main__":
|
170
|
-
ticker='AAPL'
|
171
|
-
df=get_profile('AAPL')
|
172
|
-
#==============================================================================
|
173
|
-
def print_profile_detail(df,option='basic'):
|
174
|
-
"""
|
175
|
-
功能:按照选项显示证券信息,更多细节。
|
176
|
-
输入:证券基本信息df;分段选项option。
|
177
|
-
输出:按照选项打印证券信息
|
178
|
-
返回:证券信息,数据框
|
179
|
-
注意:放弃
|
180
|
-
"""
|
181
|
-
#检查数据框的有效性
|
182
|
-
if (df is None) or (len(df)==0):
|
183
|
-
print("...Error #1(print_profile), data input invalid!")
|
184
|
-
return None
|
185
|
-
|
186
|
-
options=["basic","financial","market"]
|
187
|
-
if not(option in options):
|
188
|
-
print("...Error #2(print_profile), 仅支持选项: basic, financial, market")
|
189
|
-
return None
|
190
|
-
|
191
|
-
#遍历数据框,清洗数据
|
192
|
-
cols=list(df) #取得数据框的列名
|
193
|
-
import numpy as np
|
194
|
-
for c in cols:
|
195
|
-
dfc0=df[c][0]
|
196
|
-
#删除空值列
|
197
|
-
if dfc0 is None:
|
198
|
-
del df[c]; continue
|
199
|
-
if dfc0 is np.nan:
|
200
|
-
del df[c]; continue
|
201
|
-
#删除空表列
|
202
|
-
if isinstance(dfc0,list):
|
203
|
-
if len(dfc0)==0: del df[c]; continue
|
204
|
-
|
205
|
-
#分类型清洗内容
|
206
|
-
if isinstance(dfc0,float): df[c]=round(dfc0,4)
|
207
|
-
if isinstance(dfc0,str): df[c]=dfc0.strip()
|
208
|
-
newcols=list(df) #取得清洗后数据框的列名
|
209
|
-
|
210
|
-
#需要打印的字段,只要抓取到就打印
|
211
|
-
basiccols=['symbol','quoteType','shortName','longName','sector','industry', \
|
212
|
-
'fullTimeEmployees','address1','city','state','country','zip', \
|
213
|
-
'phone','fax','website','currency','exchange','market']
|
214
|
-
financialcols=['symbol','shortName','currency','dividendRate',
|
215
|
-
'trailingAnnualDividendRate','exDividendDate', \
|
216
|
-
'dividendYield','trailingAnnualDividendYield', \
|
217
|
-
'fiveYearAvgDividendYield','payoutRatio', \
|
218
|
-
'lastSplitDate','lastSplitFactor','trailingPE','forwardPE', \
|
219
|
-
'trailingEps','forwardEps','profitMargins','earningsQuarterlyGrowth', \
|
220
|
-
'pegRatio','priceToSalesTrailing12Months','priceToBook', \
|
221
|
-
'enterpriseToRevenue','enterpriseToEbitda','netIncomeToCommon','bookValue', \
|
222
|
-
'lastFiscalYearEnd', \
|
223
|
-
'mostRecentQuarter','nextFiscalYearEnd']
|
224
|
-
marketcols=['symbol','shortName','currency','beta','tradeable','open', \
|
225
|
-
'regularMarketOpen','dayHigh','regularMarketDayHigh', \
|
226
|
-
'dayLow','regularMarketLow','previousClose', \
|
227
|
-
'regularMarketPreviousClose','regularMarketPrice','ask','bid', \
|
228
|
-
'fiftyDayAvergae','twoHundredDayAverage','fiftyTwoWeekHigh', \
|
229
|
-
'fiftyTwoWeekLow','52WeekChange','SandP52Change','volume', \
|
230
|
-
'regularMarketVolume','averageVolume','averageDailyVolume10Day', \
|
231
|
-
'averageVolume10days', \
|
232
|
-
'sharesShortPriorMonth','sharesShortPreviousMonthDate', \
|
233
|
-
'dateShortInterest','sharesPercentSharesOut', \
|
234
|
-
'sharesOutstanding','floatShares','heldPercentInstitutions', \
|
235
|
-
'heldPercentInsiders','enterpriseValue','marketCap', \
|
236
|
-
'sharesShort','shortRatio','shortPercentOfFloat']
|
237
|
-
|
238
|
-
typecn=["公司信息","财务信息","市场信息"]
|
239
|
-
typeinfo=typecn[options.index(option)]
|
240
|
-
print("\n===",texttranslate("证券快照:")+typeinfo,"===")
|
241
|
-
typecols=[basiccols,financialcols,marketcols]
|
242
|
-
cols=typecols[options.index(option)]
|
243
|
-
|
244
|
-
from pandas.api.types import is_numeric_dtype
|
245
|
-
for i in cols:
|
246
|
-
if i in newcols:
|
247
|
-
cn=ectranslate(i)
|
248
|
-
if is_numeric_dtype(df[i][0]):
|
249
|
-
if abs(df[i][0]) >= 0.0001:
|
250
|
-
print(cn+':',format(df[i][0],','))
|
251
|
-
else:
|
252
|
-
print(cn+':',df[i][0])
|
253
|
-
|
254
|
-
import datetime as dt; todaydt=dt.date.today()
|
255
|
-
print('\n'+texttranslate("数据来源:雅虎,")+str(todaydt))
|
256
|
-
|
257
|
-
return df
|
258
|
-
|
259
|
-
if __name__ =="__main__":
|
260
|
-
option='basic'
|
261
|
-
df=print_profile_detail(df, option='basic')
|
262
|
-
df=print_profile_detail(df, option='financial')
|
263
|
-
df=print_profile_detail(df, option='market')
|
264
|
-
|
265
|
-
#==============================================================================
|
266
|
-
def print_profile(df,option='basic'):
|
267
|
-
"""
|
268
|
-
功能:按照选项显示证券信息,简化版。
|
269
|
-
输入:证券基本信息df;分段选项option。
|
270
|
-
输出:按照选项打印证券信息
|
271
|
-
返回:证券信息,数据框
|
272
|
-
注意:放弃
|
273
|
-
"""
|
274
|
-
#检查数据框的有效性
|
275
|
-
if (df is None) or (len(df)==0):
|
276
|
-
print(" #Error(print_profile), data set input invalid!")
|
277
|
-
return None
|
278
|
-
|
279
|
-
options=["basic","financial","market"]
|
280
|
-
if not(option in options):
|
281
|
-
print(" #Error(print_profile), only support types of basic, financial, market")
|
282
|
-
return None
|
283
|
-
|
284
|
-
#遍历数据框,清洗数据
|
285
|
-
cols=list(df) #取得数据框的列名
|
286
|
-
import numpy as np
|
287
|
-
for c in cols:
|
288
|
-
dfc0=df[c][0]
|
289
|
-
#删除空值列
|
290
|
-
if dfc0 is None:
|
291
|
-
del df[c]; continue
|
292
|
-
if dfc0 is np.nan:
|
293
|
-
del df[c]; continue
|
294
|
-
#删除空表列
|
295
|
-
if isinstance(dfc0,list):
|
296
|
-
if len(dfc0)==0: del df[c]; continue
|
297
|
-
|
298
|
-
#分类型清洗内容
|
299
|
-
if isinstance(dfc0,float): df[c]=round(dfc0,4)
|
300
|
-
if isinstance(dfc0,str): df[c]=dfc0.strip()
|
301
|
-
newcols=list(df) #取得清洗后数据框的列名
|
302
|
-
|
303
|
-
basiccols=['symbol','quoteType','shortName','sector','industry', \
|
304
|
-
'fullTimeEmployees','city','state','country', \
|
305
|
-
'website','currency','exchange']
|
306
|
-
financialcols=['symbol','dividendRate',
|
307
|
-
'dividendYield', \
|
308
|
-
'payoutRatio', \
|
309
|
-
'trailingPE','forwardPE', \
|
310
|
-
'trailingEps','forwardEps','profitMargins','earningsQuarterlyGrowth', \
|
311
|
-
'pegRatio','priceToSalesTrailing12Months','priceToBook', \
|
312
|
-
'bookValue', \
|
313
|
-
'lastFiscalYearEnd']
|
314
|
-
marketcols=['symbol','beta','open', \
|
315
|
-
'dayHigh', \
|
316
|
-
'dayLow','previousClose', \
|
317
|
-
'fiftyTwoWeekHigh', \
|
318
|
-
'fiftyTwoWeekLow','52WeekChange','SandP52Change','volume', \
|
319
|
-
'averageDailyVolume10Day', \
|
320
|
-
'sharesOutstanding','floatShares','heldPercentInstitutions', \
|
321
|
-
'heldPercentInsiders','marketCap']
|
322
|
-
|
323
|
-
typecn=["公司信息","财务信息","市场信息"]
|
324
|
-
typeinfo=typecn[options.index(option)]
|
325
|
-
print("\n===",texttranslate("证券快照TTM:")+typeinfo,"===")
|
326
|
-
typecols=[basiccols,financialcols,marketcols]
|
327
|
-
cols=typecols[options.index(option)]
|
328
|
-
|
329
|
-
from pandas.api.types import is_numeric_dtype
|
330
|
-
for i in cols:
|
331
|
-
if i in newcols:
|
332
|
-
cn=ectranslate(i)
|
333
|
-
if is_numeric_dtype(df[i][0]):
|
334
|
-
print(cn+':',format(df[i][0],','))
|
335
|
-
else:
|
336
|
-
print(cn+':',df[i][0])
|
337
|
-
|
338
|
-
import datetime as dt; today=dt.date.today()
|
339
|
-
print(texttranslate("数据来源:Yahoo Finance,")+str(today))
|
340
|
-
return df
|
341
|
-
|
342
|
-
if __name__ =="__main__":
|
343
|
-
option='basic'
|
344
|
-
df=print_profile(df, option='basic')
|
345
|
-
df=print_profile(df, option='financial')
|
346
|
-
df=print_profile(df, option='market')
|
347
|
-
#==============================================================================
|
348
|
-
def stock_profile(ticker,option='basic',verbose=False):
|
349
|
-
"""
|
350
|
-
功能:抓取证券快照信息,包括静态公司信息、财务信息和市场信息。
|
351
|
-
输入:证券代码ticker;选项verbose表示是否显示详细信息,默认否。
|
352
|
-
输出:一次性打印公司信息、财务信息和市场信息。
|
353
|
-
返回:证券快照信息数据表。
|
354
|
-
注意:放弃
|
355
|
-
"""
|
356
|
-
print(" Searching for security snapshot information, please wait ...")
|
357
|
-
#抓取证券静态信息
|
358
|
-
try:
|
359
|
-
df=get_profile(ticker)
|
360
|
-
except:
|
361
|
-
print(" #Error(stock_profile), failed to retrieve or decode profile info of",ticker)
|
362
|
-
return None
|
363
|
-
|
364
|
-
#检查抓取到的数据表
|
365
|
-
if (df is None) or (len(df)==0):
|
366
|
-
print(" #Error(stock_profile), retrieved empty profile info of",ticker)
|
367
|
-
return None
|
368
|
-
|
369
|
-
df=print_profile(df, option='basic')
|
370
|
-
#详细版输出信息
|
371
|
-
if verbose:
|
372
|
-
df=print_profile_detail(df, option='financial')
|
373
|
-
df=print_profile_detail(df, option='market')
|
374
|
-
|
375
|
-
return df
|
376
|
-
|
377
|
-
|
378
|
-
#==============================================================================
|
379
|
-
|
380
|
-
if __name__ =="__main__":
|
381
|
-
#美股
|
382
|
-
info=stock_profile("MSFT")
|
383
|
-
info=stock_profile("MSFT",option="market")
|
384
|
-
info=stock_profile("MSFT",option="financial")
|
385
|
-
#大陆股票
|
386
|
-
info=stock_profile("000002.SZ")
|
387
|
-
info=stock_profile("000002.SZ",option="financial")
|
388
|
-
info=stock_profile("000002.SZ",option="market")
|
389
|
-
#港股
|
390
|
-
info=stock_profile("00700.HK",option="financial")
|
391
|
-
info=stock_profile("00700.HK",option="market")
|
392
|
-
info=stock_profile("00700.HK",option="basic")
|
393
|
-
#印度股票
|
394
|
-
info=stock_profile("TCS.NS",option="financial")
|
395
|
-
info=stock_profile("TCS.NS",option="market")
|
396
|
-
info=stock_profile("TCS.NS",option="basic")
|
397
|
-
#德国股票
|
398
|
-
info=stock_profile("BMW.DE",option="financial")
|
399
|
-
info=stock_profile("BMW.DE",option="market")
|
400
|
-
info=stock_profile("BMW.DE",option="basic")
|
401
|
-
#日本股票
|
402
|
-
info=stock_profile("6758.t",option="financial")
|
403
|
-
info=stock_profile("6758.t",option="market")
|
404
|
-
info=stock_profile("6758.t",option="basic")
|
405
|
-
info=stock_profile("9501.t",option="financial")
|
406
|
-
#ETF指数基金
|
407
|
-
info=stock_profile("SPY")
|
408
|
-
info=stock_profile("SPY",option="market")
|
409
|
-
info=stock_profile("SPY",option="financial")
|
410
|
-
#债券期货
|
411
|
-
info=stock_profile("US=F")
|
412
|
-
info=stock_profile("US=F",option="market")
|
413
|
-
info=stock_profile("US=F",option="financial")
|
414
|
-
#债券基金
|
415
|
-
info=stock_profile("LBNDX",option="basic")
|
416
|
-
info=stock_profile("LBNDX",option="market")
|
417
|
-
info=stock_profile("LBNDX",option="financial")
|
418
|
-
#期货
|
419
|
-
info=stock_profile("VXX",option="basic")
|
420
|
-
info=stock_profile("VXX",option="market")
|
421
|
-
info=stock_profile("VXX",option="financial")
|
422
|
-
|
423
|
-
#==============================================================================
|
424
|
-
def security_price(ticker,fromdate,todate,adj=False, \
|
425
|
-
datatag=False,power=0,source='auto'):
|
426
|
-
"""
|
427
|
-
功能:绘制证券价格折线图。为维持兼容性,套壳函数stock_price
|
428
|
-
"""
|
429
|
-
df=stock_price(ticker=ticker,fromdate=fromdate,todate=todate, \
|
430
|
-
adj=adj,datatag=datatag,power=power,source=source)
|
431
|
-
|
432
|
-
return df
|
433
|
-
|
434
|
-
if __name__ =="__main__":
|
435
|
-
# 测试获取股价:沪深指数
|
436
|
-
df=security_price("000001.SS","2022-11-1","2022-12-15")
|
437
|
-
df=security_price("000300.SS","2022-11-1","2022-12-15")
|
438
|
-
df=security_price("399001.SZ","2022-11-1","2022-12-15")
|
439
|
-
df=security_price("399106.SZ","2022-11-1","2022-12-15")
|
440
|
-
|
441
|
-
# 测试获取股价:上交所
|
442
|
-
df=security_price("000001.SS","2022-11-1","2022-12-15")
|
443
|
-
|
444
|
-
# 测试获取股价:深交所
|
445
|
-
df=security_price("000001.SZ","2022-11-1","2022-12-15")
|
446
|
-
|
447
|
-
# 测试获取股价:北交所
|
448
|
-
df=security_price("430047.BJ","2022-11-1","2022-12-15")
|
449
|
-
df=security_price("872925.BJ","2022-11-1","2022-12-15")
|
450
|
-
|
451
|
-
# 测试获取股价:港股
|
452
|
-
df=security_price("00700.HK","2022-11-1","2022-12-15")
|
453
|
-
df=security_price("01810.HK","2022-11-1","2022-12-15")
|
454
|
-
|
455
|
-
# 测试获取股价:美股
|
456
|
-
df=security_price("JD","2022-11-1","2022-12-15")
|
457
|
-
df=security_price("AAPL","2022-11-1","2022-12-15")
|
458
|
-
|
459
|
-
#==============================================================================
|
460
|
-
if __name__ =="__main__":
|
461
|
-
ticker="185851.SS"
|
462
|
-
fromdate="2023-1-1"
|
463
|
-
todate="2023-5-20"
|
464
|
-
|
465
|
-
|
466
|
-
def stock_price(ticker,fromdate,todate,adj=False, \
|
467
|
-
datatag=False,power=0,source='auto',facecolor='whitesmoke'):
|
468
|
-
"""
|
469
|
-
功能:绘制证券价格折线图。
|
470
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;
|
471
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
472
|
-
输出:绘制证券价格折线图
|
473
|
-
返回:证券价格数据表
|
474
|
-
"""
|
475
|
-
#抓取证券价格
|
476
|
-
from siat.security_prices import get_price
|
477
|
-
df=get_price(ticker,fromdate,todate,adj=adj,source=source)
|
478
|
-
|
479
|
-
if not (df is None):
|
480
|
-
tickername=ticker_name(ticker)
|
481
|
-
|
482
|
-
import datetime; today = datetime.date.today()
|
483
|
-
lang=check_language()
|
484
|
-
if lang == 'English':
|
485
|
-
titletxt=texttranslate("Security Price Trend:")+tickername
|
486
|
-
footnote=texttranslate("Data source: Sina/EM/Stooq/Yahoo/SWHY, ")+str(today)
|
487
|
-
else:
|
488
|
-
titletxt=texttranslate("证券价格走势图:")+tickername
|
489
|
-
footnote=texttranslate("数据来源:Sina/EM/Stooq/Yahoo/SWHY,")+str(today)
|
490
|
-
|
491
|
-
pricetype='Close'
|
492
|
-
import pandas as pd
|
493
|
-
df1=pd.DataFrame(df[pricetype])
|
494
|
-
df1.dropna(inplace=True)
|
495
|
-
|
496
|
-
collabel=ectranslate(pricetype)
|
497
|
-
ylabeltxt=collabel
|
498
|
-
plot_line(df1,pricetype,collabel,ylabeltxt,titletxt,footnote, \
|
499
|
-
datatag=datatag,power=power,facecolor=facecolor)
|
500
|
-
|
501
|
-
return df
|
502
|
-
|
503
|
-
if __name__ =="__main__":
|
504
|
-
priceinfo=stock_price("AAPL","2023-1-1","2023-6-16",power=3)
|
505
|
-
|
506
|
-
#==============================================================================
|
507
|
-
if __name__ =="__main__":
|
508
|
-
fromdate='2023-1-1'
|
509
|
-
fromdate1=date_adjust(fromdate,adjust=-730)
|
510
|
-
pricedf=get_price("AAPL",fromdate1,'2023-6-16')
|
511
|
-
|
512
|
-
|
513
|
-
def ret_calculate(pricedf,fromdate):
|
514
|
-
"""
|
515
|
-
功能:单纯计算各种收益率指标
|
516
|
-
"""
|
517
|
-
#加入日收益率
|
518
|
-
from siat.security_prices import calc_daily_return,calc_rolling_return,calc_expanding_return
|
519
|
-
drdf=calc_daily_return(pricedf)
|
520
|
-
#加入滚动收益率
|
521
|
-
prdf1=calc_rolling_return(drdf, "Weekly")
|
522
|
-
prdf2=calc_rolling_return(prdf1, "Monthly")
|
523
|
-
prdf3=calc_rolling_return(prdf2, "Quarterly")
|
524
|
-
prdf4=calc_rolling_return(prdf3, "Annual")
|
525
|
-
|
526
|
-
#加入扩展收益率
|
527
|
-
try:
|
528
|
-
erdf=calc_expanding_return(prdf4,fromdate)
|
529
|
-
except:
|
530
|
-
print(" #Error(ret_calculate): A problem happens while calculating expanding returns based on",fromdate,prdf4)
|
531
|
-
return None
|
532
|
-
|
533
|
-
return erdf
|
534
|
-
|
535
|
-
if __name__ =="__main__":
|
536
|
-
pricedf=get_price("AAPL",'2023-1-1','2023-6-16',adj=True)
|
537
|
-
allind=all_calculate(pricedf,"AAPL",'2023-1-1','2023-6-16')
|
538
|
-
list(allind)
|
539
|
-
|
540
|
-
def all_calculate(pricedf,ticker1,fromdate,todate,ticker_type='auto'):
|
541
|
-
"""
|
542
|
-
功能:单纯计算所有基于证券价格的指标
|
543
|
-
|
544
|
-
注意:对于滚动指标,起始日期需要提前至少一年以上
|
545
|
-
"""
|
546
|
-
|
547
|
-
#计算其各种期间的收益率
|
548
|
-
try:
|
549
|
-
df1a=ret_calculate(pricedf,fromdate)
|
550
|
-
except:
|
551
|
-
print(" #Error(all_calculate): A problem occurs for calculating returns of",ticker1)
|
552
|
-
return None
|
553
|
-
if df1a is None:
|
554
|
-
print(" #Warning(all_calculate): insufficient data for",ticker1,'\b, ignored.')
|
555
|
-
return None
|
556
|
-
|
557
|
-
#加入价格波动指标
|
558
|
-
#df1b=price_volatility2(df1a,ticker1,fromdate,todate,graph=False)
|
559
|
-
df1b=price_volatility2(pricedf,ticker1,fromdate,todate,graph=False,ticker_type=ticker_type)
|
560
|
-
|
561
|
-
#加入收益率波动指标
|
562
|
-
df1c=ret_volatility2(pricedf,ticker1,fromdate,todate,graph=False,ticker_type=ticker_type)
|
563
|
-
|
564
|
-
#加入收益率下偏标准差指标
|
565
|
-
df1d=ret_lpsd2(pricedf,ticker1,fromdate,todate,graph=False,ticker_type=ticker_type)
|
566
|
-
|
567
|
-
# 横向拼接合并
|
568
|
-
result=pd.concat([df1a,df1b,df1c,df1d],axis=1,join='outer')
|
569
|
-
|
570
|
-
# 去掉重复的列,但要避免仅仅因为数值相同而去掉有用的列,比如误删'Close'列
|
571
|
-
result1=result.T
|
572
|
-
result1['item']=result1.index #在行中增加临时列名,避免误删
|
573
|
-
result2=result1.drop_duplicates(subset=None,keep='first',ignore_index=False)
|
574
|
-
result2.drop("item", axis=1, inplace=True) #去掉临时列名
|
575
|
-
result3=result2.T
|
576
|
-
|
577
|
-
return result3
|
578
|
-
|
579
|
-
|
580
|
-
if __name__ =="__main__":
|
581
|
-
# 测试组1
|
582
|
-
ticker='NVDA'
|
583
|
-
indicator="Exp Ret%"
|
584
|
-
indicator="Annual Ret Volatility%"
|
585
|
-
|
586
|
-
# 测试组2
|
587
|
-
ticker='GCZ25.CMX'
|
588
|
-
fromdate='2020-1-1'
|
589
|
-
todate='2020-6-30'
|
590
|
-
indicator="Close"
|
591
|
-
|
592
|
-
# 测试组3
|
593
|
-
ticker='GEM24.CME'
|
594
|
-
fromdate='2023-7-1'
|
595
|
-
todate='2023-9-17'
|
596
|
-
indicator="Close"
|
597
|
-
|
598
|
-
# 公共参数
|
599
|
-
datatag=False
|
600
|
-
power=0
|
601
|
-
graph=True
|
602
|
-
source='auto'
|
603
|
-
zeroline=False
|
604
|
-
average_value=False
|
605
|
-
|
606
|
-
# 其他测试
|
607
|
-
ticker='600519.SS'; indicator='Exp Ret Volatility%'
|
608
|
-
|
609
|
-
ticker='180202.SZ'
|
610
|
-
ticker_type='fund'
|
611
|
-
|
612
|
-
fromdate='2024-1-1'; todate='2024-5-25'
|
613
|
-
|
614
|
-
# 测试组4
|
615
|
-
ticker='AAPL'
|
616
|
-
indicator='Adj Close'
|
617
|
-
fromdate='2024-5-1'; todate='2024-5-20'
|
618
|
-
adjust=''
|
619
|
-
zeroline=False; average_value=False; datatag=False; power=0; graph=True
|
620
|
-
source='auto'
|
621
|
-
mark_top=True; mark_bottom=True; mark_end=True
|
622
|
-
ticker_type='auto'
|
623
|
-
facecolor='whitesmoke'
|
624
|
-
|
625
|
-
# 测试组5
|
626
|
-
ticker='851242.SW'
|
627
|
-
ticker='807110.SW'
|
628
|
-
indicator='Close'
|
629
|
-
fromdate='2024-5-1'; todate='2024-5-20'
|
630
|
-
adjust=''
|
631
|
-
zeroline=False; average_value=False; datatag=False; power=0; graph=True
|
632
|
-
source='auto'
|
633
|
-
mark_top=True; mark_bottom=True; mark_end=True
|
634
|
-
ticker_type='auto'
|
635
|
-
facecolor='whitesmoke'
|
636
|
-
|
637
|
-
# 测试组6
|
638
|
-
ticker='XAUUSD'
|
639
|
-
indicator='Close'
|
640
|
-
fromdate='2024-5-1'; todate='2024-5-20'
|
641
|
-
|
642
|
-
# 测试组7
|
643
|
-
ticker='BMW.DE'
|
644
|
-
indicator='Close'
|
645
|
-
fromdate='2025-6-1'; todate='2025-6-15'
|
646
|
-
|
647
|
-
# 测试组8
|
648
|
-
ticker='GEM25.CME'
|
649
|
-
indicator='Close'
|
650
|
-
fromdate='2025-1-1'; todate='2025-6-15'
|
651
|
-
|
652
|
-
zeroline=False
|
653
|
-
attention_value='';attention_value_area=''
|
654
|
-
attention_point='';attention_point_area=''
|
655
|
-
average_value=False
|
656
|
-
datatag=False;power=0;graph=True;source='auto'
|
657
|
-
mark_top=True;mark_bottom=True;mark_end=True
|
658
|
-
ticker_type='auto';facecolor='whitesmoke';loc='best'
|
659
|
-
|
660
|
-
|
661
|
-
df=security_indicator(ticker,indicator,fromdate,todate,ticker_type=ticker_type)
|
662
|
-
|
663
|
-
def security_indicator(ticker,indicator='Close', \
|
664
|
-
fromdate='MRM',todate='today',adjust='', \
|
665
|
-
zeroline=False, \
|
666
|
-
attention_value='',attention_value_area='', \
|
667
|
-
attention_point='',attention_point_area='', \
|
668
|
-
average_value=False, \
|
669
|
-
datatag=False,power=0,graph=True,source='auto', \
|
670
|
-
mark_top=True,mark_bottom=True, \
|
671
|
-
mark_start=True,mark_end=True, \
|
672
|
-
ticker_type='auto',facecolor='whitesmoke',loc='best'):
|
673
|
-
"""
|
674
|
-
功能:单只证券的全部指标
|
675
|
-
"""
|
676
|
-
fromdate,todate=start_end_preprocess(fromdate,todate)
|
677
|
-
|
678
|
-
#判断复权价
|
679
|
-
adjust_list=['','qfq','hfq']
|
680
|
-
if adjust not in adjust_list:
|
681
|
-
print(" #Warning(security_indicator): invalid adjust",adjust)
|
682
|
-
print(" Supported adjust:",adjust_list)
|
683
|
-
adjust='qfq'
|
684
|
-
|
685
|
-
if ('Adj' not in indicator):
|
686
|
-
adjust=''
|
687
|
-
if ('Adj' in indicator) and (adjust == ''):
|
688
|
-
adjust='qfq'
|
689
|
-
|
690
|
-
fromdate1=date_adjust(fromdate,adjust=-365*3)
|
691
|
-
|
692
|
-
from siat.security_price2 import get_price_1ticker_mixed
|
693
|
-
#pricedf=get_prices_all(ticker,fromdate1,todate,source=source,ticker_type=ticker_type)
|
694
|
-
pricedf,found=get_price_1ticker_mixed(ticker=ticker, \
|
695
|
-
fromdate=fromdate1,todate=todate, \
|
696
|
-
adjust=adjust, \
|
697
|
-
source=source,ticker_type=ticker_type)
|
698
|
-
if pricedf is None:
|
699
|
-
print(" #Error(security_indicator): security info not found for",ticker)
|
700
|
-
return None
|
701
|
-
if len(pricedf) == 0:
|
702
|
-
print(" #Error(security_indicator): zero record found for",ticker)
|
703
|
-
return None
|
704
|
-
|
705
|
-
#奇怪错误:仅仅抓取到1个记录,应对办法:改变开始时间,貌似仅存在于REIT基金
|
706
|
-
if len(pricedf)==1:
|
707
|
-
fromdate1=date_adjust(fromdate,adjust=-365*2)
|
708
|
-
pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=fromdate1, \
|
709
|
-
adjust=adjust, \
|
710
|
-
todate=todate,source=source,ticker_type=ticker_type)
|
711
|
-
if len(pricedf)==1:
|
712
|
-
fromdate1=date_adjust(fromdate,adjust=-365*1)
|
713
|
-
pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=fromdate1, \
|
714
|
-
adjust=adjust, \
|
715
|
-
todate=todate,source=source,ticker_type=ticker_type)
|
716
|
-
if len(pricedf)==1:
|
717
|
-
fromdate1=fromdate
|
718
|
-
pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=fromdate1, \
|
719
|
-
adjust=adjust, \
|
720
|
-
todate=todate,source=source,ticker_type=ticker_type)
|
721
|
-
|
722
|
-
if not found == "Found":
|
723
|
-
print(" #Error(security_indicator): no security info found for",ticker)
|
724
|
-
return None
|
725
|
-
|
726
|
-
# 去掉时区信息,避免日期时区冲突问题
|
727
|
-
pricedf=df_index_timezone_remove(pricedf)
|
728
|
-
"""
|
729
|
-
import pandas as pd
|
730
|
-
pricedf.index = pd.to_datetime(pricedf.index)
|
731
|
-
pricedf.index = pricedf.index.tz_localize(None)
|
732
|
-
"""
|
733
|
-
# 检查是否存在满足给定日期的记录
|
734
|
-
fromdate_pd=pd.to_datetime(fromdate)
|
735
|
-
tmp_df=pricedf[pricedf.index >= fromdate_pd]
|
736
|
-
if len(tmp_df)==0:
|
737
|
-
print(" #Warning(security_indicator): zero record exists from",fromdate,"for",ticker)
|
738
|
-
return None
|
739
|
-
|
740
|
-
erdf=all_calculate(pricedf,ticker,fromdate,todate)
|
741
|
-
erdf2=erdf[erdf.index >= fromdate_pd]
|
742
|
-
|
743
|
-
# 若indicator为Exp Ret%类指标,此处需要首行置零
|
744
|
-
colList=list(erdf2)
|
745
|
-
index1=erdf2.head(1).index.values[0]
|
746
|
-
for c in colList:
|
747
|
-
#if 'Exp Ret%' in c:
|
748
|
-
if c == 'Exp Ret%':
|
749
|
-
erdf2.loc[erdf2[erdf2.index==index1].index.tolist(),c]=0
|
750
|
-
|
751
|
-
#erdf3=pd.DataFrame(erdf2[indicator])
|
752
|
-
erdf3=erdf2
|
753
|
-
|
754
|
-
# 绘图
|
755
|
-
if not graph:
|
756
|
-
return erdf3
|
757
|
-
|
758
|
-
#titletxt=texttranslate("证券指标运动趋势:")+ticker_name(ticker)
|
759
|
-
titletxt1=text_lang("证券趋势分析:","Security Trend: ")
|
760
|
-
titletxt=titletxt1+ticker_name(ticker,ticker_type=ticker_type)
|
761
|
-
import datetime; todaydt = datetime.date.today()
|
762
|
-
sourcetxt=text_lang("数据来源:Sina/EM/Stooq/Yahoo/SWHY,","Data source: Sina/EM/Stooq/Yahoo/SWHY, ")
|
763
|
-
footnote=sourcetxt+str(todaydt)
|
764
|
-
collabel=ectranslate(indicator)
|
765
|
-
|
766
|
-
ylabeltxt=ectranslate(indicator)
|
767
|
-
try:
|
768
|
-
tickersplit=ticker.split('.')
|
769
|
-
if (len(tickersplit) > 1) and (indicator == 'Close'):
|
770
|
-
if tickersplit[1].upper() in ['M','B']:
|
771
|
-
ylabeltxt="stooq_MB" #特殊标志,告知绘图函数不显示某些标记
|
772
|
-
except: pass
|
773
|
-
|
774
|
-
ind_max=erdf3[indicator].max(); ind_min=erdf3[indicator].min()
|
775
|
-
if ind_max * ind_min <0:
|
776
|
-
#if 'Ret%' in indicator:
|
777
|
-
zeroline=True
|
778
|
-
|
779
|
-
plot_line(erdf3,indicator,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
780
|
-
power=power,zeroline=zeroline, \
|
781
|
-
average_value=average_value, \
|
782
|
-
attention_value=attention_value,attention_value_area=attention_value_area, \
|
783
|
-
attention_point=attention_point,attention_point_area=attention_point_area, \
|
784
|
-
mark_top=mark_top,mark_bottom=mark_bottom, \
|
785
|
-
mark_start=mark_start,mark_end=mark_end, \
|
786
|
-
facecolor=facecolor,loc=loc)
|
787
|
-
|
788
|
-
return erdf3
|
789
|
-
|
790
|
-
|
791
|
-
def stock_ret(ticker,fromdate,todate, \
|
792
|
-
adjust='', \
|
793
|
-
rtype="Daily Ret%", \
|
794
|
-
datatag=False,power=0,graph=True,source='auto',ticker_type='auto'):
|
795
|
-
"""
|
796
|
-
功能:绘制证券收益率折线图,单个证券,单个指标。
|
797
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;收益率类型type;
|
798
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
799
|
-
输出:绘制证券价格折线图
|
800
|
-
返回:证券价格数据表
|
801
|
-
"""
|
802
|
-
#调整抓取样本的开始日期366*2=732,以便保证有足够的样本供后续计算
|
803
|
-
fromdate1=date_adjust(fromdate, -732)
|
804
|
-
|
805
|
-
#判断复权价
|
806
|
-
adjust_list=['','qfq','hfq']
|
807
|
-
if adjust not in adjust_list:
|
808
|
-
print(" #Warning(stock_ret): invalid adjust",adjust)
|
809
|
-
print(" Supported adjust:",adjust_list)
|
810
|
-
adjust='qfq'
|
811
|
-
if 'Adj' in rtype: adjust='qfq'
|
812
|
-
|
813
|
-
#抓取证券价格
|
814
|
-
from siat.security_price2 import get_price_1ticker_mixed
|
815
|
-
#pricedf=get_price(ticker,fromdate1,todate,adj=adj,source=source)
|
816
|
-
pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=fromdate1, \
|
817
|
-
todate=todate,adjust=adjust, \
|
818
|
-
source=source,ticker_type=ticker_type)
|
819
|
-
if pricedf is None:
|
820
|
-
print(" #Error(stock_ret): failed to find price info for",ticker,fromdate,todate)
|
821
|
-
return None
|
822
|
-
pricedfcols=list(pricedf)
|
823
|
-
|
824
|
-
#加入日收益率
|
825
|
-
from siat.security_prices import calc_daily_return
|
826
|
-
drdf=calc_daily_return(pricedf)
|
827
|
-
#加入滚动收益率
|
828
|
-
prdf1=calc_rolling_return(drdf, "Weekly")
|
829
|
-
prdf2=calc_rolling_return(prdf1, "Monthly")
|
830
|
-
prdf3=calc_rolling_return(prdf2, "Quarterly")
|
831
|
-
prdf4=calc_rolling_return(prdf3, "Annual")
|
832
|
-
|
833
|
-
#加入扩展收益率:从fromdate开始而不是fromdate1
|
834
|
-
erdf=calc_expanding_return(prdf4,fromdate)
|
835
|
-
|
836
|
-
#如果不绘图则直接返回数据表
|
837
|
-
if not graph: return erdf
|
838
|
-
|
839
|
-
#获得支持的收益率类型列名
|
840
|
-
colnames=list(erdf)
|
841
|
-
for c in pricedfcols:
|
842
|
-
colnames.remove(c)
|
843
|
-
|
844
|
-
#检查type是否在支持的收益率列名中
|
845
|
-
if not (rtype in colnames):
|
846
|
-
print(" #Error(stock_ret):only support return types of",colnames)
|
847
|
-
return
|
848
|
-
|
849
|
-
import datetime; todaydt = datetime.date.today()
|
850
|
-
footnote=text_lang("数据来源:Sina/EM/Stooq/Yahoo/SWHY,","Data source: Sina/EM/Stooq/Yahoo/SWHY, ")+str(todaydt)
|
851
|
-
collabel=ectranslate(rtype)
|
852
|
-
ylabeltxt=ectranslate(rtype)
|
853
|
-
titletxt=text_lang("证券趋势分析:","Security Trend: ")+ticker_name(ticker,ticker_type=ticker_type)+text_lang(",收益率",", Rate of Return")
|
854
|
-
|
855
|
-
pltdf=erdf[erdf.index >= fromdate]
|
856
|
-
plot_line(pltdf,rtype,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
857
|
-
power=power,zeroline=True)
|
858
|
-
|
859
|
-
return erdf
|
860
|
-
|
861
|
-
if __name__ =="__main__":
|
862
|
-
ticker="000002.SZ"
|
863
|
-
fromdate="2020-1-1"
|
864
|
-
todate="2020-3-16"
|
865
|
-
type="Daily Ret%"
|
866
|
-
datatag=False
|
867
|
-
power=3
|
868
|
-
retinfo=stock_ret("000002.SZ","2020-1-1","2020-3-16",power=3)
|
869
|
-
retinfo=stock_ret("000002.SZ","2020-1-1","2020-3-16","Daily Adj Ret%",power=3)
|
870
|
-
retinfo=stock_ret("000002.SZ","2020-1-1","2020-3-16","Weekly Ret%",power=3)
|
871
|
-
retinfo=stock_ret("000002.SZ","2020-1-1","2020-3-16","Monthly Ret%",power=4)
|
872
|
-
retinfo=stock_ret("000002.SZ","2020-1-1","2020-3-16","Quarterly Ret%",power=4)
|
873
|
-
retinfo=stock_ret("000002.SZ","2019-1-1","2020-3-16","Annual Ret%",power=4)
|
874
|
-
retinfo=stock_ret("000002.SZ","2019-1-1","2020-3-16","Cum Ret%",power=4)
|
875
|
-
|
876
|
-
#==============================================================
|
877
|
-
if __name__ =="__main__":
|
878
|
-
ticker='600519.SS'
|
879
|
-
ticker='OR.PA'
|
880
|
-
measures=['Monthly Ret%','Quarterly Ret%','Annual Ret%','XYZ']
|
881
|
-
|
882
|
-
ticker='NVDA'
|
883
|
-
measures=['Close','Adj Close']
|
884
|
-
|
885
|
-
fromdate='2024-5-20'
|
886
|
-
todate='2024-6-20'
|
887
|
-
adjust=''
|
888
|
-
band_area=''
|
889
|
-
graph=True
|
890
|
-
smooth=True
|
891
|
-
loc='best'
|
892
|
-
facecolor='whitesmoke'
|
893
|
-
date_range=False
|
894
|
-
date_freq=False
|
895
|
-
annotate=False
|
896
|
-
annotate_value=False
|
897
|
-
source='auto'
|
898
|
-
mark_top=True; mark_bottom=True; mark_end=True
|
899
|
-
ticker_type='auto'
|
900
|
-
|
901
|
-
df=security_mindicators(ticker,measures,fromdate,todate)
|
902
|
-
|
903
|
-
def security_mindicators(ticker,measures,
|
904
|
-
fromdate,todate, \
|
905
|
-
attention_value='',attention_value_area='', \
|
906
|
-
attention_point='',attention_point_area='', \
|
907
|
-
adjust='', \
|
908
|
-
band_area='', \
|
909
|
-
graph=True,smooth=True,loc='best',facecolor='whitesmoke', \
|
910
|
-
date_range=False,date_freq=False, \
|
911
|
-
annotate=False,annotate_value=False, \
|
912
|
-
source='auto', \
|
913
|
-
mark_top=True,mark_bottom=True, \
|
914
|
-
mark_start=True,mark_end=True, \
|
915
|
-
ticker_type='auto'):
|
916
|
-
"""
|
917
|
-
功能:单个证券,多个指标对比
|
918
|
-
date_range=False:指定开始结束日期绘图
|
919
|
-
date_freq=False:指定横轴日期间隔,例如'D'、'2D'、'W'、'M'等,横轴一般不超过25个标注,否则会重叠
|
920
|
-
注意:
|
921
|
-
annotate:这里仅为预留,暂时未作处理
|
922
|
-
smooth:样本数目超过一定数量就默认忽略
|
923
|
-
"""
|
924
|
-
DEBUG=False
|
925
|
-
|
926
|
-
# 提前开始日期
|
927
|
-
#fromdate1=date_adjust(fromdate,adjust=-365*3)
|
928
|
-
|
929
|
-
#处理ticker,允许1个
|
930
|
-
if isinstance(ticker,list):
|
931
|
-
if len(ticker) >= 1:
|
932
|
-
ticker=ticker[0]
|
933
|
-
else:
|
934
|
-
print(" #Error(security_mindicators): need a ticker for proceed")
|
935
|
-
return None
|
936
|
-
|
937
|
-
#处理measures,允许多个
|
938
|
-
if isinstance(measures,str):
|
939
|
-
measures=[measures]
|
940
|
-
|
941
|
-
#屏蔽函数内print信息输出的类
|
942
|
-
import os, sys
|
943
|
-
class HiddenPrints:
|
944
|
-
def __enter__(self):
|
945
|
-
self._original_stdout = sys.stdout
|
946
|
-
sys.stdout = open(os.devnull, 'w')
|
947
|
-
|
948
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
949
|
-
sys.stdout.close()
|
950
|
-
sys.stdout = self._original_stdout
|
951
|
-
|
952
|
-
df=pd.DataFrame()
|
953
|
-
for m in measures:
|
954
|
-
print(" Searching",ticker,"for",m,"info ... ...")
|
955
|
-
#复权价判断
|
956
|
-
adjustm=adjust
|
957
|
-
if ('Adj' in m) and (adjust ==''):
|
958
|
-
adjustm='qfq'
|
959
|
-
|
960
|
-
with HiddenPrints():
|
961
|
-
#security_indicator未能做到同时获得Close和Adj Close
|
962
|
-
dftmp=security_indicator(ticker=ticker,indicator=m,adjust=adjustm, \
|
963
|
-
fromdate=fromdate,todate=todate, \
|
964
|
-
source=source, \
|
965
|
-
ticker_type=ticker_type, \
|
966
|
-
graph=False)
|
967
|
-
if dftmp is None:
|
968
|
-
print(" #Error(security_mindicators): info not found for",ticker)
|
969
|
-
return None
|
970
|
-
if len(dftmp) ==0:
|
971
|
-
print(" #Error(security_mindicators): empty record found for",ticker)
|
972
|
-
return None
|
973
|
-
|
974
|
-
try:
|
975
|
-
dftmp1= dftmp[[m]]
|
976
|
-
except:
|
977
|
-
print(" #Error(security_mindicators): unsupported measure for",m)
|
978
|
-
return None
|
979
|
-
|
980
|
-
if len(df)==0:
|
981
|
-
df=dftmp1
|
982
|
-
else:
|
983
|
-
df=pd.merge(df,dftmp1,left_index=True,right_index=True)
|
984
|
-
|
985
|
-
df['ticker']=ticker
|
986
|
-
|
987
|
-
if graph:
|
988
|
-
# 翻译指标名称
|
989
|
-
for c in list(df):
|
990
|
-
df.rename(columns={c:ectranslate(c)},inplace=True)
|
991
|
-
|
992
|
-
y_label=text_lang('证券指标',"Indicator")
|
993
|
-
import datetime; todaydt = datetime.date.today()
|
994
|
-
x_label=text_lang("数据来源:Sina/EM/Stooq/Yahoo/SWHY,","Data source: Sina/EM/Stooq/Yahoo/SWHY, ")+str(todaydt)
|
995
|
-
|
996
|
-
axhline_value=0; axhline_label=''
|
997
|
-
above_zero=0; below_zero=0
|
998
|
-
for c in list(df):
|
999
|
-
c_max=df[c].max(); c_min=df[c].min()
|
1000
|
-
try:
|
1001
|
-
if c_max>0 or c_min>0: above_zero+=1
|
1002
|
-
if c_max<0 or c_min<0: below_zero+=1
|
1003
|
-
except: continue
|
1004
|
-
|
1005
|
-
if above_zero>0 and below_zero>0: #有正有负
|
1006
|
-
if DEBUG:
|
1007
|
-
print("DEBUG: draw axhline=0")
|
1008
|
-
#if 'Ret%' in c:
|
1009
|
-
axhline_value=0
|
1010
|
-
axhline_label='零线'
|
1011
|
-
|
1012
|
-
titletxt=text_lang("证券趋势分析:","Security Trend: ")+ticker_name(ticker,ticker_type=ticker_type)
|
1013
|
-
|
1014
|
-
draw_lines2(df,y_label,x_label,axhline_value,axhline_label,titletxt, \
|
1015
|
-
data_label=False,resample_freq='1D',smooth=smooth, \
|
1016
|
-
date_range=date_range,date_freq=date_freq,date_fmt='%Y-%m-%d', \
|
1017
|
-
attention_value=attention_value,attention_value_area=attention_value_area, \
|
1018
|
-
attention_point=attention_point,attention_point_area=attention_point_area, \
|
1019
|
-
annotate=annotate,annotate_value=annotate_value, \
|
1020
|
-
mark_top=mark_top,mark_bottom=mark_bottom, \
|
1021
|
-
mark_start=mark_start,mark_end=mark_end, \
|
1022
|
-
facecolor=facecolor, \
|
1023
|
-
band_area=band_area,loc=loc)
|
1024
|
-
|
1025
|
-
return df
|
1026
|
-
|
1027
|
-
#==============================================================================
|
1028
|
-
def stock_price_volatility(ticker,fromdate,todate,type="Weekly Price Volatility", \
|
1029
|
-
datatag=False,power=0,graph=True):
|
1030
|
-
"""
|
1031
|
-
功能:绘制证券价格波动风险折线图。
|
1032
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;期间类型type;
|
1033
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
1034
|
-
输出:绘制证券价格波动折线图
|
1035
|
-
返回:证券价格数据表
|
1036
|
-
"""
|
1037
|
-
#调整抓取样本的开始日期,以便保证有足够的样本供后续计算
|
1038
|
-
fromdate1=date_adjust(fromdate, -400)
|
1039
|
-
|
1040
|
-
#抓取证券价格
|
1041
|
-
adj=False
|
1042
|
-
if 'Adj' in type: adj=True
|
1043
|
-
from siat.security_prices import get_price
|
1044
|
-
pricedf=get_price(ticker,fromdate1,todate,adj=adj)
|
1045
|
-
if pricedf is None:
|
1046
|
-
print(" #Error(stock_price_volatility):failed to find price info for",ticker,fromdate,todate)
|
1047
|
-
return
|
1048
|
-
pricedfcols=list(pricedf)
|
1049
|
-
|
1050
|
-
#加入滚动价格波动风险
|
1051
|
-
prdf1=rolling_price_volatility(pricedf, "Weekly")
|
1052
|
-
prdf2=rolling_price_volatility(prdf1, "Monthly")
|
1053
|
-
prdf3=rolling_price_volatility(prdf2, "Quarterly")
|
1054
|
-
prdf4=rolling_price_volatility(prdf3, "Annual")
|
1055
|
-
|
1056
|
-
#加入累计价格波动风险
|
1057
|
-
erdf=expanding_price_volatility(prdf4,fromdate)
|
1058
|
-
|
1059
|
-
#如果不绘图则直接返回数据表
|
1060
|
-
if not graph: return erdf
|
1061
|
-
|
1062
|
-
#获得支持的价格波动风险类型列名,去掉不需要的列名
|
1063
|
-
colnames=list(erdf)
|
1064
|
-
for c in pricedfcols:
|
1065
|
-
colnames.remove(c)
|
1066
|
-
|
1067
|
-
#检查type是否在支持的收益率列名中
|
1068
|
-
if not (type in colnames):
|
1069
|
-
print(" #Error(stock_price_volatility):only support price risk types of",colnames)
|
1070
|
-
return
|
1071
|
-
|
1072
|
-
titletxt=texttranslate("证券价格波动风险走势图:")+ticker_name(ticker)
|
1073
|
-
import datetime; today = datetime.date.today()
|
1074
|
-
footnote=texttranslate("数据来源:Sina/EM/Stooq/Yahoo/SWHY,")+str(today)
|
1075
|
-
collabel=ectranslate(type)
|
1076
|
-
ylabeltxt=ectranslate(type)
|
1077
|
-
pltdf=erdf[erdf.index >= fromdate]
|
1078
|
-
plot_line(pltdf,type,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
1079
|
-
power=power,zeroline=True)
|
1080
|
-
|
1081
|
-
return erdf
|
1082
|
-
|
1083
|
-
if __name__ =="__main__":
|
1084
|
-
ticker="000002.SZ"
|
1085
|
-
fromdate="2020-1-1"
|
1086
|
-
todate="2020-3-16"
|
1087
|
-
type="Daily Ret%"
|
1088
|
-
datatag=False
|
1089
|
-
power=3
|
1090
|
-
|
1091
|
-
pv=stock_price_volatility("000002.SZ","2019-1-1","2020-3-16","Annual Price Volatility")
|
1092
|
-
pv=stock_price_volatility("000002.SZ","2019-1-1","2020-3-16","Annual Exp Price Volatility")
|
1093
|
-
|
1094
|
-
#==============================================================================
|
1095
|
-
def price_volatility2(pricedf,ticker,fromdate,todate, \
|
1096
|
-
type="Weekly Price Volatility",datatag=False, \
|
1097
|
-
power=4,graph=True,ticker_type='auto'):
|
1098
|
-
"""
|
1099
|
-
功能:绘制证券价格波动风险折线图。与函数price_volatility的唯一区别是不抓取股价。
|
1100
|
-
输入:股价数据集pricedf;证券代码ticker;开始日期fromdate,结束日期todate;期间类型type;
|
1101
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
1102
|
-
输出:绘制证券价格波动折线图
|
1103
|
-
返回:证券价格数据表
|
1104
|
-
"""
|
1105
|
-
pricedfcols=list(pricedf)
|
1106
|
-
#加入滚动价格波动风险
|
1107
|
-
from siat.security_prices import rolling_price_volatility,expanding_price_volatility
|
1108
|
-
prdf1=rolling_price_volatility(pricedf, "Weekly")
|
1109
|
-
prdf2=rolling_price_volatility(prdf1, "Monthly")
|
1110
|
-
prdf3=rolling_price_volatility(prdf2, "Quarterly")
|
1111
|
-
prdf4=rolling_price_volatility(prdf3, "Annual")
|
1112
|
-
|
1113
|
-
#加入累计价格波动风险
|
1114
|
-
erdf=expanding_price_volatility(prdf4,fromdate)
|
1115
|
-
|
1116
|
-
#如果不绘图则直接返回数据表
|
1117
|
-
if not graph: return erdf
|
1118
|
-
|
1119
|
-
#获得支持的价格波动风险类型列名,去掉不需要的列名
|
1120
|
-
colnames=list(erdf)
|
1121
|
-
for c in pricedfcols:
|
1122
|
-
colnames.remove(c)
|
1123
|
-
|
1124
|
-
#检查type是否在支持的收益率列名中
|
1125
|
-
if not (type in colnames):
|
1126
|
-
print(" #Error(price_volatility2):only support price risk types of",colnames)
|
1127
|
-
return
|
1128
|
-
|
1129
|
-
titletxt=text_lang("证券趋势分析:","Security Trend: ")+ticker_name(ticker,ticker_type=ticker_type)+text_lang(",价格波动风险",", Price Volatility Risk")
|
1130
|
-
import datetime; todaydt = datetime.date.today()
|
1131
|
-
footnote=texttranslate("数据来源:Sina/EM/Stooq/Yahoo/SWHY,")+str(todaydt)
|
1132
|
-
collabel=ectranslate(type)
|
1133
|
-
ylabeltxt=ectranslate(type)
|
1134
|
-
pltdf=erdf[erdf.index >= fromdate]
|
1135
|
-
plot_line(pltdf,type,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
1136
|
-
power=power,zeroline=True)
|
1137
|
-
|
1138
|
-
return erdf
|
1139
|
-
|
1140
|
-
if __name__ =="__main__":
|
1141
|
-
ticker="000002.SZ"
|
1142
|
-
fromdate="2020-1-1"
|
1143
|
-
todate="2020-3-16"
|
1144
|
-
type="Daily Ret%"
|
1145
|
-
datatag=False
|
1146
|
-
power=3
|
1147
|
-
|
1148
|
-
df=get_price("000002.SZ","2019-1-1","2020-3-16")
|
1149
|
-
pv=price_volatility2(df,"000002.SZ","2019-1-1","2020-3-16","Annual Price Volatility")
|
1150
|
-
pv=price_volatility2(df,"000002.SZ","2019-1-1","2020-3-16","Annual Exp Price Volatility")
|
1151
|
-
|
1152
|
-
#==============================================================================
|
1153
|
-
def stock_ret_volatility(ticker,fromdate,todate,type="Weekly Ret Volatility%",datatag=False,power=4,graph=True):
|
1154
|
-
"""
|
1155
|
-
功能:绘制证券收益率波动风险折线图。
|
1156
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;期间类型type;
|
1157
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
1158
|
-
输出:绘制证券收益率波动折线图
|
1159
|
-
返回:证券收益率波动数据表
|
1160
|
-
"""
|
1161
|
-
#调整抓取样本的开始日期,以便保证有足够的样本供后续计算
|
1162
|
-
fromdate1=date_adjust(fromdate, -400)
|
1163
|
-
retdf=stock_ret(ticker,fromdate1,todate,graph=False)
|
1164
|
-
pricedfcols=list(retdf)
|
1165
|
-
|
1166
|
-
#加入滚动收益率波动风险
|
1167
|
-
prdf1=rolling_ret_volatility(retdf, "Weekly")
|
1168
|
-
prdf2=rolling_ret_volatility(prdf1, "Monthly")
|
1169
|
-
prdf3=rolling_ret_volatility(prdf2, "Quarterly")
|
1170
|
-
prdf4=rolling_ret_volatility(prdf3, "Annual")
|
1171
|
-
|
1172
|
-
#加入累计收益率波动风险
|
1173
|
-
erdf=expanding_ret_volatility(prdf4,fromdate)
|
1174
|
-
|
1175
|
-
#如果不绘图则直接返回数据表
|
1176
|
-
if not graph: return erdf
|
1177
|
-
|
1178
|
-
#获得支持的收益率波动风险类型列名,去掉不需要的列名
|
1179
|
-
colnames=list(erdf)
|
1180
|
-
for c in pricedfcols:
|
1181
|
-
colnames.remove(c)
|
1182
|
-
|
1183
|
-
#检查type是否在支持的收益率波动指标列名中
|
1184
|
-
if not (type in colnames):
|
1185
|
-
print(" #Error(stock_ret_volatility),only support return risk types of",colnames)
|
1186
|
-
return
|
1187
|
-
|
1188
|
-
titletxt=texttranslate("证券收益率波动风险走势图:")+ticker_name(ticker)
|
1189
|
-
import datetime; today = datetime.date.today()
|
1190
|
-
footnote=texttranslate("数据来源:Sina/EM/Stooq/Yahoo/SWHY,")+str(today)
|
1191
|
-
collabel=ectranslate(type)
|
1192
|
-
ylabeltxt=ectranslate(type)
|
1193
|
-
pltdf=erdf[erdf.index >= fromdate]
|
1194
|
-
plot_line(pltdf,type,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
1195
|
-
power=power,zeroline=True)
|
1196
|
-
|
1197
|
-
return erdf
|
1198
|
-
|
1199
|
-
if __name__ =="__main__":
|
1200
|
-
ticker="000002.SZ"
|
1201
|
-
fromdate="2020-1-1"
|
1202
|
-
todate="2020-3-16"
|
1203
|
-
type="Daily Ret%"
|
1204
|
-
datatag=False
|
1205
|
-
power=3
|
1206
|
-
|
1207
|
-
pv=stock_ret_volatility("000002.SZ","2019-1-1","2020-3-16","Annual Ret Volatility%")
|
1208
|
-
pv=stock_ret_volatility("000002.SZ","2019-1-1","2020-3-16","Annual Exp Ret Volatility%")
|
1209
|
-
|
1210
|
-
|
1211
|
-
#==============================================================================
|
1212
|
-
def ret_volatility2(retdf,ticker,fromdate,todate, \
|
1213
|
-
type="Weekly Ret Volatility%",datatag=False, \
|
1214
|
-
power=4,graph=True,ticker_type='auto'):
|
1215
|
-
"""
|
1216
|
-
功能:绘制证券收益率波动风险折线图。与函数ret_volatility的唯一区别是不抓取股价。
|
1217
|
-
输入:股价数据集pricedf;证券代码ticker;开始日期fromdate,结束日期todate;期间类型type;
|
1218
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
1219
|
-
输出:绘制证券收益率波动折线图
|
1220
|
-
返回:证券收益率波动数据表
|
1221
|
-
"""
|
1222
|
-
retdfcols=list(retdf)
|
1223
|
-
|
1224
|
-
#retdf=calc_daily_return(pricedf)
|
1225
|
-
#加入滚动价格波动风险
|
1226
|
-
from siat.security_prices import rolling_ret_volatility,expanding_ret_volatility
|
1227
|
-
prdf1=rolling_ret_volatility(retdf, "Weekly")
|
1228
|
-
prdf2=rolling_ret_volatility(prdf1, "Monthly")
|
1229
|
-
prdf3=rolling_ret_volatility(prdf2, "Quarterly")
|
1230
|
-
prdf4=rolling_ret_volatility(prdf3, "Annual")
|
1231
|
-
|
1232
|
-
#加入累计价格波动风险
|
1233
|
-
erdf=expanding_ret_volatility(prdf4,fromdate)
|
1234
|
-
|
1235
|
-
#如果不绘图则直接返回数据表
|
1236
|
-
if not graph: return erdf
|
1237
|
-
|
1238
|
-
#获得支持的价格波动风险类型列名,去掉不需要的列名
|
1239
|
-
colnames=list(erdf)
|
1240
|
-
for c in retdfcols:
|
1241
|
-
colnames.remove(c)
|
1242
|
-
|
1243
|
-
#检查type是否在支持的收益率列名中
|
1244
|
-
if not (type in colnames):
|
1245
|
-
print(" #Error(ret_volatility2): only support return risk types of",colnames)
|
1246
|
-
return
|
1247
|
-
|
1248
|
-
titletxt=text_lang("证券趋势分析:","Security Trend: ")+ticker_name(ticker,ticker_type=ticker_type)+text_lang(",收益率波动风险",", Return Volatility Risk")
|
1249
|
-
import datetime; todaydt = datetime.date.today()
|
1250
|
-
footnote=text_lang("数据来源:Sina/EM/Stooq/Yahoo/SWHY,","Data source: Sina/EM/Stooq/Yahoo/SWHY, ")+str(todaydt)
|
1251
|
-
collabel=ectranslate(type)
|
1252
|
-
ylabeltxt=ectranslate(type)
|
1253
|
-
pltdf=erdf[erdf.index >= fromdate]
|
1254
|
-
plot_line(pltdf,type,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
1255
|
-
power=power,zeroline=True)
|
1256
|
-
|
1257
|
-
return erdf
|
1258
|
-
|
1259
|
-
if __name__ =="__main__":
|
1260
|
-
ticker="000002.SZ"
|
1261
|
-
fromdate="2020-1-1"
|
1262
|
-
todate="2020-3-16"
|
1263
|
-
type="Daily Ret%"
|
1264
|
-
datatag=False
|
1265
|
-
power=3
|
1266
|
-
|
1267
|
-
df=get_price("000002.SZ","2019-1-1","2020-3-16")
|
1268
|
-
pv=price_volatility2(df,"000002.SZ","2019-1-1","2020-3-16","Annual Price Volatility")
|
1269
|
-
pv=price_volatility2(df,"000002.SZ","2019-1-1","2020-3-16","Annual Exp Price Volatility")
|
1270
|
-
|
1271
|
-
#==============================================================================
|
1272
|
-
def ret_lpsd(ticker,fromdate,todate,type="Weekly Ret Volatility%",datatag=False,power=4,graph=True):
|
1273
|
-
"""
|
1274
|
-
功能:绘制证券收益率波动损失风险折线图。
|
1275
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;期间类型type;
|
1276
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
1277
|
-
输出:绘制证券收益率下偏标准差折线图
|
1278
|
-
返回:证券收益率下偏标准差数据表
|
1279
|
-
"""
|
1280
|
-
#调整抓取样本的开始日期,以便保证有足够的样本供后续计算
|
1281
|
-
fromdate1=date_adjust(fromdate, -400)
|
1282
|
-
retdf=stock_ret(ticker,fromdate1,todate,graph=False)
|
1283
|
-
pricedfcols=list(retdf)
|
1284
|
-
|
1285
|
-
#加入滚动收益率下偏标准差
|
1286
|
-
prdf1=rolling_ret_lpsd(retdf, "Weekly")
|
1287
|
-
prdf2=rolling_ret_lpsd(prdf1, "Monthly")
|
1288
|
-
prdf3=rolling_ret_lpsd(prdf2, "Quarterly")
|
1289
|
-
prdf4=rolling_ret_lpsd(prdf3, "Annual")
|
1290
|
-
|
1291
|
-
#加入扩展收益率下偏标准差
|
1292
|
-
erdf=expanding_ret_lpsd(prdf4,fromdate)
|
1293
|
-
|
1294
|
-
#如果不绘图则直接返回数据表
|
1295
|
-
if not graph: return erdf
|
1296
|
-
|
1297
|
-
#获得支持的收益率波动风险类型列名,去掉不需要的列名
|
1298
|
-
colnames=list(erdf)
|
1299
|
-
for c in pricedfcols:
|
1300
|
-
colnames.remove(c)
|
1301
|
-
|
1302
|
-
#检查type是否在支持的收益率波动指标列名中
|
1303
|
-
if not (type in colnames):
|
1304
|
-
print(" #Error(ret_lpsd): only support return risk types of",colnames)
|
1305
|
-
return
|
1306
|
-
|
1307
|
-
titletxt=texttranslate("证券收益率波动损失风险走势图:")+ticker_name(ticker)
|
1308
|
-
import datetime; today = datetime.date.today()
|
1309
|
-
footnote=texttranslate("数据来源:Sina/EM/Stooq/Yahoo/SWHY,")+str(today)
|
1310
|
-
collabel=ectranslate(type)
|
1311
|
-
ylabeltxt=ectranslate(type)
|
1312
|
-
pltdf=erdf[erdf.index >= fromdate]
|
1313
|
-
plot_line(pltdf,type,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
1314
|
-
power=power,zeroline=True)
|
1315
|
-
|
1316
|
-
return erdf
|
1317
|
-
|
1318
|
-
if __name__ =="__main__":
|
1319
|
-
ticker="000002.SZ"
|
1320
|
-
fromdate="2020-1-1"
|
1321
|
-
todate="2020-3-16"
|
1322
|
-
type="Daily Ret%"
|
1323
|
-
datatag=False
|
1324
|
-
power=3
|
1325
|
-
|
1326
|
-
pv=ret_lpsd("000002.SZ","2019-1-1","2020-3-16","Annual Ret Volatility%")
|
1327
|
-
pv=ret_lpsd("000002.SZ","2019-1-1","2020-3-16","Annual Exp Ret Volatility%")
|
1328
|
-
|
1329
|
-
#==============================================================================
|
1330
|
-
def ret_lpsd2(retdf,ticker,fromdate,todate, \
|
1331
|
-
rtype="Weekly Ret Volatility%",datatag=False, \
|
1332
|
-
power=4,graph=True,ticker_type='auto'):
|
1333
|
-
"""
|
1334
|
-
功能:绘制证券收益率波动损失风险折线图。与函数ret_lpsd的唯一区别是不抓取股价。
|
1335
|
-
输入:股价数据集pricedf;证券代码ticker;开始日期fromdate,结束日期todate;期间类型type;
|
1336
|
-
是否标注数据标签datatag,默认否;多项式趋势线的阶数,若为0则不绘制趋势线。
|
1337
|
-
输出:绘制证券收益率下偏标准差折线图。
|
1338
|
-
返回:证券收益率下偏标准差数据表。
|
1339
|
-
"""
|
1340
|
-
retdfcols=list(retdf)
|
1341
|
-
#retdf=calc_daily_return(pricedf)
|
1342
|
-
#加入滚动价格波动风险
|
1343
|
-
from siat.security_prices import rolling_ret_lpsd,expanding_ret_lpsd
|
1344
|
-
prdf1=rolling_ret_lpsd(retdf, "Weekly")
|
1345
|
-
prdf2=rolling_ret_lpsd(prdf1, "Monthly")
|
1346
|
-
prdf3=rolling_ret_lpsd(prdf2, "Quarterly")
|
1347
|
-
prdf4=rolling_ret_lpsd(prdf3, "Annual")
|
1348
|
-
|
1349
|
-
#加入扩展收益率下偏标准差
|
1350
|
-
erdf=expanding_ret_lpsd(prdf4,fromdate)
|
1351
|
-
|
1352
|
-
#如果不绘图则直接返回数据表
|
1353
|
-
if not graph: return erdf
|
1354
|
-
|
1355
|
-
#获得支持的价格波动风险类型列名,去掉不需要的列名
|
1356
|
-
colnames=list(erdf)
|
1357
|
-
for c in retdfcols:
|
1358
|
-
colnames.remove(c)
|
1359
|
-
|
1360
|
-
#检查type是否在支持的收益率列名中
|
1361
|
-
if not (rtype in colnames):
|
1362
|
-
print(" #Error(ret_lpsd2): only support return risk types of",colnames)
|
1363
|
-
return
|
1364
|
-
|
1365
|
-
titletxt=text_lang("证券趋势分析:","Security Trend: ")+ticker_name(ticker,ticker_type=ticker_type)+text_lang("波动损失风险","Volatility Loss Risk")
|
1366
|
-
import datetime; todaydt = datetime.date.today()
|
1367
|
-
footnote=text_lang("数据来源:Sina/EM/Stooq/Yahoo/SWHY,","Data source: Sina/EM/Stooq/Yahoo/SWHY, ")+str(todaydt)
|
1368
|
-
collabel=ectranslate(rtype)
|
1369
|
-
ylabeltxt=ectranslate(rtype)
|
1370
|
-
pltdf=erdf[erdf.index >= fromdate]
|
1371
|
-
plot_line(pltdf,rtype,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
|
1372
|
-
power=power,zeroline=True)
|
1373
|
-
|
1374
|
-
return erdf
|
1375
|
-
|
1376
|
-
if __name__ =="__main__":
|
1377
|
-
ticker="000002.SZ"
|
1378
|
-
fromdate="2020-1-1"
|
1379
|
-
todate="2020-3-16"
|
1380
|
-
type="Daily Ret%"
|
1381
|
-
datatag=False
|
1382
|
-
power=3
|
1383
|
-
|
1384
|
-
df=get_price("000002.SZ","2019-1-1","2020-3-16")
|
1385
|
-
pv=price_lpsd2(df,"000002.SZ","2019-1-1","2020-3-16","Annual Price Volatility")
|
1386
|
-
pv=price_lpsd2(df,"000002.SZ","2019-1-1","2020-3-16","Annual Exp Price Volatility")
|
1387
|
-
#==============================================================================
|
1388
|
-
def comp_1security_2measures(df,measure1,measure2,twinx=False, \
|
1389
|
-
attention_value='',attention_value_area='', \
|
1390
|
-
attention_point='',attention_point_area='', \
|
1391
|
-
loc1='upper left',loc2='lower left', \
|
1392
|
-
graph=True,facecolor='whitesmoke', \
|
1393
|
-
ticker_type='auto'):
|
1394
|
-
"""
|
1395
|
-
功能:对比绘制一只证券两个指标的折线图。
|
1396
|
-
输入:证券指标数据集df;行情类别measure1/2。
|
1397
|
-
输出:绘制证券行情双折线图,基于twinx判断使用单轴或双轴坐标
|
1398
|
-
返回:无
|
1399
|
-
"""
|
1400
|
-
DEBUG=False
|
1401
|
-
|
1402
|
-
#筛选证券指标,检验是否支持指标
|
1403
|
-
dfcols=list(df)
|
1404
|
-
#nouselist=['date','Weekday','ticker']
|
1405
|
-
#for c in nouselist: dfcols.remove(c)
|
1406
|
-
|
1407
|
-
if not (measure1 in dfcols):
|
1408
|
-
print(" #Error(comp_1security_2measures): unsupported measures: ",measure1)
|
1409
|
-
print(" Supporting measures: ",dfcols)
|
1410
|
-
return
|
1411
|
-
if not (measure2 in dfcols):
|
1412
|
-
print(" #Error(comp_1security_2measures): unsupported measures: ",measure2)
|
1413
|
-
print(" Supporting measures: ",dfcols)
|
1414
|
-
return
|
1415
|
-
|
1416
|
-
#判断是否绘制水平0线
|
1417
|
-
pricelist=['High','Low','Open','Close','Volume','Adj Close']
|
1418
|
-
if (measure1 in pricelist) or (measure2 in pricelist):
|
1419
|
-
zeroline=False
|
1420
|
-
else: zeroline=True
|
1421
|
-
|
1422
|
-
#提取信息
|
1423
|
-
ticker=df['ticker'][0]
|
1424
|
-
fromdate=str(df.index[0].date())
|
1425
|
-
todate=str(df.index[-1].date())
|
1426
|
-
label1=ectranslate(measure1)
|
1427
|
-
label2=ectranslate(measure2)
|
1428
|
-
ylabeltxt=""
|
1429
|
-
|
1430
|
-
tname=ticker_name(ticker,ticker_type=ticker_type)
|
1431
|
-
titletxt=text_lang("证券趋势分析:","Security Trend: ")+tname
|
1432
|
-
|
1433
|
-
import datetime; todaydt = datetime.date.today()
|
1434
|
-
footnote1=text_lang("数据来源:Sina/EM/Stooq/Yahoo/SWHY,","Source: Sina/EM/Stooq/Yahoo/SWHY, ")
|
1435
|
-
footnote=footnote1+str(todaydt)
|
1436
|
-
|
1437
|
-
#绘图
|
1438
|
-
if DEBUG:
|
1439
|
-
print("plot_line2")
|
1440
|
-
print("attention_value=",attention_value)
|
1441
|
-
print("attention_point=",attention_point)
|
1442
|
-
|
1443
|
-
plot_line2(df,ticker,measure1,label1,df,ticker,measure2,label2, \
|
1444
|
-
ylabeltxt,titletxt,footnote,zeroline=zeroline,twinx=twinx, \
|
1445
|
-
yline=attention_value,attention_value_area=attention_value_area, \
|
1446
|
-
xline=attention_point,attention_point_area=attention_point_area, \
|
1447
|
-
loc1=loc1,loc2=loc2,facecolor=facecolor)
|
1448
|
-
|
1449
|
-
return
|
1450
|
-
|
1451
|
-
if __name__ =="__main__":
|
1452
|
-
ticker='000002.SZ'
|
1453
|
-
measure1='Daily Ret%'
|
1454
|
-
measure2='Daily Adj Ret%'
|
1455
|
-
fromdate='2020-1-1'
|
1456
|
-
todate='2020-3-16'
|
1457
|
-
df=stock_ret(ticker,fromdate,todate,graph=False)
|
1458
|
-
comp_1security_2measures(df,measure1,measure2)
|
1459
|
-
#==============================================================================
|
1460
|
-
def comp_2securities_1measure(df1,df2,measure,twinx=False, \
|
1461
|
-
attention_value='',attention_value_area='', \
|
1462
|
-
attention_point='',attention_point_area='', \
|
1463
|
-
loc1='best',loc2='best',graph=True, \
|
1464
|
-
ticker_type=['auto','auto'],facecolor='whitesmoke'):
|
1465
|
-
"""
|
1466
|
-
功能:对比绘制两只证券的相同指标折线图。
|
1467
|
-
输入:指标数据集df1/2;证券代码ticker1/2;指标类别measure。
|
1468
|
-
输出:绘制证券指标双折线图,基于twinx判断使用单轴或双轴坐标。
|
1469
|
-
返回:无
|
1470
|
-
"""
|
1471
|
-
|
1472
|
-
#筛选证券指标,检验是否支持指标
|
1473
|
-
dfcols=list(df1)
|
1474
|
-
#nouselist=['date','Weekday','ticker']
|
1475
|
-
#for c in nouselist: dfcols.remove(c)
|
1476
|
-
|
1477
|
-
if not (measure in dfcols):
|
1478
|
-
print(" #Error(comp_2securities_1measure):only support measurement types of",dfcols)
|
1479
|
-
return
|
1480
|
-
|
1481
|
-
#判断是否绘制水平0线
|
1482
|
-
pricelist=['High','Low','Open','Close','Volume','Adj Close']
|
1483
|
-
if measure in pricelist: zeroline=False
|
1484
|
-
else:
|
1485
|
-
df_max=max([df1[measure].max(),df2[measure].max()])
|
1486
|
-
df_min=min([df1[measure].min(),df2[measure].min()])
|
1487
|
-
if df_max * df_min >0: #同正同负
|
1488
|
-
zeroline=False
|
1489
|
-
else:
|
1490
|
-
zeroline=True
|
1491
|
-
|
1492
|
-
#提取信息
|
1493
|
-
try:
|
1494
|
-
ticker1=df1['ticker'][0]
|
1495
|
-
except:
|
1496
|
-
print(" #Error(comp_2securities_1measure): none info found for the 1st symbol")
|
1497
|
-
return
|
1498
|
-
try:
|
1499
|
-
ticker2=df2['ticker'][0]
|
1500
|
-
except:
|
1501
|
-
print(" #Error(comp_2securities_1measure): none info found for the 2nd symbol")
|
1502
|
-
return
|
1503
|
-
|
1504
|
-
fromdate=str(df1.index[0].date())
|
1505
|
-
todate=str(df1.index[-1].date())
|
1506
|
-
label=ectranslate(measure)
|
1507
|
-
ylabeltxt=ectranslate(measure)
|
1508
|
-
|
1509
|
-
tname1=ticker_name(ticker1,ticker_type=ticker_type[0])
|
1510
|
-
tname2=ticker_name(ticker2,ticker_type=ticker_type[1])
|
1511
|
-
|
1512
|
-
#绘图
|
1513
|
-
print('')
|
1514
|
-
|
1515
|
-
titletxt1=text_lang("证券趋势分析:","Security Trend: ")
|
1516
|
-
titletxt=titletxt1+tname1+" vs "+tname2
|
1517
|
-
|
1518
|
-
import datetime; todaydt = datetime.date.today()
|
1519
|
-
footnote1=text_lang("数据来源:Sina/EM/Stooq/Yahoo/SWHY,","Data source: Sina/EM/Stooq/Yahoo/SWHY, ")
|
1520
|
-
footnote=footnote1+str(todaydt)+text_lang("统计","")
|
1521
|
-
|
1522
|
-
plot_line2(df1,ticker1,measure,label,df2,ticker2,measure,label, \
|
1523
|
-
ylabeltxt,titletxt,footnote,zeroline=zeroline,twinx=twinx, \
|
1524
|
-
yline=attention_value,attention_value_area=attention_value_area, \
|
1525
|
-
xline=attention_point,attention_point_area=attention_point_area, \
|
1526
|
-
loc1=loc1,loc2=loc2,facecolor=facecolor)
|
1527
|
-
|
1528
|
-
return
|
1529
|
-
|
1530
|
-
if __name__ =="__main__":
|
1531
|
-
ticker1='000002.SZ'
|
1532
|
-
ticker2='600266.SS'
|
1533
|
-
measure='Daily Ret%'
|
1534
|
-
fromdate='2020-1-1'
|
1535
|
-
todate='2020-3-16'
|
1536
|
-
df1=stock_ret(ticker1,fromdate,todate,graph=False)
|
1537
|
-
df2=stock_ret(ticker2,fromdate,todate,graph=False)
|
1538
|
-
comp_2securities_1measure(df1,df2,measure)
|
1539
|
-
#==============================================================================
|
1540
|
-
if __name__ =="__main__":
|
1541
|
-
tickers=['MSFT','AAPL']
|
1542
|
-
measures='Annual Ret Volatility%'
|
1543
|
-
|
1544
|
-
tickers='MSFT'
|
1545
|
-
measures=['Annual Ret Volatility%','Annual Ret%']
|
1546
|
-
|
1547
|
-
tickers='NVDA'
|
1548
|
-
tickers='AAPL'
|
1549
|
-
measures=['Close','Adj Close']
|
1550
|
-
|
1551
|
-
tickers=['000001.SS','^DJI']
|
1552
|
-
measures='Close'
|
1553
|
-
|
1554
|
-
fromdate='2024-5-1'
|
1555
|
-
todate='2025-6-23'
|
1556
|
-
adjust=''
|
1557
|
-
twinx=False
|
1558
|
-
loc1='best'
|
1559
|
-
loc2='lower left'
|
1560
|
-
graph=True
|
1561
|
-
source='auto'
|
1562
|
-
ticker_type='auto'
|
1563
|
-
facecolor='whitesmoke'
|
1564
|
-
|
1565
|
-
attention_value=''; attention_value_area=''
|
1566
|
-
attention_point=''; attention_point_area=''
|
1567
|
-
|
1568
|
-
|
1569
|
-
def compare_security(tickers,measures,fromdate,todate, \
|
1570
|
-
adjust='', \
|
1571
|
-
twinx=False, \
|
1572
|
-
attention_value='',attention_value_area='', \
|
1573
|
-
attention_point='',attention_point_area='', \
|
1574
|
-
loc1='best',loc2='lower left',graph=True,source='auto', \
|
1575
|
-
ticker_type='auto',facecolor='whitesmoke'):
|
1576
|
-
"""
|
1577
|
-
功能:函数克隆compare_stock,只能处理两个ticker一个measure,或一个ticker两个measure
|
1578
|
-
可以处理twinx=True
|
1579
|
-
"""
|
1580
|
-
"""
|
1581
|
-
# 应对导入失灵的函数
|
1582
|
-
from siat.security_prices import upper_ticker
|
1583
|
-
tickers=upper_ticker(tickers)
|
1584
|
-
result=compare_stock(tickers=tickers,measures=measures, \
|
1585
|
-
fromdate=fromdate,todate=todate, \
|
1586
|
-
adjust=adjust, \
|
1587
|
-
twinx=twinx, \
|
1588
|
-
loc1=loc1,loc2=loc2,graph=graph,source=source, \
|
1589
|
-
ticker_type=ticker_type,facecolor=facecolor)
|
1590
|
-
|
1591
|
-
return result
|
1592
|
-
"""
|
1593
|
-
#调试开关
|
1594
|
-
DEBUG=False
|
1595
|
-
|
1596
|
-
# 应对导入失灵的函数
|
1597
|
-
from siat.common import upper_ticker
|
1598
|
-
tickers=upper_ticker(tickers)
|
1599
|
-
|
1600
|
-
#判断证券代码个数
|
1601
|
-
#如果tickers只是一个字符串
|
1602
|
-
security_num = 0
|
1603
|
-
if isinstance(tickers,str):
|
1604
|
-
security_num = 1
|
1605
|
-
ticker1 = tickers
|
1606
|
-
#如果tickers是一个列表
|
1607
|
-
if isinstance(tickers,list):
|
1608
|
-
security_num = len(tickers)
|
1609
|
-
if security_num != 0:
|
1610
|
-
if security_num >= 1: ticker1 = tickers[0]
|
1611
|
-
if security_num >= 2: ticker2 = tickers[1]
|
1612
|
-
else:
|
1613
|
-
print(" #Error(compare_security):security code/codes needed.")
|
1614
|
-
return None,None
|
1615
|
-
|
1616
|
-
#判断测度个数
|
1617
|
-
#如果measures只是一个字符串
|
1618
|
-
measure_num = 0
|
1619
|
-
if isinstance(measures,str):
|
1620
|
-
measure_num = 1
|
1621
|
-
#measure1 = measures
|
1622
|
-
measure1 = measure2 = measures
|
1623
|
-
#如果measures是一个列表
|
1624
|
-
if isinstance(measures,list):
|
1625
|
-
measure_num = len(measures)
|
1626
|
-
if measure_num != 0:
|
1627
|
-
if measure_num >= 1: measure1 = measures[0]
|
1628
|
-
if measure_num >= 2: measure2 = measures[1]
|
1629
|
-
else:
|
1630
|
-
print(" #Error(compare_security): a measurement indicator needed.")
|
1631
|
-
return None,None
|
1632
|
-
|
1633
|
-
#解析ticker_type
|
1634
|
-
if isinstance(ticker_type,str):
|
1635
|
-
ticker_type1=ticker_type2=ticker_type
|
1636
|
-
if isinstance(ticker_type,list) and len(ticker_type)==1:
|
1637
|
-
ticker_type1=ticker_type2=ticker_type[0]
|
1638
|
-
if isinstance(ticker_type,list) and len(ticker_type) >= 2:
|
1639
|
-
ticker_type1=ticker_type[0]
|
1640
|
-
ticker_type2=ticker_type[1]
|
1641
|
-
ticker_type_list=[ticker_type1,ticker_type2]
|
1642
|
-
|
1643
|
-
#屏蔽函数内print信息输出的类
|
1644
|
-
import os, sys
|
1645
|
-
class HiddenPrints:
|
1646
|
-
def __enter__(self):
|
1647
|
-
self._original_stdout = sys.stdout
|
1648
|
-
sys.stdout = open(os.devnull, 'w')
|
1649
|
-
|
1650
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
1651
|
-
sys.stdout.close()
|
1652
|
-
sys.stdout = self._original_stdout
|
1653
|
-
|
1654
|
-
#单一证券代码+两个测度指标
|
1655
|
-
if (security_num == 1) and (measure_num >= 2):
|
1656
|
-
|
1657
|
-
print(" Searching",ticker1,"for",measure1,"info ... ...")
|
1658
|
-
#复权价判断1
|
1659
|
-
adjust1=adjust
|
1660
|
-
if ('Adj' in measure1) and (adjust1 ==''):
|
1661
|
-
adjust1='qfq'
|
1662
|
-
|
1663
|
-
with HiddenPrints():
|
1664
|
-
#security_indicator未能做到同时获得Close和Adj Close
|
1665
|
-
df1tmp=security_indicator(ticker=ticker1,indicator=measure1,adjust=adjust1, \
|
1666
|
-
fromdate=fromdate,todate=todate, \
|
1667
|
-
source=source, \
|
1668
|
-
ticker_type=ticker_type, \
|
1669
|
-
graph=False)
|
1670
|
-
|
1671
|
-
if df_have_data(df1tmp)=="Found":
|
1672
|
-
pltdf1= df1tmp[[measure1]]
|
1673
|
-
else:
|
1674
|
-
print(" #Error(compare_security):no info found for",ticker1,"on",measure1)
|
1675
|
-
return None,None
|
1676
|
-
|
1677
|
-
print(" Searching",ticker1,"for",measure2,"info ... ...")
|
1678
|
-
#复权价判断2
|
1679
|
-
adjust2=adjust
|
1680
|
-
if ('Adj' in measure2) and (adjust2 ==''):
|
1681
|
-
adjust2='qfq'
|
1682
|
-
|
1683
|
-
with HiddenPrints():
|
1684
|
-
#security_indicator未能做到同时获得Close和Adj Close
|
1685
|
-
df2tmp=security_indicator(ticker=ticker1,indicator=measure2,adjust=adjust2, \
|
1686
|
-
fromdate=fromdate,todate=todate, \
|
1687
|
-
source=source, \
|
1688
|
-
ticker_type=ticker_type, \
|
1689
|
-
graph=False)
|
1690
|
-
|
1691
|
-
if df_have_data(df2tmp)=="Found":
|
1692
|
-
pltdf2= df2tmp[[measure2]]
|
1693
|
-
else:
|
1694
|
-
print(" #Error(compare_security):no info found for",ticker1,"on",measure2)
|
1695
|
-
return None,None
|
1696
|
-
|
1697
|
-
pltdf=pd.merge(pltdf1,pltdf2,left_index=True,right_index=True)
|
1698
|
-
pltdf['ticker']=ticker1
|
1699
|
-
|
1700
|
-
#绘制单个证券的双指标对比图
|
1701
|
-
if graph:
|
1702
|
-
if DEBUG:
|
1703
|
-
print("In compare_security:")
|
1704
|
-
print("Going to comp_1security_2measures ...")
|
1705
|
-
print("attention_value=",attention_value)
|
1706
|
-
print("attention_point=",attention_point)
|
1707
|
-
|
1708
|
-
comp_1security_2measures(pltdf,measure1,measure2,twinx=twinx, \
|
1709
|
-
attention_value=attention_value,attention_value_area=attention_value_area, \
|
1710
|
-
attention_point=attention_point,attention_point_area=attention_point_area, \
|
1711
|
-
loc1=loc1,loc2=loc2,graph=graph, \
|
1712
|
-
ticker_type=ticker_type[0],facecolor=facecolor)
|
1713
|
-
|
1714
|
-
try:
|
1715
|
-
result1=pltdf[['ticker',measure1]]
|
1716
|
-
except:
|
1717
|
-
result1=None
|
1718
|
-
try:
|
1719
|
-
result2=pltdf[['ticker',measure2]]
|
1720
|
-
except:
|
1721
|
-
result2=None
|
1722
|
-
return result1,result2
|
1723
|
-
|
1724
|
-
elif (security_num >= 2) and (measure_num >= 1):
|
1725
|
-
#双证券+单个测度指标
|
1726
|
-
if ('Adj' in measure1) and (adjust ==''):
|
1727
|
-
adjust='qfq'
|
1728
|
-
|
1729
|
-
df1tmp=security_indicator(ticker=ticker1,indicator=measure1,adjust=adjust, \
|
1730
|
-
fromdate=fromdate,todate=todate, \
|
1731
|
-
source=source, \
|
1732
|
-
ticker_type=ticker_type, \
|
1733
|
-
graph=False)
|
1734
|
-
if df_have_data(df1tmp)=="Found":
|
1735
|
-
pltdf1=df1tmp[['ticker',measure1]]
|
1736
|
-
else:
|
1737
|
-
print(" #Error(compare_security):no info found for",ticker1,"on",measure1)
|
1738
|
-
return None,None
|
1739
|
-
|
1740
|
-
df2tmp=security_indicator(ticker=ticker2,indicator=measure1,adjust=adjust, \
|
1741
|
-
fromdate=fromdate,todate=todate, \
|
1742
|
-
source=source, \
|
1743
|
-
ticker_type=ticker_type, \
|
1744
|
-
graph=False)
|
1745
|
-
if df_have_data(df2tmp)=="Found":
|
1746
|
-
pltdf2=df2tmp[['ticker',measure1]]
|
1747
|
-
else:
|
1748
|
-
print(" #Error(compare_security):no info found for",ticker2,"on",measure1)
|
1749
|
-
return None,None
|
1750
|
-
|
1751
|
-
#绘制双证券单指标对比图
|
1752
|
-
if graph:
|
1753
|
-
if DEBUG:
|
1754
|
-
print("In compare_security ...")
|
1755
|
-
print("Going to comp_2securities_1measure")
|
1756
|
-
print("attention_value=",attention_value)
|
1757
|
-
print("attention_point=",attention_point)
|
1758
|
-
|
1759
|
-
comp_2securities_1measure(pltdf1,pltdf2,measure1,twinx=twinx, \
|
1760
|
-
attention_value=attention_value,attention_value_area=attention_value_area, \
|
1761
|
-
attention_point=attention_point,attention_point_area=attention_point_area, \
|
1762
|
-
loc1=loc1,loc2=loc2,graph=graph, \
|
1763
|
-
ticker_type=ticker_type_list,facecolor=facecolor)
|
1764
|
-
|
1765
|
-
try:
|
1766
|
-
result1=pltdf1[[measure1]]
|
1767
|
-
except:
|
1768
|
-
print(" #Error(compare_secuirty): measure",measure1,"not found for",ticker1)
|
1769
|
-
result1=None
|
1770
|
-
try:
|
1771
|
-
result2=pltdf2[[measure1]]
|
1772
|
-
except:
|
1773
|
-
print(" #Error(compare_secuirty): measure",measure1,"not found for",ticker2)
|
1774
|
-
result2=None
|
1775
|
-
return result1,result2
|
1776
|
-
|
1777
|
-
else:
|
1778
|
-
print(" #Warning(compare_secuirty):only support 1 ticker + 2 measures or 2 tickers + 1 measure.")
|
1779
|
-
return None,None
|
1780
|
-
|
1781
|
-
|
1782
|
-
#==============================================================================
|
1783
|
-
if __name__ =="__main__":
|
1784
|
-
tickers=['MSFT','AAPL']
|
1785
|
-
measures='Annual Ret Volatility%'
|
1786
|
-
fromdate='2023-1-1'
|
1787
|
-
todate='2023-12-31'
|
1788
|
-
adjust=''
|
1789
|
-
twinx=False
|
1790
|
-
loc1='best'
|
1791
|
-
loc2='lower left'
|
1792
|
-
graph=True
|
1793
|
-
source='auto'
|
1794
|
-
ticker_type='auto'
|
1795
|
-
facecolor='whitesmoke'
|
1796
|
-
|
1797
|
-
|
1798
|
-
def compare_stock(tickers,measures,fromdate,todate, \
|
1799
|
-
adjust='', \
|
1800
|
-
twinx=False, \
|
1801
|
-
loc1='best',loc2='lower left',graph=True,source='auto', \
|
1802
|
-
ticker_type='auto',facecolor='whitesmoke'):
|
1803
|
-
"""
|
1804
|
-
功能:对比绘制折线图:一只证券的两种测度,或两只证券的同一个测度。
|
1805
|
-
输入:
|
1806
|
-
证券代码tickers,如果是一个列表且内含两个证券代码,则认为希望比较两个证券的
|
1807
|
-
同一个测度指标。如果是一个列表但只内含一个证券代码或只是一个证券代码的字符串,
|
1808
|
-
则认为希望比较一个证券的两个测度指标。
|
1809
|
-
测度指标measures:如果是一个列表且内含两个测度指标,则认为希望比较一个证券的
|
1810
|
-
两个测度指标。如果是一个列表但只内含一个测度指标或只是一个测度指标的字符串,
|
1811
|
-
则认为希望比较两个证券的同一个测度指标。
|
1812
|
-
如果两个判断互相矛盾,以第一个为准。
|
1813
|
-
开始日期fromdate,结束日期todate。
|
1814
|
-
输出:绘制证券价格折线图,手动指定是否使用单轴或双轴坐标。
|
1815
|
-
返回:无
|
1816
|
-
|
1817
|
-
打算废弃?
|
1818
|
-
"""
|
1819
|
-
#调试开关
|
1820
|
-
DEBUG=False
|
1821
|
-
# 应对导入失灵的函数
|
1822
|
-
from siat.common import upper_ticker
|
1823
|
-
tickers=upper_ticker(tickers)
|
1824
|
-
|
1825
|
-
#判断证券代码个数
|
1826
|
-
#如果tickers只是一个字符串
|
1827
|
-
security_num = 0
|
1828
|
-
if isinstance(tickers,str):
|
1829
|
-
security_num = 1
|
1830
|
-
ticker1 = tickers
|
1831
|
-
#如果tickers是一个列表
|
1832
|
-
if isinstance(tickers,list):
|
1833
|
-
security_num = len(tickers)
|
1834
|
-
if security_num == 0:
|
1835
|
-
print(" #Error(compare_stock):security code/codes needed.")
|
1836
|
-
return None,None
|
1837
|
-
if security_num >= 1: ticker1 = tickers[0]
|
1838
|
-
if security_num >= 2: ticker2 = tickers[1]
|
1839
|
-
|
1840
|
-
#判断测度个数
|
1841
|
-
#如果measures只是一个字符串
|
1842
|
-
measure_num = 0
|
1843
|
-
if isinstance(measures,str):
|
1844
|
-
measure_num = 1
|
1845
|
-
measure1 = measures
|
1846
|
-
#如果measures是一个列表
|
1847
|
-
if isinstance(measures,list):
|
1848
|
-
measure_num = len(measures)
|
1849
|
-
if measure_num == 0:
|
1850
|
-
print(" #Error(compare_stock): a measurement indicator needed.")
|
1851
|
-
return None,None
|
1852
|
-
if measure_num >= 1: measure1 = measures[0]
|
1853
|
-
if measure_num >= 2: measure2 = measures[1]
|
1854
|
-
|
1855
|
-
#延伸开始日期
|
1856
|
-
fromdate1=date_adjust(fromdate,adjust=-365)
|
1857
|
-
|
1858
|
-
#单一证券代码+两个测度指标
|
1859
|
-
if (security_num == 1) and (measure_num >= 2):
|
1860
|
-
if (('Adj' in measure1) or ('Adj' in measure2)) and (adjust ==''):
|
1861
|
-
adjust='qfq'
|
1862
|
-
|
1863
|
-
#证券ticker1:抓取行情,并计算其各种期间的收益率
|
1864
|
-
df1a=stock_ret(ticker1,fromdate1,todate,adjust=adjust,graph=False,source=source,ticker_type=ticker_type)
|
1865
|
-
if df1a is None: return None,None
|
1866
|
-
if DEBUG: print("compare|df1a first date:",df1a.index[0])
|
1867
|
-
#加入价格波动指标
|
1868
|
-
df1b=price_volatility2(df1a,ticker1,fromdate1,todate,graph=False,ticker_type=ticker_type)
|
1869
|
-
if DEBUG: print("compare|df1b first date:",df1b.index[0])
|
1870
|
-
#加入收益率波动指标
|
1871
|
-
df1c=ret_volatility2(df1b,ticker1,fromdate1,todate,graph=False,ticker_type=ticker_type)
|
1872
|
-
if DEBUG: print("compare|df1c first date:",df1c.index[0])
|
1873
|
-
#加入收益率下偏标准差指标
|
1874
|
-
df1d=ret_lpsd2(df1c,ticker1,fromdate1,todate,graph=False,ticker_type=ticker_type)
|
1875
|
-
if DEBUG: print("compare|df1d first date:",df1d.index[0])
|
1876
|
-
|
1877
|
-
#去掉开始日期以前的数据
|
1878
|
-
pltdf1=df1d[df1d.index >= fromdate1]
|
1879
|
-
#绘制单个证券的双指标对比图
|
1880
|
-
if graph:
|
1881
|
-
comp_1security_2measures(pltdf1,measure1,measure2,twinx=twinx, \
|
1882
|
-
loc1=loc1,loc2=loc2,graph=graph, \
|
1883
|
-
ticker_type=ticker_type,facecolor=facecolor)
|
1884
|
-
|
1885
|
-
try:
|
1886
|
-
result1=pltdf1[[measure1]]
|
1887
|
-
except:
|
1888
|
-
return None,None
|
1889
|
-
try:
|
1890
|
-
result2=pltdf1[[measure2]]
|
1891
|
-
except:
|
1892
|
-
return result1,None
|
1893
|
-
|
1894
|
-
elif (security_num >= 2) and (measure_num >= 1):
|
1895
|
-
#双证券+单个测度指标
|
1896
|
-
if ('Adj' in measure1) and (adjust ==''):
|
1897
|
-
adjust='qfq'
|
1898
|
-
|
1899
|
-
#解析ticker_type
|
1900
|
-
if isinstance(ticker_type,str):
|
1901
|
-
ticker_type1=ticker_type2=ticker_type
|
1902
|
-
if isinstance(ticker_type,list) and len(ticker_type)==1:
|
1903
|
-
ticker_type1=ticker_type2=ticker_type[0]
|
1904
|
-
if isinstance(ticker_type,list) and len(ticker_type) > 1:
|
1905
|
-
ticker_type1=ticker_type[0]
|
1906
|
-
ticker_type2=ticker_type[1]
|
1907
|
-
ticker_type_list=[ticker_type1,ticker_type2]
|
1908
|
-
|
1909
|
-
#证券ticker1:抓取行情,并计算其各种期间的收益率
|
1910
|
-
df1a=stock_ret(ticker1,fromdate1,todate,adjust=adjust,graph=False,source=source,ticker_type=ticker_type1)
|
1911
|
-
if df1a is None: return None,None
|
1912
|
-
#加入价格波动指标
|
1913
|
-
df1b=price_volatility2(df1a,ticker1,fromdate1,todate,graph=False,ticker_type=ticker_type1)
|
1914
|
-
#加入收益率波动指标
|
1915
|
-
df1c=ret_volatility2(df1b,ticker1,fromdate1,todate,graph=False,ticker_type=ticker_type1)
|
1916
|
-
#加入收益率下偏标准差指标
|
1917
|
-
df1d=ret_lpsd2(df1c,ticker1,fromdate1,todate,graph=False,ticker_type=ticker_type1)
|
1918
|
-
#去掉开始日期以前的数据
|
1919
|
-
pltdf1=df1d[df1d.index >= fromdate1]
|
1920
|
-
|
1921
|
-
#证券ticker2:
|
1922
|
-
df2a=stock_ret(ticker2,fromdate1,todate,adjust=adjust,graph=False,source=source,ticker_type=ticker_type2)
|
1923
|
-
if df2a is None: return None,None
|
1924
|
-
df2b=price_volatility2(df2a,ticker2,fromdate1,todate,graph=False,ticker_type=ticker_type2)
|
1925
|
-
df2c=ret_volatility2(df2b,ticker2,fromdate1,todate,graph=False,ticker_type=ticker_type2)
|
1926
|
-
df2d=ret_lpsd2(df2c,ticker2,fromdate1,todate,graph=False,ticker_type=ticker_type2)
|
1927
|
-
pltdf2=df2d[df2d.index >= fromdate1]
|
1928
|
-
|
1929
|
-
#绘制双证券单指标对比图
|
1930
|
-
if graph:
|
1931
|
-
comp_2securities_1measure(pltdf1,pltdf2,measure1,twinx=twinx, \
|
1932
|
-
loc1=loc1,loc2=loc2,graph=graph, \
|
1933
|
-
ticker_type=ticker_type_list,facecolor=facecolor)
|
1934
|
-
|
1935
|
-
try:
|
1936
|
-
result1=pltdf1[[measure1]]
|
1937
|
-
result2=pltdf2[[measure1]]
|
1938
|
-
except:
|
1939
|
-
print(" #Error(compare_stock): unknown measure",measure1)
|
1940
|
-
return None,None
|
1941
|
-
|
1942
|
-
else:
|
1943
|
-
print(" #Error(compare_stock):do not understand what to compare.")
|
1944
|
-
return None,None
|
1945
|
-
|
1946
|
-
return result1,result2
|
1947
|
-
|
1948
|
-
if __name__ =="__main__":
|
1949
|
-
tickers='000002.SZ'
|
1950
|
-
measures=['Close','Adj Close']
|
1951
|
-
fromdate='2020-1-1'
|
1952
|
-
todate='2020-3-16'
|
1953
|
-
compare_stock(tickers,measures,fromdate,todate)
|
1954
|
-
|
1955
|
-
tickers2=['000002.SZ','600266.SS']
|
1956
|
-
measures2=['Close','Adj Close']
|
1957
|
-
compare_stock(tickers2,measures2,fromdate,todate)
|
1958
|
-
|
1959
|
-
tickers3=['000002.SZ','600266.SS']
|
1960
|
-
measures3='Close'
|
1961
|
-
compare_stock(tickers3,measures3,fromdate,todate)
|
1962
|
-
|
1963
|
-
tickers4=['000002.SZ','600606.SS','600266.SS']
|
1964
|
-
measures4=['Close','Adj Close','Daily Return']
|
1965
|
-
compare_stock(tickers4,measures4,fromdate,todate)
|
1966
|
-
|
1967
|
-
#==============================================================================
|
1968
|
-
if __name__ =="__main__":
|
1969
|
-
# 测试组1
|
1970
|
-
tickers=["AMZN","EBAY","SHOP","BABA","JD"]
|
1971
|
-
tickers=["AMZN","EBAY","SHOP","BABA","JD","PDD"]
|
1972
|
-
tickers=['000001.SS',"399001.SZ","000300.SS"]
|
1973
|
-
tickers=['000001.SS','^N225','^KS11']
|
1974
|
-
measure="Annual Ret%"
|
1975
|
-
measure="Exp Ret%"
|
1976
|
-
measure="Close"
|
1977
|
-
measure="Annual Ret Volatility%"
|
1978
|
-
|
1979
|
-
start="2020-1-1"
|
1980
|
-
end="2022-7-31"
|
1981
|
-
|
1982
|
-
preprocess='scaling'
|
1983
|
-
linewidth=1.5
|
1984
|
-
scaling_option='start'
|
1985
|
-
|
1986
|
-
# 测试组2
|
1987
|
-
tickers=["GCZ25.CMX","GCZ24.CMX"]
|
1988
|
-
measure='Close'
|
1989
|
-
start="2020-1-1"
|
1990
|
-
end="2020-6-30"
|
1991
|
-
|
1992
|
-
# 测试组3
|
1993
|
-
tickers=["MBG.DE", "BMW.DE"]
|
1994
|
-
measure='Close'
|
1995
|
-
start="2025-6-1"
|
1996
|
-
end="2025-6-15"
|
1997
|
-
|
1998
|
-
attention_value='';attention_value_area=''
|
1999
|
-
attention_point='';attention_point_area=''
|
2000
|
-
adjust=''
|
2001
|
-
axhline_value=0;axhline_label=''
|
2002
|
-
preprocess='none';linewidth=1.5
|
2003
|
-
scaling_option='start'
|
2004
|
-
plus_sign=False
|
2005
|
-
band_area=''
|
2006
|
-
graph=True;loc='best';facecolor='whitesmoke'
|
2007
|
-
annotate=False;annotate_value=False
|
2008
|
-
smooth=True
|
2009
|
-
source='auto'
|
2010
|
-
mark_top=True;mark_bottom=True
|
2011
|
-
mark_start=False;mark_end=False
|
2012
|
-
ticker_type='auto'
|
2013
|
-
|
2014
|
-
def compare_msecurity(tickers,measure,start,end, \
|
2015
|
-
attention_value='',attention_value_area='', \
|
2016
|
-
attention_point='',attention_point_area='', \
|
2017
|
-
adjust='', \
|
2018
|
-
axhline_value=0,axhline_label='', \
|
2019
|
-
preprocess='none',linewidth=1.5, \
|
2020
|
-
scaling_option='start', \
|
2021
|
-
plus_sign=False, \
|
2022
|
-
band_area='', \
|
2023
|
-
graph=True,loc='best',facecolor='whitesmoke', \
|
2024
|
-
annotate=False,annotate_value=False, \
|
2025
|
-
smooth=True, \
|
2026
|
-
source='auto', \
|
2027
|
-
mark_top=True,mark_bottom=True, \
|
2028
|
-
mark_start=False,mark_end=False, \
|
2029
|
-
ticker_type='auto'):
|
2030
|
-
"""
|
2031
|
-
功能:比较并绘制多条证券指标曲线(多于2条),个数可为双数或单数
|
2032
|
-
注意:
|
2033
|
-
tickers中须含有2个及以上股票代码,
|
2034
|
-
measure为单一指标,
|
2035
|
-
axhline_label不为空时绘制水平线
|
2036
|
-
|
2037
|
-
preprocess:是否对绘图数据进行预处理,仅适用于股价等数量级差异较大的数据,
|
2038
|
-
不适用于比例、比率和百分比等数量级较为一致的指标。
|
2039
|
-
standardize: 标准化处理,(x - mean(x))/std(x)
|
2040
|
-
normalize: 归一化处理,(x - min(x))/(max(x) - min(x))
|
2041
|
-
logarithm: 对数处理,np.log(x)
|
2042
|
-
scaling:缩放处理,五种选项scaling_option
|
2043
|
-
(mean均值,min最小值,start开始值,percentage相对每条曲线起点值的百分比,
|
2044
|
-
change%相对每条曲线起点值变化的百分比)
|
2045
|
-
change%方式的图形更接近于持有收益率(Exp Ret%),设为默认的缩放方式。
|
2046
|
-
|
2047
|
-
"""
|
2048
|
-
DEBUG=False
|
2049
|
-
|
2050
|
-
# 应对导入失灵的函数
|
2051
|
-
from siat.common import upper_ticker
|
2052
|
-
tickers=upper_ticker(tickers)
|
2053
|
-
if not isinstance(tickers,list):
|
2054
|
-
tickers=[tickers]
|
2055
|
-
|
2056
|
-
# 去掉重复代码:有必要,重复代码将导致后续处理出错KeyError: 0!
|
2057
|
-
tickers=list(set(tickers))
|
2058
|
-
"""
|
2059
|
-
num=len(tickers)
|
2060
|
-
if num <2:
|
2061
|
-
print(" #Error(compare_msecurity): need more tickers")
|
2062
|
-
return None
|
2063
|
-
"""
|
2064
|
-
if isinstance(measure,list):
|
2065
|
-
measure=measure[0]
|
2066
|
-
|
2067
|
-
print(" Searching securities for",measure,"...")
|
2068
|
-
#屏蔽函数内print信息输出的类
|
2069
|
-
import os, sys
|
2070
|
-
class HiddenPrints:
|
2071
|
-
def __enter__(self):
|
2072
|
-
self._original_stdout = sys.stdout
|
2073
|
-
sys.stdout = open(os.devnull, 'w')
|
2074
|
-
|
2075
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
2076
|
-
sys.stdout.close()
|
2077
|
-
sys.stdout = self._original_stdout
|
2078
|
-
|
2079
|
-
#循环获取证券指标
|
2080
|
-
import pandas as pd
|
2081
|
-
from functools import reduce
|
2082
|
-
|
2083
|
-
#预处理ticker_type成为列表ticker_type_list
|
2084
|
-
if isinstance(ticker_type,str):
|
2085
|
-
ticker_type_list=[ticker_type] * len(tickers)
|
2086
|
-
if isinstance(ticker_type,list):
|
2087
|
-
ticker_type_list=ticker_type
|
2088
|
-
if len(ticker_type_list) < len(tickers): #延续最后项的ticker_type
|
2089
|
-
ticker_type_list=ticker_type_list+[ticker_type_list[-1]]*(len(tickers)-len(ticker_type_list))
|
2090
|
-
|
2091
|
-
dfs=pd.DataFrame()
|
2092
|
-
for t in tickers:
|
2093
|
-
print(" Looking security info for",t,'...')
|
2094
|
-
pos=tickers.index(t)
|
2095
|
-
tt=ticker_type_list[pos]
|
2096
|
-
|
2097
|
-
with HiddenPrints():
|
2098
|
-
df_tmp=security_indicator(t,measure,start,end,adjust=adjust,graph=False,source=source,ticker_type=tt)
|
2099
|
-
if df_tmp is None:
|
2100
|
-
print(" #Warning(compare_msecurity): security info not found for",t)
|
2101
|
-
continue
|
2102
|
-
if len(df_tmp)==0:
|
2103
|
-
print(" #Warning(compare_msecurity): security info not found for",t,'between',start,'and',end)
|
2104
|
-
continue
|
2105
|
-
|
2106
|
-
df_tmp1=pd.DataFrame(df_tmp[measure])
|
2107
|
-
|
2108
|
-
tname=ticker_name(t,tt)
|
2109
|
-
df_tmp1.rename(columns={measure:tname},inplace=True)
|
2110
|
-
|
2111
|
-
# 将band_area中的ticker替换为tname
|
2112
|
-
if band_area != '':
|
2113
|
-
for index, item in enumerate(band_area):
|
2114
|
-
if item == t:
|
2115
|
-
band_area[index] = tname
|
2116
|
-
|
2117
|
-
if len(dfs)==0:
|
2118
|
-
dfs=df_tmp1
|
2119
|
-
else:
|
2120
|
-
dfs=pd.concat([dfs,df_tmp1],axis=1,join='outer')
|
2121
|
-
|
2122
|
-
if dfs is None:
|
2123
|
-
print(" #Error(compare_msecurity): no records found for",tickers)
|
2124
|
-
return None
|
2125
|
-
if len(dfs)==0:
|
2126
|
-
print(" #Error(compare_msecurity): zero records found for",tickers)
|
2127
|
-
return None
|
2128
|
-
|
2129
|
-
dfs.sort_index(ascending=True,inplace=True)
|
2130
|
-
|
2131
|
-
# 若不绘图则返回原始数据
|
2132
|
-
if not graph:
|
2133
|
-
return dfs
|
2134
|
-
|
2135
|
-
#绘制多条曲线
|
2136
|
-
y_label=ectranslate(measure)
|
2137
|
-
tickersplit=tickers[0].split('.')
|
2138
|
-
if (len(tickersplit) > 1) and (measure == 'Close'):
|
2139
|
-
if tickersplit[1].upper() in ['M','B']:
|
2140
|
-
#y_label='指标'
|
2141
|
-
y_label='指标对比'
|
2142
|
-
|
2143
|
-
x_label1cn="数据来源: Sina/EM/Stooq/Yahoo/SWHY,"
|
2144
|
-
x_label1en="Source: Sina/EM/Stooq/Yahoo/SWHY, "
|
2145
|
-
x_label1=text_lang(x_label1cn,x_label1en)
|
2146
|
-
import datetime; todaydt = datetime.date.today()
|
2147
|
-
x_label=x_label1+str(todaydt)
|
2148
|
-
|
2149
|
-
title_txt1=text_lang("证券趋势分析","Security Trend")
|
2150
|
-
if y_label != '':
|
2151
|
-
title_txt=title_txt1+": "+y_label
|
2152
|
-
else:
|
2153
|
-
title_txt=title_txt1
|
2154
|
-
|
2155
|
-
if preprocess == 'scaling' and scaling_option == 'change%':
|
2156
|
-
title_txt2=text_lang("涨跌幅度","Changes")
|
2157
|
-
if ':' in title_txt:
|
2158
|
-
title_txt=title_txt+', '+title_txt2
|
2159
|
-
else:
|
2160
|
-
title_txt=title_txt+': '+title_txt2
|
2161
|
-
|
2162
|
-
axhline_value=0
|
2163
|
-
axhline_label="零线"
|
2164
|
-
|
2165
|
-
# 标准化处理
|
2166
|
-
try:
|
2167
|
-
dfs2,axhline_label,x_label,y_label,plus_sign=df_preprocess(dfs,measure, \
|
2168
|
-
axhline_label=axhline_label,x_label=x_label,y_label=y_label, \
|
2169
|
-
preprocess=preprocess,scaling_option=scaling_option)
|
2170
|
-
except:
|
2171
|
-
print(" #Error(compare_msecurity): preprocess failed, returning dfs for further check")
|
2172
|
-
#df_display_CSS(dfs,titletxt='Unexpected Data in dfs')
|
2173
|
-
return dfs
|
2174
|
-
|
2175
|
-
# 填充非交易日的缺失值,使得绘制的曲线连续
|
2176
|
-
dfs2.fillna(axis=0,method='ffill',inplace=True)
|
2177
|
-
#dfs2.fillna(axis=0,method='bfill',inplace=True)
|
2178
|
-
|
2179
|
-
if DEBUG:
|
2180
|
-
print("DEBUG: dfs2=",list(dfs2))
|
2181
|
-
|
2182
|
-
above_zero=0; below_zero=0
|
2183
|
-
for c in list(dfs2):
|
2184
|
-
c_max=dfs2[c].max(); c_min=dfs2[c].min()
|
2185
|
-
try:
|
2186
|
-
if c_max>0 or c_min>0: above_zero+=1
|
2187
|
-
if c_max<0 or c_min<0: below_zero+=1
|
2188
|
-
except: continue
|
2189
|
-
|
2190
|
-
if DEBUG:
|
2191
|
-
print("DEBUG: above_zero=",above_zero,'below_zero=',below_zero)
|
2192
|
-
|
2193
|
-
if above_zero>0 and below_zero>0: #有正有负
|
2194
|
-
#if 'Ret%' in measure:
|
2195
|
-
if axhline_label=='':
|
2196
|
-
axhline_label='零线'
|
2197
|
-
|
2198
|
-
#持有类指标的首行置为零
|
2199
|
-
colList=list(dfs2)
|
2200
|
-
index1=dfs2.head(1).index.values[0]
|
2201
|
-
for c in colList:
|
2202
|
-
if 'Exp Ret%' in c:
|
2203
|
-
dfs2.loc[dfs2[dfs2.index==index1].index.tolist(),c]=0
|
2204
|
-
|
2205
|
-
draw_lines(dfs2,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
2206
|
-
data_label=False,resample_freq='H',smooth=smooth,linewidth=linewidth,loc=loc, \
|
2207
|
-
attention_value=attention_value,attention_value_area=attention_value_area, \
|
2208
|
-
attention_point=attention_point,attention_point_area=attention_point_area, \
|
2209
|
-
band_area=band_area, \
|
2210
|
-
annotate=annotate,annotate_value=annotate_value,plus_sign=plus_sign, \
|
2211
|
-
mark_top=mark_top,mark_bottom=mark_bottom, \
|
2212
|
-
mark_start=mark_start,mark_end=mark_end,facecolor=facecolor)
|
2213
|
-
|
2214
|
-
return dfs2
|
2215
|
-
|
2216
|
-
if __name__ =="__main__":
|
2217
|
-
tickers=['000001.SS',"^HSI","^TWII"]
|
2218
|
-
df=compare_msecurity(tickers,'Close','2020-1-1','2022-12-14',preprocess='standardize')
|
2219
|
-
df=compare_msecurity(tickers,'Close','2020-1-1','2022-12-14',preprocess='normalize')
|
2220
|
-
df=compare_msecurity(tickers,'Close','2020-1-1','2022-12-14',preprocess='logarithm')
|
2221
|
-
df=compare_msecurity(tickers,'Close','2020-1-1','2022-12-14',preprocess='scaling')
|
2222
|
-
|
2223
|
-
#==============================================================================
|
2224
|
-
if __name__ =="__main__":
|
2225
|
-
tickers=['JD','BABA','BIDU','VIPS','PDD']
|
2226
|
-
start='2023-5-1'
|
2227
|
-
end='2023-6-16'
|
2228
|
-
ret_measure='Exp Ret%'
|
2229
|
-
ret_measure='Annual Ret%'
|
2230
|
-
|
2231
|
-
risk_type='Volatility'
|
2232
|
-
annotate=True
|
2233
|
-
graph=True
|
2234
|
-
smooth=False
|
2235
|
-
|
2236
|
-
|
2237
|
-
def compare_mrrr(tickers,start,end,ret_measure='Exp Ret%',risk_type='Volatility', \
|
2238
|
-
annotate=False,graph=True,smooth=True,winsorize_limits=[0.05,0.05], \
|
2239
|
-
facecolor='whitesmoke'):
|
2240
|
-
"""
|
2241
|
-
功能:rrr = return-risk ratio
|
2242
|
-
比较多个证券的简单收益-风险性价比,基于compare_msecurity
|
2243
|
-
ret_measure='Exp Ret%':可以为持有收益率,或滚动收益率
|
2244
|
-
risk_type='Volatility':可以为标准差,或下偏标准差
|
2245
|
-
|
2246
|
-
winsorize_limits=[0.05,0.05]:去掉最低的5%(第一个参数),去掉最高的5%(第二个参数)
|
2247
|
-
"""
|
2248
|
-
#print("Searching for return-risk performance based on",ret_measure,"it takes great time, please wait ...")
|
2249
|
-
|
2250
|
-
try:
|
2251
|
-
df_ret=compare_msecurity(tickers,ret_measure,start,end,graph=False)
|
2252
|
-
except:
|
2253
|
-
return None
|
2254
|
-
cols=list(df_ret)
|
2255
|
-
|
2256
|
-
risk_measure=ret_measure[:-1]+' '+risk_type+'%'
|
2257
|
-
try:
|
2258
|
-
df_risk=compare_msecurity(tickers,risk_measure,start,end,graph=False)
|
2259
|
-
except:
|
2260
|
-
return None
|
2261
|
-
|
2262
|
-
import pandas as pd
|
2263
|
-
df=pd.merge(df_ret,df_risk,left_index=True,right_index=True)
|
2264
|
-
#df.fillna(axis=0,method='ffill',inplace=True)
|
2265
|
-
#df.fillna(axis=0,method='bfill',inplace=True)
|
2266
|
-
|
2267
|
-
for c in cols:
|
2268
|
-
df[c]=df[c+'_x']/df[c+'_y']
|
2269
|
-
|
2270
|
-
df2=df[cols]
|
2271
|
-
|
2272
|
-
from scipy.stats.mstats import winsorize
|
2273
|
-
# 若,ret_measure为Exp类指标,此处需要首行置零
|
2274
|
-
colList=list(df2)
|
2275
|
-
index1=df2.head(1).index.values[0]
|
2276
|
-
for c in colList:
|
2277
|
-
if 'Exp' in ret_measure:
|
2278
|
-
df2.loc[df2[df2.index==index1].index.tolist(),c]=0
|
2279
|
-
|
2280
|
-
# 缩尾处理:先转换为数值类型,以防万一
|
2281
|
-
df2[c]=df2[c].astype('float')
|
2282
|
-
df2[c]=winsorize(df2[c],limits=winsorize_limits)
|
2283
|
-
|
2284
|
-
#df2.interpolate(method='polynomial',order=2,axis=0,inplace=True)
|
2285
|
-
|
2286
|
-
y_label="收益-风险性价比"
|
2287
|
-
|
2288
|
-
measure1=ectranslate(ret_measure)[:-1]
|
2289
|
-
measure2=ectranslate(risk_measure)[:-1]
|
2290
|
-
footnote1="注:图中的收益-风险性价比定义为"+measure1+"与"+measure2+"之比"
|
2291
|
-
import datetime; today = datetime.date.today()
|
2292
|
-
footnote2="数据来源:新浪财经/雅虎财经/stooq,"+str(today)
|
2293
|
-
x_label=footnote1+"\n"+footnote2
|
2294
|
-
|
2295
|
-
#title_txt="比较多只证券的简单收益-风险性价比"
|
2296
|
-
title_txt="收益-风险性价比走势"
|
2297
|
-
|
2298
|
-
print("Rendering graphics ...")
|
2299
|
-
draw_lines(df2,y_label,x_label,axhline_value=0,axhline_label='',title_txt=title_txt, \
|
2300
|
-
data_label=False,resample_freq='D',smooth=smooth,annotate=annotate, \
|
2301
|
-
facecolor=facecolor)
|
2302
|
-
|
2303
|
-
return df2
|
2304
|
-
|
2305
|
-
|
2306
|
-
#==============================================================================
|
2307
|
-
if __name__ =="__main__":
|
2308
|
-
tickers1=["AMZN","EBAY","SHOP","BABA","JD"]
|
2309
|
-
tickers2=["AMZN","EBAY","SHOP","BABA","JD","PDD"]
|
2310
|
-
measure1="Annual Ret%"
|
2311
|
-
measure2="Exp Ret%"
|
2312
|
-
start="2022-1-1"
|
2313
|
-
end="2022-7-31"
|
2314
|
-
df=compare_msecurity(tickers1,measure1,start,end)
|
2315
|
-
df=compare_msecurity(tickers1,measure2,start,end)
|
2316
|
-
|
2317
|
-
df=compare_msecurity(tickers2,measure1,start,end)
|
2318
|
-
df=compare_msecurity(tickers2,measure2,start,end)
|
2319
|
-
#==============================================================================
|
2320
|
-
def stock_Kline(ticker,start='default',end='default',volume=True, \
|
2321
|
-
style='China',facecolor='whitesmoke', \
|
2322
|
-
mav=[5,10]):
|
2323
|
-
"""
|
2324
|
-
套壳函数,为了与stock_MACD等函数相似
|
2325
|
-
"""
|
2326
|
-
|
2327
|
-
#=========== 日期转换与检查
|
2328
|
-
# 检查日期:截至日期
|
2329
|
-
import datetime as dt; today=dt.date.today()
|
2330
|
-
if end in ['default','today']:
|
2331
|
-
end=today
|
2332
|
-
else:
|
2333
|
-
validdate,end=check_date2(end)
|
2334
|
-
if not validdate:
|
2335
|
-
print(" #Warning(stock_Kline): invalid date for",end)
|
2336
|
-
end=today
|
2337
|
-
|
2338
|
-
# 检查日期:开始日期
|
2339
|
-
if start in ['default']:
|
2340
|
-
start=date_adjust(end,adjust=-31)
|
2341
|
-
else:
|
2342
|
-
validdate,start=check_date2(start)
|
2343
|
-
if not validdate:
|
2344
|
-
print(" #Warning(stock_Kline): invalid date for",start)
|
2345
|
-
start=date_adjust(todate,adjust=-31)
|
2346
|
-
|
2347
|
-
df=candlestick(stkcd=ticker,fromdate=start,todate=end,volume=volume, \
|
2348
|
-
style=style,facecolor=facecolor,mav=mav)
|
2349
|
-
|
2350
|
-
return df
|
2351
|
-
|
2352
|
-
if __name__ =="__main__":
|
2353
|
-
stkcd="BABA"
|
2354
|
-
fromdate="2024-5-1"
|
2355
|
-
todate="2024-6-20"
|
2356
|
-
volume=True
|
2357
|
-
style='China'
|
2358
|
-
mav=[5,10]
|
2359
|
-
ticker_type='auto'
|
2360
|
-
facecolor='whitesmoke'
|
2361
|
-
loc='best'
|
2362
|
-
|
2363
|
-
|
2364
|
-
|
2365
|
-
def candlestick(stkcd,start,end,volume=True,style='China',mav=[5,10], \
|
2366
|
-
ticker_type='auto',facecolor='whitesmoke',loc='best'):
|
2367
|
-
"""
|
2368
|
-
功能:绘制证券价格K线图。
|
2369
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;
|
2370
|
-
绘图类型type:默认为蜡烛图;
|
2371
|
-
是否绘制交易量volume:默认否;
|
2372
|
-
绘图风格style:默认为黑白图;
|
2373
|
-
输出:绘制证券价格蜡烛图线图
|
2374
|
-
返回:证券价格数据表
|
2375
|
-
"""
|
2376
|
-
fromdate=start; todate=end
|
2377
|
-
|
2378
|
-
#找出mav的最长天数
|
2379
|
-
mav_max=0
|
2380
|
-
for mm in mav:
|
2381
|
-
# 移除移动平均步数1,否则出错
|
2382
|
-
if mm == 1:
|
2383
|
-
mav.remove(mm)
|
2384
|
-
print(" Warning: moving average at pace=1 is invalid and removed")
|
2385
|
-
|
2386
|
-
if mm > mav_max:
|
2387
|
-
mav_max=mm
|
2388
|
-
# 如果mav为空,则默认为2
|
2389
|
-
if len(mav) == 0:
|
2390
|
-
mav=[2]
|
2391
|
-
|
2392
|
-
#延长开始日期,以便绘制长期均线
|
2393
|
-
#fromdate1=date_adjust(fromdate, adjust=-mav_max*2)
|
2394
|
-
fromdate1=fromdate
|
2395
|
-
|
2396
|
-
#检查命令参数
|
2397
|
-
stylelist=['binance','China','blueskies','brasil','charles','checkers','classic','default', \
|
2398
|
-
'mike','nightclouds','sas','starsandstripes','yahoo']
|
2399
|
-
if not (style in stylelist):
|
2400
|
-
print(" #Error(candlestick),only support graphics styles of",stylelist)
|
2401
|
-
return
|
2402
|
-
if style != 'China':
|
2403
|
-
s = mpf.make_mpf_style(base_mpf_style=style,rc=mpfrc)
|
2404
|
-
else:
|
2405
|
-
#按照中国习惯:红涨绿跌
|
2406
|
-
mc = mpf.make_marketcolors(
|
2407
|
-
up="red", # 上涨K线的颜色
|
2408
|
-
down="green", # 下跌K线的颜色
|
2409
|
-
edge="inherit", # 蜡烛图箱体的颜色
|
2410
|
-
volume="inherit", # 成交量柱子的颜色
|
2411
|
-
wick="inherit" # 蜡烛图影线的颜色
|
2412
|
-
)
|
2413
|
-
s = mpf.make_mpf_style(
|
2414
|
-
#gridaxis='both',
|
2415
|
-
#gridstyle='-.',
|
2416
|
-
y_on_right=True,
|
2417
|
-
marketcolors=mc,
|
2418
|
-
edgecolor='black',
|
2419
|
-
figcolor='white',
|
2420
|
-
facecolor=facecolor,
|
2421
|
-
#gridcolor='cyan',
|
2422
|
-
rc=mpfrc)
|
2423
|
-
|
2424
|
-
#抓取证券价格
|
2425
|
-
"""
|
2426
|
-
from siat.security_prices import get_prices_all
|
2427
|
-
daily=get_prices_all(stkcd,fromdate1,todate,ticker_type=ticker_type)
|
2428
|
-
"""
|
2429
|
-
from siat.security_price2 import get_price_mticker_mixed
|
2430
|
-
daily,found=get_price_1ticker_mixed(ticker=stkcd,fromdate=fromdate1, \
|
2431
|
-
todate=todate,ticker_type=ticker_type)
|
2432
|
-
"""
|
2433
|
-
if daily is None:
|
2434
|
-
print(" #Error(candlestick): failed to get price info of",stkcd)
|
2435
|
-
return
|
2436
|
-
if len(daily) == 0:
|
2437
|
-
print(" #Warning(candlestick): zero price info to draw K-line for",stkcd)
|
2438
|
-
return
|
2439
|
-
"""
|
2440
|
-
if found == 'None':
|
2441
|
-
print(" #Error(candlestick): failed to get price info of",stkcd)
|
2442
|
-
return
|
2443
|
-
if found == 'Empty':
|
2444
|
-
print(" #Warning(candlestick): zero price info to draw K-line for",stkcd)
|
2445
|
-
return
|
2446
|
-
|
2447
|
-
#如果抓取到的数据没有Volume字段,创造一个但填充为零
|
2448
|
-
if 'Volume' not in list(daily):
|
2449
|
-
daily['Volume']=0
|
2450
|
-
|
2451
|
-
#绘制蜡烛图
|
2452
|
-
ylabel_txt=text_lang('价格','Price')
|
2453
|
-
ylabel_lower_txt=text_lang('成交量','Volume')
|
2454
|
-
|
2455
|
-
#titletxt=ticker_name(stkcd)
|
2456
|
-
titletxt=ticker_name(stkcd,ticker_type=ticker_type)
|
2457
|
-
|
2458
|
-
#空一行
|
2459
|
-
print('')
|
2460
|
-
|
2461
|
-
fig, axlist = mpf.plot(daily,type='candle',
|
2462
|
-
volume=volume,
|
2463
|
-
show_nontrading=False,#自动剔除非交易日空白
|
2464
|
-
style=s,
|
2465
|
-
#title=titletxt,
|
2466
|
-
datetime_format='%Y-%m-%d',
|
2467
|
-
tight_layout=True,
|
2468
|
-
#tight_layout=False,
|
2469
|
-
xrotation=15,
|
2470
|
-
ylabel=texttranslate(ylabel_txt),
|
2471
|
-
ylabel_lower=texttranslate(ylabel_lower_txt),
|
2472
|
-
mav=mav,
|
2473
|
-
figratio=(12.8,7.2),
|
2474
|
-
#figscale=1.5,
|
2475
|
-
returnfig=True
|
2476
|
-
)
|
2477
|
-
|
2478
|
-
# add a title the the correct axes, 0=first subfigure
|
2479
|
-
titletxt=titletxt+text_lang(":K线图走势,日移动均线=",": Candlestick Chart, MAV Days=")+str(mav)
|
2480
|
-
axlist[0].set_title(titletxt,
|
2481
|
-
fontsize=16,
|
2482
|
-
#style='italic',
|
2483
|
-
#fontfamily='fantasy',
|
2484
|
-
loc='center')
|
2485
|
-
|
2486
|
-
#设置图例,注意前两个为图中期间开始日期的线和柱子
|
2487
|
-
mav_labels=[text_lang('期间首日线','Day 1(line)'),text_lang('期间首日柱','Day 1(bar)')]
|
2488
|
-
#mav_labels=[None,None]
|
2489
|
-
for d in mav:
|
2490
|
-
mav_labels=mav_labels+[str(d)+text_lang("日移动均线","-day MAV line")]
|
2491
|
-
axlist[0].legend(labels=mav_labels,loc=loc)
|
2492
|
-
#axlist[0].legend(mav_labels[2:],loc=loc)
|
2493
|
-
"""
|
2494
|
-
#去掉前两个无用的图例
|
2495
|
-
handles, labels = axlist[0].get_legend_handles_labels()
|
2496
|
-
axlist[0].legend(handles=handles[2:],labels=labels[2:],loc=loc)
|
2497
|
-
"""
|
2498
|
-
fig.show()
|
2499
|
-
reset_plt()
|
2500
|
-
|
2501
|
-
return daily
|
2502
|
-
|
2503
|
-
if __name__ =="__main__":
|
2504
|
-
stkcd='000002.SZ'
|
2505
|
-
fromdate='2020-2-1'
|
2506
|
-
todate='2020-3-10'
|
2507
|
-
type='candle'
|
2508
|
-
volume=True
|
2509
|
-
style='default'
|
2510
|
-
mav=0
|
2511
|
-
line=False
|
2512
|
-
price=candlestick("000002.SZ","2020-2-1","2020-2-29")
|
2513
|
-
|
2514
|
-
#==============================================================================
|
2515
|
-
def candlestick_pro(stkcd,start,end, \
|
2516
|
-
colorup='#00ff00',colordown='#ff00ff',style='nightclouds', \
|
2517
|
-
ticker_type='auto'):
|
2518
|
-
"""
|
2519
|
-
功能:绘制证券价格K线图。
|
2520
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;
|
2521
|
-
绘图类型type:默认为蜡烛图;
|
2522
|
-
是否绘制交易量volume:默认否;
|
2523
|
-
绘图风格style:nightclouds修改版;
|
2524
|
-
输出:绘制证券价格蜡烛图线图
|
2525
|
-
返回:证券价格数据表
|
2526
|
-
注意:可能导致其后的matplotlib绘图汉字乱码
|
2527
|
-
"""
|
2528
|
-
fromdate=start; todate=end
|
2529
|
-
|
2530
|
-
#抓取证券价格
|
2531
|
-
from siat.security_price2 import get_price_1ticker_mixed
|
2532
|
-
daily,found=get_price_1ticker_mixed(ticker=stkcd,fromdate=fromdate, \
|
2533
|
-
todate=todate,ticker_type=ticker_type)
|
2534
|
-
|
2535
|
-
if found in ['None','Empty']:
|
2536
|
-
print(" #Error(candlestick_pro): failed to get price info of",stkcd,fromdate,todate)
|
2537
|
-
return None
|
2538
|
-
|
2539
|
-
#绘制蜡烛图
|
2540
|
-
#在原有的风格nightclouds基础上定制阳线和阴线柱子的色彩,形成自定义风格s
|
2541
|
-
mc = mpf.make_marketcolors(up=colorup,down=colordown,inherit=True)
|
2542
|
-
s = mpf.make_mpf_style(base_mpf_style=style,marketcolors=mc,rc=mpfrc)
|
2543
|
-
#kwargs = dict(type='candle',mav=(2,4,6),volume=True,figratio=(10,8),figscale=0.75)
|
2544
|
-
#kwargs = dict(type='candle',mav=(2,4,6),volume=True,figscale=0.75)
|
2545
|
-
kwargs = dict(type='candle',mav=5,volume=True)
|
2546
|
-
|
2547
|
-
#titletxt=ticker_name(stkcd)
|
2548
|
-
titletxt=ticker_name(stkcd,ticker_type=ticker_type)
|
2549
|
-
|
2550
|
-
mpf.plot(daily,**kwargs,
|
2551
|
-
style=s,
|
2552
|
-
datetime_format='%Y-%m-%d',
|
2553
|
-
tight_layout=True,
|
2554
|
-
xrotation=15,
|
2555
|
-
title=titletxt,
|
2556
|
-
ylabel=text_lang("价格","Price"),
|
2557
|
-
ylabel_lower=text_lang("成交量","Volume"),
|
2558
|
-
figratio=(12.8,7.2)
|
2559
|
-
)
|
2560
|
-
reset_plt()
|
2561
|
-
|
2562
|
-
return daily
|
2563
|
-
|
2564
|
-
if __name__ =="__main__":
|
2565
|
-
stkcd='000002.SZ'
|
2566
|
-
fromdate='2020-2-1'
|
2567
|
-
todate='2020-3-10'
|
2568
|
-
type='candle'
|
2569
|
-
volume=True
|
2570
|
-
style='default'
|
2571
|
-
mav=0
|
2572
|
-
line=False
|
2573
|
-
price=candlestick_pro("000002.SZ","2020-2-1","2020-2-29")
|
2574
|
-
#==============================================================================
|
2575
|
-
def stock_Kline_demo(ticker,start='default',end='default', \
|
2576
|
-
colorup='red',colordown='green',width=0.5, \
|
2577
|
-
ticker_type='auto',facecolor='whitesmoke'):
|
2578
|
-
"""
|
2579
|
-
套壳函数,为了与stock_Kline保持一致
|
2580
|
-
"""
|
2581
|
-
|
2582
|
-
#=========== 日期转换与检查
|
2583
|
-
# 检查日期:截至日期
|
2584
|
-
import datetime as dt; today=dt.date.today()
|
2585
|
-
if end in ['default','today']:
|
2586
|
-
end=today
|
2587
|
-
else:
|
2588
|
-
validdate,end=check_date2(end)
|
2589
|
-
if not validdate:
|
2590
|
-
print(" #Warning(stock_Kline_demo): invalid date for",end)
|
2591
|
-
end=today
|
2592
|
-
|
2593
|
-
# 检查日期:开始日期
|
2594
|
-
if start in ['default']:
|
2595
|
-
start=date_adjust(end,adjust=-7)
|
2596
|
-
else:
|
2597
|
-
validdate,start=check_date2(start)
|
2598
|
-
if not validdate:
|
2599
|
-
print(" #Warning(stock_Kline_demo): invalid date for",start)
|
2600
|
-
start=date_adjust(todate,adjust=-7)
|
2601
|
-
|
2602
|
-
df=candlestick_demo(stkcd=ticker,fromdate=start,todate=end, \
|
2603
|
-
colorup=colorup,colordown=colordown,width=width, \
|
2604
|
-
ticker_type=ticker_type,facecolor=facecolor)
|
2605
|
-
|
2606
|
-
return df
|
2607
|
-
|
2608
|
-
|
2609
|
-
if __name__ =="__main__":
|
2610
|
-
stkcd='BABA'
|
2611
|
-
fromdate='2023-6-5'
|
2612
|
-
todate='2023-6-9'
|
2613
|
-
|
2614
|
-
colorup='red';colordown='green';width=0.7
|
2615
|
-
|
2616
|
-
def candlestick_demo(stkcd,start,end, \
|
2617
|
-
colorup='red',colordown='green',width=0.7, \
|
2618
|
-
ticker_type='auto',facecolor='whitesmoke'):
|
2619
|
-
"""
|
2620
|
-
功能:绘制证券价格K线图,叠加收盘价。
|
2621
|
-
输入:证券代码ticker;开始日期fromdate,结束日期todate;
|
2622
|
-
阳线颜色colorup='red',阴线颜色colordown='green',柱子宽度width=0.7
|
2623
|
-
输出:绘制证券价格蜡烛图线图
|
2624
|
-
返回:证券价格数据表
|
2625
|
-
"""
|
2626
|
-
fromdate=start; todate=end
|
2627
|
-
|
2628
|
-
#抓取证券价格
|
2629
|
-
from siat.security_price2 import get_price_1ticker_mixed
|
2630
|
-
p,found=get_price_1ticker_mixed(ticker=stkcd,fromdate=fromdate, \
|
2631
|
-
todate=todate,ticker_type=ticker_type)
|
2632
|
-
if found in ['None','Empty']:
|
2633
|
-
print(" #Error(candlestick_demo): failed to get prices for:",stkcd,'\b,',fromdate,'-',todate)
|
2634
|
-
return p
|
2635
|
-
|
2636
|
-
p['Date']=p.index
|
2637
|
-
|
2638
|
-
import numpy as np
|
2639
|
-
#b= np.array(p.reset_index()[['Date','Open','High','Low','Close']])
|
2640
|
-
b= np.array(p[['Date','Open','High','Low','Close']])
|
2641
|
-
|
2642
|
-
#change 1st column of b to number type
|
2643
|
-
import matplotlib.dates as dt2
|
2644
|
-
b[:,0] = dt2.date2num(b[:,0])
|
2645
|
-
|
2646
|
-
print('')
|
2647
|
-
#specify the size of the graph
|
2648
|
-
#fig,ax=plt.subplots(figsize=(12.8,6.4))
|
2649
|
-
fig,ax=plt.subplots()
|
2650
|
-
|
2651
|
-
#绘制各个价格的折线图
|
2652
|
-
open_txt=text_lang('开盘价','Open')
|
2653
|
-
high_txt=text_lang('最高价','High')
|
2654
|
-
low_txt=text_lang('最低价','Low')
|
2655
|
-
close_txt=text_lang('收盘价','Close')
|
2656
|
-
|
2657
|
-
plt.plot(p.index,p['Open'],color='green',ls="--",label=open_txt,marker='>',markersize=10,linewidth=2,alpha=0.5)
|
2658
|
-
plt.plot(p.index,p['High'],color='cyan',ls="-.",label=high_txt,marker='^',markersize=10,linewidth=2,alpha=0.5)
|
2659
|
-
plt.plot(p.index,p['Low'],color='k',ls=":",label=low_txt,marker='v',markersize=10,linewidth=2,alpha=0.5)
|
2660
|
-
plt.plot(p.index,p['Close'],color='blue',ls="-",label=close_txt,marker='<',markersize=10,linewidth=2,alpha=0.5)
|
2661
|
-
|
2662
|
-
#绘制蜡烛图
|
2663
|
-
try:
|
2664
|
-
from mplfinance.original_flavor import candlestick_ohlc
|
2665
|
-
except:
|
2666
|
-
print(" #Error(candlestick_demo): please install plugin mplfinance.")
|
2667
|
-
print(" Method:")
|
2668
|
-
print(" In Anaconda Prompt, key in a command: pip install mplfinance")
|
2669
|
-
return None
|
2670
|
-
|
2671
|
-
#candlestick_ohlc(ax,b,colorup=colorup,colordown=colordown,width=width,alpha=0.5)
|
2672
|
-
candlestick_ohlc(ax,b,colorup=colorup,colordown=colordown,width=width,alpha=0.5)
|
2673
|
-
|
2674
|
-
ax.xaxis_date() #draw dates in x axis
|
2675
|
-
ax.autoscale_view()
|
2676
|
-
fig.autofmt_xdate()
|
2677
|
-
fig.gca().set_facecolor(facecolor)
|
2678
|
-
|
2679
|
-
titletxt0=text_lang("K线图/蜡烛图演示:","Candlestick Chart Demo: ")
|
2680
|
-
titletxt=titletxt0 + ticker_name(str(stkcd),ticker_type=ticker_type)
|
2681
|
-
price_txt=text_lang('价格','Price')
|
2682
|
-
source_txt=text_lang("数据来源: Sina/EM/Stooq/Yahoo/SWHY","Data source: Sina/Stooq/Yahoo")
|
2683
|
-
|
2684
|
-
plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
|
2685
|
-
plt.ylabel(price_txt,fontsize=ylabel_txt_size)
|
2686
|
-
|
2687
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
2688
|
-
plt.gca().set_facecolor(facecolor)
|
2689
|
-
#plt.xticks(rotation=30)
|
2690
|
-
plt.legend(loc="best",fontsize=legend_txt_size)
|
2691
|
-
plt.xlabel(source_txt,fontsize=xlabel_txt_size)
|
2692
|
-
plt.show()
|
2693
|
-
|
2694
|
-
return p
|
2695
|
-
|
2696
|
-
if __name__ =="__main__":
|
2697
|
-
price=candlestick_demo("000002.SZ","2020-3-1","2020-3-6")
|
2698
|
-
|
2699
|
-
#==============================================================================
|
2700
|
-
#==============================================================================
|
2701
|
-
#==============================================================================
|
2702
|
-
if __name__ =="__main__":
|
2703
|
-
ticker="000001.SZ"
|
2704
|
-
fromdate="2021-1-1"
|
2705
|
-
todate="2022-9-26"
|
2706
|
-
|
2707
|
-
|
2708
|
-
def security_dividend(ticker,start="L5Y",end="today",facecolor='whitesmoke',fontcolor='black'):
|
2709
|
-
"""
|
2710
|
-
功能:套壳函数stock_dividend
|
2711
|
-
"""
|
2712
|
-
df=stock_dividend(ticker,start,end,facecolor,fontcolor)
|
2713
|
-
return df
|
2714
|
-
|
2715
|
-
|
2716
|
-
def stock_dividend(ticker,start="L3Y",end="today",facecolor='whitesmoke',fontcolor='black'):
|
2717
|
-
"""
|
2718
|
-
功能:显示股票的分红历史
|
2719
|
-
输入:单一股票代码
|
2720
|
-
输出:分红历史
|
2721
|
-
"""
|
2722
|
-
start,end=start_end_preprocess(start,end)
|
2723
|
-
|
2724
|
-
fromdate,todate=start,end
|
2725
|
-
|
2726
|
-
print(" Searching for dividend info of stock",ticker,"...")
|
2727
|
-
result,startdt,enddt=check_period(fromdate,todate)
|
2728
|
-
if not result:
|
2729
|
-
print(" #Error(stock_dividend): invalid period",fromdate,todate)
|
2730
|
-
return None
|
2731
|
-
|
2732
|
-
result,prefix,suffix=split_prefix_suffix(ticker)
|
2733
|
-
if result & (suffix=='HK'):
|
2734
|
-
if len(prefix)==5:
|
2735
|
-
ticker=ticker[1:]
|
2736
|
-
|
2737
|
-
import yfinance as yf
|
2738
|
-
stock = yf.Ticker(ticker)
|
2739
|
-
try:
|
2740
|
-
div=stock.dividends
|
2741
|
-
except:
|
2742
|
-
print(f" #Error(stock_dividend): dividend info inaccessible for {ticker}")
|
2743
|
-
return None
|
2744
|
-
if len(div)==0:
|
2745
|
-
print(f" #Warning(stock_dividend): failed to get dividend info for {ticker}")
|
2746
|
-
return None
|
2747
|
-
|
2748
|
-
# 去掉时区信息,避免合并中的日期时区冲突问题
|
2749
|
-
import pandas as pd
|
2750
|
-
div.index = pd.to_datetime(div.index)
|
2751
|
-
div.index = div.index.tz_localize(None)
|
2752
|
-
|
2753
|
-
#过滤期间
|
2754
|
-
div1=div[div.index >= startdt]
|
2755
|
-
div2=div1[div1.index <= enddt]
|
2756
|
-
if len(div2)==0:
|
2757
|
-
print(f" #Warning(stock_dividend): no dividends found from {fromdate} to {todate}")
|
2758
|
-
return None
|
2759
|
-
|
2760
|
-
#对齐打印
|
2761
|
-
import pandas as pd
|
2762
|
-
pd.set_option('display.unicode.ambiguous_as_wide', True)
|
2763
|
-
pd.set_option('display.unicode.east_asian_width', True)
|
2764
|
-
pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
|
2765
|
-
pd.set_option('display.colheader_justify', 'center')
|
2766
|
-
"""
|
2767
|
-
pd.set_option('display.max_columns', 1000)
|
2768
|
-
pd.set_option('display.width', 1000)
|
2769
|
-
pd.set_option('display.max_colwidth', 1000)
|
2770
|
-
"""
|
2771
|
-
divdf=pd.DataFrame(div2)
|
2772
|
-
divdf['Index Date']=divdf.index
|
2773
|
-
datefmt=lambda x : x.strftime('%Y-%m-%d')
|
2774
|
-
divdf['Dividend Date']= divdf['Index Date'].apply(datefmt)
|
2775
|
-
|
2776
|
-
#增加星期
|
2777
|
-
from datetime import datetime
|
2778
|
-
weekdayfmt=lambda x : x.isoweekday()
|
2779
|
-
divdf['Weekdayiso']= divdf['Index Date'].apply(weekdayfmt)
|
2780
|
-
#wdlist=['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
|
2781
|
-
#wdlist=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
|
2782
|
-
#wdlist=['星期一','星期二','星期三','星期四','星期五','星期六','星期日']
|
2783
|
-
wdlist=[text_lang('星期一','Mon'),text_lang('星期二','Tue'),text_lang('星期三','Wed'), \
|
2784
|
-
text_lang('星期四','Thu'),text_lang('星期五','Fri'),text_lang('星期六','Sat'),text_lang('星期日','Sun')]
|
2785
|
-
|
2786
|
-
wdfmt=lambda x : wdlist[x-1]
|
2787
|
-
divdf['Weekday']= divdf['Weekdayiso'].apply(wdfmt)
|
2788
|
-
|
2789
|
-
#增加序号
|
2790
|
-
divdf['Seq']=divdf['Dividend Date'].rank(ascending=1)
|
2791
|
-
divdf['Seq']=divdf['Seq'].astype('int')
|
2792
|
-
divprt=divdf[['Seq','Dividend Date','Weekday','Dividends']]
|
2793
|
-
|
2794
|
-
lang=check_language()
|
2795
|
-
tname=ticker_name(ticker,'stock')
|
2796
|
-
fromdatey2md=startdt.strftime('%Y/%m/%d')
|
2797
|
-
todatey2md=enddt.strftime('%Y/%m/%d')
|
2798
|
-
|
2799
|
-
titletxt=text_lang("证券分红","Stock Dividend")+': '+tname
|
2800
|
-
periodtxt=text_lang("期间:","Period:")+' '+fromdatey2md+"-"+todatey2md
|
2801
|
-
#sourcetxt=text_lang("数据来源: 雅虎财经,","Data source: Yahoo Finance,")
|
2802
|
-
sourcetxt=text_lang("数据来源: Yahoo/Sina","Data source: Yahoo/Sina")
|
2803
|
-
footnote=periodtxt+'\n'+sourcetxt
|
2804
|
-
|
2805
|
-
#修改列命为英文
|
2806
|
-
divprt.columns = [text_lang('序号','No.'),text_lang('日期','Date'),text_lang('星期','Weekday'),text_lang('每股(份)红利','Dividend/share')]
|
2807
|
-
|
2808
|
-
"""
|
2809
|
-
print(divprt.to_string(index=False))
|
2810
|
-
"""
|
2811
|
-
#print('') #空一行
|
2812
|
-
|
2813
|
-
df_display_CSS(divprt,titletxt=titletxt,footnote=footnote,facecolor=facecolor,decimals=4, \
|
2814
|
-
first_col_align='center',second_col_align='center', \
|
2815
|
-
last_col_align='right',other_col_align='center')
|
2816
|
-
|
2817
|
-
"""
|
2818
|
-
disph=divprt.style.hide() #不显示索引列
|
2819
|
-
dispp=disph.format(precision=4) #设置带有小数点的列精度调整为小数点后2位
|
2820
|
-
#设置标题/列名对齐
|
2821
|
-
dispt=dispp.set_caption(titletxt).set_table_styles(
|
2822
|
-
[{'selector':'caption', #设置标题
|
2823
|
-
'props':[('color','black'),('font-size','16px'),('font-weight','bold')]}, \
|
2824
|
-
{'selector':'th.col_heading', #设置列名
|
2825
|
-
'props':[('color','black'),('text-align','center'),('margin','auto')]}])
|
2826
|
-
#设置列数值对齐
|
2827
|
-
dispf=dispt.set_properties(**{'text-align':'center'})
|
2828
|
-
#设置前景背景颜色
|
2829
|
-
dispf2=dispf.set_properties(**{'background-color':facecolor,'color':fontcolor})
|
2830
|
-
|
2831
|
-
from IPython.display import display
|
2832
|
-
display(dispf2)
|
2833
|
-
|
2834
|
-
print(periodtxt)
|
2835
|
-
import datetime; todaydt=datetime.date.today(); todayy2md=todaydt.strftime('%y/%m/%d')
|
2836
|
-
#print('\n*** '+sourcetxt,today)
|
2837
|
-
print(sourcetxt,todayy2md)
|
2838
|
-
"""
|
2839
|
-
|
2840
|
-
return divdf
|
2841
|
-
|
2842
|
-
|
2843
|
-
if __name__ =="__main__":
|
2844
|
-
ticker='AAPL'
|
2845
|
-
fromdate='2019-1-1'
|
2846
|
-
todate='2020-6-30'
|
2847
|
-
|
2848
|
-
#==============================================================================
|
2849
|
-
def security_split(ticker,start="L10Y",end="today",facecolor='whitesmoke',fontcolor='black'):
|
2850
|
-
"""
|
2851
|
-
功能:套壳函数stock_split
|
2852
|
-
"""
|
2853
|
-
df=stock_split(ticker,start,end,facecolor,fontcolor)
|
2854
|
-
return df
|
2855
|
-
|
2856
|
-
|
2857
|
-
def stock_split(ticker,start="L10Y",end="today",facecolor='whitesmoke',fontcolor='black'):
|
2858
|
-
"""
|
2859
|
-
功能:显示股票的分拆历史
|
2860
|
-
输入:单一股票代码
|
2861
|
-
输出:分拆历史
|
2862
|
-
"""
|
2863
|
-
start,end=start_end_preprocess(start,end)
|
2864
|
-
|
2865
|
-
fromdate,todate=start,end
|
2866
|
-
|
2867
|
-
print(" Searching for split info of stock",ticker,"...")
|
2868
|
-
result,startdt,enddt=check_period(fromdate,todate)
|
2869
|
-
if not result:
|
2870
|
-
print(" #Error(stock_split): invalid period",fromdate,todate)
|
2871
|
-
return None
|
2872
|
-
|
2873
|
-
result,prefix,suffix=split_prefix_suffix(ticker)
|
2874
|
-
if result & (suffix=='HK'):
|
2875
|
-
if len(prefix)==5:
|
2876
|
-
ticker=ticker[1:]
|
2877
|
-
|
2878
|
-
import yfinance as yf
|
2879
|
-
stock = yf.Ticker(ticker)
|
2880
|
-
try:
|
2881
|
-
div=stock.splits
|
2882
|
-
except:
|
2883
|
-
print(f" #Error(stock_split): split info inaccessible for {ticker}")
|
2884
|
-
return None
|
2885
|
-
if len(div)==0:
|
2886
|
-
print(f" #Warning(stock_split): no split info found for {ticker}")
|
2887
|
-
return None
|
2888
|
-
|
2889
|
-
# 去掉时区信息,避免合并中的日期时区冲突问题
|
2890
|
-
import pandas as pd
|
2891
|
-
div.index = pd.to_datetime(div.index)
|
2892
|
-
div.index = div.index.tz_localize(None)
|
2893
|
-
|
2894
|
-
#过滤期间
|
2895
|
-
div1=div[div.index >= startdt]
|
2896
|
-
div2=div1[div1.index <= enddt]
|
2897
|
-
if len(div2)==0:
|
2898
|
-
print(" #Warning(stock_split): no split information in period",fromdate,todate)
|
2899
|
-
return None
|
2900
|
-
|
2901
|
-
#对齐打印
|
2902
|
-
import pandas as pd
|
2903
|
-
pd.set_option('display.unicode.ambiguous_as_wide', True)
|
2904
|
-
pd.set_option('display.unicode.east_asian_width', True)
|
2905
|
-
pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
|
2906
|
-
"""
|
2907
|
-
pd.set_option('display.max_columns', 1000)
|
2908
|
-
pd.set_option('display.width', 1000)
|
2909
|
-
pd.set_option('display.max_colwidth', 1000)
|
2910
|
-
"""
|
2911
|
-
divdf=pd.DataFrame(div2)
|
2912
|
-
divdf['Index Date']=divdf.index
|
2913
|
-
datefmt=lambda x : x.strftime('%Y-%m-%d')
|
2914
|
-
divdf['Split Date']= divdf['Index Date'].apply(datefmt)
|
2915
|
-
|
2916
|
-
#增加星期
|
2917
|
-
from datetime import datetime
|
2918
|
-
weekdayfmt=lambda x : x.isoweekday()
|
2919
|
-
divdf['Weekdayiso']= divdf['Index Date'].apply(weekdayfmt)
|
2920
|
-
#wdlist=['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
|
2921
|
-
wdlist=[text_lang('星期一','Mon'),text_lang('星期二','Tue'),text_lang('星期三','Wed'), \
|
2922
|
-
text_lang('星期四','Thu'),text_lang('星期五','Fri'),text_lang('星期六','Sat'),text_lang('星期日','Sun')]
|
2923
|
-
wdfmt=lambda x : wdlist[x-1]
|
2924
|
-
divdf['Weekday']= divdf['Weekdayiso'].apply(wdfmt)
|
2925
|
-
|
2926
|
-
#增加序号
|
2927
|
-
divdf['Seq']=divdf['Split Date'].rank(ascending=1)
|
2928
|
-
divdf['Seq']=divdf['Seq'].astype('int')
|
2929
|
-
|
2930
|
-
divdf['Splitint']=divdf['Stock Splits'].astype('int')
|
2931
|
-
splitfmt=lambda x: "1:"+str(x)
|
2932
|
-
divdf['Splits']=divdf['Splitint'].apply(splitfmt)
|
2933
|
-
|
2934
|
-
divprt=divdf[['Seq','Split Date','Weekday','Splits']]
|
2935
|
-
|
2936
|
-
lang=check_language()
|
2937
|
-
tname=ticker_name(ticker,'stock')
|
2938
|
-
"""
|
2939
|
-
if lang == 'English':
|
2940
|
-
print('\n======== '+texttranslate("股票分拆历史")+' ========')
|
2941
|
-
print(texttranslate("股票:"),ticker,'\b,',ticker_name(ticker))
|
2942
|
-
print(texttranslate("历史期间:"),fromdate,"-",todate)
|
2943
|
-
divprt.columns=[texttranslate('序号'),texttranslate('日期'),texttranslate('星期'),texttranslate('分拆比例')]
|
2944
|
-
|
2945
|
-
sourcetxt=texttranslate("数据来源: 雅虎财经,")
|
2946
|
-
else:
|
2947
|
-
print('\n======== '+"股票分拆历史"+' ========')
|
2948
|
-
print("股票:",ticker,'\b,',ticker_name(ticker))
|
2949
|
-
print("历史期间:",fromdate,"-",todate)
|
2950
|
-
divprt.columns=['序号','日期','星期','分拆比例']
|
2951
|
-
|
2952
|
-
sourcetxt="数据来源: 雅虎财经,"
|
2953
|
-
|
2954
|
-
print(divprt.to_string(index=False))
|
2955
|
-
|
2956
|
-
import datetime
|
2957
|
-
today = datetime.date.today()
|
2958
|
-
print('\n*** '+sourcetxt,today)
|
2959
|
-
"""
|
2960
|
-
fromdatey2md=startdt.strftime('%Y/%m/%d')
|
2961
|
-
todatey2md=enddt.strftime('%Y/%m/%d')
|
2962
|
-
|
2963
|
-
titletxt=text_lang("证券分拆","Stock Split")+': '+tname
|
2964
|
-
periodtxt=text_lang("期间:","Period:")+' '+fromdatey2md+"-"+todatey2md
|
2965
|
-
|
2966
|
-
import datetime; todaydt=datetime.date.today(); todayy2md=str(todaydt.strftime('%y/%m/%d'))
|
2967
|
-
#sourcetxt=text_lang("数据来源: 雅虎财经, ","Data source: Yahoo Finance, ")+todayy2md
|
2968
|
-
sourcetxt=text_lang("数据来源: Yahoo/Sina","Data source: Yahoo Finance")
|
2969
|
-
footnote=periodtxt+'\n'+ sourcetxt
|
2970
|
-
|
2971
|
-
#修改列命为英文
|
2972
|
-
divprt.columns = [text_lang('序号','No.'),text_lang('日期','Date'),text_lang('星期','Weekday'),text_lang('分拆比例','Split Ratio')]
|
2973
|
-
"""
|
2974
|
-
print(divprt.to_string(index=False))
|
2975
|
-
"""
|
2976
|
-
print(' ') #空一行
|
2977
|
-
|
2978
|
-
df_display_CSS(divprt,titletxt=titletxt,footnote=footnote,facecolor=facecolor,decimals=2, \
|
2979
|
-
first_col_align='center',second_col_align='center', \
|
2980
|
-
last_col_align='right',other_col_align='center')
|
2981
|
-
"""
|
2982
|
-
disph=divprt.style.hide() #不显示索引列
|
2983
|
-
dispp=disph.format(precision=4) #设置带有小数点的列精度调整为小数点后2位
|
2984
|
-
#设置标题/列名
|
2985
|
-
dispt=dispp.set_caption(titletxt).set_table_styles(
|
2986
|
-
[{'selector':'caption', #设置标题
|
2987
|
-
'props':[('color','black'),('font-size','16px'),('font-weight','bold')]}, \
|
2988
|
-
{'selector':'th.col_heading', #设置列名
|
2989
|
-
'props':[('color','black'),('text-align','center'),('margin','auto')]}])
|
2990
|
-
#设置列数值对齐
|
2991
|
-
dispf=dispt.set_properties(**{'text-align':'center'})
|
2992
|
-
#设置前景背景颜色
|
2993
|
-
dispf2=dispf.set_properties(**{'background-color':facecolor,'color':fontcolor})
|
2994
|
-
|
2995
|
-
from IPython.display import display
|
2996
|
-
display(dispf2)
|
2997
|
-
|
2998
|
-
print(periodtxt)
|
2999
|
-
import datetime; todaydt=datetime.date.today(); todayy2md=todaydt.strftime('%y/%m/%d')
|
3000
|
-
#print('\n*** '+sourcetxt,today)
|
3001
|
-
print(sourcetxt,todayy2md)
|
3002
|
-
"""
|
3003
|
-
|
3004
|
-
return divdf
|
3005
|
-
|
3006
|
-
|
3007
|
-
if __name__ =="__main__":
|
3008
|
-
ticker='AAPL'
|
3009
|
-
fromdate='1990-1-1'
|
3010
|
-
todate='2020-6-30'
|
3011
|
-
|
3012
|
-
#==============================================================================
|
3013
|
-
#==============================================================================
|
3014
|
-
#==============================================================================
|
3015
|
-
if __name__=='__main__':
|
3016
|
-
symbol='AAPL'
|
3017
|
-
symbol='BABA'
|
3018
|
-
symbol='01398.HK'
|
3019
|
-
symbol='03968.HK'
|
3020
|
-
symbol='601398.SS'
|
3021
|
-
|
3022
|
-
def stock_info(symbol):
|
3023
|
-
"""
|
3024
|
-
功能:返回静态信息, 基于yahooquery
|
3025
|
-
尚能工作,不打印
|
3026
|
-
"""
|
3027
|
-
DEBUG=False
|
3028
|
-
#print(" Searching for information of",symbol,"\b, please wait...")
|
3029
|
-
|
3030
|
-
#symbol1=symbol
|
3031
|
-
result,prefix,suffix=split_prefix_suffix(symbol)
|
3032
|
-
if result & (suffix=='HK'):
|
3033
|
-
symbol=prefix[-4:]+'.'+suffix
|
3034
|
-
|
3035
|
-
from yahooquery import Ticker
|
3036
|
-
#如果出现类似于{'AAPL': 'Invalid Cookie'}错误,则需要升级yahooquery
|
3037
|
-
#如果出现crump相关的错误,则需要更换lxml的版本以便与当前的yahooquery版本匹配
|
3038
|
-
stock = Ticker(symbol)
|
3039
|
-
|
3040
|
-
"""
|
3041
|
-
Asset Profile:
|
3042
|
-
Head office address/zip/country, Officers, Employees, industry/sector, phone/fax,
|
3043
|
-
web site,
|
3044
|
-
Risk ranks: auditRisk, boardRisk, compensationRisk, overallRisk, shareHolderRightRisk,
|
3045
|
-
compensationRisk: 薪酬风险。Jensen 及 Meckling (1976)的研究指出薪酬與管理者的風險承擔
|
3046
|
-
具有關連性,站在管理者的立場來看,創新支出的投入使管理者承受更大的薪酬風險(compensation risk),
|
3047
|
-
管理者自然地要求更高的薪酬來補貼所面臨的風險,因此企業創新投資對管理者薪酬成正相關。
|
3048
|
-
boardRisk: 董事会风险
|
3049
|
-
shareHolderRightRisk:股权风险
|
3050
|
-
"""
|
3051
|
-
try:
|
3052
|
-
#如果出现类似于{'AAPL': 'Invalid Cookie'}错误,则需要升级yahooquery
|
3053
|
-
adict=stock.asset_profile
|
3054
|
-
except:
|
3055
|
-
print(" #Error(stock_info): failed to get info of",symbol)
|
3056
|
-
print(" Reasons: Wrong stock code, or poor internet connection, or need to upgrade yahooquery")
|
3057
|
-
return None
|
3058
|
-
|
3059
|
-
if adict[symbol] == 'Invalid Cookie':
|
3060
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol,"\b. Try upgrade yahooquery and run again")
|
3061
|
-
return None
|
3062
|
-
|
3063
|
-
keylist=list(adict[symbol].keys())
|
3064
|
-
import pandas as pd
|
3065
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3066
|
-
ainfo=aframe.T
|
3067
|
-
info=ainfo.copy()
|
3068
|
-
|
3069
|
-
|
3070
|
-
"""
|
3071
|
-
ESG Scores: Risk measurements
|
3072
|
-
peerGroup, ratingYear,
|
3073
|
-
environmentScore, governanceScore, socialScore, totalEsg
|
3074
|
-
dict: peerEnvironmentPerformance, peerGovernancePerformance, peerSocialPerformance,
|
3075
|
-
peerEsgScorePerformance
|
3076
|
-
"""
|
3077
|
-
try:
|
3078
|
-
adict=stock.esg_scores
|
3079
|
-
except:
|
3080
|
-
print("#Error(stock_info): failed to get esg profile of",symbol)
|
3081
|
-
return None
|
3082
|
-
|
3083
|
-
if adict[symbol] == 'Invalid Cookie':
|
3084
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3085
|
-
return None
|
3086
|
-
|
3087
|
-
try: #一些企业无此信息
|
3088
|
-
keylist=list(adict[symbol].keys())
|
3089
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3090
|
-
ainfo=aframe.T
|
3091
|
-
info=pd.concat([info,ainfo])
|
3092
|
-
except:
|
3093
|
-
pass
|
3094
|
-
|
3095
|
-
"""
|
3096
|
-
Financial Data: TTM???
|
3097
|
-
currentPrice, targetHighPrice, targetLowPrice, targetMeanPrice, targetMedianPrice,
|
3098
|
-
currentRatio, debtToEquity, earningsGrowth, ebitda, ebitdaMargins, financialCurrency,
|
3099
|
-
freeCashflow, grossMargins, grossProfits,
|
3100
|
-
operatingCashflow, operatingMargins, profitMargins,
|
3101
|
-
quickRatio, returnOnAssets, returnOnEquity, revenueGrowth, revenuePerShare,
|
3102
|
-
totalCash, totalCashPerShare, totalDebt, totalRevenue,
|
3103
|
-
"""
|
3104
|
-
try:
|
3105
|
-
adict=stock.financial_data
|
3106
|
-
except:
|
3107
|
-
print(" #Error(stock_info): failed to get financial profile of",symbol)
|
3108
|
-
return None
|
3109
|
-
|
3110
|
-
if adict[symbol] == 'Invalid Cookie':
|
3111
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3112
|
-
return None
|
3113
|
-
|
3114
|
-
keylist=list(adict[symbol].keys())
|
3115
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3116
|
-
ainfo=aframe.T
|
3117
|
-
info=pd.concat([info,ainfo])
|
3118
|
-
|
3119
|
-
|
3120
|
-
"""
|
3121
|
-
Key Statistics: TTM???
|
3122
|
-
52WeekChang, SandP52WeekChang, beta, floatShares, sharesOutstanding,
|
3123
|
-
bookValue, earningsQuarterlyGrowth, enterpriseToEbitda, enterpriseToRevenue,
|
3124
|
-
enterpriseValue, netIncomeToCommon, priceToBook, profitMargins,
|
3125
|
-
forwardEps, trailingEps,
|
3126
|
-
heldPercentInsiders, heldPercentInstitutions,
|
3127
|
-
lastFiscalYearEnd, lastSplitDate, lastSplitFactor, mostRecentQuarter, nextFiscalYearEnd,
|
3128
|
-
"""
|
3129
|
-
try:
|
3130
|
-
adict=stock.key_stats
|
3131
|
-
except:
|
3132
|
-
print(" #Error(stock_info): failed to get key stats of",symbol)
|
3133
|
-
return None
|
3134
|
-
|
3135
|
-
if adict[symbol] == 'Invalid Cookie':
|
3136
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3137
|
-
return None
|
3138
|
-
|
3139
|
-
keylist=list(adict[symbol].keys())
|
3140
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3141
|
-
ainfo=aframe.T
|
3142
|
-
info=pd.concat([info,ainfo])
|
3143
|
-
|
3144
|
-
|
3145
|
-
"""
|
3146
|
-
Price Information:
|
3147
|
-
currency, currencySymbol, exchange, exchangeName, shortName,
|
3148
|
-
longName,
|
3149
|
-
marketCap, marketState, quoteType,
|
3150
|
-
regularMarketChange, regularMarketChangPercent, regularMarketHigh, regularMarketLow,
|
3151
|
-
regularMarketOpen, regularMarketPreviousClose, regularMarketPrice, regularMarketTime,
|
3152
|
-
regularMarketVolume,
|
3153
|
-
"""
|
3154
|
-
try:
|
3155
|
-
adict=stock.price
|
3156
|
-
except:
|
3157
|
-
print(" #Error(stock_info): failed to get stock prices of",symbol)
|
3158
|
-
return None
|
3159
|
-
|
3160
|
-
if adict[symbol] == 'Invalid Cookie':
|
3161
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3162
|
-
return None
|
3163
|
-
|
3164
|
-
keylist=list(adict[symbol].keys())
|
3165
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3166
|
-
ainfo=aframe.T
|
3167
|
-
info=pd.concat([info,ainfo])
|
3168
|
-
|
3169
|
-
|
3170
|
-
"""
|
3171
|
-
Quote Type:
|
3172
|
-
exchange, firstTradeDateEpocUtc(上市日期), longName, quoteType(证券类型:股票),
|
3173
|
-
shortName, symbol(当前代码), timeZoneFullName, timeZoneShortName, underlyingSymbol(原始代码),
|
3174
|
-
"""
|
3175
|
-
try:
|
3176
|
-
adict=stock.quote_type
|
3177
|
-
except:
|
3178
|
-
print(" #Error(stock_info): failed to get quote type of",symbol)
|
3179
|
-
return None
|
3180
|
-
|
3181
|
-
if adict[symbol] == 'Invalid Cookie':
|
3182
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3183
|
-
return None
|
3184
|
-
|
3185
|
-
keylist=list(adict[symbol].keys())
|
3186
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3187
|
-
ainfo=aframe.T
|
3188
|
-
info=pd.concat([info,ainfo])
|
3189
|
-
|
3190
|
-
|
3191
|
-
"""
|
3192
|
-
Share Purchase Activity
|
3193
|
-
period(6m), totalInsiderShares
|
3194
|
-
"""
|
3195
|
-
try:
|
3196
|
-
adict=stock.share_purchase_activity
|
3197
|
-
except:
|
3198
|
-
print(" #Error(stock_info): failed to get share purchase of",symbol)
|
3199
|
-
return None
|
3200
|
-
|
3201
|
-
if adict[symbol] == 'Invalid Cookie':
|
3202
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3203
|
-
return None
|
3204
|
-
|
3205
|
-
keylist=list(adict[symbol].keys())
|
3206
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3207
|
-
ainfo=aframe.T
|
3208
|
-
info=pd.concat([info,ainfo])
|
3209
|
-
|
3210
|
-
|
3211
|
-
"""
|
3212
|
-
# Summary detail
|
3213
|
-
averageDailyVolume10Day, averageVolume, averageVolume10days, beta, currency,
|
3214
|
-
dayHigh, dayLow, fiftyDayAverage, fiftyTwoWeekHigh, fiftyTwoWeekLow, open, previousClose,
|
3215
|
-
regularMarketDayHigh, regularMarketDayLow, regularMarketOpen, regularMarketPreviousClose,
|
3216
|
-
regularMarketVolume, twoHundredDayAverage, volume,
|
3217
|
-
forwardPE, marketCap, priceToSalesTrailing12Months,
|
3218
|
-
dividendRate, dividendYield, exDividendDate, payoutRatio, trailingAnnualDividendRate,
|
3219
|
-
trailingAnnualDividendYield, trailingPE,
|
3220
|
-
"""
|
3221
|
-
try:
|
3222
|
-
adict=stock.summary_detail
|
3223
|
-
except:
|
3224
|
-
print(" #Error(stock_info): failed to get summary detail of",symbol)
|
3225
|
-
return None
|
3226
|
-
|
3227
|
-
if adict[symbol] == 'Invalid Cookie':
|
3228
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3229
|
-
return None
|
3230
|
-
|
3231
|
-
keylist=list(adict[symbol].keys())
|
3232
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3233
|
-
ainfo=aframe.T
|
3234
|
-
info=pd.concat([info,ainfo])
|
3235
|
-
|
3236
|
-
|
3237
|
-
"""
|
3238
|
-
summary_profile
|
3239
|
-
address/city/country/zip, phone/fax, sector/industry, website/longBusinessSummary,
|
3240
|
-
fullTimeEmployees,
|
3241
|
-
"""
|
3242
|
-
try:
|
3243
|
-
adict=stock.summary_profile
|
3244
|
-
except:
|
3245
|
-
print(" #Error(stock_info): failed to get summary profile of",symbol)
|
3246
|
-
print(" Possible reasons:","\n 1.Wrong stock code","\n 2.Instable data source, try later")
|
3247
|
-
return None
|
3248
|
-
|
3249
|
-
if adict[symbol] == 'Invalid Cookie':
|
3250
|
-
print(" #Error(stock_info): failed in retrieving info of",symbol)
|
3251
|
-
return None
|
3252
|
-
|
3253
|
-
keylist=list(adict[symbol].keys())
|
3254
|
-
aframe=pd.DataFrame.from_dict(adict, orient='index', columns=keylist)
|
3255
|
-
ainfo=aframe.T
|
3256
|
-
info=pd.concat([info,ainfo])
|
3257
|
-
|
3258
|
-
|
3259
|
-
# 清洗数据项目
|
3260
|
-
info.sort_index(inplace=True) #排序
|
3261
|
-
|
3262
|
-
import numpy as np
|
3263
|
-
colList=list(info)
|
3264
|
-
info[colList[0]]=info[colList[0]].apply(lambda x: np.nan if x==[] else x)
|
3265
|
-
info.dropna(inplace=True) #去掉空值
|
3266
|
-
#去重
|
3267
|
-
info['Item']=info.index
|
3268
|
-
info.drop_duplicates(subset=['Item'],keep='first',inplace=True)
|
3269
|
-
|
3270
|
-
#删除不需要的项目
|
3271
|
-
delrows=['adult','alcoholic','animalTesting','ask','askSize','bid','bidSize', \
|
3272
|
-
'catholic','coal','controversialWeapons','furLeather','gambling', \
|
3273
|
-
'gmo','gmtOffSetMilliseconds','militaryContract','messageBoardId', \
|
3274
|
-
'nuclear','palmOil','pesticides','tobacco','uuid','maxAge']
|
3275
|
-
for r in delrows:
|
3276
|
-
info.drop(info[info['Item']==r].index,inplace=True)
|
3277
|
-
|
3278
|
-
#修改列名
|
3279
|
-
info.rename(columns={symbol:'Value'}, inplace=True)
|
3280
|
-
del info['Item']
|
3281
|
-
|
3282
|
-
return info
|
3283
|
-
|
3284
|
-
|
3285
|
-
if __name__=='__main__':
|
3286
|
-
info=stock_info('AAPL')
|
3287
|
-
info=stock_info('BABA')
|
3288
|
-
|
3289
|
-
#==============================================================================
|
3290
|
-
if __name__=='__main__':
|
3291
|
-
info=stock_info('AAPL')
|
3292
|
-
|
3293
|
-
def stock_basic(info):
|
3294
|
-
|
3295
|
-
wishlist=['sector','industry','quoteType', \
|
3296
|
-
#公司地址,网站
|
3297
|
-
'address1','address2','city','state','country','zip','phone','fax', \
|
3298
|
-
'website', \
|
3299
|
-
|
3300
|
-
#员工人数
|
3301
|
-
'fullTimeEmployees', \
|
3302
|
-
|
3303
|
-
#上市与交易所
|
3304
|
-
'exchangeName', \
|
3305
|
-
|
3306
|
-
#其他
|
3307
|
-
'currency']
|
3308
|
-
|
3309
|
-
#按照wishlist的顺序从info中取值
|
3310
|
-
rowlist=list(info.index)
|
3311
|
-
import pandas as pd
|
3312
|
-
info_sub=pd.DataFrame(columns=['Item','Value'])
|
3313
|
-
infot=info.T
|
3314
|
-
for w in wishlist:
|
3315
|
-
if w in rowlist:
|
3316
|
-
v=infot[w][0]
|
3317
|
-
s=pd.Series({'Item':w,'Value':v})
|
3318
|
-
try:
|
3319
|
-
info_sub=info_sub.append(s,ignore_index=True)
|
3320
|
-
except:
|
3321
|
-
info_sub=info_sub._append(s,ignore_index=True)
|
3322
|
-
|
3323
|
-
return info_sub
|
3324
|
-
|
3325
|
-
if __name__=='__main__':
|
3326
|
-
basic=stock_basic(info)
|
3327
|
-
|
3328
|
-
#==============================================================================
|
3329
|
-
if __name__=='__main__':
|
3330
|
-
info=stock_info('AAPL')
|
3331
|
-
|
3332
|
-
def stock_officers(info):
|
3333
|
-
|
3334
|
-
wishlist=['sector','industry','currency', \
|
3335
|
-
#公司高管
|
3336
|
-
'companyOfficers', \
|
3337
|
-
]
|
3338
|
-
|
3339
|
-
#按照wishlist的顺序从info中取值
|
3340
|
-
rowlist=list(info.index)
|
3341
|
-
import pandas as pd
|
3342
|
-
info_sub=pd.DataFrame(columns=['Item','Value'])
|
3343
|
-
infot=info.T
|
3344
|
-
for w in wishlist:
|
3345
|
-
if w in rowlist:
|
3346
|
-
v=infot[w][0]
|
3347
|
-
s=pd.Series({'Item':w,'Value':v})
|
3348
|
-
try:
|
3349
|
-
info_sub=info_sub.append(s,ignore_index=True)
|
3350
|
-
except:
|
3351
|
-
info_sub=info_sub._append(s,ignore_index=True)
|
3352
|
-
|
3353
|
-
return info_sub
|
3354
|
-
|
3355
|
-
if __name__=='__main__':
|
3356
|
-
sub_info=stock_officers(info)
|
3357
|
-
|
3358
|
-
#==============================================================================
|
3359
|
-
def stock_risk_general(info):
|
3360
|
-
|
3361
|
-
wishlist=['sector','industry', \
|
3362
|
-
|
3363
|
-
'overallRisk','boardRisk','compensationRisk', \
|
3364
|
-
'shareHolderRightsRisk','auditRisk'
|
3365
|
-
]
|
3366
|
-
|
3367
|
-
#按照wishlist的顺序从info中取值
|
3368
|
-
rowlist=list(info.index)
|
3369
|
-
import pandas as pd
|
3370
|
-
info_sub=pd.DataFrame(columns=['Item','Value'])
|
3371
|
-
infot=info.T
|
3372
|
-
for w in wishlist:
|
3373
|
-
if w in rowlist:
|
3374
|
-
v=infot[w][0]
|
3375
|
-
s=pd.Series({'Item':w,'Value':v})
|
3376
|
-
try:
|
3377
|
-
info_sub=info_sub.append(s,ignore_index=True)
|
3378
|
-
except:
|
3379
|
-
info_sub=info_sub._append(s,ignore_index=True)
|
3380
|
-
|
3381
|
-
return info_sub
|
3382
|
-
|
3383
|
-
if __name__=='__main__':
|
3384
|
-
risk_general=stock_risk_general(info)
|
3385
|
-
|
3386
|
-
#==============================================================================
|
3387
|
-
def stock_risk_esg(info):
|
3388
|
-
"""
|
3389
|
-
wishlist=[
|
3390
|
-
'peerGroup','peerCount','percentile','esgPerformance', \
|
3391
|
-
'totalEsg','peerEsgScorePerformance', \
|
3392
|
-
'environmentScore','peerEnvironmentPerformance', \
|
3393
|
-
'socialScore','peerSocialPerformance','relatedControversy', \
|
3394
|
-
'governanceScore','peerGovernancePerformance'
|
3395
|
-
]
|
3396
|
-
"""
|
3397
|
-
wishlist=[
|
3398
|
-
'peerGroup','peerCount','percentile', \
|
3399
|
-
'totalEsg','peerEsgScorePerformance', \
|
3400
|
-
'environmentScore','peerEnvironmentPerformance', \
|
3401
|
-
'socialScore','peerSocialPerformance','relatedControversy', \
|
3402
|
-
'governanceScore','peerGovernancePerformance'
|
3403
|
-
]
|
3404
|
-
|
3405
|
-
#按照wishlist的顺序从info中取值
|
3406
|
-
rowlist=list(info.index)
|
3407
|
-
import pandas as pd
|
3408
|
-
info_sub=pd.DataFrame(columns=['Item','Value'])
|
3409
|
-
infot=info.T
|
3410
|
-
for w in wishlist:
|
3411
|
-
if w in rowlist:
|
3412
|
-
v=infot[w][0]
|
3413
|
-
s=pd.Series({'Item':w,'Value':v})
|
3414
|
-
try:
|
3415
|
-
info_sub=info_sub.append(s,ignore_index=True)
|
3416
|
-
except:
|
3417
|
-
info_sub=info_sub._append(s,ignore_index=True)
|
3418
|
-
|
3419
|
-
return info_sub
|
3420
|
-
|
3421
|
-
if __name__=='__main__':
|
3422
|
-
risk_esg=stock_risk_esg(info)
|
3423
|
-
|
3424
|
-
#==============================================================================
|
3425
|
-
def stock_fin_rates(info):
|
3426
|
-
|
3427
|
-
wishlist=['financialCurrency', \
|
3428
|
-
|
3429
|
-
#偿债能力
|
3430
|
-
'currentRatio','quickRatio','debtToEquity', \
|
3431
|
-
|
3432
|
-
#盈利能力
|
3433
|
-
#'ebitdaMargins','operatingMargins','grossMargins','profitMargins', \
|
3434
|
-
'operatingMargins','profitMargins', \
|
3435
|
-
|
3436
|
-
#股东回报率
|
3437
|
-
'returnOnAssets','returnOnEquity', \
|
3438
|
-
'dividendRate','trailingAnnualDividendRate','trailingEps', \
|
3439
|
-
'payoutRatio','revenuePerShare','totalCashPerShare', \
|
3440
|
-
|
3441
|
-
#业务发展能力
|
3442
|
-
#'revenueGrowth','earningsGrowth','earningsQuarterlyGrowth'
|
3443
|
-
'revenueGrowth','earningsQuarterlyGrowth',
|
3444
|
-
]
|
3445
|
-
|
3446
|
-
#按照wishlist的顺序从info中取值
|
3447
|
-
rowlist=list(info.index)
|
3448
|
-
import pandas as pd
|
3449
|
-
info_sub=pd.DataFrame(columns=['Item','Value'])
|
3450
|
-
infot=info.T
|
3451
|
-
for w in wishlist:
|
3452
|
-
if w in rowlist:
|
3453
|
-
v=infot[w][0]
|
3454
|
-
s=pd.Series({'Item':w,'Value':v})
|
3455
|
-
try:
|
3456
|
-
info_sub=info_sub.append(s,ignore_index=True)
|
3457
|
-
except:
|
3458
|
-
info_sub=info_sub._append(s,ignore_index=True)
|
3459
|
-
|
3460
|
-
return info_sub
|
3461
|
-
|
3462
|
-
if __name__=='__main__':
|
3463
|
-
fin_rates=stock_fin_rates(info)
|
3464
|
-
|
3465
|
-
#==============================================================================
|
3466
|
-
def stock_fin_statements(info):
|
3467
|
-
|
3468
|
-
wishlist=['financialCurrency','lastFiscalYearEnd','mostRecentQuarter','nextFiscalYearEnd', \
|
3469
|
-
|
3470
|
-
#资产负债
|
3471
|
-
#'marketCap','totalAssets','totalDebt', \
|
3472
|
-
'marketCap', \
|
3473
|
-
|
3474
|
-
#利润表
|
3475
|
-
'totalRevenue','grossProfits','ebitda','netIncomeToCommon', \
|
3476
|
-
|
3477
|
-
#现金流量
|
3478
|
-
'operatingCashflow','freeCashflow','totalCash', \
|
3479
|
-
|
3480
|
-
#股票数量
|
3481
|
-
'sharesOutstanding','totalInsiderShares'
|
3482
|
-
]
|
3483
|
-
|
3484
|
-
datelist=['lastFiscalYearEnd','mostRecentQuarter','nextFiscalYearEnd']
|
3485
|
-
|
3486
|
-
#按照wishlist的顺序从info中取值
|
3487
|
-
rowlist=list(info.index)
|
3488
|
-
import pandas as pd
|
3489
|
-
info_sub=pd.DataFrame(columns=['Item','Value'])
|
3490
|
-
infot=info.T
|
3491
|
-
for w in wishlist:
|
3492
|
-
if w in rowlist:
|
3493
|
-
if not (w in datelist):
|
3494
|
-
v=infot[w][0]
|
3495
|
-
else:
|
3496
|
-
v=infot[w][0][0:10]
|
3497
|
-
|
3498
|
-
s=pd.Series({'Item':w,'Value':v})
|
3499
|
-
try:
|
3500
|
-
info_sub=info_sub.append(s,ignore_index=True)
|
3501
|
-
except:
|
3502
|
-
info_sub=info_sub._append(s,ignore_index=True)
|
3503
|
-
|
3504
|
-
return info_sub
|
3505
|
-
|
3506
|
-
if __name__=='__main__':
|
3507
|
-
fin_statements=stock_fin_statements(info)
|
3508
|
-
|
3509
|
-
#==============================================================================
|
3510
|
-
def stock_market_rates(info):
|
3511
|
-
|
3512
|
-
wishlist=['beta','currency', \
|
3513
|
-
|
3514
|
-
#市场观察
|
3515
|
-
'priceToBook','priceToSalesTrailing12Months', \
|
3516
|
-
|
3517
|
-
#市场风险与收益
|
3518
|
-
'52WeekChange','SandP52WeekChange', \
|
3519
|
-
'trailingEps','forwardEps','trailingPE','forwardPE','pegRatio', \
|
3520
|
-
|
3521
|
-
#分红
|
3522
|
-
'dividendYield', \
|
3523
|
-
|
3524
|
-
#持股
|
3525
|
-
'heldPercentInsiders','heldPercentInstitutions', \
|
3526
|
-
|
3527
|
-
#股票流通
|
3528
|
-
'sharesOutstanding','currentPrice',
|
3529
|
-
'targetHighPrice','targetMeanPrice','targetMedianPrice','targetLowPrice',
|
3530
|
-
'numberOfAnalystOpinions',
|
3531
|
-
#'recommendationKey',
|
3532
|
-
]
|
3533
|
-
|
3534
|
-
#按照wishlist的顺序从info中取值
|
3535
|
-
rowlist=list(info.index)
|
3536
|
-
import pandas as pd
|
3537
|
-
info_sub=pd.DataFrame(columns=['Item','Value'])
|
3538
|
-
infot=info.T
|
3539
|
-
for w in wishlist:
|
3540
|
-
if w in rowlist:
|
3541
|
-
v=infot[w][0]
|
3542
|
-
s=pd.Series({'Item':w,'Value':v})
|
3543
|
-
try:
|
3544
|
-
info_sub=info_sub.append(s,ignore_index=True)
|
3545
|
-
except:
|
3546
|
-
info_sub=info_sub._append(s,ignore_index=True)
|
3547
|
-
|
3548
|
-
return info_sub
|
3549
|
-
|
3550
|
-
if __name__=='__main__':
|
3551
|
-
market_rates=stock_market_rates(info)
|
3552
|
-
|
3553
|
-
#==============================================================================
|
3554
|
-
if __name__=='__main__':
|
3555
|
-
ticker='AAPL'
|
3556
|
-
ticker='00700.HK'
|
3557
|
-
ticker='03968.HK'
|
3558
|
-
ticker='01398.HK'
|
3559
|
-
|
3560
|
-
info_type='fin_rates'
|
3561
|
-
|
3562
|
-
ticker='FIBI.TA'
|
3563
|
-
info_type='officers'
|
3564
|
-
|
3565
|
-
def get_stock_profile(ticker,info_type='basic',printout=True):
|
3566
|
-
"""
|
3567
|
-
功能:抓取和获得股票的信息
|
3568
|
-
basic: 基本信息
|
3569
|
-
officers:管理层
|
3570
|
-
fin_rates: 财务比率快照
|
3571
|
-
fin_statements: 财务报表快照
|
3572
|
-
market_rates: 市场比率快照
|
3573
|
-
risk_general: 一般风险快照
|
3574
|
-
risk_esg: 可持续发展风险快照(有些股票无此信息)
|
3575
|
-
"""
|
3576
|
-
#print("\nSearching for snapshot info of",ticker,"\b, please wait...")
|
3577
|
-
|
3578
|
-
typelist=['basic','officers','fin_rates','fin_statements','market_rates','risk_general','risk_esg']
|
3579
|
-
if info_type not in typelist:
|
3580
|
-
print(" #Sorry, info_type not supported for",info_type)
|
3581
|
-
print(" Supported info_type:\n",typelist)
|
3582
|
-
return None
|
3583
|
-
|
3584
|
-
#应对各种出错情形:执行出错,返回NoneType,返回空值
|
3585
|
-
try:
|
3586
|
-
info=stock_info(ticker)
|
3587
|
-
except:
|
3588
|
-
print(" #Warning(get_stock_profile): recovering info for",ticker,"...")
|
3589
|
-
import time; time.sleep(5)
|
3590
|
-
try:
|
3591
|
-
info=stock_info(ticker)
|
3592
|
-
except:
|
3593
|
-
print(" #Error(get_stock_profile): failed to access Yahoo for",ticker)
|
3594
|
-
return None
|
3595
|
-
if info is None:
|
3596
|
-
print(" #Error(get_stock_profile): retrieved none info of",ticker)
|
3597
|
-
print(f" Solution: if {ticker} is correct, try again later!")
|
3598
|
-
return None
|
3599
|
-
if len(info) == 0:
|
3600
|
-
print(" #Error(get_stock_profile): retrieved empty info of",ticker)
|
3601
|
-
return None
|
3602
|
-
"""
|
3603
|
-
#处理公司短名字
|
3604
|
-
name0=info.T['shortName'][0]
|
3605
|
-
name1=name0.split('.',1)[0] #仅取第一个符号.以前的字符串
|
3606
|
-
name2=name1.split(',',1)[0] #仅取第一个符号,以前的字符串
|
3607
|
-
name3=name2.split('(',1)[0] #仅取第一个符号(以前的字符串
|
3608
|
-
#name4=name3.split(' ',1)[0] #仅取第一个空格以前的字符串
|
3609
|
-
#name=ticker_name(name4) #去掉空格有名字错乱风险
|
3610
|
-
name9=name3.strip()
|
3611
|
-
name=ticker_name(name9) #从短名字翻译
|
3612
|
-
"""
|
3613
|
-
if not printout: return info
|
3614
|
-
|
3615
|
-
footnote=''
|
3616
|
-
name=ticker_name(ticker) #从股票代码直接翻译
|
3617
|
-
if info_type in ['basic']:
|
3618
|
-
sub_info=stock_basic(info)
|
3619
|
-
info_text="公司基本信息"
|
3620
|
-
|
3621
|
-
if info_type in ['officers']:
|
3622
|
-
sub_info=stock_officers(info)
|
3623
|
-
info_text="公司高管信息"
|
3624
|
-
|
3625
|
-
if info_type in ['fin_rates']:
|
3626
|
-
sub_info=stock_fin_rates(info)
|
3627
|
-
info_text="基本财务比率TTM"
|
3628
|
-
|
3629
|
-
if info_type in ['fin_statements']:
|
3630
|
-
sub_info=stock_fin_statements(info)
|
3631
|
-
info_text="财报主要项目"
|
3632
|
-
|
3633
|
-
if info_type in ['market_rates']:
|
3634
|
-
sub_info=stock_market_rates(info)
|
3635
|
-
info_text="基本市场指标"
|
3636
|
-
|
3637
|
-
if info_type in ['risk_general']:
|
3638
|
-
sub_info=stock_risk_general(info)
|
3639
|
-
info_text="一般风险评估"
|
3640
|
-
footnote="注:数值越小风险越低,最高10分"
|
3641
|
-
|
3642
|
-
if info_type in ['risk_esg']:
|
3643
|
-
info_text="ESG风险评估"
|
3644
|
-
footnote="注:分数越小风险越低,最高100分"
|
3645
|
-
sub_info=stock_risk_esg(info)
|
3646
|
-
if len(sub_info)==0:
|
3647
|
-
print(" \n#Warning: ESG info not available for",ticker)
|
3648
|
-
return None
|
3649
|
-
|
3650
|
-
# 显示信息
|
3651
|
-
lang=check_language()
|
3652
|
-
if lang == 'Chinese':
|
3653
|
-
titletxt="===== "+name+": "+info_text+" =====\n"
|
3654
|
-
if len(footnote) > 0:
|
3655
|
-
footnote1='\n'+footnote
|
3656
|
-
else:
|
3657
|
-
footnote1=footnote
|
3658
|
-
else:
|
3659
|
-
titletxt="===== "+name+": "+texttranslate(info_text)+" =====\n"
|
3660
|
-
|
3661
|
-
if len(footnote) > 0:
|
3662
|
-
footnote1='\n'+texttranslate(footnote)
|
3663
|
-
else:
|
3664
|
-
footnote1=footnote
|
3665
|
-
|
3666
|
-
printdf(sub_info,titletxt,footnote1)
|
3667
|
-
|
3668
|
-
return info
|
3669
|
-
|
3670
|
-
if __name__=='__main__':
|
3671
|
-
info=get_stock_profile(ticker,info_type='basic')
|
3672
|
-
info=get_stock_profile(ticker,info_type='officers')
|
3673
|
-
info=get_stock_profile(ticker,info_type='fin_rates')
|
3674
|
-
info=get_stock_profile(ticker,info_type='fin_statements')
|
3675
|
-
info=get_stock_profile(ticker,info_type='market_rates')
|
3676
|
-
info=get_stock_profile(ticker,info_type='risk_general')
|
3677
|
-
info=get_stock_profile(ticker,info_type='risk_esg')
|
3678
|
-
|
3679
|
-
#==============================================================================
|
3680
|
-
|
3681
|
-
def stock_snapshot(ticker='AAPL',info_type="all"):
|
3682
|
-
"""
|
3683
|
-
功能:打印指定选项,套壳函数get_stock_profile
|
3684
|
-
"""
|
3685
|
-
print(" Connecting to Yahoo Finance ... ...")
|
3686
|
-
typelist=['basic',
|
3687
|
-
'officers',
|
3688
|
-
'fin_rates',
|
3689
|
-
'fin_statements',
|
3690
|
-
'market_rates',
|
3691
|
-
'risk_general',
|
3692
|
-
'risk_esg']
|
3693
|
-
|
3694
|
-
if info_type.lower() !="all" and info_type.lower() in typelist:
|
3695
|
-
info=get_stock_profile(ticker,info_type=info_type)
|
3696
|
-
return
|
3697
|
-
|
3698
|
-
if info_type.lower() =="all":
|
3699
|
-
for t in typelist:
|
3700
|
-
info=get_stock_profile(ticker,info_type=t)
|
3701
|
-
|
3702
|
-
if info is None:
|
3703
|
-
break
|
3704
|
-
return
|
3705
|
-
else:
|
3706
|
-
print(" #Error(stock_snapshot): unsupported info type",info_type)
|
3707
|
-
print(" Supporting",typelist)
|
3708
|
-
return
|
3709
|
-
|
3710
|
-
|
3711
|
-
def security_snapshot(ticker='AAPL'):
|
3712
|
-
"""
|
3713
|
-
功能:一次性打印所有选项,套壳函数get_stock_profile
|
3714
|
-
"""
|
3715
|
-
print(" Try connecting to Yahoo Finance ... ...")
|
3716
|
-
typelist=['basic',
|
3717
|
-
'officers',
|
3718
|
-
'fin_rates',
|
3719
|
-
'fin_statements',
|
3720
|
-
'market_rates',
|
3721
|
-
'risk_general',
|
3722
|
-
'risk_esg']
|
3723
|
-
for t in typelist:
|
3724
|
-
info=get_stock_profile(ticker,info_type=t)
|
3725
|
-
|
3726
|
-
if info is None:
|
3727
|
-
break
|
3728
|
-
|
3729
|
-
return
|
3730
|
-
#==============================================================================
|
3731
|
-
if __name__=='__main__':
|
3732
|
-
ticker='AAPL'
|
3733
|
-
info=stock_info(ticker)
|
3734
|
-
sub_info=stock_basic(info)
|
3735
|
-
titletxt="===== "+ticker+": Snr Management ====="
|
3736
|
-
|
3737
|
-
def printdf(sub_info,titletxt,footnote):
|
3738
|
-
"""
|
3739
|
-
功能:整齐显示股票信息快照,翻译中文,按照中文项目长度计算空格数
|
3740
|
-
"""
|
3741
|
-
print("\n"+titletxt)
|
3742
|
-
|
3743
|
-
for index,row in sub_info.iterrows():
|
3744
|
-
|
3745
|
-
#----------------------------------------------------------------------
|
3746
|
-
#特殊打印:高管信息
|
3747
|
-
if row['Item']=="companyOfficers":
|
3748
|
-
print_companyOfficers(sub_info)
|
3749
|
-
continue
|
3750
|
-
|
3751
|
-
#特殊打印:ESG同行状况
|
3752
|
-
peerlist=["peerEsgScorePerformance","peerEnvironmentPerformance", \
|
3753
|
-
"peerSocialPerformance","peerGovernancePerformance"]
|
3754
|
-
if row['Item'] in peerlist:
|
3755
|
-
print_peerPerformance(sub_info,row['Item'])
|
3756
|
-
continue
|
3757
|
-
|
3758
|
-
#特殊打印:ESG Social风险内容
|
3759
|
-
if row['Item']=="relatedControversy":
|
3760
|
-
print_controversy(sub_info,row['Item'])
|
3761
|
-
continue
|
3762
|
-
#----------------------------------------------------------------------
|
3763
|
-
|
3764
|
-
print_item(row['Item'],row['Value'],10)
|
3765
|
-
|
3766
|
-
import datetime; todaydt=datetime.date.today().strftime("%y-%m-%d")
|
3767
|
-
lang=check_language()
|
3768
|
-
if lang == 'Chinese':
|
3769
|
-
print(footnote+"\n数据来源: 雅虎/Sustainalytics,",todaydt)
|
3770
|
-
else:
|
3771
|
-
print(footnote+"\nSource: Yahoo/Sustainalytics,",todaydt)
|
3772
|
-
|
3773
|
-
return
|
3774
|
-
|
3775
|
-
if __name__=='__main__':
|
3776
|
-
printdf(sub_info,titletxt)
|
3777
|
-
|
3778
|
-
#==============================================================================
|
3779
|
-
if __name__=='__main__':
|
3780
|
-
item='currentPrice'
|
3781
|
-
value='110.08'
|
3782
|
-
maxlen=10
|
3783
|
-
|
3784
|
-
def print_item(item,value,maxlen):
|
3785
|
-
"""
|
3786
|
-
功能:打印一个项目和相应的值,中间隔开一定空间对齐
|
3787
|
-
限制:只区分字符串、整数和浮点数
|
3788
|
-
"""
|
3789
|
-
DEBUG=False
|
3790
|
-
|
3791
|
-
print(ectranslate(item)+': ',end='')
|
3792
|
-
|
3793
|
-
directprint=['zip','ratingYear','ratingMonth']
|
3794
|
-
if item in directprint:
|
3795
|
-
if DEBUG: print("...Direct print")
|
3796
|
-
print(value)
|
3797
|
-
return
|
3798
|
-
|
3799
|
-
#是否整数
|
3800
|
-
if isinstance(value,int):
|
3801
|
-
if DEBUG: print("...Integer: ",end='')
|
3802
|
-
if value != 0:
|
3803
|
-
print(format(value,','))
|
3804
|
-
else:
|
3805
|
-
#print('---')
|
3806
|
-
print('0')
|
3807
|
-
return
|
3808
|
-
|
3809
|
-
#是否浮点数
|
3810
|
-
ZERO=0.00001
|
3811
|
-
if isinstance(value,float):
|
3812
|
-
if DEBUG: print("...Float: ",end='')
|
3813
|
-
if value < 1.0:
|
3814
|
-
value1=round(value,4)
|
3815
|
-
else:
|
3816
|
-
value1=round(value,2)
|
3817
|
-
if value <= -ZERO or value >= ZERO:
|
3818
|
-
print(format(value1,','))
|
3819
|
-
else:
|
3820
|
-
#print('---')
|
3821
|
-
print('0.0')
|
3822
|
-
return
|
3823
|
-
|
3824
|
-
#是否字符串
|
3825
|
-
if not isinstance(value,str):
|
3826
|
-
print(str(value))
|
3827
|
-
|
3828
|
-
#是否字符串表示的整数
|
3829
|
-
if value.isdigit():
|
3830
|
-
value1=int(value)
|
3831
|
-
if DEBUG: print("...Integer in string: ",end='')
|
3832
|
-
if value1 != 0:
|
3833
|
-
print(format(value1,','))
|
3834
|
-
else:
|
3835
|
-
#print('---')
|
3836
|
-
print('0')
|
3837
|
-
return
|
3838
|
-
|
3839
|
-
#是否字符串表示的浮点数
|
3840
|
-
try:
|
3841
|
-
value1=float(value)
|
3842
|
-
if value1 < 1.0:
|
3843
|
-
value2=round(value1,4)
|
3844
|
-
else:
|
3845
|
-
value2=round(value1,2)
|
3846
|
-
if DEBUG: print("...Float in string")
|
3847
|
-
if value1 <= -ZERO or value1 >= ZERO:
|
3848
|
-
print(format(value2,','))
|
3849
|
-
else:
|
3850
|
-
#print('---')
|
3851
|
-
print('0.0')
|
3852
|
-
except:
|
3853
|
-
#只是字符串
|
3854
|
-
if DEBUG: print("...String")
|
3855
|
-
print(value)
|
3856
|
-
|
3857
|
-
return
|
3858
|
-
|
3859
|
-
if __name__=='__main__':
|
3860
|
-
print_item('currentPrice','110.08',10)
|
3861
|
-
|
3862
|
-
#==============================================================================
|
3863
|
-
if __name__=='__main__':
|
3864
|
-
str1='哈哈哈ROA1'
|
3865
|
-
|
3866
|
-
def str_len(str1):
|
3867
|
-
"""
|
3868
|
-
功能:计算中英文混合字符串的实际占位长度,不太准
|
3869
|
-
"""
|
3870
|
-
len_d=len(str1)
|
3871
|
-
len_u=len(str1.encode('utf_8'))
|
3872
|
-
|
3873
|
-
num_ch=(len_u - len_d)/2
|
3874
|
-
num_en=len_d - num_ch
|
3875
|
-
totallen=int(num_ch*2 + num_en)
|
3876
|
-
|
3877
|
-
return totallen
|
3878
|
-
|
3879
|
-
if __name__=='__main__':
|
3880
|
-
str_len('哈哈哈ROA1')
|
3881
|
-
|
3882
|
-
#==============================================================================
|
3883
|
-
if __name__=='__main__':
|
3884
|
-
info=stock_info('AAPL')
|
3885
|
-
sub_info=stock_officers(info)
|
3886
|
-
|
3887
|
-
def print_companyOfficers(sub_info):
|
3888
|
-
"""
|
3889
|
-
功能:打印公司高管信息
|
3890
|
-
"""
|
3891
|
-
item='companyOfficers'
|
3892
|
-
|
3893
|
-
lang=check_language()
|
3894
|
-
if lang == 'English':
|
3895
|
-
itemtxt=texttranslate('公司高管:')
|
3896
|
-
else:
|
3897
|
-
itemtxt='公司高管:'
|
3898
|
-
|
3899
|
-
key1='name'
|
3900
|
-
key2='title'
|
3901
|
-
key3='yearBorn'
|
3902
|
-
key4='age'
|
3903
|
-
|
3904
|
-
key6='totalPay'
|
3905
|
-
key7='fiscalYear'
|
3906
|
-
currency=list(sub_info[sub_info['Item'] == 'currency']['Value'])[0]
|
3907
|
-
alist=list(sub_info[sub_info['Item'] == item]['Value'])[0]
|
3908
|
-
|
3909
|
-
print(itemtxt)
|
3910
|
-
if len(alist)==0:
|
3911
|
-
print(" #Warning(print_companyOfficers): company officer info not available")
|
3912
|
-
|
3913
|
-
import datetime as dt; today=dt.date.today()
|
3914
|
-
thisyear=int(str(today)[:4])
|
3915
|
-
for i in alist:
|
3916
|
-
|
3917
|
-
#测试是否存在:姓名,职位,出生年份
|
3918
|
-
try:
|
3919
|
-
ikey1=i[key1]
|
3920
|
-
ikey2=i[key2]
|
3921
|
-
ikey3=i[key3]
|
3922
|
-
except:
|
3923
|
-
continue
|
3924
|
-
ikey4=thisyear-ikey3
|
3925
|
-
print(' '*4,ikey1)
|
3926
|
-
print(' '*8,ikey2,'\b,',ikey4,texttranslate('\b岁 (生于')+str(ikey3)+')')
|
3927
|
-
|
3928
|
-
#测试是否存在:薪酬信息
|
3929
|
-
try:
|
3930
|
-
ikey6=i[key6]
|
3931
|
-
ikey7=i[key7]
|
3932
|
-
if ikey6 > 0:
|
3933
|
-
print(' '*8,texttranslate('总薪酬'),currency+str(format(ikey6,',')),'@'+str(ikey7))
|
3934
|
-
except:
|
3935
|
-
continue
|
3936
|
-
return
|
3937
|
-
|
3938
|
-
if __name__=='__main__':
|
3939
|
-
print_companyOfficers(sub_info)
|
3940
|
-
|
3941
|
-
#==============================================================================
|
3942
|
-
if __name__=='__main__':
|
3943
|
-
info=stock_info('AAPL')
|
3944
|
-
sub_info=stock_risk_esg(info)
|
3945
|
-
item="peerEsgScorePerformance"
|
3946
|
-
|
3947
|
-
def print_peerPerformance(sub_info,item):
|
3948
|
-
"""
|
3949
|
-
功能:打印ESG信息
|
3950
|
-
"""
|
3951
|
-
|
3952
|
-
key1='min'
|
3953
|
-
key2='avg'
|
3954
|
-
key3='max'
|
3955
|
-
i=list(sub_info[sub_info['Item'] == item]['Value'])[0]
|
3956
|
-
|
3957
|
-
"""
|
3958
|
-
print(ectranslate(item)+':')
|
3959
|
-
print(' '*4,key1+':',i[key1],'\b,',key2+':',round(i[key2],2),'\b,',key3+':',i[key3])
|
3960
|
-
"""
|
3961
|
-
print(ectranslate(item)+': ',end='')
|
3962
|
-
print(texttranslate("均值")+str(round(i[key2],2)),end='')
|
3963
|
-
print(" ("+str(i[key1])+'-'+str(i[key3])+")")
|
3964
|
-
|
3965
|
-
return
|
3966
|
-
|
3967
|
-
if __name__=='__main__':
|
3968
|
-
print_peerPerformance(sub_info,item)
|
3969
|
-
|
3970
|
-
#==============================================================================
|
3971
|
-
if __name__=='__main__':
|
3972
|
-
info=stock_info('AAPL')
|
3973
|
-
sub_info=stock_risk_esg(info)
|
3974
|
-
item='relatedControversy'
|
3975
|
-
|
3976
|
-
def print_controversy(sub_info,item):
|
3977
|
-
"""
|
3978
|
-
功能:打印ESG Social风险内容
|
3979
|
-
"""
|
3980
|
-
alist=list(sub_info[sub_info['Item'] == item]['Value'])[0]
|
3981
|
-
if len(alist)==0:
|
3982
|
-
print(" #Error(print_controversy): no relevant info found.")
|
3983
|
-
|
3984
|
-
print(ectranslate(item)+':')
|
3985
|
-
for i in alist:
|
3986
|
-
print(' '*4,ectranslate(i))
|
3987
|
-
|
3988
|
-
return
|
3989
|
-
|
3990
|
-
if __name__=='__main__':
|
3991
|
-
print_controversy(sub_info,item)
|
3992
|
-
|
3993
|
-
#==============================================================================
|
3994
|
-
if __name__ =="__main__":
|
3995
|
-
stocklist=["BAC", "TD","PNC"]
|
3996
|
-
|
3997
|
-
def get_esg2(stocklist):
|
3998
|
-
"""
|
3999
|
-
功能:根据股票代码列表,抓取企业最新的可持续性发展ESG数据
|
4000
|
-
输入参数:
|
4001
|
-
stocklist:股票代码列表,例如单个股票["AAPL"], 多只股票["AAPL","MSFT","GOOG"]
|
4002
|
-
输出参数:
|
4003
|
-
企业最新的可持续性发展ESG数据,数据框
|
4004
|
-
"""
|
4005
|
-
|
4006
|
-
import pandas as pd
|
4007
|
-
collist=['symbol','totalEsg','environmentScore','socialScore','governanceScore']
|
4008
|
-
sust=pd.DataFrame(columns=collist)
|
4009
|
-
for t in stocklist:
|
4010
|
-
try:
|
4011
|
-
info=stock_info(t).T
|
4012
|
-
except:
|
4013
|
-
print(" #Error(get_esg2): esg info not available for",t)
|
4014
|
-
continue
|
4015
|
-
if (info is None) or (len(info)==0):
|
4016
|
-
print(" #Error(get_esg2): failed to get esg info for",t)
|
4017
|
-
continue
|
4018
|
-
sub=info[collist]
|
4019
|
-
sust=pd.concat([sust,sub])
|
4020
|
-
|
4021
|
-
newcols=['Stock','ESGscore','EPscore','CSRscore','CGscore']
|
4022
|
-
sust.columns=newcols
|
4023
|
-
"""
|
4024
|
-
sust=sust.rename(columns={'symbol':'Stock','totalEsg':'ESGscore', \
|
4025
|
-
'environmentScore':'EPscore', \
|
4026
|
-
'socialScore':'CSRscore', \
|
4027
|
-
'governanceScore':'CGscore'})
|
4028
|
-
"""
|
4029
|
-
sust.set_index('Stock',inplace=True)
|
4030
|
-
|
4031
|
-
return sust
|
4032
|
-
|
4033
|
-
if __name__ =="__main__":
|
4034
|
-
sust=get_esg2(stocklist)
|
4035
|
-
|
4036
|
-
#==============================================================================
|
4037
|
-
#==============================================================================
|
4038
|
-
def portfolio_esg2(portfolio):
|
4039
|
-
"""
|
4040
|
-
功能:抓取、打印和绘图投资组合portfolio的可持续性发展数据,演示用
|
4041
|
-
输入参数:
|
4042
|
-
企业最新的可持续性发展数据,数据框
|
4043
|
-
"""
|
4044
|
-
#解构投资组合
|
4045
|
-
_,_,stocklist,_,ticker_type=decompose_portfolio(portfolio)
|
4046
|
-
|
4047
|
-
#抓取数据
|
4048
|
-
try:
|
4049
|
-
sust=get_esg2(stocklist)
|
4050
|
-
except:
|
4051
|
-
print(" #Error(portfolio_esg): fail to get ESG data for",stocklist)
|
4052
|
-
return None
|
4053
|
-
if sust is None:
|
4054
|
-
#print("#Error(portfolio_esg), fail to get ESG data for",stocklist)
|
4055
|
-
return None
|
4056
|
-
|
4057
|
-
#处理小数点
|
4058
|
-
from pandas.api.types import is_numeric_dtype
|
4059
|
-
cols=list(sust)
|
4060
|
-
for c in cols:
|
4061
|
-
if is_numeric_dtype(sust[c]):
|
4062
|
-
sust[c]=round(sust[c],2)
|
4063
|
-
|
4064
|
-
#显示结果
|
4065
|
-
print(texttranslate("\n===== 投资组合的ESG风险评估 ====="))
|
4066
|
-
print(texttranslate("投资组合:"),stocklist)
|
4067
|
-
#显示各个成分股的ESG分数
|
4068
|
-
sust['Stock']=sust.index
|
4069
|
-
esgdf=sust[['Stock','ESGscore','EPscore','CSRscore','CGscore']]
|
4070
|
-
print(esgdf.to_string(index=False))
|
4071
|
-
|
4072
|
-
print("\n"+texttranslate("ESG评估分数:"))
|
4073
|
-
#木桶短板:EPScore
|
4074
|
-
esg_ep=esgdf.sort_values(['EPscore'], ascending = True)
|
4075
|
-
p_ep=esg_ep['EPscore'][-1]
|
4076
|
-
p_ep_stock=esg_ep.index[-1]
|
4077
|
-
str_ep=texttranslate(" EP分数(基于")+str(p_ep_stock)+ticker_name(str(p_ep_stock))+")"
|
4078
|
-
len_ep=hzlen(str_ep)
|
4079
|
-
|
4080
|
-
#木桶短板:CSRScore
|
4081
|
-
esg_csr=esgdf.sort_values(['CSRscore'], ascending = True)
|
4082
|
-
p_csr=esg_csr['CSRscore'][-1]
|
4083
|
-
p_csr_stock=esg_csr.index[-1]
|
4084
|
-
str_csr=texttranslate(" CSR分数(基于")+str(p_csr_stock)+ticker_name(str(p_csr_stock))+")"
|
4085
|
-
len_csr=hzlen(str_csr)
|
4086
|
-
|
4087
|
-
#木桶短板:CGScore
|
4088
|
-
esg_cg=esgdf.sort_values(['CGscore'], ascending = True)
|
4089
|
-
p_cg=esg_cg['CGscore'][-1]
|
4090
|
-
p_cg_stock=esg_cg.index[-1]
|
4091
|
-
str_cg=texttranslate(" CG分数(基于")+str(p_cg_stock)+ticker_name(str(p_cg_stock))+")"
|
4092
|
-
len_cg=hzlen(str_cg)
|
4093
|
-
|
4094
|
-
str_esg=texttranslate(" ESG总评分数")
|
4095
|
-
len_esg=hzlen(str_esg)
|
4096
|
-
|
4097
|
-
#计算对齐冒号中间需要的空格数目
|
4098
|
-
len_max=max(len_ep,len_csr,len_cg,len_esg)
|
4099
|
-
str_ep=str_ep+' '*(len_max-len_ep+1)+':'
|
4100
|
-
str_csr=str_csr+' '*(len_max-len_csr+1)+':'
|
4101
|
-
str_cg=str_cg+' '*(len_max-len_cg+1)+':'
|
4102
|
-
str_esg=str_esg+' '*(len_max-len_esg+1)+':'
|
4103
|
-
|
4104
|
-
#对齐打印
|
4105
|
-
print(str_ep,p_ep)
|
4106
|
-
print(str_csr,p_csr)
|
4107
|
-
print(str_cg,p_cg)
|
4108
|
-
#计算投资组合的ESG综合风险
|
4109
|
-
p_esg=round(p_ep+p_csr+p_cg,2)
|
4110
|
-
print(str_esg,p_esg)
|
4111
|
-
|
4112
|
-
import datetime as dt; today=dt.date.today()
|
4113
|
-
footnote=texttranslate("注:分数越高, 风险越高.")+"\n"+texttranslate("数据来源:雅虎,")+str(today)
|
4114
|
-
print(footnote)
|
4115
|
-
|
4116
|
-
return p_esg
|
4117
|
-
|
4118
|
-
if __name__ =="__main__":
|
4119
|
-
#market={'Market':('China','^HSI')}
|
4120
|
-
market={'Market':('US','^GSPC')}
|
4121
|
-
#stocks={'0939.HK':2,'1398.HK':1,'3988.HK':3}
|
4122
|
-
stocks={'VIPS':3,'JD':2,'BABA':1}
|
4123
|
-
portfolio=dict(market,**stocks)
|
4124
|
-
esg=portfolio_esg(portfolio)
|
4125
|
-
#==============================================================================
|
4126
|
-
|
4127
|
-
if __name__ =="__main__":
|
4128
|
-
ticker='AAPL'
|
4129
|
-
measures=['High','Low',"Open",'Close']
|
4130
|
-
fromdate='2022-7-1'
|
4131
|
-
todate='2022-12-1'
|
4132
|
-
|
4133
|
-
axhline_value=0
|
4134
|
-
axhline_label=''
|
4135
|
-
linewidth=1.5
|
4136
|
-
graph=True
|
4137
|
-
|
4138
|
-
df=compare_mmeasure(ticker,measures,fromdate,todate)
|
4139
|
-
|
4140
|
-
measures=['Daily Ret%',"Monthly Ret%",'Annual Ret%']
|
4141
|
-
df=compare_mmeasure(ticker,measures,fromdate,todate)
|
4142
|
-
|
4143
|
-
measures=['Daily Ret%',"Exp Ret%",'Annual Ret%']
|
4144
|
-
df=compare_mmeasure(ticker,measures,fromdate,todate,axhline_value=0,axhline_label='零线')
|
4145
|
-
|
4146
|
-
|
4147
|
-
def compare_mmeasure(ticker,measures,fromdate,todate, \
|
4148
|
-
axhline_value=0,axhline_label='',linewidth=1.5, \
|
4149
|
-
graph=True,smooth=True):
|
4150
|
-
"""
|
4151
|
-
功能:绘制单证券多指标对比图
|
4152
|
-
"""
|
4153
|
-
#检查期间的合理性
|
4154
|
-
result,startpd,endpd=check_period(fromdate,todate)
|
4155
|
-
if not result:
|
4156
|
-
print(" #Error(compare_mmeasure): invalid date period from",fromdate,"to",todate)
|
4157
|
-
return None
|
4158
|
-
|
4159
|
-
ticker1=ticker.upper()
|
4160
|
-
#fromdate1=date_adjust(fromdate,adjust=-365)
|
4161
|
-
fromdate1=fromdate
|
4162
|
-
#抓取行情,并计算其各种期间的收益率
|
4163
|
-
df1a=stock_ret(ticker1,fromdate1,todate,graph=False)
|
4164
|
-
if df1a is None:
|
4165
|
-
print(" #Error(compare_mmeasure): no price info found for",ticker,"from",fromdate,"to",todate)
|
4166
|
-
return None
|
4167
|
-
|
4168
|
-
#加入价格波动指标
|
4169
|
-
df1b=price_volatility2(df1a,ticker1,fromdate1,todate,graph=False)
|
4170
|
-
#加入收益率波动指标
|
4171
|
-
df1c=ret_volatility2(df1b,ticker1,fromdate1,todate,graph=False)
|
4172
|
-
#加入收益率下偏标准差指标
|
4173
|
-
df1d=ret_lpsd2(df1c,ticker1,fromdate1,todate,graph=False)
|
4174
|
-
|
4175
|
-
#去掉开始日期以前的数据
|
4176
|
-
df2=df1d[(df1d.index >= startpd) & (df1d.index <= endpd)]
|
4177
|
-
|
4178
|
-
#提取绘图指标
|
4179
|
-
collist=[]; collist_notfound=[]
|
4180
|
-
dflist=list(df2)
|
4181
|
-
for m in measures:
|
4182
|
-
if m in dflist:
|
4183
|
-
collist=collist+[m]
|
4184
|
-
else:
|
4185
|
-
collist_notfound=collist_notfound+[m]
|
4186
|
-
if len(collist)==0:
|
4187
|
-
print(" #Error(compare_mmeasure): no measure info found for",ticker,"from",fromdate,"to",todate)
|
4188
|
-
return None
|
4189
|
-
|
4190
|
-
if len(collist_notfound)>0:
|
4191
|
-
print(" #Warning(compare_mmeasure): unsupported measure(s) found ",collist_notfound)
|
4192
|
-
|
4193
|
-
df3=pd.DataFrame(df2[collist])
|
4194
|
-
for c in collist:
|
4195
|
-
df3.rename(columns={c:ectranslate(c)},inplace=True)
|
4196
|
-
|
4197
|
-
# 填充非交易日的缺失值,使得绘制的曲线连续
|
4198
|
-
df3.fillna(axis=0,method='ffill',inplace=True)
|
4199
|
-
#df3.fillna(axis=0,method='bfill',inplace=True)
|
4200
|
-
|
4201
|
-
#绘制单个证券的多指标对比图
|
4202
|
-
y_label=''
|
4203
|
-
import datetime; today = datetime.date.today()
|
4204
|
-
|
4205
|
-
x_label=text_lang("数据来源: Sina/EM/Stooq/Yahoo/SWHY,","Data source: Sina/Yahoo/Stooq/EM, ")+str(today)
|
4206
|
-
title_txt=text_lang("证券趋势分析:","Security Trend: ")+ticker_name(ticker)
|
4207
|
-
|
4208
|
-
draw_lines(df3,y_label=y_label,x_label=x_label, \
|
4209
|
-
axhline_value=axhline_value,axhline_label=axhline_label, \
|
4210
|
-
title_txt=title_txt, \
|
4211
|
-
data_label=False,resample_freq='H',smooth=smooth,linewidth=linewidth)
|
4212
|
-
|
4213
|
-
return df3
|
4214
|
-
|
4215
|
-
#==============================================================================
|
4216
|
-
#==============================================================================
|
4217
|
-
#==============================================================================
|
4218
|
-
#==============================================================================
|
4219
|
-
#==============================================================================
|
4220
|
-
def fix_mac_hanzi_plt():
|
4221
|
-
"""
|
4222
|
-
功能:修复MacOSX中matplotlib绘图时汉字的乱码问题,安装SimHei.ttf字体
|
4223
|
-
注意:本函数未经测试,弃用
|
4224
|
-
"""
|
4225
|
-
#判断当前的操作系统
|
4226
|
-
import platform
|
4227
|
-
pltf=platform.platform()
|
4228
|
-
os=pltf[0:5]
|
4229
|
-
if not (os == "macOS"):
|
4230
|
-
print("#Warning(fix_mac_hanzi_plt): This command is only valid for MacOSX.")
|
4231
|
-
return
|
4232
|
-
|
4233
|
-
#查找模块的安装路径
|
4234
|
-
import os
|
4235
|
-
import imp
|
4236
|
-
dir1=imp.find_module('siat')[1]
|
4237
|
-
dir2=imp.find_module('matplotlib')[1]
|
4238
|
-
|
4239
|
-
#查找matplotlib的字体地址
|
4240
|
-
pltttf=dir2+'/mpl-data/fonts/ttf'
|
4241
|
-
|
4242
|
-
#复制字体文件
|
4243
|
-
cpcmd="cp -r "+dir1+"/SimHei.ttf "+pltttf
|
4244
|
-
result=os.popen(cpcmd)
|
4245
|
-
|
4246
|
-
#修改配置文件内容
|
4247
|
-
import matplotlib
|
4248
|
-
pltrc=matplotlib.matplotlib_fname()
|
4249
|
-
|
4250
|
-
line1='\nfont.family : sans-serif\n'
|
4251
|
-
line2='font.sans-serif : SimHei,DejaVu Sans,Bitstream Vera Sans,Lucida Grande,Verdana,Geneva,Lucid,Arial,Helvetica,Avant Garde,sans-serif\n'
|
4252
|
-
line3='axes.unicode_minus : False\n'
|
4253
|
-
|
4254
|
-
filehandler=open(pltrc,'a')
|
4255
|
-
filehandler.write(line1)
|
4256
|
-
filehandler.write(line2)
|
4257
|
-
filehandler.write(line3)
|
4258
|
-
filehandler.close()
|
4259
|
-
|
4260
|
-
from matplotlib.font_manager import _rebuild
|
4261
|
-
_rebuild()
|
4262
|
-
print(" Fixed Mac Hanzi problems for matplotlib graphics!")
|
4263
|
-
print(" Please RESTART Python kernel to make it effective!")
|
4264
|
-
|
4265
|
-
return
|
4266
|
-
|
4267
|
-
|
4268
|
-
|
4269
|
-
|
4270
|
-
|
4271
|
-
|
4272
|
-
|
4273
|
-
|
4274
|
-
|
4275
|
-
|
4276
|
-
|
4277
|
-
|
4278
|
-
|
4279
|
-
|
4280
|
-
|
4281
|
-
|
4282
|
-
|
4283
|
-
|
4284
|
-
|