7. JD(京东)股票分析

本章以京东股票交易记录为案例展示一下NumPy的数学、统计学函数的使用。

7.1 JD股票交易数据下载

可以从Nasdaq下载JD的股票交易数据,进入该页面点击最下面的download下载股票历史交易记录,下载的文件:HistoricalQuotes文件的格式是csv的。

其实Yahoo、Google等网站也有某些公司的相应股票交易数据,例如JD在Yahoo上提供的数据可以从Yahoo的finance下载,本文处理的数据是从Nasdaq下载的。

7.2 JD交易数据文件分析

打开文件HistoricalQuotes.csv文件可以看到有多个字段的数据,但是都是字符串型的(实数用双引号引起来了),可以用excel或libreoffice打开,然后另存一下就是真的csv文件了,这样实数字段数据就以实数形式存在于csv文件里了。这样的处理方式是非python的,可以自己编写程序将HistoricalQuotes.csv存储成真正的.csv文件。

HistoricalQuotes.csv文件的第一行是说明各个字段的含义:"date","close","volume","open","high","low"分别代表交易记录的日期date、收盘假close、交易量volume、开盘价open、当天交易最高价high和当天交易最低假low等信息。

表1. 数据文件字段含义及位置

date close volume open high low
日期 收盘价 成交量 开盘价 最高成交价 最低成交价
0 1 2 3 4 5

文件的第二行是从网站查询时的实价,date信息是美国当地时间几点几分,第三行则是前一天的交易信息,下面的例子是2018年8月11日在Nasdaq查询的信息,可能与您看到有些不同。

HistoricalQuotes.csv文件

"date","close","volume","open","high","low"
"12:25","34.65","8,861,035","35.58","35.66","34.54"
"2018/08/10","35.7900","8676586.0000","35.6200","35.9050","35.4700"
"2018/08/09","36.0200","8154059.0000","36.2700","36.6219","35.9601"
"2018/08/08","36.0000","8668998.0000","36.5700","36.8000","35.9600"
"2018/08/07","36.5000","9413608.0000","36.2900","36.7000","35.9215"

HistoricalQuotes.csv文件还不能直接用NumPy的loadtxt函数直接读取处理,需将文件里的双引号去掉再用loadtxt函数读取,且需使用loadtxt函数的skiprows参数去掉头两行的信息。

7.3 JD数据文件预处理

从Nasdaq下载的JD数据文件,可通过Python的文件处理和Csv文件读写将从纳斯达克下载的HistoricalQuotes.csv文件处理一下得到我们想要的数据文件:去掉所有的双引号、去掉头两行、交易量是整形数据、其余是实数数据。

jd.py文件

#coding:utf-8
import csv
fn = "HistoricalQuotes.csv"
fm = "jd.csv"
fr = open(fn, 'r')
fw = open(fm, 'w')
ret = csv.writer(fw, delimiter=',')
cont = fr.readlines()

for v in cont[2:]:#去掉头两行
    x = v.split(',')
    temp = list()
    j = 0
    for t in x:
        if j == 0:# 交易日期
            temp.append(t.strip('"'))
        elif j == 2:# 交易总量
            temp.append(int(float(t.strip('"'))))
        else:# 各个价格
            temp.append(float(t.strip('"\r\n')))
        j += 1

fr.close()
fw.close()

上边的程序文件可存为jd.py,输入数据文件为HistoricalQuotes.csv、输出文件为jd.csv。jd.py和HistoricalQuotes.csv共存同一目录下,生成得到jd.csv文件也在同一个目录下,jd.csv文件如下所示:

jd.csv文件

2018/08/10,35.79,8676586,35.62,35.905,35.47
2018/08/09,36.02,8154059,36.27,36.6219,35.9601
2018/08/08,36.0,8668998,36.57,36.8,35.96
2018/08/07,36.5,9413608,36.29,36.7,35.9215
2018/08/06,35.9,9642419,36.06,36.11,35.53
2018/08/03,36.17,6741245,36.48,36.7136,35.81

7.4 NumPy对京东股票简单分析

上面对从纳斯达克下载的jd的股票交易历史数据进行了简单的数据的处理,生成了一个jd.csv文件,接下来便可利用NumPy的loadtxt函数将数据读入到程序进行必要的数据分析得到一些简单的结论。

1). 收盘价和交易量 首先通过numpy的loadtxt函数提取JD纳斯达克的历史交易记录里的收盘价和交易量,这两个数据位于csv文件的第1列和第2列,可以通过usecols参数指定。

from numpy import *
import numpy as np
close, volume = loadtxt("jd.csv",  delimiter=',', usecols = [1, 2],unpack=True)
print close
print volume

close是收盘价、volume是对应交易量。

2). VWAP(Volume-weighted average price) 成交量加权平均价格,以成交量为权重计算出来的加权平均值,常用于算法交易,VWAP的计算公式如下:

$vwap = \frac{\sum_{i = 0}^{n - 1} close_i \times weight_i}{\sum_{i = 0}^{n - 1} weight_i}$

实现vwap的算法程序如下所示:

#coding:utf-8
from numpy import *
import numpy as np
close, volume = loadtxt("jd.csv",  delimiter=',', usecols = [1, 2],unpack=True)
aver = average(close)# 无加权
print "aver ", aver 
vwap = average(close, weights=volume)
print "vwap ", vwap
meanret = mean(close)
print "mean ", meanret

程序的执行结果:

aver  40.7541501976
vwap  40.8935556255
mean  40.7541501976

没有加权的average函数和mean函数的功能一致,均是求平均值,而average使用了weight参数,即使用了加权。

3). TWAP(Time-weighted average price)时间加权平均价格, 基本思想就是最近的价格重要性大一些,所以应对近期的价格给以较高的权重,最简单的方法是用arange函数创建一个从0开始依次增长的自然数序列,自然数的个数即为收盘价的个数,自然数越大权重越大。由于纳斯达克提供的jd.csv的交易记录时间是由近向久远记录排列的,所以用arange产生的序列需要逆向变成递减,这样越近的交易记录所占的权重数值越大。

from numpy import *
import numpy as np
close, volume = loadtxt("jd.csv",  delimiter=',', usecols = [1, 2],unpack=True)
aver = average(close)
print "aver ", aver
vwap = average(close, weights=volume)
print "vwap ", vwap
vwap = close.dot(volume) / np.sum(volume)
print "vwap ", vwap
meanret = mean(close)
print "mean ", meanret
n = len(close)
w = arange(n)[::-1]
print w
twap = average(close, weights = w)
print "twap ", twap

程序的执行结果:

aver  40.7541501976
vwap  40.8935556255
vwap  40.8935556255
mean  40.7541501976
twap  40.0752189598

程序里模拟的TWAP的计算公式如下所示:

$twap = \frac{\sum_{i = n - 1}^{0}close_{n - 1 - i} \times i}{\sum_{i = 0}^{n - 1}i}$

4). 近期最高、最低成交价 每日的最高成交、最低成交价位于每行的第4、5列,如下表1所示。那么提取最高和最低成交价需使得usecols=[4, 5]即可。NumPy有max、min和ptp可以分别获得某字段下的最大值、最小值和数据值的波动值。

from numpy import *
import numpy as np
h, l = loadtxt("jd.csv",  delimiter=',', usecols = [4, 5],unpack=True)
print "hightest\t", np.max(h)
print "lowest  \t", np.min(l)
print "high_ptp\t", np.ptp(h), np.max(h) - np.min(h)
print "low_ptp \t", np.ptp(l), np.max(l) - np.min(l)

程序执行结果如下:

hightest    50.68
lowest      34.76
high_ptp    14.79 14.79
low_ptp     14.95 14.95

5). 中位数, 定义是一组有序集合s,有一半数比这个数大,有一半比这个数小,如果集合共N个数据,N为奇数那么$\frac{N}{2}$位置上的数即为中位数$s_{\frac{N}{2}}$,如果N为偶数中位数则是$\frac{s_{\frac{N}{2}} + s_{\frac{N}{2} + 1}}{2}$之值。

from numpy import *
import numpy as np
close, volume = loadtxt("jd.csv",  delimiter=',', usecols = [1, 2],unpack=True)
print len(close)
print median(close)
print median(close[1:])
print msort(close[1:])

程序执行结果:

253 # 集合元素个数
40.13 # N = 253
40.15 # N = 252
[ 35.16  ... 40.01  40.07  40.13  40.17  ... 49.94
  50.5 ]#close[1:]

N = 253的median函数的输出结果是40.13位于输出的close结果里,N = 252的输出结果为$40.15 = \frac{40.13 + 40.17}{2}$ 的计算结果。

6). 方差$\sigma$,是各个数据$s_i$与所有$s_i$的平均值$\mu$之差的平方的平均数$\sigma$,计算公式如下:

$\sigma = \frac{\sum_{i = 0}^{n - 1} (s_i - \mu)^2}{N}$

程序代码如下所示:

from numpy import *
import numpy as np
close, volume = loadtxt("jd.csv",  delimiter=',', usecols = [1, 2],unpack=True)
print "var", var(close)
print "var", np.sum(square(close - mean(close)))  / len(close)

执行结果如下:

var 12.3449112344
var 12.3449112344

第一行的输出结果是var函数计算结果,第二行的输出结果是依据上边的$\sigma$公式计算结果,两者一样。

7). 收益率, 假设您打算每天持有京东的1000股股票,在一段时间内到底是赚了还是赔了?每天赚里多少?如何计算?可以这样去操作每天开盘买(购买),每天收盘卖(抛售)一直手里有1000股,这么多天一共能收益多少呢?那些天赚了?

为了更具一般性。这里要有N天的收盘价存储在close里,N- 1天的交易量数据存储在volume里。

收益 stock_return$ = \sum_{i = 0}^{N - 2}(close_{i+1} - close_{i}) \times volume_i$

对于每日的股票差价可以借助numpy的diff函数获得,diff的作用是用数组的后一个位置上的数据减去当前位置的数据之差作为结果数组里改为位置上的数据。

import numpy as np
a = np.array([1, 4, 2, 9])
print a
print np.diff(a)

程序结果如下:

[1 4 2 9]
[ 3 -2  7]

日收益率 $= \frac{close_{i+1} - close_{i}}{close_{i}}$,假设每日持有股票数不变。

如果用Python表示即为:

diff(close) / close[:-1]

现在回到JD的股票交易数据问题上来,如何计算下载数据这段时间内的股票收益呢?

from numpy import *
import numpy as np
close, volume = loadtxt("jd.csv",  delimiter=',', usecols = [1, 2],unpack=True)
# 日收益率
stock_return =  diff(close) / close[:-1]
print stock_return
# 那天赚钱?
print where(stock_return > 0)

投资者如何评价投资的风险呢?可以计算收益率的标准方差,使用NumPy的std函数。

from numpy import *
import numpy as np
close, volume = loadtxt("jd.csv",  delimiter=',', usecols = [1, 2],unpack=True)
stock_return =  diff(close) / close[:-1]
print std(stock_return)

7.5 说明

本文主要是展示一下NumPy的一些数学、统计函数的基本使用,例子从Nasdaq下载的JD的股票历史交易价格数据,笔者非经济、金融行业从业人员,分析例程参考了一些书籍和博客,这样的分析方式可能根本不是行家们的方法,见笑谅解,如果有正确的、常用的、专业的分析股票的代码、书籍、示例或其他可发邮件给本人:mailliao艾特126点儿com。