取得期貨權益並存入excel中
使用 jupyterlab 範例,
使用 python script 範例
多年前曾用想要用pythonnet,來載入群益的api ,但最後失敗了。這篇純粹是出自個人好奇,還是想了解是否能用 pythonnet 來載入 dll,最近找到可以使用的步驟了,在windows下 python 可以呼叫群益api,讓熟悉 C# 的開發者,又想用 python 的人多一種選擇。
我的安裝環境:
要先將群益的SKCOM.dll 轉成 Type library,我有安裝 visual studio 2022,利用它附帶的 comandline prompt terminal ,下指令 tlbimp.exe 將 SKCOM.dll 轉成 typelib 形式,最終產生 SKCOMLib.dll 的檔案。之後就可以輕鬆用 pythonnet 的 clr 模組取用。
群益海期 ticks 報價範例,本範例直接在 OnNotifyTicksNineDigitLONG, OnNotifyHistoryTicksNineDigitLONG 裡做一些資料處理,當海期報價資料量很大的時候,處理速度變很慢,kline 轉換也有點慢,日後都還需要改進,朋友呀,如果有什麼更快的方法請教教我。
Notebook 範例版 jupyter notebook
群益海期報價跟下單跟api手冊上寫的有點出入,手冊也沒有明確表示下單的流程,經過一番試誤,可以報價也可以下單了。實在是太少碰海期相關的 api,每次想用的時候都重新經歷一次錯誤,因此寫下來留個紀錄好參考。真的很少用,可能有不少地雷,因此僅供參考,也歡迎大家分享曾經遇過的問題。
幾個容易有錯誤的地方
1. 海期商品與交易所的報價代號跟下單代號有些是不同的,請用 GetOverseaProductDetail 來查詢正確代號。
2. 報價代碼寫法是 "交易所代碼1,商品代碼1#交易所代碼2,商品代碼2", 不同商品間以#字號隔開
3. 海期商品連線報價主機後,只會回應 3001,之後即可查詢報價
3. 海期委託單物件填寫商品代碼跟 GetOverseaProductDetail 查詢的稍有出入,需要加工處理。例如微型小道瓊下單代碼為 MYM_202206,填入委託單物件時事拆成兩個參數,要分別填入商品代碼及年月, 如 bstrStockNo = "MYM" 及 bstrYearMonth = "202206"
4. 要能下單要有幾項前置作業, SKOrderLib_Initialize >> ReadCertByID >> GetUserAccount >> SKOrderLib_LoadOSCommodity
5. "期貨API下單同意書"要記得簽,才能進行海期報價,我都用手機掌中財神 app,下方有個同意書專區,把裡面能簽的都簽了。
6. 有網友詢問如何使用 GetOverSeaFutureOpenInterestGW,手冊寫經由 OnOverSeaFutureOpenInterestGW 回報,測試後發現並沒有反應。深查後發現手冊坑人,原來是經由 OnOFOpenInterestGWReport 來做回報,也一併加入本範例供參考。
Notebook 範例版 jupyter notebook
py 範例版,僅在 spyder IDE 下測試過
確認有安裝 ipykernel 後,terminal 下移動到新建的虛擬環境資料夾下,例如 my_env_name 執行下列指令:
python -m ipykernel install --user --name=<my_env_name>
import pythoncom, time, os from comtypes.client import GetModule, GetEvents, CreateObject from datetime import datetime #第一次使用需要將 SKCOM 包成python 的package #SKCOM.dll 在linux 下,我是放在 ~./home/yi/.wine/driv_c/skcom/x86/SKCOM.dll #但在 wine 下,是放在下列路徑, drive_c 就是windows的C槽 GetModule(r'C:\\skcom\x86\SKCOM.dll') #GetModule會在comtypes.gen 下產生 SKCOMLib package, 之後就用下面方式引用 import comtypes.gen.SKCOMLib as sk #建立COM物件 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) #輸入身分證與密碼 ID='' PW='' #股票代號 strStock="TX00" print(str(datetime.now())[0:-4], 'ID & PW entered') #建立事件類別 class skQ_events: def OnConnection(self, nKind, nCode): print(datetime.now(), 'OnConnection', nKind, nCode) if nKind == 3003: #nKind==3003 表連線報價主機就緒 print("quoate server connected..., nkind= ", nKind) ncode=skQ.SKQuoteLib_RequestStocks(1, strStock) if ncode==0: print(strStock, 'requested') def OnNotifyQuote(self, sMarket, sIndex): ts=sk.SKSTOCK() skQ.SKQuoteLib_GetStockByIndex(sMarket, sIndex, ts) print(ts.bstrStockName, ts.bstrStockNo, ts.nClose/10**ts.sDecimal) class skR_events: def OnReplyMessage(self, bstrUserID, bstrMessage, sConfirmCode=0xFFFF): print('OnReplyMessage', bstrMessage) return sConfirmCode #Event sink, 事件實體 EventQ=skQ_events() EventR=skR_events() #make connection to event sink ConnectionQ = GetEvents(skQ, EventQ)
ConnectionR = GetEvents(skR, EventR) if __name__=='__main__': #登入 nCode=skC.SKCenterLib_Login(ID, PW) print("Login,", skC.SKCenterLib_GetReturnCodeMessage(nCode)) #連線報價主機 print(f"{datetime.now()}, EnterMonitor,", skC.SKCenterLib_GetReturnCodeMessage(skQ.SKQuoteLib_EnterMonitor())) # Event loop, 持續檢查有沒有event產生 pythoncom.PumpMessages()
OSError: exception: access violation writing 0x000000000000009C
經多方詢問後, 應該是api bug, 下個版本應該就會修正. 目前非官方的解法,創建 skcom 元件的時候,將海期及海選元件同時建立即可。目前測試可以 正常SKOSQuoate_Entermonitor(), 其他的還沒試過,給大家試試看吧。也不知道其他語言會有這個問題嗎?
skOSQ = CreateObject(sk.SKOSQuoteLib, interface=sk.ISKOSQuoteLib)
# 只要多下面這列就可以正常使用了
skOOQ = CreateObject(sk.SKOOQuoteLib, interface=sk.ISKOOQuoteLib)
from comtypes.client import GetModule, CreateObject, GetEvents GetModule('C:\\skcom\\CapitalApi_2.13.17\\x64\\SKCOM.dll') import comtypes.gen.SKCOMLib as sk from datetime import datetime import getpass #建立物件,避免重複 createObject #登錄物件 if 'skC' not in globals(): skC=CreateObject(sk.SKCenterLib, interface=sk.ISKCenterLib) #下單物件 if 'skO' not in globals(): skO=CreateObject(sk.SKOrderLib , interface=sk.ISKOrderLib) #回報物件 if 'skR' not in globals(): skR=CreateObject(sk.SKReplyLib , interface=sk.ISKReplyLib) #輸入身分證與密碼 ID='' PW='' print(datetime.now(), 'ID & PW') #建立事件類別 class skO_events: def __init__(self): self.TFAcc=[] def OnAccount(self, bstrLogInID, bstrAccountData): strI=bstrAccountData.split(',') #找出期貨帳號 if len(strI) > 3 : if strI[0] == 'TF' : self.TFAcc = strI[1]+strI[3] class skR_events: def OnReplyMessage(self, bstrUserID, bstrMessage, sConfirmCode=0xFFFF): '''API 2.13.17 一定要返回 sConfirmCode=0xFFFF''' print('skR_OnReplyMessage', bstrMessage) return sConfirmCode def OnData(self, bstrUserID, bstrData):
#成交回報 self.bstrOnData.append(datetime.now().strftime('%H:%M:%S,') + bstrData) strI = bstrData.split(",") print("skR_OnData", strI) def OnNewData(self, bstrUserID , bstrData): print('skR_OnNewData', bstrData) #Event sink, 事件實體 EventO=skO_events() EventR=skR_events() #make connection to event sink ConnO = GetEvents(skO, EventO) ConnR = GetEvents(skR, EventR) print(datetime.now(), 'Making event objects') %matplotlib auto #登入模擬帳號 nCode=skC.SKCenterLib_ResetServer ("morder1.capital.com.tw") print('set server to morder1.capital.com.tw', skC.SKCenterLib_GetReturnCodeMessage(nCode)) #login nCode=skC.SKCenterLib_Login(ID,PW) print('Login', skC.SKCenterLib_GetReturnCodeMessage(nCode)) #初始 Order 物件 nCode=skO.SKOrderLib_Initialize() print("Initialize ", skC.SKCenterLib_GetReturnCodeMessage(nCode)) #初始 Cert by ID 物件,模擬平台不需要這個,會出現錯誤 nCode=skO.ReadCertByID(ID) print("ReadCertByID ", skC.SKCenterLib_GetReturnCodeMessage(nCode)) #Get User TF Account nCode=skO.GetUserAccount() print("GetUserAccount ", skC.SKCenterLib_GetReturnCodeMessage(nCode), EventO.TFAcc) #設定期貨單參數,請自行調整囉,注意參數的資料型別,例如 bstr開頭的是要輸入文字型態
#以下請再jupyter 裡用另外一個cell 跑
fo=sk.FUTUREORDER() fo.bstrFullAccount=EventO.TFAcc #TF 帳號 fo.bstrStockNo='MTX00' fo.sBuySell=0 #0 buy, 1 sell fo.bstrPrice='10700' #委託價格,「M」表示市價,{移動停損、MIT皆無須價格} fo.nQty=1 #交易口數 fo.sDayTrade=0 #當沖0:否 1:是,可當沖商品請參考交易所規定。 fo.sNewClose=2 #新平倉,0:新倉 1:平倉 2:自動{新期貨、選擇權使用} fo.sTradeType=1 #0:ROD 1:IOC 2:FOK fo.sReserved=0 #盤別,0:盤中(T盤及T+1盤);1:T盤預約{新期貨、停損單使用} #下單 nCode=skO.SendFutureOrder(ID, False, fo) print(nCode)