数据分析学习笔记(七)-- 股价分析

本例子,通过numpy分析股价

csv文件读写

CSV(Comma-Separated Value,逗号分隔值)是一种常见的文件格式,通常数据库的转存文件就是csv格式,文件中的各个字段对应于数据库表中的列。

这里有一份csv格式的文件,本文一该文件数据为例。
数据结构如下图

CSV

每一条数据对应,第一列为股票代码以标识股票(苹果公司股票代码为AAPL),第二列为dd-mm-yyyy格式的日期,第三列为空,随后各列依次是开盘价、最高价、最低价和收盘价,最后一列为当日的成交量。

我们可以通过numpy的 loadtext()和savetext()函数完成读写操作

# 从文件中读取数据
# 'data.csv':文件名
# delimiter:分隔符
# usecols:所取数据的列下标
# unpack:返回数组
c,v = np.loadtxt('data.csv', delimiter=',', usecols=(6,7), unpack=True)
# 读取了收盘价和成交量数据
x = y = z = np.arange(0.0,5.0,1.0)
np.savetxt('test.csv', x, delimiter=',')   # X 数组
np.savetxt('test.csv', (x,y,z))   # x,y,z 都是一维数组
np.savetxt('test.csv', x, fmt='%1.4e')   # 使用指数表示法

成交量加权平均价格

首先读取成交量数据和收盘价数据

c,v = np.loadtxt('data.csv', delimiter=',', usecols=(6,7), unpack=True)

# 计算成交量的平均价格
vwap = np.average(c, weights=v)
v_mean = np.average(c)
print('算术平均:{},加权平均:{}'.format(v_mean, vwap))
'''算术平均:351.0376666666667,加权平均:350.5895493532009'''

# 时间加权平均价格
t = np.arange(len(c))
twap = np.average(c, weights=t)
print('时间加权平均:{}'.format(twap))
'''时间加权平均:352.4283218390804'''

最大值和最小值

寻找最高价和最低价

# 读取最高价和最低价
h,l = np.loadtxt('data.csv', delimiter=',', usecols=(4,5), unpack=True)
# 最高价中的最大值
h.max()     # 364.9
# 最低价中的最小值
l.min()     # 333.53

# 区间差值:np.ptp()
h_ptp = np.ptp(h)
l_ptp = np.ptp(l)
print('最高价的差值为:{:.2f}'.format(h_ptp))
print('最低价的差值为:{:.2f}'.format(l_ptp))
'''最高价的差值为:24.86
最低价的差值为:26.97'''

简单统计分析

收盘价的统计分析

c = np.loadtxt('data.csv', delimiter=',', usecols=(6,), unpack=True)
print('原始数据:{}'.format(c))
'''
原始数据:[336.1  339.32 345.03 344.32 343.44 346.5  351.88 355.2  358.16 354.54
 356.85 359.18 359.9  363.13 358.3  350.56 338.61 342.62 342.88 348.16
 353.21 349.31 352.12 359.56 360.   355.36 355.76 352.47 346.67 351.99] '''
# 中位数
print('中位数为:{}'.format(np.median(c)))
'''中位数为:352.055'''
# 验证
sorted = np.sort(c)
print('排序后的数据为:{}'.format(sorted))
'''
[336.1  338.61 339.32 342.62 342.88 343.44 344.32 345.03 346.5  346.67
 348.16 349.31 350.56 351.88 351.99 352.12 352.47 353.21 354.54 355.2
 355.36 355.76 356.85 358.16 358.3  359.18 359.56 359.9  360.   363.13]'''
# 可以看出中位数为[351.99 352.12]的平均值

# 方差
print('股价的方差为:{}'.format(np.var(c)))
'''股价的方差为:50.126517888888884'''

股票收益率

  • 股票收益率:收盘价的差值/收盘价
# diff()后一项减去前一项的差,数量会少一项,对应的只取到最后一项收盘价
returns = np.diff(c) / c[:-1]
print('收益率为:{}'.format(returns))    
'''
[ 0.00958048  0.01682777 -0.00205779 -0.00255576  0.00890985  0.0155267
  0.00943503  0.00833333 -0.01010721  0.00651548  0.00652935  0.00200457
  0.00897472 -0.01330102 -0.02160201 -0.03408832  0.01184253  0.00075886
  0.01539897  0.01450483 -0.01104159  0.00804443  0.02112916  0.00122372
 -0.01288889  0.00112562 -0.00924781 -0.0164553   0.01534601]
 '''
print('收益率的标准差:{}'.format(np.std(returns))) 
'''收益率的标准差:0.012922134436826306'''

# 计算收益率为正的数据                                                      
posretindices = np.where(returns>0)                               
returns_value = returns[posretindices]                            
print('收益为正的位置为:{},数据为:{}'.format(posretindices, returns_value))  
'''
收益为正的位置为:(array([ 0,  1,  4,  5,  6,  7,  9, 10, 11, 12, 16, 17, 18, 19, 21, 22, 23,
       25, 28]),),数据为:[0.00958048 0.01682777 0.00890985 0.0155267  0.00943503 0.00833333
 0.00651548 0.00652935 0.00200457 0.00897472 0.01184253 0.00075886
 0.01539897 0.01450483 0.00804443 0.02112916 0.00122372 0.00112562
 0.01534601]
'''
  • 收益波动率

收益波动率是对资产收益率不确定性的衡量,用于反映金融资产的风险水平。波动率越高,金融资产价格的波动越剧烈,资产收益率的不确定性就越强;波动率越低,金融资产价格的波动越平缓,资产收益率的确定性就越强

公式:对数收益率的标准差/对数收益率的平均值/交易天数倒数的平方根

# 对数收益率,注意去掉0的数据,因为0没有对数                   
logreturns = np.diff(np.log(c))            
print('对数收益率为:{}'.format(logreturns)) 
# 去掉周末天数为252                                                                                       
annual_volatility = np.std(logreturns) / np.mean(logreturns) / np.sqrt(1./252.)                
print('年度化收益波动率为:{}'.format(annual_volatility))   
'''年度化收益波动率为:129.27478991115132'''                                             
month_volatility = annual_volatility * np.sqrt(1./12.)                                         
print('月度化收益率波动率为:{}'.format(month_volatility))   
'''月度化收益率波动率为:37.318417377317765'''                                          

数据汇总

  • 按日期分析

    我们按照一周中的星期几取分析,计算其收盘价,以及均价

首先,我们为了方便处理,需要将数据中的日期格式转换为数据索引形式

from datetime import datetime                                     
'''                                                               
Monday:0                                                          
Tuesday:1                                                         
Wendesday:2                                                       
Thursday:3                                                        
Friday:4                                                          
Saturday:5                                                        
Sunday:6                                                          
'''                                                               
# 日期转换函数                                                          
def datestr2num(s):                                               
    s = s.decode('utf-8')  # 将bytes转为str类型                        
    return datetime.strptime(s, '%d-%m-%Y').date().weekday()      

读取日期和收盘价数据

# converters,将数据映射,这里是将第1列上的数据通过函数datestr2num进行处理                                                                
dates,colse = np.loadtxt('data.csv', delimiter=',', usecols=(1, 6), converters={1: datestr2num}, unpack=True)    
print('dates:{}'.format(dates))    
'''dates:[4. 0. 1. 2. 3. 4. 0. 1. 2. 3. 4. 0. 1. 2. 3. 4. 1. 2. 3. 4. 0. 1. 2. 3.
 4. 0. 1. 2. 3. 4.]'''                                                                              
# 数字0-4分别对应周一到周五                                                                                                          

按日期分析

# 创建5天的数组                                                                                       
averages = np.zeros(5)                                                                            
for i in range(5):                                                                                
    # 分别取出星期几对应的位置                                                                                
    indexs = np.where(dates==i)                                                                   
    # 取出对应的价格                                                                                     
    prices = np.take(colse, indices=indexs)  # np.take(colse, indices=indexs) 等价于 colse[indexs]   
    avg = np.mean(prices)   # 算出均值                                                                
    print('Day {} prices:{} Average:{:.2f}'.format(i, prices, avg))                               
    averages[i] = avg       # 替换数据  

'''
Day 0 prices:[[339.32 351.88 359.18 353.21 355.36]] Average:351.79
Day 1 prices:[[345.03 355.2  359.9  338.61 349.31 355.76]] Average:350.64
Day 2 prices:[[344.32 358.16 363.13 342.62 352.12 352.47]] Average:352.14
Day 3 prices:[[343.44 354.54 358.3  342.88 359.56 346.67]] Average:350.90
Day 4 prices:[[336.1  346.5  356.85 350.56 348.16 360.   351.99]] Average:350.02
'''         

顺便计算5天中最高和最低均价

print('最高的均值为:{:.2f}, the week {}'.format(np.max(averages), np.argmax(averages)))   
print('最低的均值为:{:.2f}, the week {}'.format(np.min(averages), np.argmin(averages)))   

'''最高的均值为:352.14, the week 2
最低的均值为:350.02, the week 4
'''
  • 周汇总

有时候,我们想要按周进行统计汇总,例如:我想看前三周的,周一的开盘价、周五的收盘价、一周中最高和最低价信息

首先依旧是获取到我们所需要到数据

dates, open, high, low, close = np.loadtxt('data.csv', delimiter=',', usecols=(1, 3, 4, 5, 6), converters={1:datestr2num}, unpack=True)   

然后创建前三周的索引,分别对应三周中的股票名称、周一开盘价、一周中最高价、一周中最低价、周五收盘价

# split()可以将数组均等分割
weeks_indices = np.split(np.arange(0, 15), 3)  
print('Weeks indices : {}'.format(weeks_indices)) 
Weeks indices : [array([0, 1, 2, 3, 4]), array([5, 6, 7, 8, 9]), array([10, 11, 12, 13, 14])]

这里简单介绍 apply_along_axis() 函数

numpy.apply_along_axis(func, axis, arr, *args, **kwargs)
必选参数:func,axis,arr。其中func是我们自定义的一个函数,函数func(arr)中的arr是一个数组,函数的主要功能就是对数组里的每一个元素进行变换,得到目标的结果
axis表示函数func对数组arr作用的轴,1:横向,0:竖向
可选参数:*args, **kwargs。都是func()函数额外的参数
返回值:numpy.apply_along_axis()函数返回的是一个根据func()函数以及维度axis运算后得到的的数组

例子:

def my_func(a):                                                
    return (a[0] + a[-1]) * 0.5                                

b=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])                 

res = np.apply_along_axis(my_func, 0, b)   # 竖向                    

print(res)  # [5. 6. 7. 8.]                                    

res = np.apply_along_axis(my_func, 1, b)    # 横向                      

print(res)  # [ 2.5  6.5 10.5]                                 

回到之前,我们来定一个 summarize() 函数,用来计算周汇总

def summarize(a, o, h, l, c):                                       
    monday_open = o[a[0]]               # 周一开盘价                     
    week_high = np.max(np.take(h, a) )  # 一周中最高价                    
    week_low = np.min( np.take(l, a) )  # 一周中最低价                    
    friday_close = c[a[-1]]             # 周五收盘价                     
    return ("APPL", monday_open, week_high, week_low, friday_close) 

# 调用apply_along_axis()函数,传入相关参数
weeksummary = np.apply_along_axis(summarize, 1, weeks_indices, open, high, low, close)     
print('Week summary : {}'.format(weeksummary))
'''
Week summary : [['APPL' '344.17' '345.65' '333.53' '343.44']
 ['APPL' '343.61' '360.0' '343.51' '354.54']
 ['APPL' '354.75' '364.9' '353.54' '358.3']]
 '''

# 将处理后的数据保存到本地                                                                             
np.savetxt('weeksummary.csv', weeksummary, delimiter=',', fmt='%s')                        

在工程目录下,找到 weeksummary.csv 文件,打开

weeksummary.csv

真实波动幅度均值(ATR)

  • ATR介绍

1、真实波动幅度均值(ATR)是韦尔斯•王尔德(J. Welles Wilder)提出的,这一指标主要用来衡量证券价格的波动,因此,这一技术指标并不能直接反映价格走向及趋势稳定性,而只是用来表明价格波动的程度。

2、极端的高ATR或低ATR值可以被看作价格趋势的反转或下一个趋势的开始。较低的ATR表示比较冷清的市场交易气氛,而较高的ATR则表示比较旺盛的交易气氛。一段较长时间的低ATR很可能表明市场正在积蓄力量并逐渐开始下一个价格趋势,而一段非常高的ATR通常是由于短时间内价格的大幅上涨或下跌造成的,通常此数值不可能长期维持在高水平。

3、交易者也可以是使用ATR来设置自己交易的止损和止赢价位。由于ATR计算的是在某一个时间段内货币对的波动真实范围,因此可以把该范围作为计算止损和止赢的标准。

  • TR:真实波动幅度

TR=∣最高价-最低价∣和∣最高价-昨收∣和∣昨收-最低价∣的最大值
ATR=TR的N日简单移动平均,参数N设置为14日

  • 首先我们计算TR
h, l, c = np.loadtxt('data.csv', delimiter=',', usecols=(4,5,6), unpack=True)

# 我们先计算所有数据的TR
truerange = np.maximum(np.fabs(h-l), np.fabs(h-c), np.fabs(c-l))
print('真实波动幅度:{}'.format(truerange))
'''
真实波动幅度:[10.87  5.74  4.67  1.7   5.69  3.19  5.61  3.37  4.13 12.    4.26  2.77
  2.42  4.4   3.75  9.98  7.68  6.03  6.78  3.63  3.93  8.04  5.95  3.87
  2.54 10.36  5.15  4.16  4.87  7.32]
  '''

'''一般来说,真实波动幅度均值(ATR)通常以7或14个时段为基础进行计算,这个时段可以是一天内的某个时间段,也可以是一天的日价,乃至周价和月价。
第一个ATR通常是前7或14天中每天的TR的简单算术平均
'''
# 计算出第一个ATR:这里取周期n为14
n = 14  # 周期为14
ATR0 = np.mean(truerange[:n])
print('首个ATR为:{}'.format(ATR0))
'''首个ATR为:5.058571428571428'''

# 计算剩下天数的 ATR
# 剩下的数量 N
N = len(truerange) - n
print('可计算ATR的天数为:{}'.format(N))
'''可计算ATR的天数为:16'''

# 创建 N 数量的 0 数组,用来存储 ATR
atr = np.zeros(N)
# 那么 atr[0] 就是 ATR0
atr[0] = ATR0

# 剩下的 ATR 步骤如下:
'''
1.将前14天的ATR乘以13
2.将步骤一所得的值加上新一天的TR
3.将步骤二所得值除以14
'''
for i in range(1, N):
    atr[i] = (n - 1) * atr[i - 1] + truerange[i]
    atr[i] /= n
print('ATR:{}'.format(atr))
'''
ATR:[5.05857143 5.1072449  5.07601312 4.83486933 4.89595009 4.77409651
 4.8338039  4.72924648 4.68644316 5.20884008 5.14106579 4.97170394
 4.78943938 4.76162228 4.68936354 5.06726615]
 '''

移动平均线:convolve()

  • 简单移动平均线

简单移动平均线通常用于分析时间序列上的数据,例如我们计算N个交易日股票收盘价的移动平均值

import matplotlib.pyplot as plt 
# 读取数据
c = np.loadtxt('data.csv', delimiter=',', usecols=(6,), unpack=True)
N = 5

# 定义移动窗口和权重
weights = np.ones(N) / N
'''array([ 0.2,  0.2,  0.2,  0.2,  0.2])'''

# 调用卷积函数convolve,并从中截取对称数组,其数量和收盘价 len(c)-N+1 一致
sample = np.convolve(weights, c)[N-1:-(N-1)]
# 设置时间作为x轴坐标
time = np.arange(N-1, len(c))
# 绘制真实收盘价走势,颜色为red
plt.plot(time, c[N-1:], linewidth=1.0, color='red')
# 绘制简单移动平均值走势,颜色为green
plt.plot(time, sample, linewidth=1.0, color='green')
# show
plt.show()

结果图形

由上图所示:红色线部分是股价的真实收盘走势,绿色线部分则为简单移动平均值走势,其相对真实值延后,相对比较平滑

关于卷积函数convolve:numpy中的convolve的理解

  • 指数移动平均线

相比与简单移动平均线权重相等,指数移动平均线的权重是呈指数型衰减趋势的,即与当前时间越久的数据所赋予的权重以指数速度减少
我们通过使用 exp() 函数来获取指数值, linspace()函数来获取指定范围内均匀数组

# 下面获取指数移动平均线的权重
N = 5
weights = np.exp(np.linspace(0, 1, N))
# 求出每一份所占的比重
weights = weights / np.sum(weights)
print('权重为:{}'.format(weights))
'''权重为:[0.11405072 0.14644403 0.18803785 0.24144538 0.31002201]'''

此时,权重不再是0.2了,而是越靠近当前时间权重越大,反之越小

在简单移动平均线的基础上,我们再新增一条指数移动均线

exponent = np.convolve(weights, c)[N-1:-(N-1)]
# 绘制指数移动平均值走势,颜色为blue
plt.plot(time, exponent, linewidth=1.0, color='blue')
plt.show() # show函数移到后面

指数移动均线

此图中的蓝色线就是指数移动均线了。

其他相关文章:Python绘制股票移动均线

布林带

布林带:用来描述股票价格变化的区间
组成:三条线:上轨、中轨、下轨
中轨即简单移动均线,上轨为:简单移动均线加上N日两倍标准差,下轨则为:简单移动均线减去N日两倍标准差

首先取出数据,计算好简单移动均线

N = 5
weights = np.ones(N) / N
c = np.loadtxt('data.csv', delimiter=',', usecols=(6,), unpack=True)
# 简单移动均线 
sample = np.convolve(weights, c)[N-1:-(N-1)]
# 标准差数组
deviation = []

计算 N 日标准差

for i in range(N - 1, len(c)):
    # dev 为 N 日的收盘价,往前推 N 日
    dev = c[ i - ( N - 1) : i+1 ]

    # 创建 N 日,值为 0 的数组
    averages = np.zeros(N)
    # 将简单移动均线的 i - (N - 1) 分别填充到该数组中,sample是下标从 0 开始的
    averages.fill(sample[i - (N - 1)])
    # 计算标准差
    dev = dev - averages
    dev = dev ** 2
    dev = np.sqrt(np.mean(dev))
    deviation.append(dev)

# 两倍的标准差
deviation = 2 * np.array(deviation)

那么上轨道和下轨道的数据为:

# 上轨
upperBB = sample + deviation
# 下轨
lowerBB = sample - deviation

绘制布林带

time = np.arange(N-1, len(c))
# 真实收盘价
plt.plot(time, c[N-1:], linewidth=1.0, color='yellow')
# 简单移动均线
plt.plot(time, sample, linewidth=1.0, color='green')
# 上轨道
plt.plot(time, upperBB, linewidth=2.0, color='red', linestyle='--')
# 下轨道
plt.plot(time, lowerBB, linewidth=2.0, color='green', linestyle='--')
plt.show()

布林带

上图所示,两条虚线内部即为布林带,中间绿色实线为简单移动均线,黄色部分为真实收盘价走势

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值