数据分析之股票指标分析

股票技术指标是数据分析的经典案例,本文使用Python计算常见的股票技术指标,以期结合实例进一步掌握数据分析的基础技能。

库导入和设置

使用mplfinance来绘制K线图和交易量柱状图,但是用它绘制其他技术曲线形状好像有问题!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from mootdx.reader import Reader

import numpy as np

import matplotlib.pyplot as plt
import pandas as pd
#matplotlib inline
plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False

import mplfinance as mpf
my_style = mpf.make_mpf_style(
marketcolors=mpf.make_marketcolors(
up='red',
down='green',
edge='inherit',
wick='inherit',
volume='inherit'
),
base_mpf_style='charles',
gridcolor='#cccccc',
gridaxis='both',
rc={
'font.sans-serif': ['SimHei'],
'axes.unicode_minus': False
}
)

读股票数据

使用mootdx直接读取通达信的股票基本数据,在此基础上进行各种数据处理。这是我目前找到的最好用的数据来源!最基本的数据就是开盘价、最高价、最低价、收盘价、交易量和交易额。

1
2
3
4
5
6
7
8
9
10
reader = Reader.factory(market="std", tdxdir=r"C:\new_tdx64")
df = reader.daily(symbol="600869")
df.rename(columns={
'open':'Open',
'high':'High',
'low':'Low',
'close':'Close',
'volume':'Volume',
'amount':'Amount'
},inplace=True)

计算绘图数据

不同的均值计算

假设给定一个数据序列,序列长度是n:

P0、P1、P2、...、Pn−1P_0、P_1、P_2、...、P_{n-1}

简单移动平均(SMA)

SMA(w)t=∑i=t−w+1tPiw当t<w−1时,SMA无值。SMA(w)_t=\frac{\sum_{i=t-w+1}^t{P_i}}{w}\\ 当t<w-1时,SMA无值。

在Pandas中,使用series.rolling(window=w).mean()即可计算SMA。除了均值,Series也提供方法直接计算滑动窗体内各数的最大值、最小值、标准差等指标。

指数移动平均(EMA)

MACD指标主要用到这种均值。迭代计算公式:

EMA(s)t=α⋅Pt+(1−α)EMAt−1EMA(s)0=P0α=2s+1EMA(s)_t=\alpha\cdot{P_t}+(1-\alpha)EMA_{t-1}\\ EMA(s)_0=P_0\\ \alpha=\frac{2}{s+1}

Pandas中可以这样计算:series.ewm(span=n,adjust=False).mean()。

s越大,α\alpha越小,当前数据的影响占比越小,往期数据的影响占比越大。

平滑移动平均(SMMA)

KDJ、RSI指标计算会用到这种均值。迭代计算公式:

SMMA(C,N,M)t=N−MN⋅SMMA(C,N,M)t−1+MN⋅Pt关于计算起点,不同指标取法不一致。RSI取法:当t<N−1时,SMMA无值,SMMA(C,N,M)N−1=SMA(N)N−1。KDJ取法:SMMA(C,N,M)0=一个初始值SMMA(C,N,M)_t=\frac{N-M}{N}\cdot{SMMA(C,N,M)_{t-1}}+\frac{M}{N}\cdot{P_t}\\ 关于计算起点,不同指标取法不一致。 \\RSI取法:\\ 当t<N-1时,SMMA无值,SMMA(C,N,M)_{N-1}=SMA(N)_{N-1}。\\ KDJ取法:\\ SMMA(C,N,M)_0=一个初始值

通常取M=1:

SMMA(C,N,1)t=N−1N⋅SMMA(C,N,M)t−1+1N⋅PtSMMA(C,N,1)_t=\frac{N-1}{N}\cdot{SMMA(C,N,M)_{t-1}}+\frac{1}{N}\cdot{P_t}

在Python中需要通过代码逻辑实现:

1
2
3
4
5
6
def smma(series, N, M):
res = series.copy()
res.iloc[N-1] = series.iloc[:N].mean()
for i in range(N, len(series)):
res.iloc[i] = (res.iloc[i-1] * (N - M) + series.iloc[i] * M) / N
return res

具体计算代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 计算均线
df['ma5']=df['Close'].rolling(window=5).mean()
df['ma10']=df['Close'].rolling(window=10).mean()
df['ma20']=df['Close'].rolling(window=20).mean()
df['ma60']=df['Close'].rolling(window=60).mean()

# 计算BOLL布林带
df['boll_mid'] = df['Close'].rolling(window=20).mean()
df['boll_std'] = df['Close'].rolling(window=20).std()
df['boll_up'] = df['boll_mid'] + 2 * df['boll_std']
df['boll_low'] = df['boll_mid'] - 2 * df['boll_std']

# 计算VOL均线
df['vol_ma5']=df['Volume'].rolling(window=5).mean()
df['vol_ma10']=df['Volume'].rolling(window=10).mean()
df['vol_ma20']=df['Volume'].rolling(window=20).mean()

# 计算MACD
ema12=df['Close'].ewm(span=12,adjust=False).mean()
ema26=df['Close'].ewm(span=26,adjust=False).mean()
df['DIF']=ema12-ema26
df['DEA']=df['DIF'].ewm(span=9,adjust=False).mean()
df['MACD']=2*(df['DIF']-df['DEA'])

# 计算KDJ
def compute_kdj(series_low,series_high,series_close,n,m1,m2):
low_n=series_low.rolling(window=n,min_periods=1).min()
high_n=series_high.rolling(window=n,min_periods=1).max()
rsv=(series_close-low_n)/(high_n-low_n)*100
rsv=rsv.replace([np.inf,-np.inf],np.nan)
k=series_close.copy()
d=series_close.copy()
j=series_close.copy()
k.iloc[0]=rsv.iloc[0]
d.iloc[0]=rsv.iloc[0]
j.iloc[0]=rsv.iloc[0]
for i in range(1,len(series_close)):
k.iloc[i]=2.0/3.0*k.iloc[i-1]+1.0/3.0*rsv.iloc[i]
d.iloc[i]=2.0/3.0*d.iloc[i-1]+1.0/3.0*k.iloc[i]
j.iloc[i]=3.0*k.iloc[i]-2.0*d.iloc[i]
return k,d,j

k,d,j=compute_kdj(df['Low'],df['High'],df['Close'],9,3,3)
df['K']=k
df['D']=d
df['J']=j

# 计算RSI
def wilder_smooth(series, period):
res = series.copy()
res.iloc[period-1] = series.iloc[:period].mean()
for i in range(period, len(series)):
res.iloc[i] = (res.iloc[i-1] * (period - 1) + series.iloc[i]) / period
return res

def compute_rsi(close_series, n):
lc = close_series.shift(1)
u = (close_series - lc).mask(close_series - lc < 0, 0)
ud_abs = (close_series - lc).abs()
sma_u = wilder_smooth(u, n)
sma_ud = wilder_smooth(ud_abs, n)
rsi = sma_u / sma_ud * 100
rsi = rsi.mask(sma_ud == 0, 0)
rsi = rsi.replace([np.inf, -np.inf], 100)
return rsi

df['rsi6'] = compute_rsi(df['Close'],6)
df['rsi12'] = compute_rsi(df['Close'],12)
df['rsi24'] = compute_rsi(df['Close'],24)

绘制图形

K线、均线、布林带都绘制在主图中;交易量均线绘制在交易量柱状图中;其它(KDJ、MACD、RSI)图形用matplotlib库单独绘制。

绘制K线、均线、布林带

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
add_plots_ma = [
mpf.make_addplot(df["ma5"], color='#ff4444', width=1.0, label="MA5"),
mpf.make_addplot(df["ma10"], color='#ffbb33', width=1.0, label="MA10"),
mpf.make_addplot(df["ma20"], color='#0099cc', width=1.0, label="MA20"),
mpf.make_addplot(df["ma60"], color='#9933cc', width=1.0, label="MA60")
]
add_plots_boll = [
mpf.make_addplot(df["boll_mid"], color='#ff0000', width=1.5, label="BOLL_MID"),
mpf.make_addplot(df["boll_up"], color='#ff0000', width=1.5, label="BOLL_UPPER"),
mpf.make_addplot(df["boll_low"], color='#ff0000', width=1.5, label="BOLL_LOWER")
]
add_plots_volma = [
mpf.make_addplot(df["vol_ma5"], panel=1, color='#ff6600', width=1.0, label="MA5"),
mpf.make_addplot(df["vol_ma10"], panel=1, color='#0066FF', width=1.0, label="MA10"),
mpf.make_addplot(df["vol_ma20"], panel=1, color='#ff00FF', width=1.0, label="MA20")
]
all_addplot = add_plots_ma +add_plots_boll+ add_plots_volma
panel_ratios = [2, 1]
mpf.plot(
df,
type="candle",
volume=True,
style=my_style,
addplot=all_addplot,
title="日K线",
figsize=(16,10),
ylabel="价格",
ylabel_lower="VOL",
show_nontrading=False,
datetime_format="%Y%m%d",
xrotation=0,
panel_ratios=panel_ratios
)

绘制KDJ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
dates=df.index.strftime("%Y-%m-%d")
step = 10

fig, ax =plt.subplots(figsize=(14,3))

ax.plot(dates, df['K'].to_numpy(), lw=1.2, label="K")
ax.plot(dates, df['D'].to_numpy(), lw=1.2, label="D")
ax.plot(dates, df['J'].to_numpy(), lw=1.2, label="J")

# 这里设置横坐标刻度的间隔
ax.set_xticks(dates[::step])
ax.set_xticklabels(dates[::step])

# 设置图形边框
ax.spines['top'].set_visible(True)
ax.spines['right'].set_visible(True)
ax.spines['bottom'].set_visible(True)
ax.spines['left'].set_visible(True)
ax.spines['top'].set_color('black')
ax.spines['right'].set_color('black')
ax.spines['bottom'].set_color('black')
ax.spines['left'].set_color('black')
ax.spines['top'].set_linewidth(0.5)
ax.spines['right'].set_linewidth(0.5)
ax.spines['bottom'].set_linewidth(0.5)
ax.spines['left'].set_linewidth(0.5)

plt.title("KDJ")
plt.grid(alpha=0.8)
plt.legend()
plt.tight_layout()
plt.show()

绘制MACD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
dates=df.index.strftime("%Y-%m-%d")
step = 10

fig, ax =plt.subplots(figsize=(14,3))

bar_color = np.where(df["MACD"]>0, "#ff4444", "#00aa44")
ax.bar(dates, df["MACD"].to_numpy(), width=0.5, alpha=0.8, label="MACD",color=bar_color)
ax.plot(dates, df['DIF'].to_numpy(), lw=1.2, label="DIF")
ax.plot(dates, df['DEA'].to_numpy(), lw=1.2, label="DEA")

ax.set_xticks(dates[::step])
ax.set_xticklabels(dates[::step])

ax.spines['top'].set_visible(True)
ax.spines['right'].set_visible(True)
ax.spines['bottom'].set_visible(True)
ax.spines['left'].set_visible(True)
ax.spines['top'].set_color('black')
ax.spines['right'].set_color('black')
ax.spines['bottom'].set_color('black')
ax.spines['left'].set_color('black')
ax.spines['top'].set_linewidth(0.5)
ax.spines['right'].set_linewidth(0.5)
ax.spines['bottom'].set_linewidth(0.5)
ax.spines['left'].set_linewidth(0.5)

plt.title("MACD")
plt.grid(alpha=0.8)
plt.legend()
plt.tight_layout()
plt.show()

绘制RSI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
dates=df.index.strftime("%Y-%m-%d")
step = 10

fig, ax =plt.subplots(figsize=(14,3))

ax.plot(dates, df['rsi6'].to_numpy(), lw=1.2, label="RSI6")
ax.plot(dates, df['rsi12'].to_numpy(), lw=1.2, label="RSI12")
ax.plot(dates, df['rsi24'].to_numpy(), lw=1.2, label="RSI24")

ax.set_xticks(dates[::step])
ax.set_xticklabels(dates[::step])

ax.spines['top'].set_visible(True)
ax.spines['right'].set_visible(True)
ax.spines['bottom'].set_visible(True)
ax.spines['left'].set_visible(True)
ax.spines['top'].set_color('black')
ax.spines['right'].set_color('black')
ax.spines['bottom'].set_color('black')
ax.spines['left'].set_color('black')
ax.spines['top'].set_linewidth(0.5)
ax.spines['right'].set_linewidth(0.5)
ax.spines['bottom'].set_linewidth(0.5)
ax.spines['left'].set_linewidth(0.5)

plt.title("RSI")
plt.grid(alpha=0.8)
plt.legend()
plt.tight_layout()
plt.show()

(转载本站文章请注明作者和出处lihaohello.top,请勿用于任何商业用途)