天天看點

用Python淺析股票資料

用Python淺析股票資料

本文将使用Python來可視化股票資料,比如繪制K線圖,并且探究各項名額的含義和關系,最後使用移動平均線方法初探投資政策。

資料導入

這裡将股票資料存儲在

stockData.txt

文本檔案中,我們使用

pandas.read_table()

函數将檔案資料讀入成DataFrame格式。

其中參數

usecols=range(15)

限制隻讀取前15列資料,

parse_dates=[0]

表示将第一列資料解析成時間格式,

index_col=0

則将第一列資料指定為索引。

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

%matplotlib inline

%config InlineBackend.figure_format = 'retina'

%pylab inline

pylab.rcParams['figure.figsize'] = (10, 6) #設定繪圖尺寸

#讀取資料

stock = pd.read_table('stockData.txt', usecols=range(15), parse_dates=[0], index_col=0)

stock = stock[::-1] #逆序排列

stock.head()

用Python淺析股票資料

以上顯示了前5行資料,要得到資料的更多資訊,可以使用

.info()

方法。它告訴我們該資料一共有20行,索引是時間格式,日期從2015年1月5日到2015年1月30日。總共有14列,并列出了每一列的名稱和資料格式,并且沒有缺失值。

stock.info()           

<class 'pandas.core.frame.DataFrame'>

DatetimeIndex: 20 entries, 2015-01-05 to 2015-01-30

Data columns (total 14 columns):

open 20 non-null float64

high 20 non-null float64

close 20 non-null float64

low 20 non-null float64

volume 20 non-null float64

price_change 20 non-null float64

p_change 20 non-null float64

ma5 20 non-null float64

ma10 20 non-null float64

ma20 20 non-null float64

v_ma5 20 non-null float64

v_ma10 20 non-null float64

v_ma20 20 non-null float64

turnover 20 non-null float64

dtypes: float64(14)

memory usage: 2.3 KB

在觀察每一列的名稱時,我們發現'open'的列名前面似乎與其它列名不太一樣,為了更清楚地檢視,使用

.columns

得到該資料所有的列名如下:

stock.columns           

Index([' open', 'high', 'close', 'low', 'volume', 'price_change',

'p_change', 'ma5', 'ma10', 'ma20', 'v_ma5', 'v_ma10', 'v_ma20',

'turnover'], dtype='object')

于是發現'open'列名前存在多餘的空格,我們使用如下方法修正列名。

stock.rename(columns={' open':'open'}, inplace=True)           

至此,我們完成了股票資料的導入和清洗工作,接下來将使用可視化的方法來觀察這些資料。

資料觀察

首先,我們觀察資料的列名,其含義對應如下:

用Python淺析股票資料

這些名額總體可分為兩類:

 ●  價格相關名額

 ●  當日價格:開盤、收盤價,最高、最低價

 ●  價格變化:價格變動和漲跌幅

 ●  均價:5、10、20日均價

 ●  成交量相關名額

 ●  成交量

 ●  換手率:成交量/發行總股數×100%

 ●  成交量均量:5、10、20日均量

由于這些名額都是随時間變化的,是以讓我們先來觀察它們的時間序列圖。

時間序列圖

以時間為橫坐标,每日的收盤價為縱坐标,做折線圖,可以觀察股價随時間的波動情況。這裡直接使用DataFrame資料格式自帶的做圖工具,其優點是能夠快速做圖,并自動優化圖形輸出形式。

stock['close'].plot(grid=True)           
用Python淺析股票資料

如果我們将每日的開盤、收盤價和最高、最低價以折線的形式繪制在一起,難免顯得淩亂,也不便于分析。那麼有什麼好的方法能夠在一張圖中顯示出這四個名額?答案下面揭曉。

K線圖

相傳K線圖起源于日本德川幕府時代,當時的商人用此圖來記錄米市的行情和價格波動,後來K線圖被引入到股票市場。每天的四項名額資料用如下蠟燭形狀的圖形來記錄,不同的顔色代表漲跌情況。

用Python淺析股票資料

圖檔來源:http://wiki.mbalib.com/wiki/K線理論

Matplotlib.finance子產品提供了繪制K線圖的函數

candlestick_ohlc()

,但如果要繪制比較美觀的K線圖還是要下點功夫的。下面定義了

pandas_candlestick_ohlc()

函數來繪制适用于本文資料的K線圖,其中大部分代碼都是在設定坐标軸的格式。

from matplotlib.finance import candlestick_ohlc

from matplotlib.dates import DateFormatter, WeekdayLocator,DayLocator, MONDAY

def pandas_candlestick_ohlc(stock_data,otherseries=None):

# 設定繪圖參數,主要是坐标軸

mondays = WeekdayLocator(MONDAY)

alldays = DayLocator()

dayFormatter = DateFormatter('%d')

fig, ax = plt.subplots()

fig.subplots_adjust(bottom=0.2)

if stock_data.index[-1] - stock_data.index[0] < pd.Timedelta('730 days'):

weekFormatter = DateFormatter('%b %d')

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)

# 建立K線圖

stock_array = np.array(stock_data.reset_index()[['date','open','high','low','close']])

stock_array[:,0] = date2num(stock_array[:,0])

candlestick_ohlc(ax, stock_array, colorup = "red", colordown="green", width=0.4)

# 可同時繪制其他折線圖

if otherseries is not None:

for each in otherseries:

plt.plot(stock_data[each], label=each)

plt.legend()

ax.xaxis_date()

ax.autoscale_view()

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

plt.show()

pandas_candlestick_ohlc(stock)           
用Python淺析股票資料

這裡紅色代表上漲,綠色代表下跌。

相對變化量

股票中關注的不是價格的絕對值,而是相對變化量。有多種方式可以衡量股價的相對值,最簡單的方法就是将股價除以初始時的價格。

用Python淺析股票資料

stock['return'] = stock['close'] / stock.close.iloc[0]

stock['return'].plot(grid=True)

用Python淺析股票資料

第二種方法是計算每天的漲跌幅,但計算方式有兩種:

用Python淺析股票資料

這兩者可能導緻不同的分析結果,樣例資料中的漲跌幅使用的是第一個公式,并乘上了100%。

stock['p_change'].plot(grid=True).axhline(y=0, color='black', lw=2)           
用Python淺析股票資料

為了解決第二種方法中的兩難選擇,我們引入第三種方法,就是計算價格的對數之差,公式如下:

用Python淺析股票資料

close_price = stock['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)

用Python淺析股票資料

相關關系

在觀察了價格的走勢之後,我們來看看各名額之間的關系。下面挑選了部分代表性的名額,并使用

pandas.scatter_matrix()

函數,将各項名額資料兩兩關聯做散點圖,對角線是每個名額資料的直方圖。

small = stock[['close', 'price_change', 'ma20','volume', 'v_ma20', 'turnover']]

_ = pd.scatter_matrix(small)

用Python淺析股票資料

圖中可以明顯發現成交量(volume)和換手率(turnover)有非常明顯的線性關系,其實換手率的定義就是:成交量除以發行總股數,再乘以100%。是以下面的分析中我們将換手率名額去除,這裡使用了相關性關系來實作資料降維。

上面的散點圖看着有些眼花缭亂,我們可以使用

numpy.corrcof()

來直接計算各名額資料間的相關系數。

small = stock[['close', 'price_change', 'ma20','volume', 'v_ma20']]

cov = np.corrcoef(small.T)

cov

array([[ 1. , 0.30308764, 0.10785519, 0.91078009, -0.37602193],

[ 0.30308764, 1. , -0.45849273, 0.3721832 , -0.25950305],

[ 0.10785519, -0.45849273, 1. , -0.06002202, 0.51793654],

[ 0.91078009, 0.3721832 , -0.06002202, 1. , -0.37617624],

[-0.37602193, -0.25950305, 0.51793654, -0.37617624, 1. ]])

如果覺得看數字還是不夠友善,我們繼續将上述相關性矩陣轉換成圖形,如下圖所示,其中用顔色來代表相關系數。我們發現位于(0,3)位置的相關系數非常大,檢視數值達到0.91。這兩個強烈正相關的名額是收盤價和成交量。

img = plt.matshow(cov,cmap=plt.cm.winter)

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

plt.show()

用Python淺析股票資料

以上我們用矩陣圖表的方式在多個名額中迅速找到了強相關的名額。接着做出收盤價和成交量的折線圖,因為它們的數值差異很大,是以我們采用兩套縱坐标體系來做圖。

stock[['close','volume']].plot(secondary_y='volume', grid=True)           
用Python淺析股票資料

觀察這兩個名額的走勢,在大部分時候股價上漲,成交量也上漲,反之亦然。但個别情況下則不成立,可能是成交量受到前期的慣性影響,或者還有其他因素。

移動平均線

吳軍老師曾講述他的投資經驗,大意是說好的投資方式不是做預測,而是能在合适的時機做出合适的應對和決策。同樣股市也沒法預測,我們能做的是選擇恰當的政策應對不同的情況。

好的名額是能驅動決策的。在上面的分析中我們一直沒有使用的一類名額是5、10、20日均價,它們又稱為移動平均值,下面我們就使用這項名額來示範一個簡單的股票交易政策。(警告:這裡僅僅是示範說明,并非投資建議。)

為了得到更多的資料來示範,我們使用

pandas_datareader

直接從雅虎中下載下傳最近一段時間的谷歌股票資料。

import datetime

import pandas_datareader.data as web

# 設定股票資料的時間跨度

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()

用Python淺析股票資料

資料中隻有每天的價格和成交量,是以我們需要自己算出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'])

用Python淺析股票資料

觀察上圖,我們發現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)

用Python淺析股票資料

為了更友善觀察,上述計算得到的均價內插補點,再取其相鄰日期的內插補點,得到信号名額。當信号為1時,表示買入股票;當信号為-1時,表示賣出股票;當信号為0時,不進行任何操作。

goog['signal'] = np.sign(goog['diff'] - goog['diff'].shift(1))

goog['signal'].plot(ylim=(-2,2))

用Python淺析股票資料

從上圖中看出,從今年初到現在,一共有兩輪買進和賣出的時機。到目前為止,似乎一切順利,那麼讓我們看下這兩輪交易的收益怎麼樣吧。

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

用Python淺析股票資料

上述表格列出了交易日期、操作和當天的價格。但很遺憾地發現,這兩輪交易的賣出價都小于買入價,實際上按上述方法交易我們虧本了!!!

你是否很憤怒呢?原來分析到現在,都是假的呀!我之前就警告過,這裡的分析隻是示範移動平均線政策的思想,而并非真正的投資建議。股票市場是何其的複雜多變,又如何是一個小小的政策所能戰勝的呢?

那麼這個政策就一無是處嗎?非也!如果考慮更長的時間跨度,比如5年、10年,并考慮更長的均線,比如将20日均線和50日均線比較;雖然過程中也有虧損的時候,但赢的機率更大。也就是說,在更長的時間尺度上該政策也是可行的。但即使你賺了,又能跑赢大盤嗎?這時候還需用到其他方法,比如合理配置投資比例等。

還是那句話,股市有風險,投資需謹慎。本文不是分析股票的文章,而是借用股票資料來說明資料分析的基本方法,以及示範什麼樣的名額是好的名額。

原文釋出時間為:2018-11-18

本文作者:魚心DrFish

本文來自雲栖社群合作夥伴“

Python愛好者社群

”,了解相關資訊可以關注“

”。