iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
永豐金融APIs

深入解析 Shioaji API系列 第 26

Day 26 - 建立自己的K線資料庫 (上)

本篇重點

本篇目標是要下載kbar資料及建立自已的K線資料庫

  • 抓取所有股票Contract
  • 抓取所有股票Kbars資料
  • 關於1分K

抓取所有股票Contract

Day 18 - 取得所有Contract程式範例,有示範要如何抓取所有的Contract資料。在這篇也會依照之前的範例,先抓取所有的Contract資料後,再抓取Kbar資料並存入資料庫中。
做數據收集前,第一步就是要看一下資料是不是有我們所不需要的。在上次的程式中,有將股票所有的Contract取出來並匯出成CSV檔。打開CSV後,可以看到在Contract.Stocks中,有一些Contract是屬於權證,可以看到這些權證的Category都是0;而有一些Contract的Category的欄位內容為空值
https://ithelp.ithome.com.tw/upload/images/20211009/201408272M2KH6hpRB.png
另外,還有一些雖然是股票,但update_date欄位中的日期卻不是最後一個交易日,這些都是暫停交易或是下市櫃的股票
https://ithelp.ithome.com.tw/upload/images/20211009/20140827r4LGaM5HGw.png
所以,在抓取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中的資料,發現有幾筆股票代碼是英文字母結尾
https://ithelp.ithome.com.tw/upload/images/20211010/20140827cTsCio6S5q.png
這類的股票是所謂的特別股,這些目前也不需要,所以要稍微修改我們的程式

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。

抓取所有股票Kbars資料

我們這裡就先依上面的程式碼,再結合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在程式一開始就給定一個值,這個值在後面的程式中都不應該去修改它的值
一般程式的命名規則,都是將常數的名稱設為全部大寫

關於1分K

如果你有看Shioaji的1分K資料時間,或許有人會發現在kbar時間上跟部份的看盤軟體不同。
例如,在XQ的看盤軟體中,每個交易日的第一個1分K,時間會是09:00
https://ithelp.ithome.com.tw/upload/images/20211011/201408274d9EVWXsNa.png
但是在三竹的看盤軟體中,每個交易日的第一個1分K,時間會是09:01
https://ithelp.ithome.com.tw/upload/images/20211011/20140827fDdGNrDTES.png
但其實這兩家的第一個1分K,在成交價格及成交量都是相同的,看個人習慣用哪一種方式。若你想把1分K的時間,調整成跟XQ一樣,可以透過pd.Timedelta來達成,只要在ts欄位內容轉換成datetime後,增加下列的程式碼即可

df.ts = df.ts - pd.Timedelta(minutes=1) #將所有ts的值減1min

上一篇
Day 25 - 重覆呼叫shioaji.Shioaji()產生的記憶體問題-修正篇
下一篇
Day 27 - 建立自己的K線資料庫 (中)
系列文
深入解析 Shioaji API30

尚未有邦友留言

立即登入留言