2017-12-20

Python Tutorial–Lesson: 簡單的金融案例

這篇,我們透過實作一個簡單的財務金融案例-「台灣股市資料的取得和呈現」來了解幾個對財務金融分析很有用的 Python 套件的使用方式。


學習目標:
◆ 套件 scipy, matplotlib
◆ 取得台灣股市資料並呈現
◆ 如何合併多個股市資料檔案
◆ 呈現股價走勢圖
◆ 將股市資料存入資料庫中

Python 的社群提供了非常多很有用的套件(module),我們實作一個簡單的財務金融案例,讀取台灣股市資料,並以圖形化的方式呈現這些資料,來運用並了解這幾個對財務金融分析很有用的 Python 套件。
第一個要介紹的是 SciPy。
若您使用的是 Anaconda,SciPy 已預安裝好了,可以直接 import 使用。若是在 import scipy 時出現錯誤訊息,表示 scipy 套件尚未安裝。因為 Python 的 NumPy, SciPy, Pandas, Matplotlib 這幾個套件間有相依關係(dependence)存在,不太容易安裝,建議您安裝 Anaconda 比較省事。可以參考《Python 超強套件包 - Anaconda Distribution》這篇安裝 Anaconda。
SciPy 中有好幾個財務功能如:PV, FV, NPV, IRR 的計算,我們先從這些簡單的開始:
PV (現值, present value)、FV (終值/未來值, future value) 相信大家都了解他們的概念,簡單的說就是現在的一筆金額(PV, 現值)在未來某個時點的價值(FV, 終值),可以用這個公式表示:
FV = PV * (1+rate)n
這是 SciPy FV function 的使用方式:fv(rate, nper, pmt, pv, when='end')
scipy.fv() 會回傳現值(pv)以利率(rate)計算幾期(nper)後的終值,完整的參數說明如下:
Parameters:
rate : 每期利率,輸入數值而非百分比。
nper : 複利期數。
pmt : 每期年金。
pv : 現值。
when : 期初年金(when='begin' / 1)或期末年金(when='end' / 0),預設為期末。
這個例子,期初存入銀行 10,000元,年利率 10%,每年複利計算一次,請問6年後可領回多少?這是基本未來值的計算:pv = 10000;nper = 6;rate = 10%

# Python finance examples  @author: Sean Lu.
import scipy as sp
sp.fv(0.1,6,0,10000)

您會發現計算出來的答案是負值 -17715.610000000008,這是因為 scipy.fv() 功能把現值當現金流的投入,所以要輸入負的,改成這樣,答案就變正值(17715.610000000008)了。

# Python finance examples  @author: Sean Lu.
import scipy as sp
sp.fv(0.1,6,0,-10000)  # pv是現金投入,加負號。

這裡的回傳值小數點以下的位數很多,這是因為浮點數精確度的問題,可使用 round(x [,n]) 四捨五入功能處理,這個功能會回傳 x 的小數點四捨五入到第 n 個數字,參數 x:待四捨五入的數值,參數 n:小數點下的位數。
既然有 pmt: 每期年金,所以這個功能也可以計算年金的未來值。 例如,每年均於年終時存入銀行1,000元,總共存了6年,年利率10%,每年複利計算一次,到了第六年終時,總共可領回多少錢?這時 pmt = 1000;nper = 6;rate = 10%,記得 pmt 是每年年金投入要加負號!

# Python finance examples  @author: Sean Lu.
import scipy as sp
print(round(sp.fv(0.1,6,-1000,0),2))  # pmt是年金投入,加負號,並四捨五入至小數點下第2位。

答案是可領回 7,715.61元。若是每年均於期初時存入,就再加上參數 when='begin' 或 1,如 sp.fv(0.1,6,-1000,0,1)。如果忘記了使用方法或參數,可以用這個方式查詢:

# Python finance examples  @author: Sean Lu.
import scipy as sp
help(sp.fv)

同樣可以用 help() 功能來了解 scipy.pv() 計算現值。
SciPy PV function 的使用方式:pv(rate, nper, pmt, fv=0.0, when='end')
參數和 fv() 相同,只是 pv, fv 對調而已。
Parameters:
rate : 每期利率,輸入數值而非百分比。
nper : 複利期數。
pmt : 每期年金。
fv : 終值。
when : 期初年金(when='begin' / 1)或期末年金(when='end' / 0),預設為期末。
計算看看這個例子年利率 10%,每年複利計算一次,現在要存多少錢,6年後可領回 17,716?這是現值計算:fv = 17716;nper = 6;rate = 10%;

# Python finance examples  @author: Sean Lu.
import scipy as sp
print(sp.pv(0.1,6,0,17716))

答案是 -10,000,表示期初要投入 10,000元。再加上年金的概念,若每月均於月底需償還本息 10,000元,總共繳了 12個月,年利率 2%,每月複利一次,當時借款多少?pmt = 10000;nper = 12;rate = 2%/12;

# Python finance examples  @author: Sean Lu.
import scipy as sp
sp.pv(0.02/12,12,-10000)

答案是 118,710元。
Net Present Value (NPV, 淨現值)也是很常用的運算,淨現值是指投資後未來獲利的現金流量,全部折現成投資日的價值並減去投資日投入的成本,來了解投資長期的獲利究竟是正的還是負的,它的公式如下:
凈現值 = 未來現金淨流量現值 - 原始投資額現值
SciPy npv() 使用方式為:npv(rate, values)
SciPy npv() 回傳未來現金淨流量(values)以利率(rate)折現後的淨現值,參數如下:
Parameters:
rate : 利率 / 折現率
values : 未來現金淨流量的 list
如果有一個投資方案要投入 100,000元,第2年起每年可領回 30,000元,連續 4年,假設當時定存利率為 10%,這樣的投資方案值不值得呢?

# Python finance examples  @author: Sean Lu.
import scipy as sp
print(round(sp.npv(0.1,[-100000, 30000, 30000, 30000, 30000]),2))

計算發現淨現值 NPV 為 -4904.04元,以 10% 的利率水準來評估,這個投資方案並不比定存好喔!
再介紹 Internet Rate of Return (IRR, 內部報酬率),IRR 和 NPV 是兩個常用來評估投資的方法,內部報酬率(IRR)的涵意為:如果讓投資後未來獲利的現金流量,全部折現成投資日的價值並減去投資日投入的成本,也就是淨現值NPV等於 0 時,利率/報酬率是多少。如果計算出的內部報酬率(IRR)大於當時的利率,就表示這個方案有大於當時利率的報酬率。
SciPy irr() 使用方式為:irr(values)
SciPy irr() 回傳未來現金淨流量(values)的內部報酬率(IRR),參數如下:
Parameters:
values : 未來現金淨流量的 list
同樣用剛剛那個例子某投資方案要投入 100,000元,第2年起每年可領回 30,000元,連續 4年,那內部報酬率(IRR)是多少?

# Python finance examples  @author: Sean Lu.
import scipy as sp
print(round(sp.irr([-100000, 30000, 30000, 30000, 30000]),5))

答案是 0.07714 = 7.714%,若當時的定存利率為 10%,那會低於定存獲利!
我們只介紹 SciPy 中幾個簡單的財務公式,但 SciPy 的功能非常多,可以到這裡來了解有那些功能 SciPy API Reference,記得可以用 help(sp.functionName) 來查詢它們的使用方式。
接下來要介紹 Python 一個強大的繪製圖表套件 matplotlib。
同樣的建議您安裝 Anaconda,matplotlib 套件就會預設安裝好了。先簡單的畫一個折線圖(line chart):

# Python matplotlib.pyplot examples  @author: Sean Lu.
import matplotlib.pyplot as plt
plt.plot([1,3,9,20])  # 繪製折線圖 (x,y)=(0,1),(1,3),(2,9),(3,20)
plt.show()  # 顯示



import matplotlib.pyplot 後,plot() 功能裡的參數是 y 軸,而 x 軸就是第幾位 (0,1,2,3,...)。所以 plt.plot([1,3,9,20]) 的 (x,y)=(0,1),(1,3),(2,9),(3,20),plt.show() 就會顯示折線圖。
也可以這樣使用 plot(x,y),x 是 x 軸的數值 list;y 是 y 軸的數值 list,像這個例子:

# Python matplotlib.pyplot examples  @author: Sean Lu.
import matplotlib.pyplot as plt
plt.plot([0.5, 1, 2, 4], [1, 5, 9, 13])  # 繪製折線圖 (x,y)=(0.5,1),(1,5),(2,9),(4,13)
plt.show()  # 顯示



也可以畫散佈圖(Scatter Diagram),如下:

# Python matplotlib.pyplot examples  @author: Sean Lu.
import matplotlib.pyplot as plt
import random
# 自訂 randomList(number) 函數將回傳 number 個介於0和1的亂數
def randomList(number):
    rList=[]
    for i in range(number):
        rList.append(random.random())
    return rList
dotN=20  # 繪製 20個點
x=randomList(dotN)  # 回傳 n個值在 0 ~ 1 間的array為x軸
y=randomList(dotN)  # 回傳 n個值在 0 ~ 1 間的array為y軸
plt.scatter(x,y)  # 繪製散佈圖
plt.show()  # 顯示     


結合前面的例子,我們來計算看看一個投資金額為 100萬元,之後每期獲利為40萬元、50萬元、60萬元的投資方案其 NPV 隨利率變動的狀況:

# Python matplotlib.pyplot examples  @author: Sean Lu.
import scipy as sp
import matplotlib.pyplot as plt
cash_flow=[-100,40,50,60]
rate=[]
npv=[]
x=(0,0.5)
y=(0,0)
for i in range(1,50):
    rate.append(0.01*i)
    npv.append(sp.npv(0.01*i,cash_flow))
plt.plot(rate,npv)  # 隨折現率變動的NPV
plt.plot(x,y)  # NPV=0的基準線
plt.show()  # 顯示

不難理解 NPV 會隨利率增加而下降,當時的利率水準越高,NPV 就會越低,中間的 plt.plot(x,y) 是為了顯示 NPV=0 時的基準線,兩線交會的地方,就是內部報酬率(IRR)。我們計算看看現金流 cash_flow=[-100,40,50,60] 的內部報酬率(IRR)驗證一下是否相等:

# Python finance examples  @author: Sean Lu.
import scipy as sp
cash_flow=[-100,40,50,60]
print(round(sp.irr(cash_flow),5))

計算結果 IRR=0.21648 和圖中 y=0 交叉點位置的 x 軸座標是不是很接近!

有了對套件 scipy, matplotlib 的基本認識後,我們來進行一個簡單的金融案例,這個案例中我們將結合套件 os, csv, datetime, matplotlib 逐步完成以下工作並解決可能遭遇的問題:
◆ 取得公開的台灣股市資料
◆ 將單一股票單個資料檔讀入 Python
◆ 如何以 Python 合併單一股票多個資料檔成為長期資料?
◆ 如何同時處理多個股票的長期資料?
◆ 繪製單一及多個股票股價的走勢圖
◆ 將取得的股票資料存入資料庫中
◆ 將資料庫中的股票資料取出
接下來就一步一步進行…
◆ 取得公開的台灣股市資料
Python 有不少套件可以取得台灣和美國的股市資料如:grs, twstock 以及 yahoo-finance 等,不過這些套件都有頼開發者的長期維護,因為股市公開資料網站是會變動的,一旦資料公開的方式改變,套件就可能必須隨之調整,所以這些套件的開發者是很辛苦的。這篇我們不介紹使用這些套件,而是用最基本的方式 - 下載歷史股價資料檔,然後用 Python 讀入這些資料。這是最基本的方式,一定可行,不怕資料公開的方式改變,而且這個方法也是抓取股市資料套件開發的基礎知識。
下載台灣股市資料的來源是-TWSE 臺灣證券交易所,由「交易資訊」 --> 「盤後資訊」 --> 「個股日成交資訊」就可以股票代碼查詢某年月的個股日成交資訊,並下載 CSV檔。


我們任意輸入股票代碼及時間,按左上角的「CSV 下載」下載「個股日成交資訊」檔案。
◆ 將單一股票單個資料檔讀入 Python
接下來我們要將這個 CSV檔案讀入 Python 中。先將 CSV檔案用 NotePad++, Visual Studio Code 等程式編輯器開啟,一方面可以用這些編輯器檢視使用的編碼 (Encoding),另方面我們檢查一下 CSV檔的格式是否都正確,並將我們要的數值資料以外的東西,連同標題都刪除,但要記住資料欄位的順序喔!就可以用以下的程式透過 CSV 套件以 csv.reader() 將 CSV檔讀入。要注意的地方是檔案使用的編碼,可以在 open() 中以 encoding='utf-8' 來設定為使用 utf-8 編碼。

# Get one csv file @author: Sean Lu.
import csv
myFile=open('201711_2002_DAY.csv')  # open CSV file encoding='Big5'
# myFile=open('201711_1216_DAY.csv',encoding='utf-8')  # open CSV file encoding='utf-8'
myReader=csv.reader(myFile)  # csv reader object
myDataList=list(myReader)  # 將 csv reader object 轉成 list
myFile.close()  # close the file
for i in myDataList:  # 顯示合併完成的股市資料
    print(i)

◆ 如何以 Python 合併單一股票多個資料檔成為長期資料?
讀入一個 CSV檔很容易,那怎麼讀入一支股票不同時間的 TWSE 臺灣證券交易所 CSV檔,並合併成為個股的長期資料?我們將個股以年月區分的 CSV檔用前述方式處理後,以年月+股票代號命名檔案,如 201709_1201_DAY.csv 表示為 2017年9月股票代碼1201的個股日成交資訊,將該股票多個檔案放到同一目錄下,以 OS 套件的 os.listdir(filePath) 將該目錄內多個檔案讀入後,一個一個合併起來。

# Get multiple csv files for one stock, using CSV module @author: Sean Lu.
import os
import csv

filePath='.\\stocks1'  # 放 csv 檔的目錄位置,可以使用絕對路徑/相對路徑
fileList=[]  # 存放多個檔案
myDataList=[]  # 存放合併後的資料
for i in os.listdir(filePath):  # listdir() 將裡面的資料都取出
    if os.path.splitext(i)[-1]=='.csv':  # 如果是 .csv 檔才進一步處理
        fileList.append(i)  # 將 .csv 檔存入 fileList 中

print('取得 {0}個csv檔,開始讀取檔案…'.format(len(fileList)))
for i in fileList:
    dataTemp=[]
    print('讀取 {0} …'.format(i),end=' ')
    myFile=open(os.path.join(filePath,i))  # open CSV file
    myReader=csv.reader(myFile)  # csv reader object
    dataTemp=list(myReader)  # csv reader轉成list後暫存至dataTemp中
    myDataList.extend(dataTemp)  # 以extend()合併至myDataList中
    myFile.close()
    print('完成!')
for i in myDataList:  # 顯示合併完成的股市資料
    print(i)

◆ 如何同時處理多個股票的長期資料?
接下來要處理多個股票不同時間的檔案,一樣用前述年月+股票代碼的檔案命名方式,將不同個股的 csv檔案都存入目錄中,我們加一段程式把檔名的股票代碼取出,並在資料中多存入一個股票代碼 stock_Id 欄以區分不同股票的資料。insert(0,stockId) 會將 stockId 加到股票資料 list 的第一欄;append(stockId) 則會加到股票資料 list 的最後一欄。

# Get multiple csv files for two stock, using CSV module @author: Sean Lu.
import os
import csv
filePath='.\\stocks2'  # 放 csv檔的位置,可以用絕對路徑/相對路徑
fileList=[]  # 取得待讀取檔案 List
myDataList=[]  # 存放股市資料 List
for i in os.listdir(filePath):  # listdir() 將裡面的資料都取出
    if os.path.splitext(i)[-1]=='.csv':  # 如果是 .csv檔才進一步處理
        fileList.append(i)  # 將 .csv檔存入fileList中

print('取得 {0}個csv檔,開始讀取檔案…'.format(len(fileList)))
for i in fileList:
    dataTemp=[]  # 暫存股市資料 List
    print('讀取 {0} …'.format(i),end=' ')
    myFile=open(os.path.join(filePath,i))  # open CSV file
    myReader=csv.reader(myFile)  # csv reader object
    dataTemp=list(myReader)  # csv reader object轉成list
    stockId=i[7:11]  # 將檔名內的個股代碼取出存入 stockId 中以區別個股
    for i in dataTemp:
        i.insert(0,stockId)  # 將 stockId 加到暫存資料第一欄
#         i.append(stockId)  # 將 stockId 加到暫存資料最後一欄
    myDataList.extend(dataTemp)  # 將暫存資料加到股市資料 List後
    myFile.close()  # 關閉CSV檔
    print('完成!')
for i in myDataList:  # 顯示合併完成的股市資料
    print(i)

◆ 繪製單一及多個股票股價的走勢圖
多個股票的長期資料都讀入 Python 中了,我們可以使用 matplotlib 套件來繪製圖形,但在繪圖前還必須作一個資料的加工。因為繪製股價走勢圖,X軸的資料是時間,我們必須先將 TWSE 臺灣證券交易所資料裡的民國年月日轉換成西元年月日,再以 datetime 套件的 datetime.datetime.strptime().date() 將年月日字串轉換成 Python 日期物件,matplotlib 才看得懂日期資料。以下的程式可以看到,我們以begDate=datetime.date(2017,10,1)、endDate=datetime.date(2017,11,1)來控制要取出的時間區間,建立X軸時間資料list,Y軸不同個股的收盤價list。matplotlib.pyplot 繪製趨勢圖的程式還蠻固定的,您可以從註解了解該指令的用途。這個例子裡我們繪製了3條走勢折線,分別是股票代碼2002收盤價、股票代碼1201股票開盤價、股票代碼1201收盤價。您可以任意繪製多條折線,只要建立好資料List,然後用 plt.plot(x,y) 繪圖即可。

# Get multiple csv files for two stock, using CSV module @author: Sean Lu.
import os
import csv
import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

filePath='.\\stocks2'  # 放 csv檔的位置,可以用絕對路徑/相對路徑
fileList=[]  # 取得待讀取檔案 List
myDataList=[]  # 存放股市資料 List
for i in os.listdir(filePath):  # listdir() 將裡面的資料都取出
    if os.path.splitext(i)[-1]=='.csv':  # 如果是 .csv檔才進一步處理
        fileList.append(i)  # 將 .csv檔存入fileList中

print('取得 {0}個csv檔,開始讀取檔案…'.format(len(fileList)))
for i in fileList:
    dataTemp=[]  # 暫存股市資料 List
    print('讀取 {0} …'.format(i),end=' ')
    myFile=open(os.path.join(filePath,i))  # open CSV file
    myReader=csv.reader(myFile)  # csv reader object
    dataTemp=list(myReader)  # csv reader object轉成list
    stockId=i[7:11]  # 將檔名內的個股代碼取出存入 stockId 中以區別個股
    for i in dataTemp:
        i.insert(0,stockId)  # 將 stockId 加到暫存資料第一欄
#         i.append(stockId)  # 將 stockId 加到暫存資料最後一欄
    myDataList.extend(dataTemp)  # 將暫存資料加到股市資料 List後
    myFile.close()  # 關閉CSV檔
    print('完成!')

# 設定處理資料日期區間
begDate=datetime.date(2017,10,1)
endDate=datetime.date(2017,11,1)
x=[]  # x軸的資料-日期
y2002_e=[]  # y軸的資料-2002收盤價
y1201_o=[]  # y軸的資料-1201開盤價
y1201_e=[]  # y軸的資料-1201收盤價

# 取出日期將民國年轉換為西元年格式
for i in myDataList:
    date_str=i[1]  #取得日期欄
    date_str=date_str.replace(date_str[0:3],str(int(date_str[0:3])+ 1911))
    i[1]=datetime.datetime.strptime(date_str,'%Y/%m/%d').date()  # 轉換成Python date物件並update i[1]
    if i[1]>=begDate and i[1]<endDate and i[0]=='2002':  # 取出日期區間內某股票資料
        x.append(i[1])  # 建立 x軸資料-日期
        y2002_e.append(i[7])  # 建立 y軸資料-2002收盤價
    if i[1]>=begDate and i[1]<endDate and i[0]=='1201':  # 取出日期區間內某股票資料
        y1201_o.append(i[4])  # 建立 y軸資料-1201開盤價
        y1201_e.append(i[7])  # 建立 y軸資料-1201收盤價
plt.title('2017 Stock Price',fontsize=18)  # 圖形標題 title
plt.xlabel('Date',fontsize=14)  # x軸標題
plt.ylabel('Price',fontsize=14)  # y軸標題
plt.text(datetime.date(2017,10,5),19,'1201-End Price',fontsize=16,bbox=dict(facecolor='purple', alpha=0.1))  # 標示個股1201收盤價,alpha參數是透明度
plt.text(datetime.date(2017,10,5),19.7,'1201-Open Price',fontsize=16,bbox=dict(facecolor='red', alpha=0.1))  # 標示個股1201收盤價,alpha參數是透明度
plt.text(datetime.date(2017,10,5),24.3,'2002-End Price',fontsize=16,bbox=dict(facecolor='green', alpha=0.1))  # 標示個股2002收盤價,alpha參數是透明度
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d'))  # 設定X軸日期格式
plt.gca().xaxis.set_major_locator(mdates.DayLocator())
plt.gcf().set_size_inches(25, 15, forward=True)  # 設定圖形長寬大小
plt.plot(x,y2002_e)  # 繪製y軸資料-2002收盤價走勢圖
plt.plot(x,y1201_o)  # 繪製y軸資料-1201開盤價走勢圖
plt.plot(x,y1201_e)  # 繪製y軸資料-1201收盤價走勢圖
plt.gcf().autofmt_xdate()  # 按日期格式建立X軸
plt.show()

◆ 將取得的股票資料存入資料庫中
從 TWSE 臺灣證券交易所的 CSV檔獲得了多個股票的長期資料,但不能每次要處理資料都重新讀檔再整理,這樣會很花時間。我們可以把處理好的資料寫進資料庫中,方便日後進一步處理。這裡我們運用 MongoDB 提供的 mLab 雲端資料庫,示範怎麼把已讀入的股市資料存入資料庫裡,再依需要由資料庫取出。請參考《mLab - DBaaS 雲端 MongoDB 與 Python 快速試作範例》這篇,申請您的 mLab 雲端資料庫,並建立資料庫、Collection(資料表)和User(資料庫使用者)以取得您的 MongoDB URI。
Python 可使用 pymongo 套件進行 MongoDB 資料庫操作,pymongo 非 Anaconda 預設安裝套件,必須自行安裝。開啟「Anacoda Prompt」輸入指令 conda install -c anaconda pymongo,按 y 執行安裝後,就可以使用了。
因為我們要新增多筆資料進資料庫,依照 pymongo 文件說明,在新增大量資料時,應使用 Pymongo Bulk Inserts 方法,所以我們以 Bulk Insert 和 Pymongo insert_one() 方法來作比較,看看兩者的新增速度。126筆資料以 Pymongo Bulk Inserts 方法新增花費時間 0.5942504405975342秒;以 Pymongo insert_one() 方法新增花費時間 21.127482891082764秒,相差 35.56倍,真的差異蠻大的。

# Get multiple csv files for multiple stock and insert data into MongoDB @author: Sean Lu.
import os
import csv
import datetime

filePath='.\\stocks2'  # 放 csv 檔的位置,可以用絕對路徑/相對路徑
fileList=[]  # 取得待讀取檔案 List
myDataList=[]  # 存放股市資料 List
for i in os.listdir(filePath):  # listdir() 將裡面的資料都取出
    if os.path.splitext(i)[-1]=='.csv':  # 如果是 .csv檔才進一步處理
        fileList.append(i)  # 將 .csv檔存入fileList中

print('取得 {0}個csv檔,開始讀取檔案…'.format(len(fileList)))
for i in fileList:
    dataTemp=[]  # 暫存股市資料 List
    print('讀取 {0} …'.format(i),end=' ')
    myFile=open(os.path.join(filePath,i))  # open CSV file
    myReader=csv.reader(myFile)  # csv reader object
    dataTemp=list(myReader)  # csv reader object轉成List
    stockId=i[7:11]  # 將檔名內的個股代碼取出存入 stockId 中以區別個股
    for i in dataTemp:
        i.insert(0,stockId)  # 將 stockId 加到第一欄
#         i.append(stockId)  # 將 stockId 加到最後一欄
    myDataList.extend(dataTemp)
    myFile.close()
    print('完成!')

#---------------------連接 mongoDB mLab 雲端資料庫---------------------
#-----mongoDB 宣告
import pymongo
from pymongo import MongoClient
myClient = MongoClient("mongodb://mongouser:mongouser@ds135537.mlab.com:35537/trymongodb")  # MongoDB URI-etf_project
myDB = myClient.trymongodb  # 資料庫 trymongodb object
myColl_stock = myDB.stockdata  # 資料表 collection - stockdata

#-----測試 mongoDB 資料庫連接
if myColl_stock.count() >= 0:
    print('mongoDB(trymongodb)資料庫連接成功!')
else:
    print('mongoDB(trymongodb)資料庫連接失敗!')

import time
startTime=time.time()  # 執行開始時間
print('資料 {0}筆,開始存入資料庫…'.format(len(myDataList)))

# 先刪除資料表內所有資料
myResult = myColl_stock.delete_many({})
print('刪除 {0}筆資料庫已有資料…'.format(myResult.deleted_count))   # 幾筆資料被刪除

#-----Pymongo Bulk Inserts方法
bulkData=[]
for i in myDataList:
    date_str=i[1]  # 取得日期欄
    date_str=date_str.replace(date_str[0:3],str(int(date_str[0:3])+ 1911))  # 民國年-->西元年
    i[1]=datetime.datetime.strptime(date_str,'%Y/%m/%d')  # 轉換成 Python datetime 物件 update i[1]
    bulkData.append({'stockid':i[0],'date':i[1],'ts':i[2],'to':i[3],'open':i[4],'high':i[5],'low':i[6],'close':i[7],'Spread':i[8],'volume':i[9]})
myColl_stock.insert_many(bulkData)

# #-----Pymongo insert_one()方法
# for i in myDataList:
#     date_str=i[1]  # 取得日期欄
#     date_str=date_str.replace(date_str[0:3],str(int(date_str[0:3])+ 1911))  # 民國年-->西元年
#     i[1]=datetime.datetime.strptime(date_str,'%Y/%m/%d')  # 轉換成 Python datetime 物件 update i[1]
#     myColl_stock.insert_one({'stockid':i[0],'date':i[1],'ts':i[2],'to':i[3],'open':i[4],'high':i[5],'low':i[6],'close':i[7],'Spread':i[8],'volume':i[9]})
    
endTime=time.time()  # 執行結束時間
print('股市資料 {0}筆,存入資料庫 {1}筆,花費時間 {2}秒'.format(len(myDataList),myColl_stock.count(),(endTime-startTime)))

◆ 將資料庫中的股票資料取出
這個例子示範怎麼把我們新增進 MongoDB mLab 雲端資料庫的股市資料,依需要的個股、時間區間將資料取出。重點在這句 myColl_stock.find({'stockid':'1201','date': {'$gte': dt_s, '$lt': dt_e}},{'_id':0,'stockid':1,'date':1,'close':1}),find()方法的第一組參數設定取出的股票代碼(stockid)、時間區間(date),$gte 是大於等於,$lt 是小於,如果要小於等於就是用 $lte。第二組參數控制要取出的欄位,'_id':0 表示該欄位不取出,'stockid':1表示要取出。

# Retrieve data from MongoDB. @author: Sean Lu.
#---------------------連接 mongoDB mLab 雲端資料庫---------------------
#-----以下是 mongoDB 宣告
import pymongo
from pymongo import MongoClient
myClient = MongoClient("mongodb://mongouser:mongouser@ds135537.mlab.com:35537/trymongodb")  # MongoDB URI-etf_project
myDB = myClient.trymongodb  # 資料庫 trymongodb
myColl_stock = myDB.stockdata  # 資料表 collection - stockdata
   
import datetime
dt_s = datetime.datetime(2017, 10, 1, 0, 0, 0)  # 資料區間
dt_e = datetime.datetime(2017, 11, 1, 0, 0, 0)  # 資料區間

for myDocs in myColl_stock.find({'stockid':'1201','date': {'$gte': dt_s, '$lt': dt_e}},{'_id':0,'stockid':1,'date':1,'close':1}):
    print(myDocs)

這是一個處理多個 CSV檔彙整資料的簡單案例,並用 matplotlib 繪製圖形,再將資料存入 MongoDB 資料庫中。matplotlib 的功能很強大,您可以到 matplotlib.org 了解更詳細的功能,MongoDB 的使用則可以參考《mLab 雲端資料庫與 Python 之 CRUD – I. 新增/查詢篇》、《mLab 雲端資料庫與 Python 之 CRUD – II. 修改/刪除篇》這兩篇有簡單的介紹。

參考資料 (References):
😺 SciPy Tutorial
😺 matplotlib.org Documents
😺 Getting Started with MongoDB (Python Edition)
😺 PyMongo 3.6.0 Tutorial