siat 3.10.125__py3-none-any.whl → 3.10.127__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/common.py +106 -2
- siat/exchange_bond_china.pickle +0 -0
- siat/fund_china.pickle +0 -0
- siat/stock.py +10 -2
- siat/stock_info.pickle +0 -0
- {siat-3.10.125.dist-info → siat-3.10.127.dist-info}/METADATA +235 -226
- siat-3.10.127.dist-info/RECORD +76 -0
- {siat-3.10.125.dist-info → siat-3.10.127.dist-info}/WHEEL +1 -1
- {siat-3.10.125.dist-info → siat-3.10.127.dist-info/licenses}/LICENSE +0 -0
- {siat-3.10.125.dist-info → siat-3.10.127.dist-info}/top_level.txt +0 -0
- siat/__init__ -20240701.py +0 -65
- siat/__init__.py.backup_20250214.py +0 -73
- siat/alpha_vantage_test.py +0 -24
- siat/assets_liquidity_test.py +0 -44
- siat/barrons_scraping_test.py +0 -276
- siat/beta_adjustment_test.py +0 -77
- siat/bond_test.py +0 -142
- siat/capm_beta_test.py +0 -49
- siat/cmat_commons.py +0 -961
- siat/compare_cross_test.py +0 -117
- siat/concepts_iwencai.py +0 -86
- siat/concepts_kpl.py +0 -93
- siat/cryptocurrency_test.py +0 -71
- siat/derivative.py +0 -1111
- siat/economy-20230125.py +0 -1206
- siat/economy_test.py +0 -360
- siat/esg_test.py +0 -63
- siat/fama_french_test.py +0 -115
- siat/financial_statements_test.py +0 -31
- siat/financials2 - /321/205/320/231/320/277/321/206/320/254/320/274.py" +0 -341
- siat/financials_china2_test.py +0 -67
- siat/financials_china2_test2.py +0 -88
- siat/financials_china2_test3.py +0 -87
- siat/financials_china_test.py +0 -475
- siat/financials_china_test2.py +0 -197
- siat/financials_china_test2_fin_indicator.py +0 -197
- siat/financials_test.py +0 -713
- siat/fred_test.py +0 -40
- siat/fund_china_test.py +0 -175
- siat/fund_test.py +0 -40
- siat/future_china_test.py +0 -37
- siat/global_index_test.py +0 -66
- siat/grafix_test.py +0 -112
- siat/holding_risk_test.py +0 -13
- siat/local_debug_test.py +0 -100
- siat/markowitz2-20240620.py +0 -2614
- siat/markowitz_ccb_test.py +0 -37
- siat/markowitz_ef_test.py +0 -136
- siat/markowitz_old.py +0 -871
- siat/markowitz_simple-20230709.py +0 -370
- siat/markowitz_test.py +0 -164
- siat/markowitz_test2.py +0 -69
- siat/ml_cases_example1.py +0 -60
- siat/option_china_test.py +0 -447
- siat/option_pricing_test.py +0 -81
- siat/option_sina_api_test.py +0 -112
- siat/proxy_test.py +0 -84
- siat/quandl_test.py +0 -39
- siat/risk_adjusted_return_test.py +0 -81
- siat/risk_evaluation_test.py +0 -96
- siat/risk_free_rate_test.py +0 -127
- siat/sector_china_test.py +0 -203
- siat/security_price.py +0 -831
- siat/security_prices_test.py +0 -310
- siat/security_trend2-20240620.py +0 -493
- siat/setup.py +0 -41
- siat/shenwan index history test.py +0 -41
- siat/stock_china_test.py +0 -38
- siat/stock_info_test.py +0 -189
- siat/stock_list_china_test.py +0 -33
- siat/stock_technical-20240620.py +0 -2736
- siat/stock_test.py +0 -487
- siat/temp.py +0 -36
- siat/test2_graphviz.py +0 -484
- siat/test_graphviz.py +0 -411
- siat/test_markowitz_simple.py +0 -198
- siat/test_markowitz_simple_revised.py +0 -215
- siat/test_markowitz_simple_revised2.py +0 -218
- siat/transaction_test.py +0 -436
- siat/translate-20230125.py +0 -2107
- siat/translate-20230206.py +0 -2109
- siat/translate-20230215.py +0 -2158
- siat/translate_20240606.py +0 -4206
- siat/translate_241003_keep.py +0 -4300
- siat/universal_test.py +0 -100
- siat/valuation_market_china_test.py +0 -36
- siat-3.10.125.dist-info/RECORD +0 -152
siat/stock_technical-20240620.py
DELETED
@@ -1,2736 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
"""
|
3
|
-
本模块功能:股票技术分析 technical analysis
|
4
|
-
所属工具包:证券投资分析工具SIAT
|
5
|
-
SIAT:Security Investment Analysis Tool
|
6
|
-
创建日期:2023年1月27日
|
7
|
-
最新修订日期:2023年1月27日
|
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
|
-
from siat.stock import *
|
25
|
-
from siat.valuation import *
|
26
|
-
#==============================================================================
|
27
|
-
import matplotlib.pyplot as plt
|
28
|
-
plt.rcParams['figure.figsize']=(12.8,7.2)
|
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
|
-
#==============================================================================
|
63
|
-
#==============================================================================
|
64
|
-
|
65
|
-
if __name__ =="__main__":
|
66
|
-
RSI_days=[6,24]
|
67
|
-
|
68
|
-
OBV_days=5
|
69
|
-
|
70
|
-
MA_days=[5,20]; MACD_fastperiod=12; MACD_slowperiod=26; MACD_signalperiod=9
|
71
|
-
|
72
|
-
KDJ_fastk_period=5; KDJ_slowk_period=3; KDJ_slowk_matype=0; KDJ_slowd_period=3
|
73
|
-
KDJ_slowd_matype=0
|
74
|
-
|
75
|
-
VOL_fastperiod=5; VOL_slowperiod=10
|
76
|
-
|
77
|
-
PSY_days=12
|
78
|
-
|
79
|
-
ARBR_days=26
|
80
|
-
|
81
|
-
CR_day=16; CR_madays=[5,10,20]
|
82
|
-
|
83
|
-
EMV_day=14; EMV_madays=9
|
84
|
-
|
85
|
-
BULL_days=20; BULL_nbdevup=2; BULL_nbdevdn=2; BULL_matype=0
|
86
|
-
|
87
|
-
TRIX_day=12; TRIX_madays=20
|
88
|
-
|
89
|
-
DMA_fastperiod=10; DMA_slowperiod=50; DMA_madays=10
|
90
|
-
|
91
|
-
BIAS_days=[6,12,24]
|
92
|
-
|
93
|
-
CCI_days=14
|
94
|
-
|
95
|
-
WR_days=[10,6]
|
96
|
-
|
97
|
-
ROC_day=12; ROC_madays=6
|
98
|
-
|
99
|
-
DMI_DIdays=14; DMI_ADXdays=6
|
100
|
-
|
101
|
-
ticker='AAPL'
|
102
|
-
|
103
|
-
start='2024-3-1'; end='2024-4-12'; ahead_days=30*3
|
104
|
-
start1=date_adjust(start,adjust=-ahead_days)
|
105
|
-
|
106
|
-
df=get_price(ticker,start1,end)
|
107
|
-
ta=calc_technical(df,start,end)
|
108
|
-
|
109
|
-
df1,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1,todate=end)
|
110
|
-
ta=calc_technical(df1,start,end)
|
111
|
-
|
112
|
-
ta=calc_technical(df,start,end)
|
113
|
-
|
114
|
-
#OBV: 纵轴数量级大(千万),判断是否加零线(有正有负),如加零线则不加均值线,不加零线时判断是否可加均值线
|
115
|
-
df_ta[['obv','obv_ma']].plot(title=ticker_name(ticker)+': '+'能量潮OBV')
|
116
|
-
|
117
|
-
#SAR: 纵轴数量级不大
|
118
|
-
df_ta[['sar']].plot(title=ticker_name(ticker)+': '+'抛物转向SAR')
|
119
|
-
|
120
|
-
#VOL: 纵轴数量级大,无零线,可加均值线
|
121
|
-
df_ta[['vol5','vol10']].plot(title=ticker_name(ticker)+': '+'成交量VOL')
|
122
|
-
|
123
|
-
#ARBR: 纵轴数量级不大,无零线,无均值线
|
124
|
-
df_ta[['ar','br']].plot(title=ticker_name(ticker)+': '+'人气与意愿ARBR')
|
125
|
-
|
126
|
-
#CR: 纵轴数量级不大,无零线
|
127
|
-
df_ta[['cr','crma5','crma10','crma20']].plot(title=ticker_name(ticker)+': '+'能力与意愿CR')
|
128
|
-
df_ta[['cr','crma5','crma20']].plot(title=ticker_name(ticker)+': '+'能力与意愿CR')
|
129
|
-
df_ta[['cr']].plot(title=ticker_name(ticker)+': '+'能力与意愿CR')
|
130
|
-
|
131
|
-
#EMV: 纵轴数量级大
|
132
|
-
df_ta[['em','emv','emva']].plot(title=ticker_name(ticker)+': '+'简易波动EMV')
|
133
|
-
df_ta[['em','emv']].plot(title=ticker_name(ticker)+': '+'简易波动EMV')
|
134
|
-
|
135
|
-
#TRIX: 纵轴数量级不大
|
136
|
-
df_ta[['trix','trma']].plot(title=ticker_name(ticker)+': '+'三重指数平滑TRIX')
|
137
|
-
|
138
|
-
#DMA: 纵轴数量级不大
|
139
|
-
df_ta[['dma']].plot(title=ticker_name(ticker)+': '+'均线差DMA')
|
140
|
-
|
141
|
-
#BIAS: 纵轴数量级不大
|
142
|
-
df_ta[['bias','bias2','bias3']].plot(title=ticker_name(ticker)+': '+'乖离率BIAS')
|
143
|
-
df_ta[['bias']].plot(title=ticker_name(ticker)+': '+'乖离率BIAS')
|
144
|
-
|
145
|
-
#CCI: 纵轴数量级不大
|
146
|
-
df_ta[['cci']].plot(title=ticker_name(ticker)+': '+'顺势CCI')
|
147
|
-
|
148
|
-
#W%R: 纵轴数量级不大
|
149
|
-
df_ta[['wr6','wr10']].plot(title=ticker_name(ticker)+': '+'威廉W%R')
|
150
|
-
|
151
|
-
#ROC: 纵轴数量级不大
|
152
|
-
df_ta[['roc','rocma']].plot(title=ticker_name(ticker)+': '+'变动速率ROC')
|
153
|
-
|
154
|
-
#DMI: 纵轴数量级不大
|
155
|
-
df_ta[['pdi','mdi','adx','adxr']].plot(title=ticker_name(ticker)+': '+'趋向DMI')
|
156
|
-
df_ta[['pdi','mdi']].plot(title=ticker_name(ticker)+': '+'趋向DMI')
|
157
|
-
df_ta[['adx','adxr']].plot(title=ticker_name(ticker)+': '+'趋向DMI')
|
158
|
-
|
159
|
-
#PSY: 纵轴数量级不大
|
160
|
-
df_ta[['psy']].plot(title=ticker_name(ticker)+': '+'心理线PSY')
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
def calc_technical(df,start,end, \
|
165
|
-
RSI_days=14, \
|
166
|
-
OBV_days=5, \
|
167
|
-
|
168
|
-
MA_days=[5,20], \
|
169
|
-
MACD_fastperiod=12,MACD_slowperiod=26,MACD_signalperiod=9, \
|
170
|
-
|
171
|
-
KDJ_fastk_period=9,KDJ_slowk_period=5,KDJ_slowk_matype=1, \
|
172
|
-
KDJ_slowd_period=5,KDJ_slowd_matype=1, \
|
173
|
-
|
174
|
-
VOL_fastperiod=5,VOL_slowperiod=10, \
|
175
|
-
PSY_days=12, \
|
176
|
-
ARBR_days=26, \
|
177
|
-
CR_day=16,CR_madays=[5,10,20], \
|
178
|
-
EMV_day=14,EMV_madays=9, \
|
179
|
-
BULL_days=20,BULL_nbdevup=2,BULL_nbdevdn=2,BULL_matype=0, \
|
180
|
-
DMA_fastperiod=10,DMA_slowperiod=50,DMA_madays=10, \
|
181
|
-
TRIX_day=12,TRIX_madays=20, \
|
182
|
-
BIAS_days=[6,12,24], \
|
183
|
-
CCI_days=14, \
|
184
|
-
WR_days=[10,6], \
|
185
|
-
ROC_day=12,ROC_madays=6, \
|
186
|
-
DMI_DIdays=14,DMI_ADXdays=6, \
|
187
|
-
|
188
|
-
indicator='Close', \
|
189
|
-
more_details=False):
|
190
|
-
"""
|
191
|
-
功能:计算股票的技术分析指标
|
192
|
-
输入:df,四种股价Open/Close/High/Low,成交量Volume
|
193
|
-
输出:df
|
194
|
-
支持的指标:
|
195
|
-
RSI、OBV、MACD、 KDJ、 SAR、 VOL、 PSY、 ARBR、 CR、 EMV、
|
196
|
-
BOLL、 TRIX、 DMA、 BIAS、 CCI、 W%R、 ROC、 DMI
|
197
|
-
|
198
|
-
注意:indicator='Close'为不使用复权价,'Adj Close'为前复权价(需要指定source='yahoo')
|
199
|
-
"""
|
200
|
-
if indicator not in ['Close','Adj Close']:
|
201
|
-
print(" #Error(calc_technical): unsupported indicator",indicator)
|
202
|
-
print(" Supported indicator: Close, Adj Close")
|
203
|
-
|
204
|
-
return None
|
205
|
-
|
206
|
-
# 导入需要的包
|
207
|
-
try:
|
208
|
-
import talib
|
209
|
-
except:
|
210
|
-
print(" #Error(calc_technical): lack of necessary package - talib")
|
211
|
-
talib_install_method()
|
212
|
-
return None
|
213
|
-
|
214
|
-
#=========== RSI,相对强弱指标Relative Strength Index
|
215
|
-
"""
|
216
|
-
计算公式:RSI有两种计算方法:
|
217
|
-
第一种方法:
|
218
|
-
假设A为N日内收盘价涨幅的正数之和,B为N日内收盘价涨幅的负数之和再乘以(-1),
|
219
|
-
这样,A和B均为正,将A,B代入RSI计算公式,则:
|
220
|
-
RSI(N) = A ÷ (A + B) × 100
|
221
|
-
第二种方法:
|
222
|
-
RS(相对强度) = N日内收盘价涨数和之均值 ÷ N日内收盘价跌数和之均值
|
223
|
-
RSI = 100 - 100 ÷ (1+RS)
|
224
|
-
|
225
|
-
指标解读:
|
226
|
-
80-100 极强 卖出
|
227
|
-
50-80 强 观望,谨慎卖出
|
228
|
-
30-50 弱 观望,谨慎买入
|
229
|
-
0-30 极弱 买入
|
230
|
-
"""
|
231
|
-
if not isinstance(RSI_days,list):
|
232
|
-
RSI_days=[RSI_days]
|
233
|
-
for d in RSI_days:
|
234
|
-
df['rsi'+str(d)] = talib.RSI(df[indicator], timeperiod=d)
|
235
|
-
#注意:rsi1没有意义
|
236
|
-
|
237
|
-
#=========== OBV:能量潮
|
238
|
-
"""
|
239
|
-
OBV的英文全称是:On Balance Volume,是由美国的投资分析家Joe Granville所创。
|
240
|
-
该指标通过统计成交量变动的趋势来推测股价趋势。
|
241
|
-
OBV指标所谓股市人气,指投资者活跃在股市上的程度。
|
242
|
-
如果买卖双方交易热情高,股价、成交量就上升,股市气氛则热烈。
|
243
|
-
因此,利用股价和股票成交量的指标来反映人气的兴衰,就形成了OBV指标。
|
244
|
-
|
245
|
-
OBV = 前一天的OBV ± 当日成交量
|
246
|
-
说明:(当日收盘价高于前日收盘价,成交量定位为正值,取加号;
|
247
|
-
当日收盘价低于前日收盘价,成交量定义为负值,取减号;二者相等计为0)
|
248
|
-
|
249
|
-
指标解读:
|
250
|
-
1、当股价上升而OBV线下降,表示买盘无力,股价可能会回跌。
|
251
|
-
2、股价下降时而OBV线上升,表示买盘旺盛,逢低接手强股,股价可能会止跌回升。
|
252
|
-
3、OBV线缓慢上升,表示买气逐渐加强,为买进信号。
|
253
|
-
4、OBV线急速上升时,表示力量将用尽为卖出信号。
|
254
|
-
5、OBV线从正的累积数转为负数时,为下跌趋势,应该卖出持有股票。反之,OBV线从负的累积数转为正数时,应该买进股票。
|
255
|
-
6、OBV线最大的用处,在于观察股市盘局整理后,何时会脱离盘局以及突破后的未来走势,OBV线变动方向是重要参考指数,其具体的数值并无实际意义。
|
256
|
-
|
257
|
-
缺点
|
258
|
-
OBV指标是建立在国外成熟市场上的经验总结。
|
259
|
-
用在内地股市坐庄的股票上就不灵了,这时股价涨得越高成交量反而越少。
|
260
|
-
这是因为主力控盘较重,股价在上涨过程中没有获利筹码加以兑现,所以此时股票会涨得很“疯”,但成交量并不增加,OBV自然就无法发挥作用。
|
261
|
-
另外,涨跌停板的股票也会导致指标失真。
|
262
|
-
由于内地股市采用了涨跌停板的限制,很多股票在连续涨停的时候,由于股民预期后市会继续大涨,往往会持股观望,导致出现越涨越无量的现象。
|
263
|
-
因此,对于那些达到涨跌停板的股票,OBV指标也无法正常发挥作用。
|
264
|
-
|
265
|
-
"""
|
266
|
-
df['obv'] = talib.OBV(df[indicator],df['Volume'])
|
267
|
-
|
268
|
-
if not isinstance(OBV_days,list):
|
269
|
-
OBV_days=[OBV_days]
|
270
|
-
for d in OBV_days:
|
271
|
-
df['obv_ma'+str(d)] = talib.MA(df['obv'],timeperiod=d)
|
272
|
-
|
273
|
-
if not more_details:
|
274
|
-
df.drop(columns = ['obv'],inplace=True)
|
275
|
-
|
276
|
-
#=========== MA: 简单、加权移动平均
|
277
|
-
"""
|
278
|
-
MA,又称移动平均线,是借助统计处理方式将若干天的股票价格加以平均,然后连接成一条线,用以观察股价趋势。
|
279
|
-
移动平均线通常有3日、6日、10日、12日、24日、30日、72日、200日、288日、13周、26周、52周等等,不一而足,
|
280
|
-
其目的在取得某一段期间的平均成本,而以此平均成本的移动曲线配合每日收盘价的线路变化分析某一期间多空的优劣形势,
|
281
|
-
以研判股价的可能变化。
|
282
|
-
一般来说,现行价格在平均价之上,意味着市场买力(需求)较大,行情看好;
|
283
|
-
反之,行情价在平均价之下,则意味着供过于求,卖压显然较重,行情看淡。
|
284
|
-
"""
|
285
|
-
if not isinstance(MA_days,list):
|
286
|
-
MA_days=[MA_days]
|
287
|
-
|
288
|
-
for d in MA_days:
|
289
|
-
df['ma'+str(d)] = talib.MA(df[indicator],timeperiod=d)
|
290
|
-
df['ema'+str(d)] = talib.EMA(df[indicator],timeperiod=d)
|
291
|
-
|
292
|
-
#=========== MACD:指数平滑异同平均线
|
293
|
-
"""
|
294
|
-
计算方法:快速时间窗口设为12日,慢速时间窗口设为26日,DIF参数设为9日
|
295
|
-
3.1) 计算指数平滑移动平均值(EMA)
|
296
|
-
12日EMA的计算公式为:
|
297
|
-
EMA(12) = 昨日EMA(12) × 11 ÷ 13 + 今日收盘价 × 2 ÷ 13
|
298
|
-
26日EMA的计算公式为:
|
299
|
-
EMA(26) = 昨日EMA(26) × 25 ÷ 27 + 今日收盘价 × 2 ÷ 27
|
300
|
-
|
301
|
-
3.2) 计算离差值(DIF)
|
302
|
-
DIF = 今日EMA(12) – 今日EMA(26)
|
303
|
-
|
304
|
-
3.3) 计算DIF的9日DEA
|
305
|
-
根据差值计算其9日的DEA,即差值平均
|
306
|
-
今日DEA = 昨日DEA × 8 ÷ 10 + 今日DIF × 2 ÷ 10
|
307
|
-
|
308
|
-
形态解读:
|
309
|
-
1.DIF、DEA均为正,DIF向上突破DEA,买入信号。
|
310
|
-
2.DIF、DEA均为负,DIF向下跌破DEA,卖出信号。
|
311
|
-
3.DEA线与K线发生背离,行情反转信号。
|
312
|
-
4.分析MACD柱状线,由红变绿(正变负),卖出信号;由绿变红,买入信号。
|
313
|
-
|
314
|
-
MACD一则去掉移动平均线频繁的假讯号缺陷,二则能确保移动平均线最大的战果。
|
315
|
-
1. MACD金叉:DIF由下向上突破DEM,为买入信号。
|
316
|
-
2. MACD死叉:DIF由上向下突破DEM,为卖出信号。
|
317
|
-
3. MACD绿转红:MACD值由负变正,市场由空头转为多头。
|
318
|
-
4. MACD红转绿:MACD值由正变负,市场由多头转为空头。
|
319
|
-
"""
|
320
|
-
df['DIF'],df['DEA'],df['MACD']=talib.MACD(df[indicator], \
|
321
|
-
fastperiod=MACD_fastperiod, \
|
322
|
-
slowperiod=MACD_slowperiod, \
|
323
|
-
signalperiod=MACD_signalperiod)
|
324
|
-
|
325
|
-
#=========== KDJ: 随机指标
|
326
|
-
"""
|
327
|
-
计算公式:
|
328
|
-
1) 以日KDJ数值的计算为例
|
329
|
-
N日RSV = (CN – LN)÷(HN-LN) ×100
|
330
|
-
说明:CN为第N日收盘价;LN为N日内的最低价;HN为N日内的最高价,RSV值始终在1~100间波动
|
331
|
-
2) 计算K值与D值
|
332
|
-
当日K值 = 2/3 × 前一日K值 + 1/3 × 当日RSV
|
333
|
-
当日D值 = 2/3 × 前一日D值 + 1/3 × 当日K值
|
334
|
-
如果没有前一日K值与D值,则可分别用50来代替
|
335
|
-
3) 计算J值
|
336
|
-
J = 3D – 2K
|
337
|
-
|
338
|
-
指标解读:
|
339
|
-
多头区域,空头区域
|
340
|
-
金叉,死叉
|
341
|
-
"""
|
342
|
-
df['kdj_k'],df['kdj_d'] = talib.STOCH(df['High'],df['Low'],df['Close'], \
|
343
|
-
fastk_period=KDJ_fastk_period,
|
344
|
-
slowk_period=KDJ_slowk_period,
|
345
|
-
slowk_matype=KDJ_slowk_matype,
|
346
|
-
slowd_period=KDJ_slowd_period,
|
347
|
-
slowd_matype=KDJ_slowd_matype)
|
348
|
-
df['kdj_j'] = 3*df['kdj_k'] - 2*df['kdj_d']
|
349
|
-
|
350
|
-
#=========== SAR: 抛物转向
|
351
|
-
"""
|
352
|
-
计算过程:
|
353
|
-
1)先选定一段时间判断为上涨或下跌
|
354
|
-
2)如果是看涨,则第一天的SAR值必须是近期内的最低价;
|
355
|
-
如果是看跌,则第一天的SAR值必须是近期的最高价。
|
356
|
-
3)第二天的SAR值,则为第一天的最高价(看涨时)或是最低价(看跌时)与第一天的SAR值的差距乘上加速因子,
|
357
|
-
再加上第一天的SAR值就可以求得。
|
358
|
-
4)每日的SAR值都可用上述方法类推,公式归纳如下:
|
359
|
-
SAR(N) = SAR(N-1) + AF × [(EP(N-1) – SAR(N-1))]
|
360
|
-
SAR(N) = 第N日的SAR值
|
361
|
-
SAR(N-1) = 第(N-1)日的SAR值
|
362
|
-
说明:AF表示加速因子;EP表示极点价,如果是看涨一段期间,则EP为这段时间的最高价,
|
363
|
-
如果是看跌一段期间,则EP为这段时间的最低价;EP(N-1)等于第(N-1)日的极点价
|
364
|
-
5)加速因子第一次取0.02,假若第一天的最高价比前一天的最高价还高,则加速因子增加0.02,
|
365
|
-
如无新高则加速因子沿用前一天的数值,但加速因子最高不能超过0.2。反之,下跌也类推
|
366
|
-
6)如果是看涨期间,计算出某日的SAR值比当日或前一日的最低价高,则应以当日或前一日的最低价为某日之SAR值;
|
367
|
-
如果是看跌期间,计算某日的SAR值比当日或前一日的最高价低,则应以当日或前一日的最高价为某日的SAR值。
|
368
|
-
7)SAR指标基准周期的参数为2,如2日、2周、2月等,其计算周期的参数变动范围为2~8
|
369
|
-
8)SAR指标在股价分析系统的主图上显示为“O”形点状图。
|
370
|
-
"""
|
371
|
-
df['sar'] = talib.SAR(df['High'],df['Low'])
|
372
|
-
|
373
|
-
#=========== VOL: 成交量
|
374
|
-
"""
|
375
|
-
柱状图是成交量,两条曲线是成交量的移动平均
|
376
|
-
"""
|
377
|
-
df['vol'+str(VOL_fastperiod)] = talib.MA(df['Volume'],timeperiod=VOL_fastperiod)
|
378
|
-
df['vol'+str(VOL_slowperiod)] = talib.MA(df['Volume'],timeperiod=VOL_slowperiod)
|
379
|
-
|
380
|
-
#=========== PSY: 心理线
|
381
|
-
"""
|
382
|
-
计算公式:
|
383
|
-
PSY(N) = A/N × 100
|
384
|
-
说明:N为天数,A为在这N天之中股价上涨的天数
|
385
|
-
"""
|
386
|
-
df['ext_0'] = df[indicator]-df[indicator].shift(1)
|
387
|
-
df['ext_1'] = 0
|
388
|
-
df.loc[df['ext_0']>0,'ext_1'] = 1
|
389
|
-
|
390
|
-
if not isinstance(PSY_days,list):
|
391
|
-
PSY_days=[PSY_days]
|
392
|
-
for d in PSY_days:
|
393
|
-
df['ext_2'] = df['ext_1'].rolling(window=d).sum()
|
394
|
-
df['psy'+str(d)] = (df['ext_2']/float(d))*100
|
395
|
-
|
396
|
-
df.drop(columns = ['ext_0','ext_1','ext_2'],inplace=True)
|
397
|
-
|
398
|
-
#=========== ARBR: 人气和意愿指标, AR为人气指标,BR为买卖意愿指标
|
399
|
-
"""
|
400
|
-
计算公式:
|
401
|
-
AR(N) = N日内(H-O)之和 ÷ N日内(O-L)之和 × 100
|
402
|
-
说明:H表示当天最高价;L表示当天最低价;O表示当天开盘价;N表示设定的时间参数,一般原始参数日缺省值为26日
|
403
|
-
BR(N) = N日内(H-CY)之和 ÷ N日内(CY-L)之和 × 100
|
404
|
-
说明:H表示当天最高价;L表示当天最低价;CY表示前一交易日的收盘价,N表示设定的时间参数,一般原始参数缺省值为26日
|
405
|
-
"""
|
406
|
-
df['h_o'] = df['High'] - df['Open']
|
407
|
-
df['o_l'] = df['Open'] - df['Low']
|
408
|
-
|
409
|
-
if not isinstance(ARBR_days,list):
|
410
|
-
ARBR_days=[ARBR_days]
|
411
|
-
for d in ARBR_days:
|
412
|
-
df['h_o_sum'] = df['h_o'].rolling(window=d).sum()
|
413
|
-
df['o_l_sum'] = df['o_l'].rolling(window=d).sum()
|
414
|
-
df['ar'+str(d)] = (df['h_o_sum']/df['o_l_sum'])*100
|
415
|
-
|
416
|
-
|
417
|
-
df['h_c'] = df['High'] - df['Close']
|
418
|
-
df['c_l'] = df['Close'] - df['Low']
|
419
|
-
for d in ARBR_days:
|
420
|
-
df['h_c_sum'] = df['h_c'].rolling(window=d).sum()
|
421
|
-
df['c_l_sum'] = df['c_l'].rolling(window=d).sum()
|
422
|
-
df['br'+str(d)] = (df['h_c_sum']/df['c_l_sum'])*100
|
423
|
-
|
424
|
-
df.drop(columns = ['h_o','o_l','h_o_sum','o_l_sum','h_c','c_l','h_c_sum','c_l_sum'],inplace=True)
|
425
|
-
|
426
|
-
#=========== CR: 带状能力线或中间意愿指标
|
427
|
-
"""
|
428
|
-
计算过程:
|
429
|
-
1)计算中间价,取以下四种中一种,任选:
|
430
|
-
中间价 = (最高价 + 最低价)÷2
|
431
|
-
中间价 = (最高价 + 最低价 + 收盘价)÷3
|
432
|
-
中间价 = (最高价 + 最低价 + 开盘价 + 收盘价)÷4
|
433
|
-
中间价 = (2倍的开盘价 + 最高价 + 最低价)÷4
|
434
|
-
2)计算CR:
|
435
|
-
CR = N日内(当日最高价 – 上个交易日的中间价)之和 ÷ N日内(上个交易日的中间价 – 当日最低价)之和
|
436
|
-
说明:N为设定的时间周期参数,一般原始参数日设定为26日
|
437
|
-
3)计算CR值在不同时间周期内的移动平均值:这三条移动平均曲线分别为MA1 MA2 MA3,时间周期分别为5日 10日 20日
|
438
|
-
"""
|
439
|
-
df['m_price'] = (df['High'] + df['Low'])/2
|
440
|
-
df['h_m'] = df['High']-df['m_price'].shift(1)
|
441
|
-
df['m_l'] = df['m_price'].shift(1)-df['Low']
|
442
|
-
|
443
|
-
df['h_m_sum'] = df['h_m'].rolling(window=CR_day).sum()
|
444
|
-
df['m_l_sum'] = df['m_l'].rolling(window=CR_day).sum()
|
445
|
-
df['cr'] = (df['h_m_sum']/df['m_l_sum'])*100
|
446
|
-
|
447
|
-
for d in CR_madays:
|
448
|
-
df['cr_ma'+str(d)] = talib.MA(df['cr'],timeperiod=d)
|
449
|
-
|
450
|
-
if more_details:
|
451
|
-
#保留cr
|
452
|
-
df.drop(columns = ['m_price','h_m','m_l','h_m_sum','m_l_sum'],inplace=True)
|
453
|
-
else:
|
454
|
-
df.drop(columns = ['m_price','h_m','m_l','h_m_sum','m_l_sum','cr'],inplace=True)
|
455
|
-
|
456
|
-
#=========== EMV: 简易波动指标
|
457
|
-
"""
|
458
|
-
计算方法:
|
459
|
-
1)先计算出三个因子A B C的数值。
|
460
|
-
A = (当日最高价 + 当日最低价)÷2
|
461
|
-
B = (上个交易日最高价 + 上个交易日最低价) ÷2
|
462
|
-
C = 当日最高价 – 当日最低价
|
463
|
-
2)求出EM数值
|
464
|
-
EM = (A-B) ×C÷当日成交额
|
465
|
-
3)求出EMV数值
|
466
|
-
EMV = EM数值的N个交易日之和,N为时间周期,一般设为14日
|
467
|
-
4)求出EMV的移动平均值EMVA
|
468
|
-
EMVA = EMV的M日移动平均值,M一般设置9日
|
469
|
-
"""
|
470
|
-
df['a'] = (df['High']+df['Low'])/2
|
471
|
-
df['b'] = (df['High'].shift(1)+df['Low'].shift(1))/2
|
472
|
-
df['c'] = df['High'] - df['Low']
|
473
|
-
df['Amount']=df['Close']*df['Volume']
|
474
|
-
df['em'] = (df['a']-df['b'])*df['c']/df['Amount']
|
475
|
-
|
476
|
-
df['emv'] = df['em'].rolling(window=EMV_day).sum()
|
477
|
-
|
478
|
-
if not isinstance(EMV_madays,list):
|
479
|
-
EMV_madays=[EMV_madays]
|
480
|
-
for d in EMV_madays:
|
481
|
-
df['emv_ma'+str(d)] = talib.MA(df['emv'],timeperiod=d)
|
482
|
-
|
483
|
-
df.drop(columns = ['a','b','c','em'],inplace=True)
|
484
|
-
|
485
|
-
#=========== BOLL: 布林线指标
|
486
|
-
"""
|
487
|
-
计算公式:
|
488
|
-
中轨线 = N日的移动平均线
|
489
|
-
上轨线 = 中轨线 + 两倍的标准差
|
490
|
-
下轨线 = 中轨线 – 两倍的标准差
|
491
|
-
计算过程:
|
492
|
-
1)先计算出移动平均值MA
|
493
|
-
MA = N日内的收盘价之和÷N
|
494
|
-
2)计算出标准差MD的平方
|
495
|
-
MD的平方 = 每个交易日的(收盘价-MA)的N日累加之和的两次方 ÷ N
|
496
|
-
3)求出MD
|
497
|
-
MD = (MD的平方)的平方根
|
498
|
-
4)计算MID、UPPER、LOWER的数值
|
499
|
-
MID = (N-1)日的MA
|
500
|
-
UPPER = MID + 2×MD
|
501
|
-
LOWER = MID – 2×MD
|
502
|
-
说明:N一般原始参数日缺省值为20日
|
503
|
-
|
504
|
-
指标解读:
|
505
|
-
BOLL指标即布林线指标,其利用统计原理,求出股价的标准差及其信赖区间,
|
506
|
-
从而确定股价的波动范围及未来走势,利用波带显示股价的安全高低价位,因而也被称为布林带。
|
507
|
-
其上下限范围不固定,随股价的滚动而变化。布林指标股价波动在上限和下限的区间之内,
|
508
|
-
这条带状区的宽窄,随着股价波动幅度的大小而变化,股价涨跌幅度加大时,带状区变宽,
|
509
|
-
涨跌幅度狭小盘整时,带状区则变窄。
|
510
|
-
"""
|
511
|
-
df['upper'],df['mid'],df['lower'] = talib.BBANDS(df[indicator], \
|
512
|
-
timeperiod=BULL_days, \
|
513
|
-
nbdevup=BULL_nbdevup,nbdevdn=BULL_nbdevdn,matype=BULL_matype)
|
514
|
-
|
515
|
-
#=========== TRIX:三重指数平滑移动平均指标
|
516
|
-
"""
|
517
|
-
|
518
|
-
"""
|
519
|
-
df['trix'] = talib.TRIX(df[indicator],timeperiod=TRIX_day)
|
520
|
-
|
521
|
-
if not isinstance(TRIX_madays,list):
|
522
|
-
TRIX_madays=[TRIX_madays]
|
523
|
-
for d in TRIX_madays:
|
524
|
-
df['trix_ma'+str(d)] = talib.MA(df['trix'],timeperiod=d)
|
525
|
-
|
526
|
-
if not more_details:
|
527
|
-
#不保留TRIX
|
528
|
-
df.drop(columns = ['trix'],inplace=True)
|
529
|
-
|
530
|
-
#=========== DMA: 平均线差
|
531
|
-
"""
|
532
|
-
计算公式:
|
533
|
-
DDD(N) = N日短期平均值 – M日长期平均值
|
534
|
-
AMA(N) = DDD的N日短期平均值
|
535
|
-
计算过程:
|
536
|
-
以求10日、50日为基准周期的DMA指标为例
|
537
|
-
1)求出周期不等的两条移动平均线MA之间的差值
|
538
|
-
DDD(10) = MA10 – MA50
|
539
|
-
2)求DDD的10日移动平均数值
|
540
|
-
DMA(10) = DDD(10)÷10
|
541
|
-
"""
|
542
|
-
df['ma_shortperiod'] = talib.MA(df[indicator],timeperiod=DMA_fastperiod)
|
543
|
-
df['ma_longperiod'] = talib.MA(df[indicator],timeperiod=DMA_slowperiod)
|
544
|
-
df['ddd'] = df['ma_shortperiod'] - df['ma_longperiod']
|
545
|
-
|
546
|
-
if not isinstance(DMA_madays,list):
|
547
|
-
DMA_madays=[DMA_madays]
|
548
|
-
for d in DMA_madays:
|
549
|
-
df['dma'+str(d)] = talib.MA(df['ddd'],timeperiod=d)
|
550
|
-
#注意:dma1似乎没有意义
|
551
|
-
|
552
|
-
df.drop(columns = ['ma_shortperiod','ma_longperiod','ddd'],inplace=True)
|
553
|
-
|
554
|
-
#=========== BIAS: 乖离率
|
555
|
-
"""
|
556
|
-
N日BIAS = (当日收盘价 – N日移动平均价)÷N日移动平均价×100
|
557
|
-
|
558
|
-
指标解读:
|
559
|
-
6日BIAS>+5%,是卖出时机;<-5%,为买入时机。
|
560
|
-
12日BIAS>+6%是卖出时机;<-5.5%,为买入时机。
|
561
|
-
24日BIAS>+9%是卖出时机;<-8%,为买入时机。
|
562
|
-
"""
|
563
|
-
if not isinstance(BIAS_days,list):
|
564
|
-
BIAS_days=[BIAS_days]
|
565
|
-
for d in BIAS_days:
|
566
|
-
df['ma'] = talib.MA(df[indicator],timeperiod=d)
|
567
|
-
df['bias'+str(d)] = ((df[indicator]-df['ma'])/df['ma'])*100
|
568
|
-
|
569
|
-
df.drop(columns = ['ma'],inplace=True)
|
570
|
-
|
571
|
-
#=========== CCI: 顺势指标
|
572
|
-
"""
|
573
|
-
计算过程:
|
574
|
-
CCI(N日) = (TP-MA)÷MD÷0.015
|
575
|
-
说明:TP = (最高价+最低价+收盘价)÷3;MA=最近N日收盘价的累计之和÷N;MD=最近N日(MA-收盘价)的累计之和÷N;0.015为计算系数;N为计算周期,默认为14天
|
576
|
-
"""
|
577
|
-
if not isinstance(CCI_days,list):
|
578
|
-
CCI_days=[CCI_days]
|
579
|
-
for d in CCI_days:
|
580
|
-
df['cci'+str(d)] = talib.CCI(df['High'],df['Low'],df['Close'],timeperiod=d)
|
581
|
-
|
582
|
-
#=========== W%R: 威廉指标
|
583
|
-
"""
|
584
|
-
N日W%R = [(Hn-Ct)/(Hn-Ln)]*100
|
585
|
-
Ct是计算日的收盘价;Hn/Ln为包括计算日当天的N周期内的最高(低)价
|
586
|
-
|
587
|
-
指标解读:
|
588
|
-
威廉指标(William's %R) 原理:用当日收盘价在最近一段时间股价分布的相对位置来描述超买和超卖程度。
|
589
|
-
算法:N日内最高价与当日收盘价的差,除以N日内最高价与最低价的差,结果放大100倍。
|
590
|
-
参数:N 统计天数 一般取14天
|
591
|
-
用法:
|
592
|
-
1.低于30,超买,即将见顶,应及时卖出
|
593
|
-
2.高于80,超卖,即将见底,应伺机买进
|
594
|
-
3.与RSI、MTM指标配合使用,效果更好
|
595
|
-
"""
|
596
|
-
if not isinstance(WR_days,list):
|
597
|
-
WR_days=[WR_days]
|
598
|
-
for d in WR_days:
|
599
|
-
df['h_10'] = df['High'].rolling(window=d).max()
|
600
|
-
df['l_10'] = df['Low'].rolling(window=d).min()
|
601
|
-
df['wr'+str(d)] = ((df['h_10']-df['Close'])/(df['h_10']-df['l_10']))*100
|
602
|
-
|
603
|
-
df.drop(columns = ['h_10','l_10'],inplace=True)
|
604
|
-
|
605
|
-
#=========== ROC: 变动速率指标
|
606
|
-
"""
|
607
|
-
计算过程:
|
608
|
-
1)计算出ROC数值
|
609
|
-
ROC = (当日收盘价 – N日前收盘价)÷N日前收盘价×100
|
610
|
-
说明:N一般取值为12日
|
611
|
-
2)计算ROC移动平均线(ROCMA)数值
|
612
|
-
ROCMA = ROC的M日数值之和÷M
|
613
|
-
说明:M一般取值为6日
|
614
|
-
"""
|
615
|
-
df['roc'] = talib.ROC(df[indicator],timeperiod=ROC_day)
|
616
|
-
|
617
|
-
if not isinstance(ROC_madays,list):
|
618
|
-
ROC_madays=[ROC_madays]
|
619
|
-
for d in ROC_madays:
|
620
|
-
df['roc_ma'+str(d)] = talib.MA(df['roc'],timeperiod=d)
|
621
|
-
|
622
|
-
if not more_details:
|
623
|
-
#不保留roc
|
624
|
-
df.drop(columns = ['roc'],inplace=True)
|
625
|
-
|
626
|
-
#=========== DMI: 趋向指标
|
627
|
-
"""
|
628
|
-
|
629
|
-
"""
|
630
|
-
if not isinstance(DMI_DIdays,list):
|
631
|
-
DMI_DIdays=[DMI_DIdays]
|
632
|
-
for d in DMI_DIdays:
|
633
|
-
df['pdi'+str(d)] = talib.PLUS_DI(df['High'],df['Low'],df['Close'],timeperiod=d)
|
634
|
-
df['mdi'+str(d)] = talib.MINUS_DI(df['High'],df['Low'],df['Close'],timeperiod=d)
|
635
|
-
|
636
|
-
if not isinstance(DMI_ADXdays,list):
|
637
|
-
DMI_ADXdays=[DMI_ADXdays]
|
638
|
-
for d in DMI_ADXdays:
|
639
|
-
df['adx'+str(d)] = talib.ADX(df['High'],df['Low'],df['Close'],timeperiod=d)
|
640
|
-
df['adxr'+str(d)] = talib.ADXR(df['High'],df['Low'],df['Close'],timeperiod=d)
|
641
|
-
|
642
|
-
#过滤日期
|
643
|
-
_,startpd,endpd=check_period(start,end)
|
644
|
-
df1=df[(df.index >= startpd) & (df.index <= endpd)]
|
645
|
-
|
646
|
-
|
647
|
-
return df1
|
648
|
-
|
649
|
-
|
650
|
-
#==============================================================================
|
651
|
-
#==============================================================================
|
652
|
-
#==============================================================================
|
653
|
-
|
654
|
-
if __name__ =="__main__":
|
655
|
-
ticker='600519.SS'
|
656
|
-
start='2022-6-1'
|
657
|
-
end='2022-6-30'
|
658
|
-
|
659
|
-
MA_days=[5,20]
|
660
|
-
EMA_days=[5,20]
|
661
|
-
|
662
|
-
MACD_fastperiod=12
|
663
|
-
MACD_slowperiod=26
|
664
|
-
MACD_signalperiod=9
|
665
|
-
|
666
|
-
loc1='upper left'
|
667
|
-
loc2='center right'
|
668
|
-
|
669
|
-
resample_freq='H'
|
670
|
-
smooth=True
|
671
|
-
linewidth=1.5
|
672
|
-
graph=['MA']
|
673
|
-
graph=['EMA']
|
674
|
-
graph=['MACD']
|
675
|
-
printout=True
|
676
|
-
|
677
|
-
def security_MACD(ticker,start='default',end='default', \
|
678
|
-
MA_days=[5,20],EMA_days=[5,20], \
|
679
|
-
MACD_fastperiod=12,MACD_slowperiod=26,MACD_signalperiod=9, \
|
680
|
-
resample_freq='6H',smooth=True,linewidth=1.5, \
|
681
|
-
loc1='lower left',loc2='lower right', \
|
682
|
-
graph=['ALL'],printout=True,ticker_type='auto',source='auto'):
|
683
|
-
"""
|
684
|
-
套壳函数:可用于股票、交易所债券、交易所基金、部分期货期权(限美股)
|
685
|
-
"""
|
686
|
-
df=stock_MACD(ticker=ticker,start=start,end=end, \
|
687
|
-
MA_days=MA_days,EMA_days=EMA_days, \
|
688
|
-
MACD_fastperiod=MACD_fastperiod,MACD_slowperiod=MACD_slowperiod, \
|
689
|
-
MACD_signalperiod=MACD_signalperiod, \
|
690
|
-
resample_freq=resample_freq,smooth=smooth,linewidth=linewidth, \
|
691
|
-
loc1=loc1,loc2=loc2, \
|
692
|
-
graph=graph,printout=printout,ticker_type=ticker_type,source=source)
|
693
|
-
return df
|
694
|
-
|
695
|
-
|
696
|
-
def stock_MACD(ticker,start='default',end='default', \
|
697
|
-
MA_days=[5,20],EMA_days=[5,20], \
|
698
|
-
MACD_fastperiod=12,MACD_slowperiod=26,MACD_signalperiod=9, \
|
699
|
-
resample_freq='H',smooth=True,linewidth=1.5, \
|
700
|
-
loc1='lower left',loc2='lower right', \
|
701
|
-
graph=['ALL'],printout=True,ticker_type='auto',source='auto'):
|
702
|
-
"""
|
703
|
-
功能:计算股票的技术分析指标MACD
|
704
|
-
输入:df,四种股价Open/Close/High/Low,成交量Volume
|
705
|
-
输出:df
|
706
|
-
含有指标:
|
707
|
-
MA5、MA20、MACD_fastperiod、 MACD_slowperiod、MACD_signalperiod
|
708
|
-
"""
|
709
|
-
|
710
|
-
#=========== 导入需要的包
|
711
|
-
try:
|
712
|
-
import talib
|
713
|
-
except:
|
714
|
-
print(" #Error(stock_MACD): lack of necessary module - ta-lib")
|
715
|
-
talib_install_method()
|
716
|
-
return None
|
717
|
-
|
718
|
-
#=========== 日期转换与检查
|
719
|
-
# 检查日期:截至日期
|
720
|
-
import datetime as dt; today=dt.date.today()
|
721
|
-
if end in ['default','today']:
|
722
|
-
end=today
|
723
|
-
else:
|
724
|
-
validdate,end=check_date2(end)
|
725
|
-
if not validdate:
|
726
|
-
print(" #Warning(stock_MACD): invalid date for",end)
|
727
|
-
end=today
|
728
|
-
|
729
|
-
# 检查日期:开始日期
|
730
|
-
if start in ['default']:
|
731
|
-
start=date_adjust(end,adjust=-31)
|
732
|
-
else:
|
733
|
-
validdate,start=check_date2(start)
|
734
|
-
if not validdate:
|
735
|
-
print(" #Warning(stock_MACD): invalid date for",start)
|
736
|
-
start=date_adjust(todate,adjust=-31)
|
737
|
-
|
738
|
-
#=========== 获取股价和成交量数据
|
739
|
-
result,startpd,endpd=check_period(start,end)
|
740
|
-
|
741
|
-
days_list=MA_days+EMA_days+[MACD_fastperiod]+[MACD_slowperiod]+[MACD_signalperiod]
|
742
|
-
max_days=max(days_list)
|
743
|
-
start1=date_adjust(start,adjust=-max_days * 3)
|
744
|
-
|
745
|
-
#df=get_price(ticker,start1,end)
|
746
|
-
df,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1,todate=end,ticker_type=ticker_type,source=source)
|
747
|
-
if df is None:
|
748
|
-
print(" #Error(stock_MACD): no info found for",ticker,"from",start,"to",end)
|
749
|
-
return None
|
750
|
-
if len(df)==0:
|
751
|
-
print(" #Error(stock_MACD): zero record found for",ticker,"from",start,"to",end)
|
752
|
-
return None
|
753
|
-
|
754
|
-
#=========== MA: 简单、加权移动平均
|
755
|
-
"""
|
756
|
-
MA,又称移动平均线,是借助统计处理方式将若干天的股票价格加以平均,然后连接成一条线,用以观察股价趋势。
|
757
|
-
移动平均线通常有3日、6日、10日、12日、24日、30日、72日、200日、288日、13周、26周、52周等等,不一而足,
|
758
|
-
其目的在取得某一段期间的平均成本,而以此平均成本的移动曲线配合每日收盘价的线路变化分析某一期间多空的优劣形势,
|
759
|
-
以研判股价的可能变化。
|
760
|
-
一般来说,现行价格在平均价之上,意味着市场买力(需求)较大,行情看好;
|
761
|
-
反之,行情价在平均价之下,则意味着供过于求,卖压显然较重,行情看淡。
|
762
|
-
"""
|
763
|
-
#if ('MA' in graph) or ('ALL' in graph):
|
764
|
-
if (graph in ['MA',['MA']]) or ('ALL' in graph):
|
765
|
-
MA_cols=[]
|
766
|
-
for mad in MA_days:
|
767
|
-
col='MA'+str(mad)+'简单移动均线'
|
768
|
-
MA_cols=MA_cols+[col]
|
769
|
-
df[col] = talib.MA(df['Close'],timeperiod=mad)
|
770
|
-
|
771
|
-
# MA快慢线交叉
|
772
|
-
dft=df.copy()
|
773
|
-
dft['datepd']=dft.index
|
774
|
-
dft['日期']=dft['datepd'].apply(lambda x: x.strftime("%Y-%m-%d"))
|
775
|
-
|
776
|
-
dft['short-long']=dft[MA_cols[0]]-dft[MA_cols[1]]
|
777
|
-
dft['sign']=dft['short-long'].apply(lambda x: 1 if x>0 else -1)
|
778
|
-
dft['sign_cross']=dft['sign']-dft['sign'].shift(1)
|
779
|
-
dft['交叉类型']=dft['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
780
|
-
dft2=dft[dft['sign_cross'] != 0]
|
781
|
-
dft3=dft2[(dft2.index >= startpd) & (dft2.index <= endpd)]
|
782
|
-
dft3.dropna(inplace=True)
|
783
|
-
|
784
|
-
# 限定日期范围
|
785
|
-
df1=df[(df.index >= startpd) & (df.index <= endpd)]
|
786
|
-
|
787
|
-
y_label="价格"
|
788
|
-
import datetime as dt; today=dt.date.today()
|
789
|
-
source="数据来源:sina/yahoo/stooq/fred,"+str(today)
|
790
|
-
footnote="MA参数:"+str(MA_days)
|
791
|
-
x_label=footnote+'\n'+source
|
792
|
-
|
793
|
-
axhline_value=0
|
794
|
-
axhline_label=''
|
795
|
-
|
796
|
-
# 简单移动均线MA绘图:moving average
|
797
|
-
df2=df1[['Close']+MA_cols]
|
798
|
-
df2.rename(columns={'Close':'收盘价'},inplace=True)
|
799
|
-
|
800
|
-
title_txt="证券价格走势分析:"+ticker_name(ticker,ticker_type)+",简单移动均线"
|
801
|
-
|
802
|
-
print(" Rendering graphics ...")
|
803
|
-
draw_lines(df2,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
804
|
-
data_label=False,resample_freq=resample_freq,smooth=smooth,linewidth=linewidth*2)
|
805
|
-
|
806
|
-
if printout:
|
807
|
-
if len(dft3)!=0:
|
808
|
-
print("\n== 简单移动均线交叉 ==")
|
809
|
-
alignlist=['left','center']
|
810
|
-
print(dft3[['日期','交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
811
|
-
else:
|
812
|
-
print(" Note: no cross of lines incurred for",ticker,"from",start,"to",end)
|
813
|
-
|
814
|
-
# 指数移动均线EMA绘图:exponential moving average
|
815
|
-
"""
|
816
|
-
MA是用每天的收盘价来计算简单的平均值,
|
817
|
-
而EMA是需要给每天的最高最低等价位数值做一个权重处理后,再平均计算,
|
818
|
-
所以,EMA更具有平均价值一些。
|
819
|
-
EMA最大特点也是由于它的计算方式导致的,因为后期的k线价格在计算均价时,比重更大,
|
820
|
-
所以相同参数EMA比MA更加贴近行情,更加平滑,产生的变盘信号、交易信号也更加激进?
|
821
|
-
EMA的变盘信号:熊市转牛市,比MA迟钝?牛市转熊市,比MA敏感?
|
822
|
-
"""
|
823
|
-
if ('EMA' in graph) or ('ALL' in graph):
|
824
|
-
EMA_cols=[]
|
825
|
-
for mad in EMA_days:
|
826
|
-
col='EMA'+str(mad)+'指数加权均线'
|
827
|
-
EMA_cols=EMA_cols+[col]
|
828
|
-
df[col] = talib.EMA(df['Close'],timeperiod=mad)
|
829
|
-
|
830
|
-
# EMA快慢线交叉
|
831
|
-
dft=df.copy()
|
832
|
-
dft['datepd']=dft.index
|
833
|
-
dft['日期']=dft['datepd'].apply(lambda x: x.strftime("%Y-%m-%d"))
|
834
|
-
|
835
|
-
dft['short-long']=dft[EMA_cols[0]]-dft[EMA_cols[1]]
|
836
|
-
dft['sign']=dft['short-long'].apply(lambda x: 1 if x>0 else -1)
|
837
|
-
dft['sign_cross']=dft['sign']-dft['sign'].shift(1)
|
838
|
-
dft['交叉类型']=dft['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
839
|
-
dft2=dft[dft['sign_cross'] != 0]
|
840
|
-
dft3=dft2[(dft2.index >= startpd) & (dft2.index <= endpd)]
|
841
|
-
dft3.dropna(inplace=True)
|
842
|
-
|
843
|
-
# 限定日期范围
|
844
|
-
df1=df[(df.index >= startpd) & (df.index <= endpd)]
|
845
|
-
|
846
|
-
y_label="价格"
|
847
|
-
import datetime as dt; today=dt.date.today()
|
848
|
-
source="数据来源:sina/yahoo/stooq/fred,"+str(today)
|
849
|
-
footnote="EMA参数:"+str(EMA_days)
|
850
|
-
x_label=footnote+'\n'+source
|
851
|
-
|
852
|
-
axhline_value=0
|
853
|
-
axhline_label=''
|
854
|
-
|
855
|
-
df3=df1[['Close']+EMA_cols]
|
856
|
-
df3.rename(columns={'Close':'收盘价'},inplace=True)
|
857
|
-
title_txt="证券价格走势分析:"+ticker_name(ticker,ticker_type)+",指数加权均线"
|
858
|
-
draw_lines(df3,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
859
|
-
data_label=False,resample_freq=resample_freq,smooth=smooth,linewidth=linewidth*2)
|
860
|
-
|
861
|
-
if printout:
|
862
|
-
if len(dft3)!=0:
|
863
|
-
print("\n== 指数加权均线交叉 ==")
|
864
|
-
alignlist=['left','center']
|
865
|
-
print(dft3[['日期','交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
866
|
-
else:
|
867
|
-
print(" Note: no cross of lines incurred for",ticker,"from",start,"to",end)
|
868
|
-
|
869
|
-
#=========== MACD:指数平滑异同平均线
|
870
|
-
"""
|
871
|
-
计算方法:快速时间窗口设为12日,慢速时间窗口设为26日,DIF参数设为9日
|
872
|
-
3.1) 计算指数平滑移动平均值(EMA)
|
873
|
-
12日EMA的计算公式为:
|
874
|
-
EMA(12) = 昨日EMA(12) × 11 ÷ 13 + 今日收盘价 × 2 ÷ 13
|
875
|
-
26日EMA的计算公式为:
|
876
|
-
EMA(26) = 昨日EMA(26) × 25 ÷ 27 + 今日收盘价 × 2 ÷ 27
|
877
|
-
|
878
|
-
3.2) 计算离差值(DIF)
|
879
|
-
DIF = 今日EMA(12) – 今日EMA(26)
|
880
|
-
|
881
|
-
3.3) 计算DIF的9日DEA
|
882
|
-
根据差值计算其9日的DEA,即差值平均
|
883
|
-
今日DEA = 昨日DEA × 8 ÷ 10 + 今日DIF × 2 ÷ 10
|
884
|
-
|
885
|
-
形态解读:
|
886
|
-
1.DIF、DEA均为正,DIF向上突破DEA,买入信号。
|
887
|
-
2.DIF、DEA均为负,DIF向下跌破DEA,卖出信号。
|
888
|
-
3.DEA线与K线发生背离,行情反转信号。
|
889
|
-
4.分析MACD柱状线,由红变绿(正变负),卖出信号;由绿变红,买入信号。
|
890
|
-
|
891
|
-
MACD一则去掉移动平均线频繁的假讯号缺陷,二则能确保移动平均线最大的战果。
|
892
|
-
1. MACD金叉:DIF由下向上突破DEM,为买入信号。
|
893
|
-
2. MACD死叉:DIF由上向下突破DEM,为卖出信号。
|
894
|
-
3. MACD绿转红:MACD值由负变正,市场由空头转为多头。
|
895
|
-
4. MACD红转绿:MACD值由正变负,市场由多头转为空头。
|
896
|
-
"""
|
897
|
-
if ('MACD' in graph) or ('ALL' in graph):
|
898
|
-
df['DIF'],df['DEA'],df['MACD']=talib.MACD(df['Close'], \
|
899
|
-
fastperiod=MACD_fastperiod, \
|
900
|
-
slowperiod=MACD_slowperiod, \
|
901
|
-
signalperiod=MACD_signalperiod)
|
902
|
-
|
903
|
-
# DIF/DEA快慢线交叉
|
904
|
-
dft=df.copy()
|
905
|
-
dft['datepd']=dft.index
|
906
|
-
dft['日期']=dft['datepd'].apply(lambda x: x.strftime("%Y-%m-%d"))
|
907
|
-
|
908
|
-
dft['short-long']=dft['DIF']-dft['DEA']
|
909
|
-
dft['sign']=dft['short-long'].apply(lambda x: 1 if x>0 else -1)
|
910
|
-
dft['sign_cross']=dft['sign']-dft['sign'].shift(1)
|
911
|
-
dft['交叉类型']=dft['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
912
|
-
dft2=dft[dft['sign_cross'] != 0]
|
913
|
-
dft3=dft2[(dft2.index >= startpd) & (dft2.index <= endpd)]
|
914
|
-
dft3.dropna(inplace=True)
|
915
|
-
|
916
|
-
# 限定日期范围
|
917
|
-
df1=df[(df.index >= startpd) & (df.index <= endpd)]
|
918
|
-
|
919
|
-
# MACD绘图
|
920
|
-
df4=df1[['Close','DIF','DEA','MACD']]
|
921
|
-
df4.rename(columns={'Close':'收盘价','DIF':'快线DIF','DEA':'慢线DEA','MACD':'柱线MACD'},inplace=True)
|
922
|
-
title_txt="证券价格走势分析:"+ticker_name(ticker,ticker_type)+",MACD"
|
923
|
-
|
924
|
-
import datetime as dt; today=dt.date.today()
|
925
|
-
source="数据来源:sina/yahoo/stooq/fred,"+str(today)
|
926
|
-
|
927
|
-
#设置绘图风格:关闭网格虚线
|
928
|
-
plt.rcParams['axes.grid']=False
|
929
|
-
|
930
|
-
# 设置绘图区的背景颜色为黑色
|
931
|
-
fig=plt.figure()
|
932
|
-
ax=fig.add_subplot(111)
|
933
|
-
ax.patch.set_facecolor('black')
|
934
|
-
|
935
|
-
# 绘制曲线
|
936
|
-
ax.plot(df4['快线DIF'],label='快线DIF',linewidth=linewidth*2,color='white')
|
937
|
-
ax.plot(df4['慢线DEA'],label='慢线DEA',linewidth=linewidth*2,color='orange')
|
938
|
-
|
939
|
-
# 绘制红绿柱子
|
940
|
-
"""
|
941
|
-
MACD指标由三部分组成,分别是:DIF (差离值)、 DEA (差离值平均数)和BAR(柱状线),
|
942
|
-
DIF在图中用白线表示,称为“快线”,DEA在图中用黄线表示,称为“慢线”,
|
943
|
-
最早的MACD只有这两条快慢线,通过两条曲线的聚合和分离来判断市场状况。
|
944
|
-
后来随着MACD的广泛运用,又引入了柱状线(BAR),俗称“红绿柱”。
|
945
|
-
红绿柱表示的是快线与慢线之间的距离,对指标实质没有影响,只是为了更便于观察和使用指标。
|
946
|
-
"""
|
947
|
-
macd_plus=df4[df4['柱线MACD']>=0]
|
948
|
-
macd_minus=df4[df4['柱线MACD']<=0]
|
949
|
-
ax.bar(macd_plus.index,macd_plus['柱线MACD'],color='red')
|
950
|
-
ax.bar(macd_minus.index,macd_minus['柱线MACD'],color='green',label='柱线MACD')
|
951
|
-
|
952
|
-
# 绘制水平辅助线
|
953
|
-
#plt.axhline(y=0,label='指标零线',color='cyan',linestyle=':',linewidth=linewidth*2)
|
954
|
-
plt.axhline(y=0,label='',color='cyan',linestyle=':',linewidth=linewidth*2)
|
955
|
-
|
956
|
-
# 设置左侧坐标轴
|
957
|
-
ax.set_ylabel('DIF/DEA/MACD指标',fontsize=ylabel_txt_size)
|
958
|
-
|
959
|
-
footnote="MACD参数:"+str([MACD_fastperiod,MACD_slowperiod,MACD_signalperiod])
|
960
|
-
x_label=footnote+'\n'+source
|
961
|
-
ax.set_xlabel(x_label,fontsize=xlabel_txt_size)
|
962
|
-
ax.legend(loc=loc1,fontsize=legend_txt_size)
|
963
|
-
|
964
|
-
# 绘制股价在右侧
|
965
|
-
#绘证券2:建立第二y轴
|
966
|
-
|
967
|
-
#插值平滑
|
968
|
-
if smooth:
|
969
|
-
try:
|
970
|
-
df5=df_smooth_manual(df4,resample_freq=resample_freq)
|
971
|
-
except:
|
972
|
-
df5=df4
|
973
|
-
else:
|
974
|
-
df5=df4
|
975
|
-
|
976
|
-
ax2 = ax.twinx()
|
977
|
-
ax2.plot(df5['收盘价'],label='收盘价',linewidth=linewidth,color='gray',ls='--')
|
978
|
-
|
979
|
-
# 右侧坐标轴标记
|
980
|
-
ax2.set_ylabel('收盘价',fontsize=ylabel_txt_size)
|
981
|
-
ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
982
|
-
|
983
|
-
# 图示标题
|
984
|
-
plt.title(title_txt,fontweight='bold',fontsize=title_txt_size)
|
985
|
-
#plt.xticks(rotation=30)
|
986
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
987
|
-
#plt.legend(loc='best',fontsize=legend_txt_size)
|
988
|
-
plt.show()
|
989
|
-
|
990
|
-
if printout:
|
991
|
-
if len(dft3)!=0:
|
992
|
-
print("\n== DIF与DEA交叉 ==")
|
993
|
-
alignlist=['left','center']
|
994
|
-
print(dft3[['日期','交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
995
|
-
else:
|
996
|
-
print(" Note: no cross of lines incurred for",ticker,"from",start,"to",end)
|
997
|
-
|
998
|
-
return df1
|
999
|
-
|
1000
|
-
#==============================================================================
|
1001
|
-
#==============================================================================
|
1002
|
-
#==============================================================================
|
1003
|
-
|
1004
|
-
def talib_install_method():
|
1005
|
-
"""
|
1006
|
-
功能:提示必需的talib安装方法
|
1007
|
-
"""
|
1008
|
-
print(" Installation method 1: pip install TA_Lib")
|
1009
|
-
print(" Note: method 1 is subject to fail")
|
1010
|
-
print(" Installation method 2: conda install -c quantopian ta-lib")
|
1011
|
-
print(" Note: method 2 may need scientific internet access")
|
1012
|
-
print(" Installation method 3: recommended")
|
1013
|
-
print(" Step1. Goto website https://www.lfd.uci.edu/~gohlke/pythonlibs/")
|
1014
|
-
print(" Step2. On the web page, search for TA_lib")
|
1015
|
-
print(" Step3. Select the file suitable for the Python version and OS 32/64")
|
1016
|
-
print(" Your Python version:",check_python_version(),"\b, your operating system:",check_os())
|
1017
|
-
|
1018
|
-
print(" Step4. Download the file")
|
1019
|
-
print(" Step5. pip install <downloaded file name>")
|
1020
|
-
print(" Note: method 3 is troublesome, but more likely successful")
|
1021
|
-
|
1022
|
-
print(" How to check the Python version in your computer? ")
|
1023
|
-
print(" python --version")
|
1024
|
-
print(" How to check the win32/win_amd64 in your computer? ")
|
1025
|
-
print(" On Windows, right click Start, click System")
|
1026
|
-
|
1027
|
-
print(" Important: after installing ta-lib, restart Python environment.")
|
1028
|
-
|
1029
|
-
return
|
1030
|
-
#==============================================================================
|
1031
|
-
|
1032
|
-
if __name__ =="__main__":
|
1033
|
-
ticker='600519.SS'
|
1034
|
-
start='2022-1-1'
|
1035
|
-
end='2022-12-31'
|
1036
|
-
|
1037
|
-
RSI_days=[6,12,24]
|
1038
|
-
RSI_lines=[20,50,80]
|
1039
|
-
|
1040
|
-
loc1='upper left'
|
1041
|
-
loc2='center right'
|
1042
|
-
|
1043
|
-
resample_freq='H'
|
1044
|
-
smooth=True
|
1045
|
-
linewidth=1.5
|
1046
|
-
graph=['ALL']
|
1047
|
-
printout=True
|
1048
|
-
|
1049
|
-
def security_RSI(ticker,start='default',end='default', \
|
1050
|
-
RSI_days=[6,12,24],RSI_lines=[20,50,80], \
|
1051
|
-
resample_freq='6H',smooth=True,linewidth=1.5, \
|
1052
|
-
loc1='lower left',loc2='lower right', \
|
1053
|
-
graph=['ALL'],printout=True,ticker_type='auto',source='auto'):
|
1054
|
-
"""
|
1055
|
-
套壳函数,除了股票,还可用于交易所债券和交易所基金(如ETF和REITS)
|
1056
|
-
"""
|
1057
|
-
df=stock_RSI(ticker=ticker,start=start,end=end, \
|
1058
|
-
RSI_days=RSI_days,RSI_lines=RSI_lines, \
|
1059
|
-
resample_freq=resample_freq,smooth=smooth,linewidth=linewidth, \
|
1060
|
-
loc1=loc1,loc2=loc2, \
|
1061
|
-
graph=graph,printout=printout,ticker_type=ticker_type,source=source)
|
1062
|
-
return df
|
1063
|
-
|
1064
|
-
|
1065
|
-
def stock_RSI(ticker,start='default',end='default', \
|
1066
|
-
RSI_days=[6,12,24],RSI_lines=[20,50,80], \
|
1067
|
-
resample_freq='H',smooth=True,linewidth=1.5, \
|
1068
|
-
loc1='lower left',loc2='lower right', \
|
1069
|
-
graph=['ALL'],printout=True,ticker_type='auto',source='auto'):
|
1070
|
-
"""
|
1071
|
-
功能:计算股票的技术分析指标RSI
|
1072
|
-
输入:df,四种股价Open/Close/High/Low,成交量Volume
|
1073
|
-
输出:df
|
1074
|
-
含有指标:
|
1075
|
-
RSI1(快速线,周线,黄色)、RSI2(中速线,双周线,紫色)、RSI3(慢速线,月线,白色)
|
1076
|
-
"""
|
1077
|
-
|
1078
|
-
#=========== 导入需要的包
|
1079
|
-
try:
|
1080
|
-
import talib
|
1081
|
-
except:
|
1082
|
-
print(" #Error(stock_RSI): lack of necessary module - ta-lib")
|
1083
|
-
talib_install_method()
|
1084
|
-
return None
|
1085
|
-
|
1086
|
-
#=========== 日期转换与检查
|
1087
|
-
# 检查日期:截至日期
|
1088
|
-
import datetime as dt; today=dt.date.today()
|
1089
|
-
if end in ['default','today']:
|
1090
|
-
end=today
|
1091
|
-
else:
|
1092
|
-
validdate,end=check_date2(end)
|
1093
|
-
if not validdate:
|
1094
|
-
print(" #Warning(stock_RSI): invalid date for",end)
|
1095
|
-
end=today
|
1096
|
-
|
1097
|
-
# 检查日期:开始日期
|
1098
|
-
if start in ['default']:
|
1099
|
-
start=date_adjust(end,adjust=-31)
|
1100
|
-
else:
|
1101
|
-
validdate,start=check_date2(start)
|
1102
|
-
if not validdate:
|
1103
|
-
print(" #Warning(stock_RSI): invalid date for",start)
|
1104
|
-
start=date_adjust(todate,adjust=-31)
|
1105
|
-
|
1106
|
-
#=========== 获取股价和成交量数据
|
1107
|
-
result,startpd,endpd=check_period(start,end)
|
1108
|
-
|
1109
|
-
days_list=RSI_days
|
1110
|
-
max_days=max(days_list)
|
1111
|
-
start1=date_adjust(start,adjust=-max_days * 3)
|
1112
|
-
|
1113
|
-
#df=get_price(ticker,start1,end)
|
1114
|
-
df,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1,todate=end,ticker_type=ticker_type,source=source)
|
1115
|
-
if df is None:
|
1116
|
-
print(" #Error(stock_RSI): no info found for",ticker,"from",start,"to",end)
|
1117
|
-
return None
|
1118
|
-
if len(df)==0:
|
1119
|
-
print(" #Error(stock_RSI): zero record found for",ticker,"from",start,"to",end)
|
1120
|
-
return None
|
1121
|
-
|
1122
|
-
#============ 计算RSI
|
1123
|
-
#df['rsi'] = talib.RSI(df['Close'], timeperiod=RSI_days)
|
1124
|
-
RSI_cols=[]
|
1125
|
-
RSI_seq=1
|
1126
|
-
for mad in RSI_days:
|
1127
|
-
col='RSI'+str(RSI_seq)
|
1128
|
-
RSI_cols=RSI_cols+[col]
|
1129
|
-
df[col] = talib.RSI(df['Close'],timeperiod=mad)
|
1130
|
-
RSI_seq=RSI_seq+1
|
1131
|
-
|
1132
|
-
df['datepd']=df.index
|
1133
|
-
df['日期']=df['datepd'].apply(lambda x: x.strftime("%Y-%m-%d"))
|
1134
|
-
del df['datepd']
|
1135
|
-
|
1136
|
-
# RSI1/RSI3快慢线交叉
|
1137
|
-
dft1=df.copy()
|
1138
|
-
dft1['short-long']=dft1['RSI1']-dft1['RSI3']
|
1139
|
-
dft1['sign']=dft1['short-long'].apply(lambda x: 1 if x>0 else -1)
|
1140
|
-
dft1['sign_cross']=dft1['sign']-dft1['sign'].shift(1)
|
1141
|
-
dft1['RSI1/3交叉类型']=dft1['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
1142
|
-
dft1b=dft1[dft1['sign_cross'] != 0]
|
1143
|
-
dft1c=dft1b[(dft1b.index >= startpd) & (dft1b.index <= endpd)]
|
1144
|
-
dft1c.dropna(inplace=True)
|
1145
|
-
|
1146
|
-
# RSI2/RSI3快慢线交叉
|
1147
|
-
dft2=df.copy()
|
1148
|
-
dft2['short-long']=dft2['RSI2']-dft2['RSI3']
|
1149
|
-
dft2['sign']=dft2['short-long'].apply(lambda x: 1 if x>0 else -1)
|
1150
|
-
dft2['sign_cross']=dft2['sign']-dft2['sign'].shift(1)
|
1151
|
-
dft2['RSI2/3交叉类型']=dft2['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
1152
|
-
dft2b=dft2[dft2['sign_cross'] != 0]
|
1153
|
-
dft2c=dft2b[(dft2b.index >= startpd) & (dft2b.index <= endpd)]
|
1154
|
-
dft2c.dropna(inplace=True)
|
1155
|
-
|
1156
|
-
# RSI限定日期范围
|
1157
|
-
df1=df[(df.index >= startpd) & (df.index <= endpd)]
|
1158
|
-
|
1159
|
-
if len(graph) != 0:
|
1160
|
-
|
1161
|
-
# RSI绘图
|
1162
|
-
df4=df1[['Close']+RSI_cols]
|
1163
|
-
df4.rename(columns={'Close':'收盘价'},inplace=True)
|
1164
|
-
title_txt="证券价格走势分析:"+ticker_name(ticker,ticker_type)+",RSI"
|
1165
|
-
|
1166
|
-
import datetime as dt; today=dt.date.today()
|
1167
|
-
source="数据来源:sina/yahoo/stooq/fred,"+str(today)
|
1168
|
-
footnote="RSI参数:"+str(RSI_days)
|
1169
|
-
x_label=footnote+'\n'+source
|
1170
|
-
|
1171
|
-
# 设置绘图区的背景颜色为黑色
|
1172
|
-
fig=plt.figure()
|
1173
|
-
ax=fig.add_subplot(111)
|
1174
|
-
ax.patch.set_facecolor('black')
|
1175
|
-
|
1176
|
-
# 绘制曲线
|
1177
|
-
if ('RSI1' in graph) or ('ALL' in graph):
|
1178
|
-
ax.plot(df4['RSI1'],label='快速(周)线RSI1',linewidth=linewidth*2,color='orange')
|
1179
|
-
if ('RSI2' in graph) or ('ALL' in graph):
|
1180
|
-
ax.plot(df4['RSI2'],label='中速(双周)线RSI2',linewidth=linewidth*2,color='purple')
|
1181
|
-
if ('RSI3' in graph) or ('ALL' in graph):
|
1182
|
-
ax.plot(df4['RSI3'],label='慢速(月)线RSI3',linewidth=linewidth*2,color='white')
|
1183
|
-
|
1184
|
-
# 绘制水平辅助线
|
1185
|
-
hl_linestyle_list=['dashed','-.','dotted']
|
1186
|
-
#plt.axhline(y=0,label='指标零线',color='cyan',linestyle=':',linewidth=linewidth*2)
|
1187
|
-
for hl in RSI_lines:
|
1188
|
-
pos=RSI_lines.index(hl)
|
1189
|
-
hl_ls=hl_linestyle_list[pos]
|
1190
|
-
plt.axhline(y=hl,label='',color='cyan',linestyle=hl_ls,linewidth=linewidth)
|
1191
|
-
|
1192
|
-
# 设置左侧坐标轴
|
1193
|
-
ax.set_ylabel('RSI指标',fontsize=ylabel_txt_size)
|
1194
|
-
ax.set_xlabel(x_label,fontsize=xlabel_txt_size)
|
1195
|
-
ax.legend(loc=loc1,fontsize=legend_txt_size)
|
1196
|
-
|
1197
|
-
# 绘制股价在右侧
|
1198
|
-
#绘证券2:建立第二y轴
|
1199
|
-
|
1200
|
-
#插值平滑
|
1201
|
-
if smooth:
|
1202
|
-
print(" Smoothening curves directly ...")
|
1203
|
-
try:
|
1204
|
-
df5=df_smooth_manual(df4,resample_freq=resample_freq)
|
1205
|
-
except:
|
1206
|
-
df5=df4
|
1207
|
-
else:
|
1208
|
-
df5=df4
|
1209
|
-
|
1210
|
-
ax2 = ax.twinx()
|
1211
|
-
ax2.plot(df5['收盘价'],label='收盘价',linewidth=linewidth,color='gray',ls='--')
|
1212
|
-
|
1213
|
-
# 右侧坐标轴标记
|
1214
|
-
ax2.set_ylabel('收盘价',fontsize=ylabel_txt_size)
|
1215
|
-
ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
1216
|
-
|
1217
|
-
# 图示标题
|
1218
|
-
plt.title(title_txt,fontweight='bold',fontsize=title_txt_size)
|
1219
|
-
#plt.xticks(rotation=30)
|
1220
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
1221
|
-
#plt.legend(loc='best',fontsize=legend_txt_size)
|
1222
|
-
plt.show()
|
1223
|
-
|
1224
|
-
if printout:
|
1225
|
-
if (('RSI1' in graph) & ('RSI3' in graph)) or ('ALL' in graph):
|
1226
|
-
if len(dft1c)!=0:
|
1227
|
-
print("\n=== RSI1与RSI3的交叉点 ===")
|
1228
|
-
alignlist=['left','center']
|
1229
|
-
print(dft1c[['日期','RSI1/3交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
1230
|
-
else:
|
1231
|
-
print(" Note: no RSI1/3 cross of lines incurred for",ticker,"from",start,"to",end)
|
1232
|
-
if printout:
|
1233
|
-
if (('RSI2' in graph) & ('RSI3' in graph)) or ('ALL' in graph):
|
1234
|
-
if len(dft2c)!=0:
|
1235
|
-
print("\n=== RSI2与RSI3的交叉点 ===")
|
1236
|
-
alignlist=['left','center']
|
1237
|
-
print(dft2c[['日期','RSI2/3交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
1238
|
-
else:
|
1239
|
-
print(" Note: no RSI2/3 cross of lines incurred for",ticker,"from",start,"to",end)
|
1240
|
-
|
1241
|
-
return df1
|
1242
|
-
#==============================================================================
|
1243
|
-
#==============================================================================
|
1244
|
-
#==============================================================================
|
1245
|
-
|
1246
|
-
def findout_cross(df,start,end,fast_line,slow_line):
|
1247
|
-
"""
|
1248
|
-
功能:技术分析,找出fast_line与slow_line两条线交叉的记录,并给出交叉类型:上穿,下穿
|
1249
|
-
"""
|
1250
|
-
|
1251
|
-
dft1=df.copy()
|
1252
|
-
dft1['short-long']=dft1[fast_line]-dft1[slow_line]
|
1253
|
-
dft1['sign']=dft1['short-long'].apply(lambda x: 1 if x>0 else -1)
|
1254
|
-
dft1['sign_cross']=dft1['sign']-dft1['sign'].shift(1)
|
1255
|
-
dft1['交叉类型']=dft1['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
1256
|
-
dft1b=dft1[dft1['sign_cross'] != 0]
|
1257
|
-
|
1258
|
-
result,startpd,endpd=check_period(start,end)
|
1259
|
-
dft1c=dft1b[(dft1b.index >= startpd) & (dft1b.index <= endpd)]
|
1260
|
-
|
1261
|
-
dft1c.dropna(inplace=True)
|
1262
|
-
|
1263
|
-
return dft1c
|
1264
|
-
|
1265
|
-
#==============================================================================
|
1266
|
-
#==============================================================================
|
1267
|
-
#==============================================================================
|
1268
|
-
|
1269
|
-
|
1270
|
-
if __name__ =="__main__":
|
1271
|
-
ticker='600519.SS'
|
1272
|
-
start='2010-1-1'
|
1273
|
-
end='2022-12-31'
|
1274
|
-
|
1275
|
-
KDJ_days=[9,3,3]
|
1276
|
-
matypes=[0,0]
|
1277
|
-
|
1278
|
-
loc1='upper left'
|
1279
|
-
loc2='center right'
|
1280
|
-
|
1281
|
-
resample_freq='H'
|
1282
|
-
smooth=True
|
1283
|
-
linewidth=1.5
|
1284
|
-
|
1285
|
-
graph=['ALL']
|
1286
|
-
printout=True
|
1287
|
-
|
1288
|
-
graph=False
|
1289
|
-
|
1290
|
-
def security_KDJ(ticker,start='default',end='default', \
|
1291
|
-
KDJ_days=[9,3,3],matypes=[0,0],KDJ_lines=[20,50,80], \
|
1292
|
-
resample_freq='6H',smooth=True,linewidth=1.5, \
|
1293
|
-
loc1='lower left',loc2='lower right', \
|
1294
|
-
graph=['ALL'],printout=True,ticker_type='auto',source='auto'):
|
1295
|
-
"""
|
1296
|
-
套壳函数
|
1297
|
-
"""
|
1298
|
-
df=stock_KDJ(ticker=ticker,start=start,end=end, \
|
1299
|
-
KDJ_days=KDJ_days,matypes=matypes,KDJ_lines=KDJ_lines, \
|
1300
|
-
resample_freq=resample_freq,smooth=smooth,linewidth=linewidth, \
|
1301
|
-
loc1=loc1,loc2=loc2, \
|
1302
|
-
graph=graph,printout=printout,ticker_type=ticker_type,source=source)
|
1303
|
-
return df
|
1304
|
-
|
1305
|
-
|
1306
|
-
def stock_KDJ(ticker,start='default',end='default', \
|
1307
|
-
KDJ_days=[9,3,3],matypes=[0,0],KDJ_lines=[20,50,80], \
|
1308
|
-
resample_freq='H',smooth=True,linewidth=1.5, \
|
1309
|
-
loc1='lower left',loc2='lower right', \
|
1310
|
-
graph=['ALL'],printout=True,ticker_type='auto',source='auto'):
|
1311
|
-
"""
|
1312
|
-
功能:计算股票的技术分析指标KDJ
|
1313
|
-
输入:df,四种股价Open/Close/High/Low,成交量Volume
|
1314
|
-
输出:df
|
1315
|
-
含有指标:
|
1316
|
-
K线(快速线,黄色)、D线(慢速线,绿色)、J线(差额,紫色)
|
1317
|
-
"""
|
1318
|
-
|
1319
|
-
#=========== 导入需要的包
|
1320
|
-
try:
|
1321
|
-
import talib
|
1322
|
-
except:
|
1323
|
-
print(" #Error(stock_KDJ): lack of necessary module - talib")
|
1324
|
-
talib_install_method()
|
1325
|
-
return None
|
1326
|
-
|
1327
|
-
#=========== 日期转换与检查
|
1328
|
-
# 检查日期:截至日期
|
1329
|
-
import datetime as dt; today=dt.date.today()
|
1330
|
-
if end in ['default','today']:
|
1331
|
-
end=today
|
1332
|
-
else:
|
1333
|
-
validdate,end=check_date2(end)
|
1334
|
-
if not validdate:
|
1335
|
-
print(" #Warning(stock_KDJ): invalid date for",end)
|
1336
|
-
end=today
|
1337
|
-
|
1338
|
-
# 检查日期:开始日期
|
1339
|
-
if start in ['default']:
|
1340
|
-
start=date_adjust(end,adjust=-31)
|
1341
|
-
else:
|
1342
|
-
validdate,start=check_date2(start)
|
1343
|
-
if not validdate:
|
1344
|
-
print(" #Warning(stock_KDJ): invalid date for",start)
|
1345
|
-
start=date_adjust(todate,adjust=-31)
|
1346
|
-
|
1347
|
-
#=========== 获取股价和成交量数据
|
1348
|
-
result,startpd,endpd=check_period(start,end)
|
1349
|
-
|
1350
|
-
days_list=KDJ_days
|
1351
|
-
max_days=max(days_list)
|
1352
|
-
start1=date_adjust(start,adjust=-max_days * 3)
|
1353
|
-
|
1354
|
-
#df=get_price(ticker,start1,end)
|
1355
|
-
df,found=get_price_1ticker_mixed(ticker=ticker,fromdate=start1,todate=end,ticker_type=ticker_type,source=source)
|
1356
|
-
if df is None:
|
1357
|
-
print(" #Error(stock_RSI): no info found for",ticker,"from",start,"to",end)
|
1358
|
-
return None
|
1359
|
-
if len(df)==0:
|
1360
|
-
print(" #Error(stock_RSI): zero record found for",ticker,"from",start,"to",end)
|
1361
|
-
return None
|
1362
|
-
|
1363
|
-
df['datepd']=df.index
|
1364
|
-
df['日期']=df['datepd'].apply(lambda x: x.strftime("%Y-%m-%d"))
|
1365
|
-
del df['datepd']
|
1366
|
-
|
1367
|
-
#============ 计算KDJ
|
1368
|
-
"""
|
1369
|
-
df['kdj_k'],df['kdj_d'] = talib.STOCH(df['High'],df['Low'],df['Close'], \
|
1370
|
-
fastk_period=KDJ_fastk_period,
|
1371
|
-
slowk_period=KDJ_slowk_period,
|
1372
|
-
slowk_matype=KDJ_slowk_matype,
|
1373
|
-
slowd_period=KDJ_slowd_period,
|
1374
|
-
slowd_matype=KDJ_slowd_matype)
|
1375
|
-
df['kdj_j'] = 3*df['kdj_k'] - 2*df['kdj_d']
|
1376
|
-
"""
|
1377
|
-
KDJ_cols=['K','D','J']
|
1378
|
-
df['K'],df['D'] = talib.STOCH(df['High'],df['Low'],df['Close'], \
|
1379
|
-
fastk_period=KDJ_days[0],
|
1380
|
-
slowk_period=KDJ_days[1],
|
1381
|
-
slowk_matype=matypes[0],
|
1382
|
-
slowd_period=KDJ_days[2],
|
1383
|
-
slowd_matype=matypes[1])
|
1384
|
-
df['J'] = 3*df['K'] - 2*df['D']
|
1385
|
-
|
1386
|
-
# 限制J线在0-100之间
|
1387
|
-
if graph:
|
1388
|
-
df['J']=df['J'].apply(lambda x:100 if x>100 else 0 if x<0 else x)
|
1389
|
-
|
1390
|
-
if not graph:
|
1391
|
-
return df
|
1392
|
-
|
1393
|
-
# J/K线交叉
|
1394
|
-
"""
|
1395
|
-
dft1=df.copy()
|
1396
|
-
dft1['short-long']=dft1['J']-dft1['K']
|
1397
|
-
dft1['sign']=dft1['short-long'].apply(lambda x: 1 if x>0 else -1)
|
1398
|
-
dft1['sign_cross']=dft1['sign']-dft1['sign'].shift(1)
|
1399
|
-
dft1['J/K线交叉类型']=dft1['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
1400
|
-
dft1b=dft1[dft1['sign_cross'] != 0]
|
1401
|
-
dft1c=dft1b[(dft1b.index >= startpd) & (dft1b.index <= endpd)]
|
1402
|
-
dft1c.dropna(inplace=True)
|
1403
|
-
"""
|
1404
|
-
dft1c=findout_cross(df,start,end,'J','K')
|
1405
|
-
|
1406
|
-
# J/D线交叉
|
1407
|
-
"""
|
1408
|
-
dft2=df.copy()
|
1409
|
-
dft2['short-long']=dft2['J']-dft2['D']
|
1410
|
-
dft2['sign']=dft2['short-long'].apply(lambda x: 1 if x>0 else -1)
|
1411
|
-
dft2['sign_cross']=dft2['sign']-dft2['sign'].shift(1)
|
1412
|
-
dft2['J/D线交叉类型']=dft2['sign_cross'].apply(lambda x: '上穿' if x > 0 else '下穿' if x < 0 else '')
|
1413
|
-
dft2b=dft2[dft2['sign_cross'] != 0]
|
1414
|
-
dft2c=dft2b[(dft2b.index >= startpd) & (dft2b.index <= endpd)]
|
1415
|
-
dft2c.dropna(inplace=True)
|
1416
|
-
"""
|
1417
|
-
dft2c=findout_cross(df,start,end,'J','D')
|
1418
|
-
|
1419
|
-
# K/D线交叉
|
1420
|
-
dft3c=findout_cross(df,start,end,'K','D')
|
1421
|
-
|
1422
|
-
# 限定日期范围
|
1423
|
-
df1=df[(df.index >= startpd) & (df.index <= endpd)]
|
1424
|
-
|
1425
|
-
if len(graph) != 0:
|
1426
|
-
|
1427
|
-
# 绘图
|
1428
|
-
df4=df1[['Close']+KDJ_cols]
|
1429
|
-
df4.rename(columns={'Close':'收盘价'},inplace=True)
|
1430
|
-
title_txt="证券价格走势分析:"+ticker_name(ticker,ticker_type)+",KDJ"
|
1431
|
-
|
1432
|
-
import datetime as dt; today=dt.date.today()
|
1433
|
-
source="数据来源:sina/yahoo/stooq/fred,"+str(today)
|
1434
|
-
footnote="KDJ参数:"+str(KDJ_days)
|
1435
|
-
x_label=footnote+'\n'+source
|
1436
|
-
|
1437
|
-
# 设置绘图区的背景颜色为黑色
|
1438
|
-
fig=plt.figure()
|
1439
|
-
ax=fig.add_subplot(111)
|
1440
|
-
ax.patch.set_facecolor('black')
|
1441
|
-
|
1442
|
-
# 绘制曲线
|
1443
|
-
if ('K' in graph) or ('ALL' in graph):
|
1444
|
-
ax.plot(df4['K'],label='快速线K',linewidth=linewidth*2,color='orange')
|
1445
|
-
if ('D' in graph) or ('ALL' in graph):
|
1446
|
-
ax.plot(df4['D'],label='慢速线D',linewidth=linewidth*2,color='green')
|
1447
|
-
if ('J' in graph) or ('ALL' in graph):
|
1448
|
-
ax.plot(df4['J'],label='超快确认线J',linewidth=linewidth*2,color='purple')
|
1449
|
-
|
1450
|
-
# 绘制水平辅助线: 某些情况下不绘制,以便展现KDJ线细节
|
1451
|
-
maxK=df4['K'].max()
|
1452
|
-
maxD=df4['D'].max()
|
1453
|
-
maxJ=df4['J'].max()
|
1454
|
-
maxKDJ=max(maxK,maxD,maxJ)
|
1455
|
-
|
1456
|
-
minK=df4['K'].min()
|
1457
|
-
minD=df4['D'].min()
|
1458
|
-
minJ=df4['J'].min()
|
1459
|
-
minKDJ=min(minK,minD,minJ)
|
1460
|
-
|
1461
|
-
hl_linestyle_list=['dashed','dashed','-.','dotted','dotted']
|
1462
|
-
hl_color_list=['red','red','white','cyan','cyan']
|
1463
|
-
#plt.axhline(y=0,label='指标零线',color='cyan',linestyle=':',linewidth=linewidth*2)
|
1464
|
-
for hl in KDJ_lines:
|
1465
|
-
draw=True
|
1466
|
-
if (hl > maxKDJ):
|
1467
|
-
draw=False
|
1468
|
-
if (hl < minKDJ):
|
1469
|
-
draw=False
|
1470
|
-
|
1471
|
-
if draw:
|
1472
|
-
pos=KDJ_lines.index(hl)
|
1473
|
-
hl_ls=hl_linestyle_list[pos]
|
1474
|
-
hl_color=hl_color_list[pos]
|
1475
|
-
plt.axhline(y=hl,label='',color=hl_color,linestyle=hl_ls,linewidth=linewidth)
|
1476
|
-
|
1477
|
-
# 设置左侧坐标轴
|
1478
|
-
ax.set_ylabel('KDJ指标',fontsize=ylabel_txt_size)
|
1479
|
-
ax.set_xlabel(x_label,fontsize=xlabel_txt_size)
|
1480
|
-
ax.legend(loc=loc1,fontsize=legend_txt_size)
|
1481
|
-
|
1482
|
-
# 设置纵轴刻度
|
1483
|
-
from matplotlib.pyplot import MultipleLocator
|
1484
|
-
y_major_locator=MultipleLocator(10)
|
1485
|
-
ax.yaxis.set_major_locator(y_major_locator)
|
1486
|
-
#ax.set_ylim(-5,105)
|
1487
|
-
|
1488
|
-
# 绘制股价在右侧
|
1489
|
-
#绘证券2:建立第二y轴
|
1490
|
-
|
1491
|
-
#插值平滑
|
1492
|
-
if smooth:
|
1493
|
-
print(" Smoothening curves directly ...")
|
1494
|
-
try:
|
1495
|
-
df5=df_smooth_manual(df4,resample_freq=resample_freq)
|
1496
|
-
except:
|
1497
|
-
df5=df4
|
1498
|
-
else:
|
1499
|
-
df5=df4
|
1500
|
-
|
1501
|
-
ax2 = ax.twinx()
|
1502
|
-
ax2.plot(df5['收盘价'],label='收盘价',linewidth=linewidth,color='gray',ls='--')
|
1503
|
-
|
1504
|
-
# 右侧坐标轴标记
|
1505
|
-
ax2.set_ylabel('收盘价',fontsize=ylabel_txt_size)
|
1506
|
-
ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
1507
|
-
|
1508
|
-
# 图示标题
|
1509
|
-
plt.title(title_txt,fontweight='bold',fontsize=title_txt_size)
|
1510
|
-
#plt.xticks(rotation=30)
|
1511
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
1512
|
-
#plt.legend(loc='best',fontsize=legend_txt_size)
|
1513
|
-
plt.show()
|
1514
|
-
|
1515
|
-
if printout:
|
1516
|
-
alignlist=['left','center']
|
1517
|
-
if (('J' in graph) & ('K' in graph)) or ('ALL' in graph):
|
1518
|
-
if len(dft1c)!=0:
|
1519
|
-
print("\n**** J线与K线的交叉点")
|
1520
|
-
print(dft1c[['日期','交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
1521
|
-
else:
|
1522
|
-
print(" Note: no J/K cross of lines incurred for",ticker,"from",start,"to",end)
|
1523
|
-
|
1524
|
-
if (('J' in graph) & ('D' in graph)) or ('ALL' in graph):
|
1525
|
-
if len(dft2c)!=0:
|
1526
|
-
print("\n**** J线与K线的交叉点")
|
1527
|
-
alignlist=['left','center']
|
1528
|
-
print(dft2c[['日期','交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
1529
|
-
else:
|
1530
|
-
print(" Note: no J/D cross of lines incurred for",ticker,"from",start,"to",end)
|
1531
|
-
|
1532
|
-
if (('K' in graph) & ('D' in graph)) or ('ALL' in graph):
|
1533
|
-
if len(dft3c)!=0:
|
1534
|
-
print("\n**** K线与D线的交叉点")
|
1535
|
-
alignlist=['left','center']
|
1536
|
-
print(dft3c[['日期','交叉类型']].to_markdown(index=False,tablefmt='plain',colalign=alignlist))
|
1537
|
-
else:
|
1538
|
-
print(" Note: no K/D cross of lines incurred for",ticker,"from",start,"to",end)
|
1539
|
-
|
1540
|
-
return df1
|
1541
|
-
#==============================================================================
|
1542
|
-
#==============================================================================
|
1543
|
-
#==============================================================================
|
1544
|
-
|
1545
|
-
if __name__ =="__main__":
|
1546
|
-
ticker='600519.SS'
|
1547
|
-
start='2020-1-1'
|
1548
|
-
end='2022-12-31'
|
1549
|
-
|
1550
|
-
past_days=1
|
1551
|
-
momemtum_days=0
|
1552
|
-
future_days=1
|
1553
|
-
|
1554
|
-
threshhold=0.001
|
1555
|
-
|
1556
|
-
df=stock_KDJ(ticker,start,end,graph=False)
|
1557
|
-
|
1558
|
-
def price_trend_technical(df, \
|
1559
|
-
past_days=1,momemtum_days=1,future_days=1, \
|
1560
|
-
threshhold=0.001):
|
1561
|
-
"""
|
1562
|
-
功能:检查技术结果中每日股价的变化趋势:上涨,下跌,振荡,转折
|
1563
|
-
past_days: 当前日期前几个交易日的算术收益率天数
|
1564
|
-
momemtum_days: 股价发生变化的惯性天数,从当前日期开始计算
|
1565
|
-
future_days: 当前日期未来几个交易日的算术收益率天数,从当前日期+惯性天数开始计算
|
1566
|
-
threshhold: 股价算术收益率变化的最低门槛,
|
1567
|
-
不小于threshhold:上涨,+;
|
1568
|
-
小于-threshhold:下跌,-;
|
1569
|
-
其余:振荡,~
|
1570
|
-
|
1571
|
-
注意:可能存在尚不明了的问题!!!
|
1572
|
-
"""
|
1573
|
-
|
1574
|
-
cols=list(df)
|
1575
|
-
cols.remove('Close')
|
1576
|
-
cols1=cols+['Close']
|
1577
|
-
df1=df[cols1]
|
1578
|
-
|
1579
|
-
df1['Close_past']=df1['Close'].shift(past_days)
|
1580
|
-
df1['ret_past']=df1['Close'] - df1['Close_past']
|
1581
|
-
|
1582
|
-
# +=上涨,~=振荡,-=下跌
|
1583
|
-
df1['trend_past']=df1['ret_past'].apply(lambda x:'+' if x >= threshhold else
|
1584
|
-
'-' if x <= -threshhold
|
1585
|
-
else '~' if -threshhold < x < threshhold
|
1586
|
-
else '')
|
1587
|
-
if momemtum_days != 0:
|
1588
|
-
df1['Close_mom']=df1['Close'].shift(-momemtum_days)
|
1589
|
-
else:
|
1590
|
-
df1['Close_mom']=df1['Close']
|
1591
|
-
|
1592
|
-
df1['Close_future']=df1['Close'].shift(-momemtum_days-future_days)
|
1593
|
-
df1['ret_future']=df1['Close_future'] - df1['Close_mom']
|
1594
|
-
df1['trend_future']=df1['ret_future'].apply(lambda x:'+' if x >= threshhold
|
1595
|
-
else '-' if x <= -threshhold
|
1596
|
-
else '~' if -threshhold < x < threshhold
|
1597
|
-
else '')
|
1598
|
-
|
1599
|
-
df1['trend_past2future']=''
|
1600
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1601
|
-
'+2+' if (x['trend_past']=='+') & (x['trend_future']=='+')
|
1602
|
-
else x['trend_past2future'],axis=1)
|
1603
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1604
|
-
'-2-' if (x['trend_past']=='-') & (x['trend_future']=='-')
|
1605
|
-
else x['trend_past2future'],axis=1)
|
1606
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1607
|
-
'~2~' if (x['trend_past']=='~') & (x['trend_future']=='~')
|
1608
|
-
else x['trend_past2future'],axis=1)
|
1609
|
-
|
1610
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1611
|
-
'+2-' if (x['trend_past']=='+') & (x['trend_future']=='-')
|
1612
|
-
else x['trend_past2future'],axis=1)
|
1613
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1614
|
-
'+2~' if (x['trend_past']=='+') & (x['trend_future']=='~')
|
1615
|
-
else x['trend_past2future'],axis=1)
|
1616
|
-
|
1617
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1618
|
-
'-2+' if (x['trend_past']=='-') & (x['trend_future']=='+')
|
1619
|
-
else x['trend_past2future'],axis=1)
|
1620
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1621
|
-
'-2~' if (x['trend_past']=='-') & (x['trend_future']=='~')
|
1622
|
-
else x['trend_past2future'],axis=1)
|
1623
|
-
|
1624
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1625
|
-
'~2+' if (x['trend_past']=='~') & (x['trend_future']=='+')
|
1626
|
-
else x['trend_past2future'],axis=1)
|
1627
|
-
df1['trend_past2future']=df1.apply(lambda x:
|
1628
|
-
'~2-' if (x['trend_past']=='~') & (x['trend_future']=='-')
|
1629
|
-
else x['trend_past2future'],axis=1)
|
1630
|
-
|
1631
|
-
|
1632
|
-
tempcols=['Close_past','ret_past','Close_mom','Close_future','ret_future']
|
1633
|
-
df1.drop(tempcols, axis=1, inplace=True)
|
1634
|
-
|
1635
|
-
# 记录参数
|
1636
|
-
df1['past_days']=past_days
|
1637
|
-
df1['momemtum_days']=momemtum_days
|
1638
|
-
df1['future_days']=future_days
|
1639
|
-
df1['threshhold']=threshhold
|
1640
|
-
|
1641
|
-
return df1
|
1642
|
-
|
1643
|
-
|
1644
|
-
#==============================================================================
|
1645
|
-
#==============================================================================
|
1646
|
-
|
1647
|
-
if __name__ =="__main__":
|
1648
|
-
|
1649
|
-
ticker='600519.SS'
|
1650
|
-
start='2020-1-1'
|
1651
|
-
end='2022-12-31'
|
1652
|
-
df=stock_KDJ(ticker,start,end,graph=False)
|
1653
|
-
|
1654
|
-
df1=price_trend_technical(df)
|
1655
|
-
KDJ_values=[20,20,10]
|
1656
|
-
J_days=3
|
1657
|
-
JvsK='above'
|
1658
|
-
JKcross='bottom up'
|
1659
|
-
value_type='ceiling'
|
1660
|
-
|
1661
|
-
otc1,dfs1=check_KDJ_market(df1,start,end,KDJ_values=[50,50,50],value_type='floor')
|
1662
|
-
otc2,dfs2=check_KDJ_market(df1,start,end,KDJ_values=[80,80,100],J_days=3,value_type='floor')
|
1663
|
-
dfs_down=dfs2[dfs2['trend_future']=='-']
|
1664
|
-
|
1665
|
-
otp1,dfs1=check_KDJ_market(df1,start,end,KDJ_values=[50,50,50],value_type='ceiling')
|
1666
|
-
otp2,dfs2=check_KDJ_market(df1,start,end,KDJ_values=[20,20,0],J_days=3,value_type='ceiling')
|
1667
|
-
dfs_up=dfs2[dfs2['trend_future']=='+']
|
1668
|
-
|
1669
|
-
|
1670
|
-
def check_KDJ_market(df1,start,end,KDJ_values=[50,50,50],J_days=1,JvsK='any',JKcross='any',
|
1671
|
-
value_type='ceiling'):
|
1672
|
-
"""
|
1673
|
-
功能:检查KDJ计算结果中,多头/空头市场的股价趋势比例
|
1674
|
-
start,end:日期范围
|
1675
|
-
value_type='ceiling':K、D、J值位于KDJ_values=[50,50,50]以下,假设为空头市场
|
1676
|
-
value_type='floor':K、D、J值位于KDJ_values=[50,50,50]以上,假设为多头市场
|
1677
|
-
|
1678
|
-
注意:可能存在尚不明了的问题!!!
|
1679
|
-
"""
|
1680
|
-
import pandas as pd
|
1681
|
-
|
1682
|
-
ticker=df1['ticker'].values[0]
|
1683
|
-
past_days=df1['past_days'].values[0]
|
1684
|
-
momemtum_days=df1['momemtum_days'].values[0]
|
1685
|
-
future_days=df1['future_days'].values[0]
|
1686
|
-
threshhold=df1['threshhold'].values[0]
|
1687
|
-
|
1688
|
-
# J值的持续天数
|
1689
|
-
if J_days > 1:
|
1690
|
-
for jj in range(1,J_days):
|
1691
|
-
df1['J_'+str(jj)]=df1['J'].shift(jj)
|
1692
|
-
|
1693
|
-
result,startpd,endpd=check_period(start,end)
|
1694
|
-
df2=df1[(df1.index >= startpd) & (df1.index <=endpd)]
|
1695
|
-
df2=df2[df2['trend_future'] != '']
|
1696
|
-
|
1697
|
-
# 多头市场:KDJ值的下限floor;空头市场:KDJ值的上限ceiling
|
1698
|
-
if value_type == 'floor':
|
1699
|
-
df3=df2[(df2['K'] > KDJ_values[0]) &
|
1700
|
-
(df2['D'] > KDJ_values[1]) &
|
1701
|
-
(df2['J'] > KDJ_values[2])]
|
1702
|
-
if J_days > 1:
|
1703
|
-
for jj in range(1,J_days):
|
1704
|
-
#print('jj=',jj)
|
1705
|
-
df3=df3[df3['J_'+str(jj)] > KDJ_values[2]]
|
1706
|
-
elif value_type == 'ceiling':
|
1707
|
-
df3=df2[(df2['K'] < KDJ_values[0]) &
|
1708
|
-
(df2['D'] < KDJ_values[1]) &
|
1709
|
-
(df2['J'] < KDJ_values[2])]
|
1710
|
-
if J_days > 1:
|
1711
|
-
for jj in range(1,J_days):
|
1712
|
-
#print('jj=',jj)
|
1713
|
-
df3=df3[df3['J_'+str(jj)] < KDJ_values[2]]
|
1714
|
-
else:
|
1715
|
-
pass
|
1716
|
-
|
1717
|
-
# J/K线上下位置
|
1718
|
-
if JvsK=='above':
|
1719
|
-
df3=df3[df3['J'] > df3['K']]
|
1720
|
-
elif JvsK=='below':
|
1721
|
-
df3=df3[df3['J'] < df3['K']]
|
1722
|
-
else:
|
1723
|
-
pass
|
1724
|
-
|
1725
|
-
# J/K线交叉
|
1726
|
-
if JKcross in ['bottom up','top down']:
|
1727
|
-
df3cross=findout_cross(df3,start,end,'J','K')
|
1728
|
-
df3cross2=pd.DataFrame(df3cross['交叉类型'])
|
1729
|
-
|
1730
|
-
import pandas as pd
|
1731
|
-
df3=pd.merge(df3,df3cross2,how='inner',left_index=True,right_index=True)
|
1732
|
-
|
1733
|
-
if JKcross == 'bottom up':
|
1734
|
-
df3=df3[df3['交叉类型']=='上穿']
|
1735
|
-
elif JKcross == 'top down':
|
1736
|
-
df3=df3[df3['交叉类型']=='下穿']
|
1737
|
-
else:
|
1738
|
-
pass
|
1739
|
-
|
1740
|
-
if len(df3)==0:
|
1741
|
-
print(" #Warning(check_KDJ_market): no designated signals found for",ticker,"from",start,'to',end)
|
1742
|
-
return None,None
|
1743
|
-
|
1744
|
-
# 未来上涨、下跌、振荡的比例
|
1745
|
-
df4a=df3[df3['trend_future'] == '+']
|
1746
|
-
pcta=len(df4a)/len(df3)
|
1747
|
-
df4b=df3[df3['trend_future'] == '-']
|
1748
|
-
pctb=len(df4b)/len(df3)
|
1749
|
-
df4c=df3[df3['trend_future'] == '~']
|
1750
|
-
pctc=len(df4c)/len(df3)
|
1751
|
-
|
1752
|
-
# 未来转为下跌、上涨的比例
|
1753
|
-
df4d=df3[df3['trend_past2future'].isin(['+2-','~2-'])]
|
1754
|
-
pctd=len(df4d)/len(df3)
|
1755
|
-
df4e=df3[df3['trend_past2future'].isin(['-2+','~2+'])]
|
1756
|
-
pcte=len(df4e)/len(df3)
|
1757
|
-
|
1758
|
-
import pandas as pd
|
1759
|
-
outcome=pd.DataFrame()
|
1760
|
-
|
1761
|
-
row=pd.Series({'ticker':ticker,'start':start,'end':end, \
|
1762
|
-
'K value':KDJ_values[0],'D value':KDJ_values[1],'J value':KDJ_values[2],
|
1763
|
-
'J days':J_days,'JvsK':JvsK,'JKcross':JKcross,
|
1764
|
-
'value type':value_type, \
|
1765
|
-
'past_days':past_days,'momemtum_days':momemtum_days,'future_days':future_days,
|
1766
|
-
'threshhold':threshhold, \
|
1767
|
-
'future up ratio':pcta,'future down ratio':pctb,'future vibrate ratio':pctc, \
|
1768
|
-
't2- ratio':pctd,'t2+ ratio':pcte})
|
1769
|
-
try:
|
1770
|
-
outcome=outcome.append(row,ignore_index=True)
|
1771
|
-
except:
|
1772
|
-
outcome=outcome._append(row,ignore_index=True)
|
1773
|
-
|
1774
|
-
return outcome,df3
|
1775
|
-
|
1776
|
-
#==============================================================================
|
1777
|
-
#==============================================================================
|
1778
|
-
if __name__ =="__main__":
|
1779
|
-
ticker='600518.SS'
|
1780
|
-
start='2013-1-1'
|
1781
|
-
end='2022-12-31'
|
1782
|
-
|
1783
|
-
KDJ_days=[9,3,3]
|
1784
|
-
matypes=[0,0]
|
1785
|
-
|
1786
|
-
past_days=1
|
1787
|
-
momemtum_days=0
|
1788
|
-
future_days=1
|
1789
|
-
threshhold=0.001
|
1790
|
-
|
1791
|
-
KDJ_values=[50,50,50]
|
1792
|
-
J_days=1
|
1793
|
-
JvsK='above'
|
1794
|
-
JKcross='bottom up'
|
1795
|
-
|
1796
|
-
value_type='floor'
|
1797
|
-
|
1798
|
-
# 多头市场与空头市场
|
1799
|
-
dfs1=backtest_KDJ(ticker,start,end,KDJ_values=[50,50,50],value_type='floor')
|
1800
|
-
dfs2=backtest_KDJ(ticker,start,end,KDJ_values=[50,50,50],value_type='ceiling')
|
1801
|
-
|
1802
|
-
# 超买区:茅台买家众多,即使在超买区,下跌趋势也比较有限
|
1803
|
-
dfs3=backtest_KDJ(ticker,start,end,KDJ_values=[80,80,80],value_type='floor')
|
1804
|
-
dfs4=backtest_KDJ(ticker,start,end,KDJ_values=[80,80,90],value_type='floor')
|
1805
|
-
dfs4=backtest_KDJ(ticker,start,end,KDJ_values=[80,80,90],J_days=3,value_type='floor')
|
1806
|
-
|
1807
|
-
# 超卖区
|
1808
|
-
dfs12=backtest_KDJ(ticker,start,end,KDJ_values=[20,20,10],value_type='ceiling')
|
1809
|
-
dfs15=backtest_KDJ(ticker,start,end,KDJ_values=[20,20,0],J_days=3,value_type='ceiling')
|
1810
|
-
|
1811
|
-
# K线在D线上方/下方
|
1812
|
-
dfs21=backtest_KDJ(ticker,start,end,KDJ_values=[50,50,50],JvsK='above',value_type='floor')
|
1813
|
-
dfs22=backtest_KDJ(ticker,start,end,KDJ_values=[50,50,50],JvsK='below',value_type='floor')
|
1814
|
-
|
1815
|
-
# K/D线交叉
|
1816
|
-
dfs31=backtest_KDJ(ticker,start,end,KDJ_values=[50,50,50],JKcross='top down',value_type='floor')
|
1817
|
-
dfs32=backtest_KDJ(ticker,start,end,KDJ_values=[50,50,50],JKcross='bottom up',value_type='floor')
|
1818
|
-
dfs32=backtest_KDJ(ticker,start,end,KDJ_values=[20,20,10],JKcross='bottom up',value_type='ceiling')
|
1819
|
-
|
1820
|
-
# 高位死叉
|
1821
|
-
dfs33=backtest_KDJ(ticker,start,end,KDJ_values=[80,80,90],JKcross='top down',value_type='floor')
|
1822
|
-
|
1823
|
-
# 低位金叉
|
1824
|
-
dfs34=backtest_KDJ(ticker,start,end,KDJ_values=[20,20,10],JKcross='bottom up',value_type='ceiling')
|
1825
|
-
|
1826
|
-
def backtest_KDJ(ticker,start,end,
|
1827
|
-
KDJ_days=[9,3,3],matypes=[0,0],
|
1828
|
-
past_days=3,momemtum_days=0,future_days=3,threshhold=0.005,
|
1829
|
-
KDJ_values=[50,50,50],J_days=1,JvsK='any',JKcross='any',
|
1830
|
-
value_type='floor'):
|
1831
|
-
"""
|
1832
|
-
功能:验证KDJ指标对股价未来走势判断的准确度概率
|
1833
|
-
|
1834
|
-
注意:可能存在尚不明了的问题!!!
|
1835
|
-
"""
|
1836
|
-
# 计算KDJ指标
|
1837
|
-
df=stock_KDJ(ticker,start,end,KDJ_days=KDJ_days,matypes=matypes,graph=False)
|
1838
|
-
total_obs=len(df)
|
1839
|
-
|
1840
|
-
# 计算股价波动率,约等于1,无法作为threshhold使用
|
1841
|
-
#=df['Close'].std() / df['Close'].mean()
|
1842
|
-
|
1843
|
-
# 计算某一日期前后的股价走势及其变化
|
1844
|
-
df1=price_trend_technical(df,
|
1845
|
-
past_days=past_days,
|
1846
|
-
momemtum_days=momemtum_days,future_days=future_days,
|
1847
|
-
threshhold=threshhold)
|
1848
|
-
|
1849
|
-
# KDJ特定情景:股价走势情形概率
|
1850
|
-
checkdf,dfs=check_KDJ_market(df1,start,end,
|
1851
|
-
KDJ_values=KDJ_values,J_days=J_days,
|
1852
|
-
JvsK=JvsK,JKcross=JKcross,
|
1853
|
-
value_type=value_type)
|
1854
|
-
if checkdf is None:
|
1855
|
-
#print(" #Warning(backtest_KDJ): no KDJ signals found for",ticker)
|
1856
|
-
print(" Possible reasons: conditions are too strict, or period is too short")
|
1857
|
-
return None
|
1858
|
-
|
1859
|
-
if value_type == 'floor':
|
1860
|
-
rel=' > '
|
1861
|
-
elif value_type == 'ceiling':
|
1862
|
-
rel=' < '
|
1863
|
-
else:
|
1864
|
-
rel=' ? '
|
1865
|
-
|
1866
|
-
signals1="K"+rel+str(KDJ_values[0])+', D'+rel+str(KDJ_values[1])+', J'+rel+str(KDJ_values[2])
|
1867
|
-
|
1868
|
-
signals2=''
|
1869
|
-
if J_days > 1:
|
1870
|
-
signals2='\n J值条件至少持续'+str(J_days)+'个交易日'
|
1871
|
-
|
1872
|
-
signals3=''
|
1873
|
-
if JvsK=='above':
|
1874
|
-
signals3='\n J线在K线上方'
|
1875
|
-
elif JvsK=='below':
|
1876
|
-
signals3='\n J线在K线下方'
|
1877
|
-
else:
|
1878
|
-
pass
|
1879
|
-
|
1880
|
-
signals4=''
|
1881
|
-
if JKcross=='bottom up':
|
1882
|
-
signals4='\n J/K线出现金叉'
|
1883
|
-
elif JKcross=='top down':
|
1884
|
-
signals4='\n J/K线出现死叉'
|
1885
|
-
else:
|
1886
|
-
pass
|
1887
|
-
|
1888
|
-
signals=signals1 +signals2 +signals3 +signals4
|
1889
|
-
print_KDJ_result(checkdf,dfs,signals,ticker,start,end,total_obs,JKcross,
|
1890
|
-
past_days,momemtum_days,future_days,threshhold)
|
1891
|
-
"""
|
1892
|
-
market_upward=dfs[dfs['trend_future']=='+']
|
1893
|
-
market_downward=dfs[dfs['trend_future']=='-']
|
1894
|
-
market_vibrate=dfs[dfs['trend_future']=='~']
|
1895
|
-
market_upturn=dfs[dfs['trend_past2future'].isin(['~2+','-2+'])]
|
1896
|
-
market_downturn=dfs[dfs['trend_past2future'].isin(['~2-','+2-'])]
|
1897
|
-
"""
|
1898
|
-
return dfs
|
1899
|
-
|
1900
|
-
|
1901
|
-
|
1902
|
-
#==============================================================================
|
1903
|
-
def print_KDJ_result(checkdf,dfs,KDJ_signals,
|
1904
|
-
ticker,start,end,total_obs,JKcross,
|
1905
|
-
past_days,momemtum_days,future_days,threshhold):
|
1906
|
-
"""
|
1907
|
-
功能:打印KDJ验证结果
|
1908
|
-
注意:KDJ_signals需要提前编辑条件字符串输入,例如"K > 80, D > 80, J > 100且至少持续3个交易日"
|
1909
|
-
"""
|
1910
|
-
|
1911
|
-
print("\n*** KDJ研判价格趋势:回溯验证,"+ticker_name(ticker))
|
1912
|
-
print(" "+start+'至'+end+',共计'+str(total_obs)+'个样本')
|
1913
|
-
|
1914
|
-
print(" KDJ信号:期间内出现"+str(len(dfs))+'次')
|
1915
|
-
print(" "+KDJ_signals)
|
1916
|
-
|
1917
|
-
print(" 验证参数:")
|
1918
|
-
print(" 之前趋势期间:"+str(past_days)+'个交易日')
|
1919
|
-
|
1920
|
-
if momemtum_days > 0:
|
1921
|
-
print(" 滞后变化/提前反应:滞后"+str(momemtum_days)+'个交易日')
|
1922
|
-
elif momemtum_days == 0:
|
1923
|
-
print(" 滞后变化/提前反应:无,立即变化")
|
1924
|
-
else:
|
1925
|
-
print(" 滞后变化/提前反应:提前"+str(momemtum_days)+'个交易日')
|
1926
|
-
|
1927
|
-
print(" 之后趋势期间:"+str(future_days)+'个交易日')
|
1928
|
-
print(" 股价变化过滤门限:"+str(threshhold*100)+'%')
|
1929
|
-
|
1930
|
-
print(" 回溯检验结果:")
|
1931
|
-
print(" 之后出现价格上涨趋势的比例:",round(checkdf['future up ratio'].values[0]*100,1),'\b%')
|
1932
|
-
print(" 之后出现价格下跌趋势的比例:",round(checkdf['future down ratio'].values[0]*100,1),'\b%')
|
1933
|
-
print(" 之后出现价格振荡情形的比例:",round(checkdf['future vibrate ratio'].values[0]*100,1),'\b%')
|
1934
|
-
|
1935
|
-
"""
|
1936
|
-
if JKcross =='bottom up':
|
1937
|
-
print(" 出现JD金叉后价格上涨的比例:",round(checkdf['t2+ ratio'].values[0]*100,1),'\b%')
|
1938
|
-
elif JKcross =='top down':
|
1939
|
-
print(" 出现JK死叉后价格下跌的比例:",round(checkdf['t2- ratio'].values[0]*100,1),'\b%')
|
1940
|
-
else:
|
1941
|
-
pass
|
1942
|
-
"""
|
1943
|
-
|
1944
|
-
return
|
1945
|
-
#==============================================================================
|
1946
|
-
#==============================================================================
|
1947
|
-
if __name__ =="__main__":
|
1948
|
-
ticker='600519.SS'
|
1949
|
-
fromdate='2023-1-1'
|
1950
|
-
todate='2023-6-25'
|
1951
|
-
boll_days=20
|
1952
|
-
graph=True
|
1953
|
-
smooth=False
|
1954
|
-
loc='best'
|
1955
|
-
date_range=False
|
1956
|
-
date_freq=False
|
1957
|
-
annotate=False
|
1958
|
-
|
1959
|
-
def security_Bollinger(ticker,start='default',end='default',boll_days=20, \
|
1960
|
-
graph=True,smooth=True,loc='best', \
|
1961
|
-
date_range=False,date_freq=False,annotate=False, \
|
1962
|
-
ticker_type='auto',source='auto'):
|
1963
|
-
"""
|
1964
|
-
套壳函数,为了与security_MACD/RSI/KDJ保持相似
|
1965
|
-
"""
|
1966
|
-
df=stock_Bollinger(ticker=ticker,start=start,end=end,boll_days=boll_days, \
|
1967
|
-
graph=graph,smooth=smooth,loc=loc, \
|
1968
|
-
date_range=date_range,date_freq=date_freq, \
|
1969
|
-
annotate=annotate,ticker_type=ticker_type,source=source)
|
1970
|
-
return df
|
1971
|
-
|
1972
|
-
def stock_Bollinger(ticker,start='default',end='default',boll_days=20, \
|
1973
|
-
graph=True,smooth=True,loc='best', \
|
1974
|
-
date_range=False,date_freq=False,annotate=False, \
|
1975
|
-
mark_end=True,ticker_type='auto',source='auto'):
|
1976
|
-
"""
|
1977
|
-
套壳函数,为了与stock_MACD/RSI/KDJ保持相似
|
1978
|
-
"""
|
1979
|
-
|
1980
|
-
#=========== 日期转换与检查
|
1981
|
-
# 检查日期:截至日期
|
1982
|
-
import datetime as dt; today=dt.date.today()
|
1983
|
-
if end in ['default','today']:
|
1984
|
-
end=today
|
1985
|
-
else:
|
1986
|
-
validdate,end=check_date2(end)
|
1987
|
-
if not validdate:
|
1988
|
-
print(" #Warning(stock_Bollinger): invalid date for",end)
|
1989
|
-
end=today
|
1990
|
-
|
1991
|
-
# 检查日期:开始日期
|
1992
|
-
if start in ['default']:
|
1993
|
-
start=date_adjust(end,adjust=-31)
|
1994
|
-
else:
|
1995
|
-
validdate,start=check_date2(start)
|
1996
|
-
if not validdate:
|
1997
|
-
print(" #Warning(stock_Bollinger): invalid date for",start)
|
1998
|
-
start=date_adjust(todate,adjust=-31)
|
1999
|
-
|
2000
|
-
df=security_bollinger(ticker=ticker,fromdate=start,todate=end,boll_days=boll_days, \
|
2001
|
-
graph=graph,smooth=smooth,loc=loc, \
|
2002
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate, \
|
2003
|
-
mark_end=mark_end,ticker_type=ticker_type,source=source)
|
2004
|
-
return df
|
2005
|
-
|
2006
|
-
|
2007
|
-
def security_bollinger(ticker,fromdate,todate,boll_days=20, \
|
2008
|
-
graph=True,smooth=True,loc='best', \
|
2009
|
-
date_range=False,date_freq=False,annotate=False, \
|
2010
|
-
mark_end=True,ticker_type='auto',source='auto'):
|
2011
|
-
"""
|
2012
|
-
功能:单个证券,绘制布林带
|
2013
|
-
date_range=False:指定开始结束日期绘图
|
2014
|
-
date_freq=False:指定横轴日期间隔,例如'D'、'2D'、'W'、'M'等,横轴一般不超过25个标注,否则会重叠
|
2015
|
-
|
2016
|
-
解释布林带:
|
2017
|
-
1、上线:压力线;下线:支撑线;均线:中界线
|
2018
|
-
2、上下限之间:置信区间+/-2std
|
2019
|
-
3、价格由下向上穿越下线时,可视为买进信号;价格由下向上穿越中间线时,则有可能加速上行,是加仓买进的信号。
|
2020
|
-
4、价格在中界线与上线之间波动运行时为多头市场,可持多或加码;价格长时间在中界线与上线间运行后,由上往下跌破中间线为卖出信号。
|
2021
|
-
5、价格在中界线与下线之间向下波动运行时为空头市场,可持空或加抛。
|
2022
|
-
"""
|
2023
|
-
# 提前开始日期
|
2024
|
-
fromdate1=date_adjust(fromdate,adjust=-365*1)
|
2025
|
-
|
2026
|
-
try:
|
2027
|
-
#pricedf=get_price(ticker,fromdate1,todate)
|
2028
|
-
pricedf,found=get_price_1ticker_mixed(ticker=ticker,fromdate=fromdate1, \
|
2029
|
-
todate=todate,ticker_type=ticker_type,source=source)
|
2030
|
-
except:
|
2031
|
-
print(" #Error(security_bollinger): price info not found for",ticker)
|
2032
|
-
return None
|
2033
|
-
if found not in ['Found']:
|
2034
|
-
print(" #Error(security_bollinger): ticker info either inaccessible or not found for",ticker)
|
2035
|
-
return None
|
2036
|
-
|
2037
|
-
# 滚动均值与标准差
|
2038
|
-
pricedf['bmiddle']=pricedf["Close"].rolling(window=boll_days).mean()
|
2039
|
-
pricedf['bsd']=pricedf["Close"].rolling(window=boll_days).std()
|
2040
|
-
pricedf['bupper']=pricedf["bmiddle"] + pricedf['bsd']*2
|
2041
|
-
pricedf['blower']=pricedf["bmiddle"] - pricedf['bsd']*2
|
2042
|
-
|
2043
|
-
df=pricedf[['bupper','bmiddle','blower','Close']]
|
2044
|
-
df.rename(columns={'bupper':'上(压力)线','bmiddle':'中(界)线','blower':'下(支撑)线','Close':'收盘价'},inplace=True)
|
2045
|
-
|
2046
|
-
# 截取时间段
|
2047
|
-
result,start,end=check_period(fromdate,todate)
|
2048
|
-
df1=df[(df.index >= start) & (df.index <= end)]
|
2049
|
-
|
2050
|
-
y_label='价格'
|
2051
|
-
import datetime; today = datetime.date.today()
|
2052
|
-
x_label="数据来源:综合新浪/东方财富/stooq/雅虎财经,"+str(today)
|
2053
|
-
|
2054
|
-
axhline_value=0
|
2055
|
-
axhline_label=''
|
2056
|
-
title_txt="证券价格趋势分析:"+ticker_name(ticker,ticker_type)+",布林带"
|
2057
|
-
|
2058
|
-
"""
|
2059
|
-
draw_lines(df1,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
2060
|
-
data_label=False,resample_freq='H',smooth=smooth,loc=loc,annotate=annotate)
|
2061
|
-
"""
|
2062
|
-
#plt.rcParams['axes.facecolor']='gray'
|
2063
|
-
colorlist=['orange','black','purple','red']
|
2064
|
-
lslist=['--',':','-.','-']
|
2065
|
-
lwlist=[2.0,2.0,2.0,2.5]
|
2066
|
-
draw_lines2(df1,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
2067
|
-
data_label=False,resample_freq='6H',smooth=smooth, \
|
2068
|
-
date_range=date_range,date_freq=date_freq,date_fmt='%Y-%m-%d', \
|
2069
|
-
colorlist=colorlist,lslist=lslist,lwlist=lwlist, \
|
2070
|
-
band_area=['上(压力)线','下(支撑)线'],mark_end=mark_end,loc=loc)
|
2071
|
-
|
2072
|
-
return df1
|
2073
|
-
|
2074
|
-
#==============================================================================
|
2075
|
-
#==============================================================================
|
2076
|
-
if __name__ =="__main__":
|
2077
|
-
ticker='600809.SS'
|
2078
|
-
start='2013-11-06'
|
2079
|
-
end='2024-02-29'
|
2080
|
-
boll_years=7
|
2081
|
-
graph=True
|
2082
|
-
smooth=False
|
2083
|
-
loc='best'
|
2084
|
-
date_range=False
|
2085
|
-
date_freq=False
|
2086
|
-
annotate=False
|
2087
|
-
|
2088
|
-
indicator='PE'
|
2089
|
-
|
2090
|
-
def security_Bubble(ticker,start='default',end='default',boll_years=7, \
|
2091
|
-
indicator='MV', \
|
2092
|
-
graph=True,smooth=True,loc='best', \
|
2093
|
-
date_range=False,date_freq=False,annotate=False, \
|
2094
|
-
mark_end=True):
|
2095
|
-
"""
|
2096
|
-
套壳函数,为了与security_MACD/RSI/KDJ保持相似
|
2097
|
-
"""
|
2098
|
-
|
2099
|
-
#=========== 日期转换与检查
|
2100
|
-
# 检查日期:截至日期
|
2101
|
-
import datetime as dt; todaydt=dt.date.today()
|
2102
|
-
if end in ['default','today']:
|
2103
|
-
end=todaydt
|
2104
|
-
else:
|
2105
|
-
validdate,end=check_date2(end)
|
2106
|
-
if not validdate:
|
2107
|
-
print(" #Warning(security_Bubble): invalid date for",end)
|
2108
|
-
end=todaydt
|
2109
|
-
|
2110
|
-
# 检查日期:开始日期
|
2111
|
-
if start in ['default']:
|
2112
|
-
start=date_adjust(end,adjust=-31)
|
2113
|
-
else:
|
2114
|
-
validdate,start=check_date2(start)
|
2115
|
-
if not validdate:
|
2116
|
-
print(" #Warning(security_Bubble): invalid date for",start)
|
2117
|
-
start=date_adjust(todate,adjust=-31)
|
2118
|
-
|
2119
|
-
df=security_bubble(ticker=ticker,fromdate=start,todate=end,boll_years=boll_years, \
|
2120
|
-
indicator=indicator, \
|
2121
|
-
graph=graph,smooth=smooth,loc=loc, \
|
2122
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate, \
|
2123
|
-
mark_end=mark_end)
|
2124
|
-
return df
|
2125
|
-
|
2126
|
-
if __name__ =="__main__":
|
2127
|
-
ticker='600519.SS'
|
2128
|
-
ticker='AAPL'
|
2129
|
-
fromdate='2023-7-3'
|
2130
|
-
todate='2024-2-21'
|
2131
|
-
boll_years=7
|
2132
|
-
indicator='MV'
|
2133
|
-
|
2134
|
-
def security_bubble(ticker,fromdate,todate,boll_years=7, \
|
2135
|
-
indicator='MV', \
|
2136
|
-
graph=True,smooth=True,loc='best', \
|
2137
|
-
date_range=False,date_freq=False,annotate=False, \
|
2138
|
-
mark_end=True):
|
2139
|
-
"""
|
2140
|
-
功能:单个证券,绘制估值布林带(MV, PE, PB)
|
2141
|
-
date_range=False:指定开始结束日期绘图
|
2142
|
-
date_freq=False:指定横轴日期间隔,例如'D'、'2D'、'W'、'M'等,横轴一般不超过25个标注,否则会重叠
|
2143
|
-
|
2144
|
-
解释布林带:
|
2145
|
-
1、上线:压力线;下线:支撑线;均线:中界线
|
2146
|
-
2、上下限之间:置信区间+/-2std,2倍标准差的含义是落在此区间内的概率不低于95%!
|
2147
|
-
3、估值由下向上穿越下线时,可视为买进信号;估值由下向上穿越中间线时,则有可能加速上行,是加仓买进的信号。
|
2148
|
-
4、估值在中界线与上线之间波动运行时为多头市场,可持多或加码;估值长时间在中界线与上线间运行后,由上往下跌破中间线为卖出信号。
|
2149
|
-
5、估值在中界线与下线之间向下波动运行时为空头市场,可持空或加抛。
|
2150
|
-
"""
|
2151
|
-
if not isinstance(ticker,str):
|
2152
|
-
print(" #Warning(security_bubble): not a valid stock code format for",ticker)
|
2153
|
-
return None
|
2154
|
-
|
2155
|
-
if indicator.lower()=='mv':
|
2156
|
-
indicatorname='市值(十亿)'
|
2157
|
-
elif indicator.lower()=='pe':
|
2158
|
-
indicatorname='市盈率'
|
2159
|
-
elif indicator.lower()=='pb':
|
2160
|
-
indicatorname='市净率'
|
2161
|
-
elif indicator.lower()=='roe':
|
2162
|
-
indicatorname='净资产回报率'
|
2163
|
-
else:
|
2164
|
-
print(" #Error(security_bubble): unsupported valuation indicator for",indicator)
|
2165
|
-
return None
|
2166
|
-
|
2167
|
-
# 提前开始日期
|
2168
|
-
fromdate1=date_adjust(fromdate,adjust=-365*boll_years)
|
2169
|
-
|
2170
|
-
try:
|
2171
|
-
val=get_valuation(ticker,indicator,fromdate1,todate)
|
2172
|
-
colname=list(val[indicator])[0]
|
2173
|
-
pricedf=val[indicator]
|
2174
|
-
pricedf.rename(columns={colname:indicator},inplace=True)
|
2175
|
-
except:
|
2176
|
-
print(" #Error(security_bubble): valuation info not found for",ticker,'on',indicator)
|
2177
|
-
return None
|
2178
|
-
|
2179
|
-
# 滚动均值与标准差
|
2180
|
-
#注意:A股估值数据为非日频,港股/美股/波兰股为日频
|
2181
|
-
day0=pricedf.index[0].strftime("%Y-%m-%d")
|
2182
|
-
day1=pricedf.index[-1].strftime("%Y-%m-%d")
|
2183
|
-
day_diff=calculate_days(day0,day1)
|
2184
|
-
annualTradeDays=len(pricedf) / (day_diff/365)
|
2185
|
-
#boll_days=int(boll_years*annualTradeDays+0.5)
|
2186
|
-
boll_days=int(boll_years*annualTradeDays)
|
2187
|
-
"""
|
2188
|
-
_,_,suffix=split_prefix_suffix(ticker)
|
2189
|
-
if suffix in ['SS','SZ','BJ']:
|
2190
|
-
boll_days=int(boll_years*annualTradeDays)
|
2191
|
-
else:
|
2192
|
-
boll_days=boll_years*252
|
2193
|
-
"""
|
2194
|
-
if boll_days >= len(pricedf):
|
2195
|
-
boll_years=int(len(pricedf)/annualTradeDays)
|
2196
|
-
boll_days=int(boll_years*annualTradeDays)
|
2197
|
-
|
2198
|
-
record_left=len(pricedf)-boll_days
|
2199
|
-
|
2200
|
-
if record_left < 65:#一个季度
|
2201
|
-
boll_years=boll_years-1
|
2202
|
-
#boll_days=boll_years*252
|
2203
|
-
boll_days=int(boll_years*annualTradeDays)
|
2204
|
-
record_left=len(pricedf)-boll_days
|
2205
|
-
|
2206
|
-
pricedf_left=pricedf.tail(record_left)
|
2207
|
-
start_left=pricedf_left.index[0].strftime('%Y-%m-%d')
|
2208
|
-
|
2209
|
-
pricedf['bmiddle']=pricedf[indicator].rolling(window=boll_days).mean()
|
2210
|
-
pricedf['bsd']=pricedf[indicator].rolling(window=boll_days).std()
|
2211
|
-
pricedf['bupper']=pricedf["bmiddle"] + pricedf['bsd']*2
|
2212
|
-
pricedf['blower']=pricedf["bmiddle"] - pricedf['bsd']*2
|
2213
|
-
|
2214
|
-
df=pricedf[['bupper','bmiddle','blower',indicator]]
|
2215
|
-
|
2216
|
-
df.rename(columns={'bupper':'上(压力)线','bmiddle':'中(界)线','blower':'下(支撑)线',indicator:indicatorname},inplace=True)
|
2217
|
-
|
2218
|
-
# 截取时间段
|
2219
|
-
result,start,end=check_period(fromdate,todate)
|
2220
|
-
start1=date_adjust(start,adjust=-10)
|
2221
|
-
if start_left > start1:
|
2222
|
-
start1=start_left
|
2223
|
-
|
2224
|
-
df1=df[(df.index >= start1) & (df.index <= end)]
|
2225
|
-
|
2226
|
-
y_label=indicatorname
|
2227
|
-
import datetime; today = datetime.date.today()
|
2228
|
-
x_label="数据来源:综合新浪/东方财富/stooq/雅虎财经,"+str(today)+",估值前置窗口期"+str(boll_years)+"年"
|
2229
|
-
|
2230
|
-
axhline_value=0
|
2231
|
-
axhline_label=''
|
2232
|
-
title_txt="证券估值趋势布林带:"+ticker_name(ticker)
|
2233
|
-
|
2234
|
-
"""
|
2235
|
-
draw_lines(df1,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
2236
|
-
data_label=False,resample_freq='H',smooth=smooth,loc=loc,annotate=annotate)
|
2237
|
-
"""
|
2238
|
-
#plt.rcParams['axes.facecolor']='gray'
|
2239
|
-
colorlist=['orange','black','purple','red']
|
2240
|
-
lslist=['--',':','-.','-']
|
2241
|
-
lwlist=[2.0,2.0,2.0,2.5]
|
2242
|
-
draw_lines2(df1,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
2243
|
-
data_label=False,resample_freq='6H',smooth=smooth, \
|
2244
|
-
date_range=date_range,date_freq=date_freq,date_fmt='%Y-%m-%d', \
|
2245
|
-
colorlist=colorlist,lslist=lslist,lwlist=lwlist, \
|
2246
|
-
band_area=['上(压力)线','下(支撑)线'],loc=loc, \
|
2247
|
-
mark_end=mark_end)
|
2248
|
-
|
2249
|
-
return df1
|
2250
|
-
|
2251
|
-
#==============================================================================
|
2252
|
-
if __name__ =="__main__":
|
2253
|
-
ticker='600519.SS'
|
2254
|
-
ticker='600809.SS'
|
2255
|
-
start='MRH'
|
2256
|
-
start='L10Y'
|
2257
|
-
end='default'
|
2258
|
-
|
2259
|
-
start='2023-1-1'
|
2260
|
-
end='2024-2-20'
|
2261
|
-
|
2262
|
-
boll_years=7
|
2263
|
-
technical='MV'
|
2264
|
-
technical='Bollinger'
|
2265
|
-
indicator='PE'
|
2266
|
-
|
2267
|
-
def security_technical(ticker,start='default',end='default', \
|
2268
|
-
MA_days=[5,20],EMA_days=[5,20], \
|
2269
|
-
MACD_fastperiod=12,MACD_slowperiod=26,MACD_signalperiod=9, \
|
2270
|
-
RSI_days=[6,12,24],RSI_lines=[20,50,80], \
|
2271
|
-
KDJ_days=[9,3,3],matypes=[0,0],KDJ_lines=[20,50,80], \
|
2272
|
-
boll_days=20,boll_years=7, \
|
2273
|
-
resample_freq='6H',smooth=True,linewidth=1.5, \
|
2274
|
-
loc1='best',loc2='best', \
|
2275
|
-
graph=['ALL'],printout=False, \
|
2276
|
-
date_range=False,date_freq=False,annotate=False, \
|
2277
|
-
technical=['MACD'],indicator='Close', \
|
2278
|
-
ticker_type='auto',source='auto'):
|
2279
|
-
|
2280
|
-
"""
|
2281
|
-
功能:技术分析中的MACD/RSI/KDJ/布林带,支持教学演示,支持参数调节。
|
2282
|
-
支持的产品:全球股票,债券(限中国内地的上市债券),基金(支持中国和美国的上市基金)。
|
2283
|
-
ticker:证券代码
|
2284
|
-
start/end:起止日期。支持简洁方式,仅需使用start指定近期的期间长度。
|
2285
|
-
简洁方式:mrw(近1周),l2w(近2周),l3w(近3周),mrm(近1个月),l2m(近2个月),
|
2286
|
-
mrq(近3个月),mrh(近6个月),mry(近1年),l2y(近2年),l3y(近3年),
|
2287
|
-
l5y(近5年),l8y(近8年),l10y(近10年),l20y(近20年),l30y(近30年),ytd(今年以来)
|
2288
|
-
|
2289
|
-
其他说明:套壳函数security_MACD/RSI/KDJ/Bollinger
|
2290
|
-
"""
|
2291
|
-
|
2292
|
-
# 检查日期:如有错误自动更正
|
2293
|
-
fromdate,todate=start_end_preprocess(start=start,end=end)
|
2294
|
-
|
2295
|
-
# 检查类别
|
2296
|
-
if isinstance(technical,str):
|
2297
|
-
technical1=[technical]
|
2298
|
-
else:
|
2299
|
-
technical1=technical
|
2300
|
-
|
2301
|
-
technical_list=['MACD','RSI','KDJ','Bollinger']
|
2302
|
-
technical1 = [x.upper() for x in technical1]
|
2303
|
-
technical1 = [x.replace('BOLLINGER','Bollinger') if 'BOLLINGER' in x else x for x in technical1]
|
2304
|
-
|
2305
|
-
for t in technical1:
|
2306
|
-
if t not in technical_list:
|
2307
|
-
print(" Warning(security_technical): unsupported technical pattern",t)
|
2308
|
-
print(" Supported patterns:",technical_list)
|
2309
|
-
|
2310
|
-
#检查布林带的指标
|
2311
|
-
if isinstance(indicator,str):
|
2312
|
-
indicator1=[indicator]
|
2313
|
-
else:
|
2314
|
-
indicator1=indicator
|
2315
|
-
|
2316
|
-
indicator_list1=['MV','PE','PB','Close','ROE']
|
2317
|
-
indicator1 = [x.upper() for x in indicator1]
|
2318
|
-
indicator1 = [x.replace('CLOSE','Close') if 'CLOSE' in x else x for x in indicator1]
|
2319
|
-
|
2320
|
-
for t in indicator1:
|
2321
|
-
if t not in indicator_list1:
|
2322
|
-
print(" Warning(security_technical): unsupported Bollinger indicator",t)
|
2323
|
-
print(" Supported Bollinger indicator:",indicator_list1)
|
2324
|
-
|
2325
|
-
# 检查绘图种类
|
2326
|
-
if isinstance(graph,str):
|
2327
|
-
graph1=[graph]
|
2328
|
-
else:
|
2329
|
-
graph1=graph
|
2330
|
-
|
2331
|
-
if 'MACD' in technical1:
|
2332
|
-
df=security_MACD(ticker=ticker,start=fromdate,end=todate, \
|
2333
|
-
MA_days=MA_days,EMA_days=EMA_days, \
|
2334
|
-
MACD_fastperiod=MACD_fastperiod,MACD_slowperiod=MACD_slowperiod,MACD_signalperiod=MACD_signalperiod, \
|
2335
|
-
resample_freq=resample_freq,smooth=smooth,linewidth=linewidth, \
|
2336
|
-
loc1=loc1,loc2=loc2, \
|
2337
|
-
graph=graph1,printout=printout,ticker_type=ticker_type,source=source)
|
2338
|
-
|
2339
|
-
if 'RSI' in technical1:
|
2340
|
-
df=security_RSI(ticker=ticker,start=fromdate,end=todate, \
|
2341
|
-
RSI_days=RSI_days,RSI_lines=RSI_lines, \
|
2342
|
-
resample_freq=resample_freq,smooth=smooth,linewidth=linewidth, \
|
2343
|
-
loc1=loc1,loc2=loc2, \
|
2344
|
-
graph=graph1,printout=printout,ticker_type=ticker_type,source=source)
|
2345
|
-
|
2346
|
-
if 'KDJ' in technical1:
|
2347
|
-
df=security_KDJ(ticker=ticker,start=fromdate,end=todate, \
|
2348
|
-
KDJ_days=KDJ_days,matypes=matypes,KDJ_lines=KDJ_lines, \
|
2349
|
-
resample_freq=resample_freq,smooth=smooth,linewidth=linewidth, \
|
2350
|
-
loc1=loc1,loc2=loc2, \
|
2351
|
-
graph=graph1,printout=printout,ticker_type=ticker_type,source=source)
|
2352
|
-
|
2353
|
-
if 'Bollinger' in technical1 and 'Close' in indicator1:
|
2354
|
-
df=security_Bollinger(ticker=ticker,start=fromdate,end=todate,boll_days=boll_days, \
|
2355
|
-
graph=True,smooth=smooth,loc=loc1, \
|
2356
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate, \
|
2357
|
-
ticker_type=ticker_type,source=source)
|
2358
|
-
|
2359
|
-
"""
|
2360
|
-
if 'Bollinger' in technical1 and 'MV' in indicator1:
|
2361
|
-
df=security_Bubble(ticker=ticker,start=fromdate,end=todate,boll_years=boll_years, \
|
2362
|
-
indicator='MV', \
|
2363
|
-
graph=True,smooth=smooth,loc=loc1, \
|
2364
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate)
|
2365
|
-
|
2366
|
-
if 'Bollinger' in technical1 and 'PE' in indicator1:
|
2367
|
-
df=security_Bubble(ticker=ticker,start=fromdate,end=todate,boll_years=boll_years, \
|
2368
|
-
indicator='PE', \
|
2369
|
-
graph=True,smooth=smooth,loc=loc1, \
|
2370
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate)
|
2371
|
-
|
2372
|
-
if 'Bollinger' in technical1 and 'PB' in indicator1:
|
2373
|
-
df=security_Bubble(ticker=ticker,start=fromdate,end=todate,boll_years=boll_years, \
|
2374
|
-
indicator='PB', \
|
2375
|
-
graph=True,smooth=smooth,loc=loc1, \
|
2376
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate)
|
2377
|
-
|
2378
|
-
if 'Bollinger' in technical1 and 'ROE' in indicator1:
|
2379
|
-
df=security_Bubble(ticker=ticker,start=fromdate,end=todate,boll_years=boll_years, \
|
2380
|
-
indicator='ROE', \
|
2381
|
-
graph=True,smooth=smooth,loc=loc1, \
|
2382
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate)
|
2383
|
-
"""
|
2384
|
-
|
2385
|
-
if 'Bollinger' in technical1:
|
2386
|
-
vallist=['MV','PE','PB','ROE']
|
2387
|
-
#if val in vallist and val in indicator1: #只能处理股票估值,无需ticker_type
|
2388
|
-
if any(val in indicator1 for val in vallist):
|
2389
|
-
val=list(set(indicator1).intersection(set(vallist)))[0] #找出2个列表中第1个共同元素
|
2390
|
-
df=security_Bubble(ticker=ticker,start=fromdate,end=todate,boll_years=boll_years, \
|
2391
|
-
indicator=val, \
|
2392
|
-
graph=True,smooth=smooth,loc=loc1, \
|
2393
|
-
date_range=date_range,date_freq=date_freq,annotate=annotate)
|
2394
|
-
|
2395
|
-
return df
|
2396
|
-
|
2397
|
-
#==============================================================================
|
2398
|
-
if __name__ =="__main__":
|
2399
|
-
RSI_days=[6,24]
|
2400
|
-
|
2401
|
-
OBV_days=5
|
2402
|
-
|
2403
|
-
MA_days=[5,20]; MACD_fastperiod=12; MACD_slowperiod=26; MACD_signalperiod=9
|
2404
|
-
|
2405
|
-
KDJ_fastk_period=5; KDJ_slowk_period=3; KDJ_slowk_matype=0; KDJ_slowd_period=3
|
2406
|
-
KDJ_slowd_matype=0
|
2407
|
-
|
2408
|
-
VOL_fastperiod=5; VOL_slowperiod=10
|
2409
|
-
|
2410
|
-
PSY_days=12
|
2411
|
-
|
2412
|
-
ARBR_days=26
|
2413
|
-
|
2414
|
-
CR_day=16; CR_madays=[5,10,20]
|
2415
|
-
|
2416
|
-
EMV_day=14; EMV_madays=9
|
2417
|
-
|
2418
|
-
BULL_days=20; BULL_nbdevup=2; BULL_nbdevdn=2; BULL_matype=0
|
2419
|
-
|
2420
|
-
TRIX_day=12; TRIX_madays=20
|
2421
|
-
|
2422
|
-
DMA_fastperiod=10; DMA_slowperiod=50; DMA_madays=10
|
2423
|
-
|
2424
|
-
BIAS_days=[6,12,24]
|
2425
|
-
|
2426
|
-
CCI_days=14
|
2427
|
-
|
2428
|
-
WR_days=[10,6]
|
2429
|
-
|
2430
|
-
ROC_day=12; ROC_madays=6
|
2431
|
-
|
2432
|
-
DMI_DIdays=14; DMI_ADXdays=6
|
2433
|
-
|
2434
|
-
ticker='AAPL';ticker_type='auto'
|
2435
|
-
|
2436
|
-
start='2024-3-1'; end='2024-4-12'; ahead_days=30*3
|
2437
|
-
|
2438
|
-
technical='EMV'; indicator='Close'; line='default'
|
2439
|
-
|
2440
|
-
df=security_technical_research(ticker,start,end,technical=technical, \
|
2441
|
-
loc1='lower left',loc2='lower right')
|
2442
|
-
|
2443
|
-
tlist=['RSI','OBV','MACD','KDJ','VOL','PSY','ARBR','CR','EMV','Bollinger', \
|
2444
|
-
'TRIX','DMA','BIAS','CCI','W%R','ROC','DMI']
|
2445
|
-
for t in tlist:
|
2446
|
-
df=security_technical2(ticker,start,end,technical=t,loc1='lower left',loc2='lower right')
|
2447
|
-
|
2448
|
-
def security_technical2(ticker,start='default',end='default', \
|
2449
|
-
RSI_days=[6,24], \
|
2450
|
-
OBV_days=5, \
|
2451
|
-
|
2452
|
-
MA_days=[5,20], \
|
2453
|
-
MACD_fastperiod=12,MACD_slowperiod=26,MACD_signalperiod=9, \
|
2454
|
-
|
2455
|
-
KDJ_fastk_period=9,KDJ_slowk_period=5,KDJ_slowk_matype=1, \
|
2456
|
-
KDJ_slowd_period=5,KDJ_slowd_matype=1, \
|
2457
|
-
|
2458
|
-
VOL_fastperiod=5,VOL_slowperiod=10, \
|
2459
|
-
PSY_days=12, \
|
2460
|
-
ARBR_days=26, \
|
2461
|
-
CR_day=16,CR_madays=[5,20], \
|
2462
|
-
EMV_day=14,EMV_madays=9, \
|
2463
|
-
|
2464
|
-
BULL_days=20,BULL_nbdevup=2,BULL_nbdevdn=2,BULL_matype=0, \
|
2465
|
-
|
2466
|
-
DMA_fastperiod=10,DMA_slowperiod=50,DMA_madays=10, \
|
2467
|
-
|
2468
|
-
TRIX_day=12,TRIX_madays=20, \
|
2469
|
-
BIAS_days=[6,24], \
|
2470
|
-
CCI_days=14, \
|
2471
|
-
WR_days=[10,6], \
|
2472
|
-
ROC_day=12,ROC_madays=6, \
|
2473
|
-
DMI_DIdays=14,DMI_ADXdays=6, \
|
2474
|
-
|
2475
|
-
ahead_days=30*4, \
|
2476
|
-
resample_freq='6H',smooth=True,linewidth=1.5, \
|
2477
|
-
date_range=False,date_freq=False,annotate=False, \
|
2478
|
-
|
2479
|
-
technical=['MACD'],indicator='Close', \
|
2480
|
-
graph=['ALL'],printout=False, \
|
2481
|
-
loc1='best',loc2='best', \
|
2482
|
-
ticker_type='auto',source='auto', \
|
2483
|
-
|
2484
|
-
attention_values=[0,30,50,80], \
|
2485
|
-
facecolor='papayawhip',price_line_color='red', \
|
2486
|
-
|
2487
|
-
more_details=False):
|
2488
|
-
"""
|
2489
|
-
功能:计算和绘制证券技术分析指标的简易图,仅供进一步探索使用,仅用于单个证券(股债基)
|
2490
|
-
|
2491
|
-
支持的探索指标:仅供探索使用
|
2492
|
-
OBV、SAR、VOL、PSY、ARBR、CR、EMV、TRIX、DMA、BIAS、CCI、W%R、ROC、DMI
|
2493
|
-
支持的其他指标:不如单独的指令功能强
|
2494
|
-
MACD、RSI、KDJ、BOLL
|
2495
|
-
|
2496
|
-
关注的阈值:默认[0,30,50,80],attention_values=[0,30,50,80], 可以自定义,但0线一般建议保留。
|
2497
|
-
收盘价折线:默认红色虚线,price_line_color='red'
|
2498
|
-
"""
|
2499
|
-
#检查证券代码
|
2500
|
-
if not isinstance(ticker,str):
|
2501
|
-
print(" #Warning(security_technical2): not a security code for",ticker)
|
2502
|
-
return None
|
2503
|
-
|
2504
|
-
#检查indicator
|
2505
|
-
if indicator not in ['Open','Close','High','Low']:
|
2506
|
-
print(" #Warning(security_technical2): not a valid price type for",indicator)
|
2507
|
-
return None
|
2508
|
-
|
2509
|
-
#检查日期:如有错误自动更正
|
2510
|
-
fromdate,todate=start_end_preprocess(start=start,end=end)
|
2511
|
-
|
2512
|
-
#检查指标类别
|
2513
|
-
tech_list={'Bollinger':'布林带','MACD':'指数平滑异同平均', \
|
2514
|
-
'RSI':'相对强弱','KDJ':'随机指标','OBV':'能量潮', \
|
2515
|
-
'SAR':'抛物转向','VOL':'成交量','ARBR':'人气与意愿', \
|
2516
|
-
'CR':'能力线','EMV':'简易波动','TRIX':'三重指数平滑', \
|
2517
|
-
'DMA':'均线差','BIAS':'乖离率','CCI':'顺势线', \
|
2518
|
-
'W%R':'威廉比率','ROC':'变动速率','DMI':'趋向线','PSY':'心理线'}
|
2519
|
-
|
2520
|
-
technical1=technical
|
2521
|
-
if isinstance(technical,list):
|
2522
|
-
technical1=technical[0]
|
2523
|
-
technical1=technical1.upper()
|
2524
|
-
if technical1 == 'BOLLINGER': technical1=technical1.title()
|
2525
|
-
|
2526
|
-
if technical1 not in list(tech_list):
|
2527
|
-
print(" #Warning(security_technical2): unsupported technical pattern",technical)
|
2528
|
-
print(" Supported patterns:",list(tech_list))
|
2529
|
-
return None
|
2530
|
-
|
2531
|
-
#抓取抓取价格数据
|
2532
|
-
fromdate1=date_adjust(fromdate,adjust=-ahead_days)
|
2533
|
-
price,found=get_price_1ticker_mixed(ticker=ticker,fromdate=fromdate1, \
|
2534
|
-
todate=todate,ticker_type=ticker_type,fill=False,source=source)
|
2535
|
-
|
2536
|
-
if found not in ['Found']:
|
2537
|
-
print(" #Warning(security_technical2): no prices found for",ticker,'as type',ticker_type)
|
2538
|
-
return None
|
2539
|
-
|
2540
|
-
|
2541
|
-
#计算技术指标
|
2542
|
-
df=calc_technical(price,fromdate,todate, \
|
2543
|
-
RSI_days=RSI_days, \
|
2544
|
-
OBV_days=OBV_days, \
|
2545
|
-
|
2546
|
-
MA_days=MA_days, \
|
2547
|
-
MACD_fastperiod=MACD_fastperiod, \
|
2548
|
-
MACD_slowperiod=MACD_slowperiod, \
|
2549
|
-
MACD_signalperiod=MACD_signalperiod, \
|
2550
|
-
|
2551
|
-
KDJ_fastk_period=KDJ_fastk_period, \
|
2552
|
-
KDJ_slowk_period=KDJ_slowk_period, \
|
2553
|
-
KDJ_slowk_matype=KDJ_slowk_matype, \
|
2554
|
-
KDJ_slowd_period=KDJ_slowd_period, \
|
2555
|
-
KDJ_slowd_matype=KDJ_slowd_matype, \
|
2556
|
-
|
2557
|
-
VOL_fastperiod=VOL_fastperiod, \
|
2558
|
-
VOL_slowperiod=VOL_slowperiod, \
|
2559
|
-
|
2560
|
-
PSY_days=PSY_days, \
|
2561
|
-
ARBR_days=ARBR_days, \
|
2562
|
-
CR_day=CR_day,CR_madays=CR_madays, \
|
2563
|
-
EMV_day=EMV_day,EMV_madays=EMV_madays, \
|
2564
|
-
|
2565
|
-
BULL_days=BULL_days,BULL_nbdevup=BULL_nbdevup, \
|
2566
|
-
BULL_nbdevdn=BULL_nbdevdn,BULL_matype=BULL_matype, \
|
2567
|
-
|
2568
|
-
DMA_fastperiod=DMA_fastperiod, \
|
2569
|
-
DMA_slowperiod=DMA_slowperiod,DMA_madays=DMA_madays, \
|
2570
|
-
|
2571
|
-
TRIX_day=TRIX_day,TRIX_madays=TRIX_madays, \
|
2572
|
-
BIAS_days=BIAS_days, \
|
2573
|
-
CCI_days=CCI_days, \
|
2574
|
-
WR_days=WR_days, \
|
2575
|
-
ROC_day=ROC_day,ROC_madays=ROC_madays, \
|
2576
|
-
DMI_DIdays=DMI_DIdays,DMI_ADXdays=DMI_ADXdays, \
|
2577
|
-
|
2578
|
-
indicator=indicator, \
|
2579
|
-
more_details=more_details)
|
2580
|
-
|
2581
|
-
#技术指标的绘图线
|
2582
|
-
tech_line_default={'RSI':['rsi'],
|
2583
|
-
'OBV':['obv'],
|
2584
|
-
'MACD':['DIF','DEA'],
|
2585
|
-
#'KDJ':['kdj_k','kdj_d','kdj_j'],
|
2586
|
-
'KDJ':['kdj'],
|
2587
|
-
'SAR':['sar'],
|
2588
|
-
'VOL':['vol'],
|
2589
|
-
'PSY':['psy'],
|
2590
|
-
'ARBR':['ar','br'],
|
2591
|
-
'CR':['cr'],
|
2592
|
-
'EMV':['emv'],
|
2593
|
-
'Bollinger':['upper','mid','lower'],
|
2594
|
-
'TRIX':['trix'],
|
2595
|
-
'BIAS':['bias'],
|
2596
|
-
'CCI':['cci'],
|
2597
|
-
'W%R':['wr'],
|
2598
|
-
'ROC':['roc'],
|
2599
|
-
'DMI':['pdi','mdi'],
|
2600
|
-
'DMA':['dma'],}
|
2601
|
-
|
2602
|
-
#数据后处理
|
2603
|
-
magnitude_list={'RSI':[1,''],
|
2604
|
-
'OBV':[1/1000000,'百万'],
|
2605
|
-
'MACD':[1,''],
|
2606
|
-
'KDJ':[1,''],
|
2607
|
-
'SAR':[1,''],
|
2608
|
-
'VOL':[1/1000000,'百万'],
|
2609
|
-
'PSY':[1,''],
|
2610
|
-
'ARBR':[1,''],
|
2611
|
-
'CR':[1,''],
|
2612
|
-
'EMV':[1,''],
|
2613
|
-
'Bollinger':[1,''],
|
2614
|
-
'TRIX':[1,''],
|
2615
|
-
'BIAS':[1,''],
|
2616
|
-
'CCI':[1,''],
|
2617
|
-
'W%R':[1,''],
|
2618
|
-
'ROC':[1,''],
|
2619
|
-
'DMI':[1,''],
|
2620
|
-
'DMA':[1,''],}
|
2621
|
-
|
2622
|
-
mag_times=magnitude_list[technical1][0]
|
2623
|
-
mag_label=magnitude_list[technical1][1]
|
2624
|
-
|
2625
|
-
if 'ALL' in graph or 'all' in graph or 'All' in graph:
|
2626
|
-
tech_line_prefix=tech_line_default[technical1]
|
2627
|
-
else:
|
2628
|
-
if not isinstance(graph,list):
|
2629
|
-
tech_line_prefix=[graph]
|
2630
|
-
else:
|
2631
|
-
tech_line_prefix=graph
|
2632
|
-
|
2633
|
-
tech_line_collist=[]
|
2634
|
-
df_collist=list(df)
|
2635
|
-
for p in tech_line_prefix:
|
2636
|
-
for c in df_collist:
|
2637
|
-
if p in c:
|
2638
|
-
tech_line_collist=tech_line_collist+[c]
|
2639
|
-
#去掉重复项
|
2640
|
-
tech_line_collist=list(set(tech_line_collist))
|
2641
|
-
#去掉误选项
|
2642
|
-
if technical1 == 'ARBR':
|
2643
|
-
remove_cols=[]; remove_item='sar'
|
2644
|
-
for c in tech_line_collist:
|
2645
|
-
if remove_item in c:
|
2646
|
-
tech_line_collist.remove(c)
|
2647
|
-
|
2648
|
-
#改变测度
|
2649
|
-
for c in tech_line_collist:
|
2650
|
-
df[c]=df[c] * mag_times
|
2651
|
-
|
2652
|
-
#字段排序
|
2653
|
-
tech_line_collist.sort()
|
2654
|
-
df1=df[tech_line_collist+[indicator]]
|
2655
|
-
|
2656
|
-
#绘图----------------------------------------------------------------------
|
2657
|
-
import matplotlib.pyplot as plt
|
2658
|
-
|
2659
|
-
fig = plt.figure()
|
2660
|
-
ax = fig.add_subplot(111)
|
2661
|
-
#plt.gca().set_facecolor('whitesmoke')
|
2662
|
-
fig.gca().set_facecolor(facecolor) #放在这里生效,放尾部不生效
|
2663
|
-
|
2664
|
-
"""
|
2665
|
-
line0=False; line30=False; line50=False; line80=False
|
2666
|
-
for l in tech_line_collist:
|
2667
|
-
lmax=df1[l].max(); lmin=df1[l].min()
|
2668
|
-
if lmax * lmin < 0: line0=True
|
2669
|
-
if 100 >= lmax >= 30 and 0 <= lmin <= 30: line30=True
|
2670
|
-
if 100 >= lmax >= 50 and 0 <= lmin <= 50: line50=True
|
2671
|
-
if 100 >= lmax >= 80 and 0 <= lmin <= 80: line80=True
|
2672
|
-
|
2673
|
-
ax.plot(df1.index,df1[l],label=l)
|
2674
|
-
|
2675
|
-
#绘制0线
|
2676
|
-
if line0:
|
2677
|
-
plt.axhline(y=0,ls=":",c="black",linewidth=2)
|
2678
|
-
#绘制30线
|
2679
|
-
if line30:
|
2680
|
-
plt.axhline(y=30,ls=":",c="green",linewidth=2)
|
2681
|
-
#绘制50线
|
2682
|
-
if line50:
|
2683
|
-
plt.axhline(y=50,ls=":",c="black",linewidth=2)
|
2684
|
-
#绘制50线
|
2685
|
-
if line80:
|
2686
|
-
plt.axhline(y=80,ls=":",c="black",linewidth=2)
|
2687
|
-
"""
|
2688
|
-
color_list=['k','g','b','c','m','y','r']
|
2689
|
-
attention_draws=[False] * len(attention_values)
|
2690
|
-
|
2691
|
-
for l in tech_line_collist:
|
2692
|
-
ax.plot(df1.index,df1[l],label=l.upper())
|
2693
|
-
|
2694
|
-
#判断是否绘制关注线
|
2695
|
-
lmax=df1[l].max(); lmin=df1[l].min()
|
2696
|
-
|
2697
|
-
for al in attention_values:
|
2698
|
-
pos=attention_values.index(al)
|
2699
|
-
|
2700
|
-
line_al=False
|
2701
|
-
if lmax >= al >= lmin:
|
2702
|
-
line_al=True
|
2703
|
-
|
2704
|
-
#如果需要绘制关注线,且尚未绘制过,则绘制
|
2705
|
-
if line_al and not attention_draws[pos]:
|
2706
|
-
plt.axhline(y=attention_values[pos],ls=":",c=color_list[pos],linewidth=2)
|
2707
|
-
|
2708
|
-
attention_draws[pos]=True
|
2709
|
-
|
2710
|
-
ylabeltxt1=technical1+'指标'
|
2711
|
-
if mag_label != '':
|
2712
|
-
ylabeltxt1=ylabeltxt1+'('+mag_label+')'
|
2713
|
-
ax.set_ylabel(ylabeltxt1,fontsize=ylabel_txt_size)
|
2714
|
-
ax.legend(loc=loc1,fontsize=legend_txt_size)
|
2715
|
-
|
2716
|
-
ax2 = ax.twinx()
|
2717
|
-
ylabeltxt2='收盘价'
|
2718
|
-
ax2.set_ylabel(ylabeltxt2,fontsize=ylabel_txt_size)
|
2719
|
-
ax2.plot(df1.index,df1[indicator],label=ylabeltxt2,linestyle='dotted',color=price_line_color)
|
2720
|
-
ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
2721
|
-
|
2722
|
-
titletxt=ticker_name(ticker)+': '+tech_list[technical1]+technical1
|
2723
|
-
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
2724
|
-
|
2725
|
-
plt.gcf().autofmt_xdate()
|
2726
|
-
|
2727
|
-
plt.show(); plt.close()
|
2728
|
-
|
2729
|
-
return df
|
2730
|
-
|
2731
|
-
|
2732
|
-
#==============================================================================
|
2733
|
-
#==============================================================================
|
2734
|
-
#==============================================================================
|
2735
|
-
|
2736
|
-
|