您的当前位置:首页正文

Python玩转股票数据以及简单交易策略

2023-09-07 来源:好走旅游网
Python 玩转股票数据以及简单交易策略

前面的文档《Python获取股票历史数据并分析》详细说明如何获取股票数据,并进行了简单的分布分析。今天我们将详细讲解如何玩转历史数据,基础数据来源于《Python获取股票历史数据并分析》。为了取数和查询方便,我把所有的历史交易数据放在了sqlite3数据库文件中,这也是python自带的数据库,操作很方便。当然你也可以把数据放在其他数据库中。本文将使用Python来可视化股票数据,比如绘制K线图,并且探究各项指标的含义和关系,最后使用移动平均线方法初探投资策略。下面开始玩转数据,

数据导入

为了数据的存储和读取方便,我们预先把历史数据存在路径为'E:\\myprog\\TestData.db的sqlite文件中。要分析先从这个数据文件中读取。

我们把股票编码为600866的2017-02-01至2017-06-01的交易数据读取到stdata中。

以上显示了前9行数据,要得到数据的更多信息,可以使用.info()方法。它告诉我们该数据一共有72行,索引是时间格式,日期从2017-02-01至2017-06-01。总共有16列,并列出了每一列的名称和数据格式,并且没有缺失值。

除了index,code是object类型外,其他的都是float型。我们可以将index转化为datetime类型 stdata.index= pd.to_datetime(stdata.index) 变化后如下:

至此,我们完成了股票数据的导入和清洗工作,接下来将使用可视化的方法来观察这些数据。

数据观察

首先,我们观察数据的列名,其含义对应如下:

这些指标总体可分为两类:

 价格相关指标

 当日价格:开盘、收盘价,最高、最低价  价格变化:价格变动和涨跌幅  均价:5、10、20日均价  成交量相关指标

 成交量

 换手率:成交量/发行总股数×100%  成交量均量:5、10、20日均量

由于这些指标都是随时间变化的,所以让我们先来观察它们的时间序列图。

时间序列图

以时间为横坐标,每日的收盘价为纵坐标,做折线图,可以观察股价随时间的波动情况。这里直接使用DataFrame数据格式自带的做图工具,其优点是能够快速做图,并自动优化图形输出形式。 stdata[['close','turnover']].plot(figsize=(33,8),secondary_y='close',grid=True)

这是换手率、收盘价的时间序列图,右轴是收盘价,左轴是换手率,随着股价的从7.5跌倒4.8,换手率也下降了很多。

如果我们将每日的开盘、收盘价和最高、最低价以折线的形式绘制在一起,难免显得凌乱,也不便于分析。那么有什么好的方法能够在一张图中显示出这四个指标?这就是K线图。 K线图

相传K线图起源于日本德川幕府时代,当时的商人用此图来记录米市的行情和价格波动,后来K线图被引入到股票市场。每天的四项指标数据用如下蜡烛形状的图形来记录,不同的颜色代表涨跌情况。

Matplotlib.finance模块提供了绘制K线图的函数candlestick_ohlc(),但如果要绘制比较美观的K线图还是要下点功夫的。下面定义了pandas_candlestick_ohlc()函数来绘制适用于本文数据的K线图,其中大部分代码都是在设置坐标轴的格式。这里可以画日K线,周K线、月K线、年K线等4中K线图

下为日K线:

周K线:

这里红色代表上涨,绿色代表下跌。 相对变化量

股票中关注的不是价格的绝对值,而是相对变化量。有多种方式可以衡量股价的相对值,最简单的方法就是将股价除以初始时的价格。

stdata['return'] = stdata['close'] / stdata.close.iloc[0] stdata['return'].plot(grid=True)

第二种方法是计算每天的涨跌幅,但计算方式有两种:

这两者可能导致不同的分析结果,样例数据中的涨跌幅使用的是第一个公式,并乘上了100%。

stdata['p_change'].plot(grid=True,figsize=(12,7)).axhline(y=0, color='black', lw=2)

为了解决第二种方法中的两难选择,我们引入第三种方法,就是计算价格的对数之差,公式如下:

close_price = stdata['close']

log_change = np.log(close_price) - np.log(close_price.shift(1)) log_change.plot(grid=True).axhline(y=0,color='black',lw=2)

相关关系

在观察了价格的走势之后,我们来看看各指标之间的关系。下面挑选了部分代表性的指标,并使用pandas.scatter_matrix()函数,将各项指标数据两两关联做散点图,对角线是每个指标数据的直方图。

small = stdata[['close','price_change','ma20','volume','v_ma20','turnover']] pd.scatter_matrix(small)

如下图,图中可以明显发现成交量(volume)和换手率(turnover)有非常明显的线性关系,其实换手率的定义就是:成交量除以发行总股数,再乘以100%。所以下面的分析中我们将换手率指标去除,这里使用了相关性关系来实现

数据降维

上面的散点图看着有些眼花缭乱,我们可以使用numpy.corrcof()来直接计算各指标数据间的相关系数。

small = stdata[['close','price_change','ma20','volume','v_ma20','turnover']] cov = np.corrcoef(small.T)

cov

如果觉得看数字还是不够方便,我们继续将上述相关性矩阵转换成图形,如下图所示,其中用颜色来代表相关系数。我们发现位于(0,2)位置的相关系数非常大,查看数值达到0.89。这两个强烈正相关的指标是收盘价和20日均价。

img = plt.matshow(cov,cmap=plt.cm.winter) plt.colorbar(img,ticks=[-1,0,1]) plt.show()

以上我们用矩阵图表的方式在多个指标中迅速找到了强相关的指标。接着做出收盘价和成交量的折线图,因为它们的数值差异很大,所以我们采用两套纵坐标体系来做图。

stdata[['close','ma20']].plot(secondary_y='ma20', grid=True)

根据20日均线判断股价日后的走势极为重要。

股票交易策略

吴军老师曾讲述他的投资经验,大意是说好的投资方式不是做预测,而是能在合适的时机做出合适的应对和决策。同样股市也没法预测,我们能做的是选择恰当的策略应对不同的情况。

好的指标是能驱动决策的。在上面的分析中我们一直没有使用的一类指标是5、10、20日均价,它们又称为移动平均值,下面我们就使用这项指标来演示一个简单的股票交易策略。(警告:这里仅仅是演示说明,并非投资建议)

为了得到更多的数据来演示,我们使用pandas_datareader直接从雅虎中下载最近一段时间的谷歌股票数据。 import datetime

import pandas_datareader.dataasweb

# 设置股票数据的时间跨度

start = datetime.datetime(2016,10,1) end = datetime.date.today()

# 从yahoo中获取google的股价数据。

goog = web.DataReader(\"GOOG\",\"yahoo\",start,end)

#修改索引和列的名称,以适应本文的分析

goog.index.rename('date',inplace=True)

goog.rename(columns={'Open':'open','High':'high','Low':'low','Close':'close'},inplace=True)

goog.head()

数据中只有每天的价格和成交量,所以我们需要自己算出5日均价和10日均价,并将均价的折线图(也称移

动平均线)与K线图画在一起。

goog[\"ma5\"] = np.round(goog[\"close\"].rolling(window = 5,center = False).mean(),2) goog[\"ma20\"] = np.round(goog[\"close\"].rolling(window = 20,center = False).mean(),2) goog = goog['2017-01-01':]

pandas_candlestick_ohlc(goog,['ma5','ma20'])

如果,我们用以及存好的数据,则不需要计算ma5、ma20,

pandas_candlestick_ohlc(stdata,stick = \"day\

观察上图,我们发现5日均线与K线图较为接近,而20日均线则更平坦,可见移动平均线具有抹平短期波

动的作用,更能反映长期的走势。比较5日均线和20日均线,特别是关注它们的交叉点,这些是交易的时机。移动平均线策略,最简单的方式就是:当5日均线从下方超越20日均线时,买入股票,当5日均线从上方越到20日均线之下时,卖出股票。

为了找出交易的时机,我们计算5日均价和20日均价的差值,并取其正负号,作于下图。当图中水平线出现跳跃的时候就是交易时机。

goog['ma5-20'] = goog['ma5'] - goog['ma20'] goog['diff'] = np.sign(goog['ma5-20'])

goog['diff'].plot(ylim=(-2,2)).axhline(y=0,color='black',lw=2)

为了更方便观察,上述计算得到的均价差值,再取其相邻日期的差值,得到信号指标。当信号为1时,表示买入股票;当信号为-1时,表示卖出股票;当信号为0时,不进行任何操作。

goog['signal'] = np.sign(goog['diff'] - goog['diff'].shift(1)) goog['signal'].plot(ylim=(-2,2))

从上图中看出,从今年初到现在,一共有两轮买进和卖出的时机。到目前为止,似乎一切顺利,那么让我们看下这两轮交易的收益怎么样吧。

trade = pd.concat([

pd.DataFrame({\"price\": goog.loc[goog[\"signal\"] == 1,\"close\"], \"operation\": \"Buy\"}),

pd.DataFrame({\"price\": goog.loc[goog[\"signal\"] == -1,\"close\"], \"operation\": \"Sell\"})

])

trade.sort_index(inplace=True)

trade

上述表格列出了交易日期、操作和当天的价格。但很遗憾地发现,这两轮交易的卖出价都小于买入价,实际上按上述方法交易我们亏本了!!!

你是否很愤怒呢?原来分析到现在,都是假的呀!我之前就警告过,这里的分析只是演示移动平均线策略的思想,而并非真正的投资建议。股票市场是何其的复杂多变,又如何是一个小小的策略所能战胜的呢?

那么这个策略就一无是处吗?非也!如果考虑更长的时间跨度,比如5年、10年,并考虑更长的均线,比如将20日均线和50日均线比较;虽然过程中也有亏损的时候,但赢的概率更大。也就是说,在更长的时间尺度上该策略也是可行的。但即使你赚了,又能跑赢大盘吗?这时候还需用到其他方法,比如合理配置投资比例等。

还是那句话,股市有风险,投资需谨慎。本文不是分析股票的文章,而是借用股票数据来说明数据分析的基本方法,以及演示什么样的指标是好的指标。 源代码:

import pandas as pd

from matplotlib.dates import DateFormatter, WeekdayLocator,DayLocator, MONDAY,date2num from matplotlib.finance import candlestick_ohlc import numpy as np

import matplotlib.pyplot as plt from sqlalchemy import create_engine

dbpath='E:\\myprog\\TestData.db' #数据库文件完整路径 stockcode='600866' # 股票代码 begindate='2017-02-01' #交易开始日期 enddate='2017-06-01' #交易结束日期

engine= create_engine('sqlite:///{0}'.format(dbpath))

# 使用sqlalchemy连接数据库,python可以简单连接sqlite,但是为了方便数据库类型变更使用sqlalchemy,支持多种数据库

stdata = pd.read_sql_query(\"select * from stocks where code='{0}' and date between '{1}' and '{2}'\".format(stockcode,begindate,enddate),con= engine,index_col='date')

stdata.index= pd.to_datetime(stdata.index) stdata=stdata.sort_index()

stdata['return'] = stdata ['close'] / stdata.close.iloc[0] stdata['return'].plot(grid=True)

stdata['p_change'].plot(grid=True,figsize=(12,7)).axhline(y=0, color='black', lw=2) stdata[['close','turnover']].plot(figsize=(22,8),secondary_y='close',grid=True) close_price = stdata['close']

log_change = np.log(close_price) - np.log(close_price.shift(1))

log_change.plot(grid=True,figsize=(12,7)).axhline(y=0, color='black', lw=2) small = stdata[['close', 'price_change', 'ma20','volume', 'v_ma20', 'turnover']] #矩阵散点图

pd.scatter_matrix(small,figsize=(18,12))

small = stdata[['close', 'price_change', 'ma20','volume', 'v_ma20', 'turnover']] #相关性,相关系数 cov = np.corrcoef(small.T) plt.figure(figsize=(12,7))

img = plt.matshow(cov,cmap=plt.cm.winter) #相关性矩阵图

plt.colorbar(img, ticks=[-1,0,1]) plt.show()

#收盘价和Ma20的关系图

stdata[['close','ma20']].plot(secondary_y='ma20',figsize=(12,7), grid=True) def pandas_candlestick_ohlc(dat, stick = \"day\mondays = WeekdayLocator(MONDAY) # major ticks on the mondays

alldays = DayLocator() # minor ticks on the days #dayFormatter = DateFormatter('%d') # e.g., 12

# Create a new DataFrame which includes OHLC data for each period specified by stick input transdat = dat.loc[:,[\"open\ if (type(stick) == str): if stick == \"day\": plotdat = transdat

stick = 1 # Used for plotting elif stick in [\"week\ if stick == \"week\":

transdat[\"week\"] = pd.to_datetime(transdat.index).map(lambda x: x.isocalendar()[1]) # Identify weeks elif stick == \"month\":

transdat[\"month\"] = pd.to_datetime(transdat.index).map(lambda x: x.month) # Identify months transdat[\"year\"] = pd.to_datetime(transdat.index).map(lambda x: x.isocalendar()[0]) # Identify years grouped = transdat.groupby(list(set([\"year\variable

plotdat = pd.DataFrame({\"open\": [], \"high\": [], \"low\": [], \"close\": []}) # Create empty data frame containing what will be plotted for name, group in grouped:

plotdat = plotdat.append(pd.DataFrame({\"open\": group.iloc[0,0], \"high\": max(group.high), \"low\": min(group.low), \"close\": group.iloc[-1,3]}, index = [group.index[0]]))

if stick == \"week\": stick = 5 elif stick == \"month\": stick = 30 elif stick == \"year\": stick = 365

elif (type(stick) == int and stick >= 1):

transdat[\"stick\"] = [np.floor(i / stick) for i in range(len(transdat.index))] grouped = transdat.groupby(\"stick\")

plotdat = pd.DataFrame({\"open\": [], \"high\": [], \"low\": [], \"close\": []}) # Create empty data frame containing what will be plotted for name, group in grouped:

plotdat = plotdat.append(pd.DataFrame({\"open\": group.iloc[0,0], \"high\": max(group.High), \"low\": min(group.Low), \"close\": group.iloc[-1,3]}, index = [group.index[0]])) else:

raise ValueError('Valid inputs to argument \"stick\" include the strings \"day\or a positive integer')

# Set plot parameters, including the axis object ax used for plotting fig, ax = plt.subplots() fig.subplots_adjust(bottom=0.2)

if plotdat.index[-1] - plotdat.index[0] ax.xaxis.set_major_locator(mondays) ax.xaxis.set_minor_locator(alldays) else:

weekFormatter = DateFormatter('%b %d, %Y') ax.xaxis.set_major_formatter(weekFormatter) ax.grid(True)

# Create the candelstick chart

candlestick_ohlc(ax, list(zip(list(date2num(plotdat.index.tolist())), plotdat[\"open\"].tolist(), plotdat[\"high\"].tolist(),

plotdat[\"low\"].tolist(), plotdat[\"close\"].tolist())),

colorup = \"red\ # Plot other series (such as moving averages) as lines if otherseries != None: if type(otherseries) != list: otherseries = [otherseries]

dat.loc[:,otherseries].plot(ax = ax, lw = 1.3, grid = True) ax.xaxis_date() ax.autoscale_view() fig.set_figheight(6) fig.set_figwidth(10)

plt.setp(plt.gca().get_xticklabels(), rotation=45, horizontalalignment='right') plt.show()

pandas_candlestick_ohlc(stdata,stick = \"day\

因篇幅问题不能全部显示,请点此查看更多更全内容