2017年8月2日 星期三

群益api in python 取得歷史kline報價

今天來用群益API取得歷史報價,群益API可以取得從上市櫃開始到今日的所有日K歷史報價,有了歷史報價就可以做很多樣回測。一分K歷史報價目前可以取得約 60000 根的資料,且幾秒鐘內就可以取回來,相當便利喔!

到目前2021年,kline 改變了不少,目前定位為 "歷史kline",只能取得當日以前的kline,若要取得當日kline,需要自己收集 tick 資料,自己組成 kline
可以參考:
  1. 群益api 使用python 取得 tick 報價
  2. tick data 轉換成 k Bar

################SKCOMAPI 2.13.17 以上版本適用##################
# 本範例要用 jupyter 來跑, 如果要在console 下跑,要加pythoncom來處理eventloop
# Eventloop 參考這幾篇的用法,或是像群益官方範例建立GUI方式產生

# skcomapi 2.13.23 以上適用
from comtypes.client import GetModule, CreateObject, GetEvents
# 將sckcom轉成python package
# GetModule 只要執行一次就好,除非有更新API,再參考官網範例,清除comtypes.gen 
# 下的檔案,再重新呼叫 GetModule 包裝
GetModule('C:\\skcom\\CapitalAPI_2.13.23\\x64\\SKCOM.dll')
import comtypes.gen.SKCOMLib as sk
import pythoncom
#config #ID and Password ID = "" PW = "" #request stock stockName='TSEA' #建立物件,避免重複 createObject #登錄物件 if 'skC' not in globals(): skC=CreateObject(sk.SKCenterLib, interface=sk.ISKCenterLib) #報價物件 if 'skQ' not in globals(): skQ=CreateObject(sk.SKQuoteLib, interface=sk.ISKQuoteLib) #回報物件 if 'skR' not in globals(): skR=CreateObject(sk.SKReplyLib , interface=sk.ISKReplyLib) #建立事件類別 class skR_events: def OnReplyMessage(self, bstrUserID, bstrMessage, sConfirmCode=0xFFFF): print('OnReplyMessage', bstrMessage) return sConfirmCode class skQ_events: def __init__(self): self.KlineData = [] def OnConnection(self, nKind, nCode): if nKind == 3003: print("skQ連線成功, nkind= ", nKind) nCode=skQ.SKQuoteLib_RequestKLine(stockName, sKLineType=4, sOutType=1) else: print('please check, nCode, nKind=', nCode, nKind) def OnNotifyKLineData(self, bstrStockNo, bstrData): self.KlineData.append([bstrStockNo] + bstrData.split(',')) #Event sink, 事件實體 EventR=skR_events() EventQ=skQ_events() #make connection to event sink ConnR = GetEvents(skR, EventR) ConnQ = GetEvents(skQ, EventQ) print('load module and event handler') #########在新 cell 執行 ############ # magic function to get event loop, 只有jupyter 可以用,
# console 介面要用 pythoncom.PumpWaitingMessages(1)
%matplotlib auto
 
#登入,連線報價主機
nCode=skC.SKCenterLib_Login(ID, PW)
print("Login,", skC.SKCenterLib_GetReturnCodeMessage(nCode))
 
#Enter quote server
nCode=skQ.SKQuoteLib_EnterMonitor()
print('SKQuoteLib_EnterMonitor()', skC.SKCenterLib_GetReturnCodeMessage(nCode))


######以下為 2.13.17以下版本 年久失修僅供參考#################################
#群益api in python 取得歷史報價
import pythoncom, time, os
import comtypes.client as cc
cc.GetModule(r'C:\SKCOM\x86\SKCOM.dll')
import comtypes.gen.SKCOMLib as sk
#建立COM物件
skC=cc.CreateObject(sk.SKCenterLib, interface=sk.ISKCenterLib)
skQ=cc.CreateObject(sk.SKQuoteLib, interface=sk.ISKQuoteLib)

#Some Configuration
ID='身分證'
PW='密碼'

#顯示 event 事件, t 秒內,每隔一秒,檢查有沒有 event 發生
def pumpwait(t=1):
    for i in range(t):
        time.sleep(1)
        pythoncom.PumpWaitingMessages()

#建立事件類別
class skQ_events:
    def __init__(self):
        self.KlineData=[]
    def OnConnection(self, nKind, nCode):
        if nCode == 0 :
            if nKind == 3001 :
                print("連線中, nkind= ", nKind)
            elif nKind == 3003:
                print("連線成功, nkind= ", nKind)
    def OnNotifyKLineData(self, bstrStockNo, bstrData):
        self.KlineData.append(bstrData.split(','))
                    
#Event sink, 事件實體  
EventQ=skQ_events()
#make connection to event sink
ConnectionQ = cc.GetEvents(skQ, EventQ)        

#登入,連線報價主機
nCode=skC.SKCenterLib_Login(ID, PW)
print("Login,", skC.SKCenterLib_GetReturnCodeMessage(nCode))
nCode=skQ.SKQuoteLib_EnterMonitor()
print("EnterMonitor,", skC.SKCenterLib_GetReturnCodeMessage(nCode))

#讀取加權日k歷史報價
#bstrStockNo 放股票代碼
#sKLineType,0 = 1分鐘線, 3 =日線288天, 4 =完整日線, 5 =週線, 6 =月線。
#sOutType, 0=舊版輸出格式, 1=新版輸出格式。新版格式一分日期與分時資料是各自一個欄位
#將data 佔存至  EventQ.KlineData
EventQ.KlineData=[]
#請求歷史報價,參數可能有出入,請自行參考最新官方 API 文件
nCode=skQ.SKQuoteLib_RequestKLine('TSEA',, sKLineType=4, sOutType=1)
#data 輸出
EventQ.KlineData[0:2]
#取1分k歷史報價
EventQ.KlineData=[]
tic=time.time()
nCode=skQ.SKQuoteLib_RequestKLine('TSEA', sKLineType=0, sOutType=1)
toc=time.time()
print('取得', len(EventQ.KlineData),  '筆1分k報價, 花費', round(toc-tic,2), '秒')


43 則留言:

  1. 您好,感謝您的分享
    想請教為何call getKline()之後
    不用執行pythoncom.PumpWaitingMessages()
    OnNotifyKLineData()也會被呼叫呢?

    回覆刪除
  2. 其實我也不知道耶,我也有這個疑問,不過確實呼叫getKline()後就可以經由OnNotifyKlineData()取得歷史報價,所以我就沒有細探了。你有試過我上面的方式得到歷史報價了嗎?

    回覆刪除
    回覆
    1. 有喔,剛好在卡解COM structure物件的時候發現你的文章,覺得幸運 btw我也有在ML trading群裡

      刪除
    2. 最近看到山豆兒的文章,用pythonnet接群益報價,感覺很不錯,但我還沒研究,推薦給你看看他的做法
      https://kwedr.blogspot.tw/2017/07/api-python-pythonnet.html

      刪除
    3. 加了你的line了,文,可以一起研究嗎

      刪除
    4. 這個line群,找不到, 我想加入一起討論

      刪除
    5. 被人放翻群機器人了,誰那麼無聊啊 ,我的天啊

      刪除
    6. 想加line群+1
      剛要從頭開始學python還請前輩多指教

      刪除
    7. 賴被人放機器人把每個成員都踢掉了,不想再弄賴群了,改到FB社團好了,但目前沒有人氣就是了
      https://www.facebook.com/groups/1805224676441902/

      刪除
  3. 好棒..總算有人用PYTHON連進群益...........好感謝您..可以加我嗎..

    回覆刪除
  4. 請問一下
    在連結EnterMonitor後會透過OnConnection回傳連線狀態會慢幾秒
    但是我是直接跑整個程式碼,變成他回傳會跑在我其他程式碼後,造成跳error
    剛剛拜讀大大的程式碼後發現有加上"pumpwait"
    這個是用來等資料回傳後再繼續跑我後續的程式碼嗎?!

    回覆刪除
    回覆
    1. 你要等一等,等收到 OnConnection 回傳 3003 代碼後再開始跑其他程式碼,主要會影響的應該是需要登入報價伺服器的方法 像是SKQuoteLib_RequestStocks/ ticks 等。
      另外, 另外登錄報價伺服器前不要跑 skR.SKReplyLib_ConnectByID(),好像會無法讓 OnConnection 回傳 3003,這個要再測試我印像有點模糊了,至少可以先只跑EnterMonitor看看

      刪除
    2. 感謝大大的回覆
      對~~我先跑EnterMoniter後他會先回傳0,然後OnConn會先回傳3001過一會再回傳3003
      現在就是在思考如何等他回覆3003後在繼續後面的call data。因為我用timesleep(30),結果發現還是一樣等我程式跳error後才出現3003。感覺像室等我程式碼跑完才會回覆

      我會再繼續找答案的。
      感謝您

      刪除
    3. 可以參考官方範例唷,在Onconnection裡寫個判斷式,如果出現3003,就呼叫你執行的function

      Onconnection():
      if code==3003:
      do_something()

      刪除
    4. 喔喔喔~~
      好的~~感謝你

      刪除
  5. 我參考以上寫法,
    會出現"u'SK_ERROR_INITIALIZE_FAIL' "
    請問一下有人遇到同樣問題嗎?
    謝謝大家

    回覆刪除
    回覆
    1. 看起來不像是 code 問題,你看看報價篇或 tick篇的你可以跑嗎? 你有沒有簽署API使用申請書之類的? 帳號密碼有對嗎?

      刪除
    2. Eason非常感謝你的回覆, 我有簽署API使用,並對帳號跟密碼,然後再跑即時報價 出現Login, SK_ERROR_INITIALIZE_FAIL
      ConnectByID, SK_ERROR_LOGIN_FIRST
      EnterMonitor, SK_ERROR_PERMISSION_TIMEOUT
      我該如何找出問題點,感謝你的幫忙

      刪除
    3. 我是用Anaconda2 and Anaconda3 的Spyder跑這程式,是否因為環境不同而有所影響

      刪除
    4. 我知道很多人都可以跑這程式,只是我試過很多方法,不知道自己哪一個環節出了問題 :)

      刪除
    5. skcom.dll有照說明文件安裝嗎?skcom 與python位元版本有無一致,32/64bit, 作業系統用的是?
      comtypes 版本1.13以上?

      還不行的話,到Fintech.py FB社團問問,看其他版友知不知道

      刪除
    6. Eason 真的很感謝你這麼晚了還幫忙細心解答,我現在重新照說明文件安裝,若成功的話,我會上來寫我成功了 :) 你真的很棒

      刪除
    7. Eason感謝你,我拿另外一台電腦重新安裝已經可以動作了~ 謝謝你的幫忙:)

      刪除
    8. 你原本是用 XP 嗎? 還有 API 版本用太舊嗎?? 昨天拿舊電腦測試,發現API版本太舊,有出現你這個錯誤訊息!!

      刪除
    9. Eason 我兩台都是Win 10 64位元, 一台是ThinkPad i5有異常,一台是ASUS i7這台是安裝正常,感謝你樂於分享你的程式給其他人,讓很多人受惠,有機會你可以開課~我願意付學費去跟你學習唷 :)

      刪除
  6. 請教一下,如果單純想用 Python 連結群益API 股票下單可行嗎?

    最基本的功能而已:下單委買(賣)、查詢帳號庫存。謝謝

    回覆刪除
    回覆
    1. 可行,使用上沒什麼問題,了解如何使用 comtypes 取用群益API後,剩下的就是看 API 文件的說明來操做了

      刪除
    2. 非常謝謝。你好神

      刪除
  7. 請問一下,我讀tick資料的時後發現由於tick太過頻繁
    假設總共設定100筆股票資訊,但tick更新的只會更新前面50筆
    有什麼方法可以解決嗎?

    回覆刪除
  8. 群益tick報價好像最多只有50檔,要再多要去另外申請的樣子,這可能要問營業員。如果你只是要收集tick資料,我目前是在盤後到 14:30 前,一檔一檔重新登錄,藉由 OnNotifyHistoryTicks 回補資料的方式取得,可你拿到當天所有ticks資料。

    回覆刪除
    回覆
    1. 謝謝~~難怪我怎麼弄都拿不到資料~~QQ

      刪除
  9. 感謝,已經測試成功
    想請教一下
    def pumpwait(t=1):
    for i in range(t):
    time.sleep(1)
    pythoncom.PumpWaitingMessages()

    pumpwait(8) 是做何用?

    回覆刪除
    回覆
    1. 這下面是我自己的理解,我太不懂這底層實際的運作。PumpWaitingMessages 是用來推出 event,讓收到的 event 可以被 event handler 處理, pumpwait 是我自己定義的function, 希望可以每秒叫一次 pumpWaitingMessages, 讓 event 能被處理,共持續8秒鐘。這只是我用來除錯用的,如果用 while loop 很難除錯

      刪除
  10. 你好, 請問出現這個錯誤該如何解決?
    skC=cc.CreateObject(sk.SKCenterLib, interface=sk.ISKCenterLib)

    OSError: [WinError -2147221164] 類別未登錄

    回覆刪除
    回覆
    1. 很久沒遇到這樣的錯誤了,忘記怎麼發生的,好像沒有很難處理。你有正確安裝群益API嗎? 要用管理員權限安裝 API, API 跟 python 的位元版本要一致,然後重開機或重啟python IDE,有機會可以正常使用

      刪除
    2. 成功了,我API和python 位元版本不一致,剛剛重新調整後就可以執行了
      Eason 非常謝謝你!

      刪除
  11. 您好
    參考您的程式在登入的時候會出現SKReplyLib_OnReplyMessage
    想請問這個問題該如何解決?

    回覆刪除
    回覆
    1. 你是不是用2.13.17 的版本? 這個我也第一次遇到... 還不知道怎麼解

      刪除
  12. 請問 使用 SKCOMAPI 2.13.17 以上版本適用 程式 ,用 jupyter 來跑登入正常.
    在 CapitalAPI_2.13.23 版本環境下
    使用以下程式取得 分k資料
    EventQ.KlineData=[]
    nCode=skQ.SKQuoteLib_RequestKLine('TSEA', sKLineType=0, sOutType=1)
    print('取得', len(EventQ.KlineData), '筆1分k報價')

    螢幕可以看到,取得的資料,大概跑1-2分鐘.
    最後顯示取得0筆1分k報價 , print(EventQ.KlineData)是空的,無法存入df中
    請問該如何解決? 謝謝!

    回覆刪除
    回覆
    1. 我偷懶只用印的,OnNotify裡面要另外處理,像是加 append 去處理,目前版本已調整,你再試試看。另外我最近用jupyterlab 處理 eventloop 好像機制有變,我用 %matplotlib auto 去處理 event ,很容易出問題,但我猜你是第一個的原因,

      刪除
    2. 新的skcomapi 2.13.23 以上適用,可以正確將k線放到EventQ.KlineData中,謝謝!
      不過新版程式中
      import comtypes.gen.SKCOMLib as sk
      .....
      print('load module and event handler')
      這段碼重複兩次,是故意的嗎?

      刪除
    3. 痾不是故意的啦,不小心重複貼了

      刪除