本篇目標是要下載kbar資料及建立自已的K線資料庫
在Day 18 - 取得所有Contract程式範例,有示範要如何抓取所有的Contract資料。在這篇也會依照之前的範例,先抓取所有的Contract資料後,再抓取Kbar資料並存入資料庫中。
做數據收集前,第一步就是要看一下資料是不是有我們所不需要的。在上次的程式中,有將股票所有的Contract取出來並匯出成CSV檔。打開CSV後,可以看到在Contract.Stocks中,有一些Contract是屬於權證,可以看到這些權證的Category都是0;而有一些Contract的Category的欄位內容為空值
另外,還有一些雖然是股票,但update_date欄位中的日期卻不是最後一個交易日,這些都是暫停交易或是下市櫃的股票
所以,在抓取kbar資料時,都要排除這類不需要的Contract,程式內容改為以下:
import os
import shioaji as sj
from shioaji.constant import Exchange
from dotenv import load_dotenv
import pandas as pd
LAST_TRADE_DATE = '2021/10/08' #宣告最後交易日的常數
load_dotenv('D:/python/shioaji/.env')
api = sj.Shioaji()
api.login(
person_id=os.getenv('YOUR_PERSON_ID'),
passwd=os.getenv('YOUR_PASSWORD')
)
print(api.Contracts.Stocks) #確認api.Contracts.Stocks資料已下載完成
stock_list = []
for exchange in api.Contracts.Stocks:
for stock in exchange:
if stock.exchange in (Exchange.TSE, Exchange.OTC):
if stock.category == '00' or stock.category == '':
continue
elif stock.update_date != LAST_TRADE_DATE:
continue
else:
stock_list.append({**stock})
df = pd.DataFrame(stock_list)
df.to_csv('stock_list.csv', index=False, encoding="utf_8_sig")
api.logout()
執行完後,再看一下stock_list.csv中的資料,發現有幾筆股票代碼是英文字母結尾
這類的股票是所謂的特別股,這些目前也不需要,所以要稍微修改我們的程式
import os
import shioaji as sj
from shioaji.constant import Exchange
from dotenv import load_dotenv
import pandas as pd
import re #匯入re模組
LAST_TRADE_DATE = '2021/10/08'
load_dotenv('D:/python/shioaji/.env')
api = sj.Shioaji()
api.login(
person_id=os.getenv('YOUR_PERSON_ID'),
passwd=os.getenv('YOUR_PASSWORD')
)
print(api.Contracts.Stocks)
stock_list = []
for exchange in api.Contracts.Stocks:
for stock in exchange:
if stock.exchange in (Exchange.TSE, Exchange.OTC):
if stock.category == '00' or stock.category == '':
continue
elif stock.update_date != LAST_TRADE_DATE:
continue
elif re.search('[A-Z]', stock.code) is None:
#使用re.search,排除code欄位中有英文字母的Contract
stock_list.append({**stock})
df = pd.DataFrame(stock_list)
df.to_csv('stock_list.csv', index=False, encoding="utf_8_sig")
api.logout()
基本上這樣抓下來的Contract就是我們需要的一般股票Contract。
我們這裡就先依上面的程式碼,再結合https://ithelp.ithome.com.tw/articles/10269151 中的程式,先抓每檔股票的1分K資料,並儲存至資料庫中,這裡我們一樣先用SQLite,程式範例如下:
import os, datetime
import shioaji as sj
from shioaji.constant import Exchange
from dotenv import load_dotenv
import pandas as pd
import re #匯入re模組
import sqlite3
LAST_TRADE_DATE = '2021/10/08' #宣告最後交易日的常數
TODAY = datetime.date.today().strftime("%Y-%m-%d") #宣告當天日期
load_dotenv('D:/python/shioaji/.env')
conn = sqlite3.connect('D:/shioaji.db') #在D槽底下建立資料庫
api = sj.Shioaji()
api.login(
person_id=os.getenv('YOUR_PERSON_ID'),
passwd=os.getenv('YOUR_PASSWORD')
)
print(api.Contracts.Stocks)
for exchange in api.Contracts.Stocks:
for stock in exchange:
if stock.exchange in (Exchange.TSE, Exchange.OTC):
if stock.category == '00' or stock.category == '':
continue
elif stock.update_date != LAST_TRADE_DATE:
continue
elif re.search('[A-Z]', stock.code) is None:
print(f'start download {stock.code} {stock.name} kbar data...')
kbars = api.kbars(stock, start='2018-12-07', end=TODAY)
df = pd.DataFrame({**kbars})
df.ts = pd.to_datetime(df.ts)
df['code'] = stock.code
df.to_sql('stocks_1min_kbars', conn, if_exists='append', index=False)
print(f'{stock.code} {stock.name} kbar data is stored to sqlite')
api.logout()
股票kbar資料,shioaji上最舊可抓到2018/12/07。這裡我又不小心踩了一個坑,一開始我在抓kbar資料時,把end設為LAST_TRADE_DATE發生錯誤,因為日期格式不同,在Contract的update_date中年月日分隔符號是用「/」,而在kbar中的start跟end的年月日分隔符號是要用「-」,所以後來才再多加一個TODAY這個常數。
程式中所謂的常數,並非一定要是一個固定的值,而是指這個值在宣告後,就不能再次修改它的值
例如TODAY在程式一開始就給定一個值,這個值在後面的程式中都不應該去修改它的值
一般程式的命名規則,都是將常數的名稱設為全部大寫
如果你有看Shioaji的1分K資料時間,或許有人會發現在kbar時間上跟部份的看盤軟體不同。
例如,在XQ的看盤軟體中,每個交易日的第一個1分K,時間會是09:00
但是在三竹的看盤軟體中,每個交易日的第一個1分K,時間會是09:01
但其實這兩家的第一個1分K,在成交價格及成交量都是相同的,看個人習慣用哪一種方式。若你想把1分K的時間,調整成跟XQ一樣,可以透過pd.Timedelta來達成,只要在ts欄位內容轉換成datetime後,增加下列的程式碼即可
df.ts = df.ts - pd.Timedelta(minutes=1) #將所有ts的值減1min