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 29} Seaborn

>>:  C++时间日期,需收费另外再跟我说明

Day 7 - 目前(传统)的机器学习三步骤(2)-关键特徵

第二步 Features 找出关键特徵 特徵精准分三等级 1.原始数据:由目前的原始数据,尝试找出关...

Day-17 就是要重现这一部!没有极限的 PS2!

在这第六世代的战争中、面对来势汹汹的 DC、SONY 当然也早就有准备、非常机歪的选在 DC 发售的...

CSS微动画 - Loading来了!小精灵?这个小家伙吃蛋

Q: 这是小精灵吗? A: 这是... 吃蛋的小家伙!(不知道有没有版权问题呢?) 本篇的灵感来自...

Day 29 | GitHub Pages 初体验

今天想分享一下如何上传自己的 GitHub Pages, 可以参考高见龙的 使用 GitHub 免费...

​修复:隐藏受保护的作业系统档案选项在资料夹选项中丢失

在Windows中,默认情况下看不到具有隐藏的系统属性档案。即使在「资料夹选项」中启用了「显示档案和...