顯示具有 comtypes 標籤的文章。 顯示所有文章
顯示具有 comtypes 標籤的文章。 顯示所有文章

2025年4月16日 星期三

用 pythonnet 重塑群益 SKOrderLib,提升功能完整性

以前都使用 comtypes 來包裹群益 SKCOM API。然而,幾次API更新後,在處理 SKOrderLib 時,發現愈來愈多使用 comtypes 包裝後功能不完善,一些事件無法正常運作。因此,我嘗試使用 pythonnet 來包裝 SKCOM API (請參考這篇),結果 SKOrderLib 的功能得以順利運作。 以下是我比較 comtypes 和 pythonnet,最佳化 SKOrderLib 的實際經驗 ,用jupyter notebook來運行可以得到最好效果。     


comtypes 範例

很多功能呼叫後都沒有回應,向是 GetRealBalaceReport, GetMarginPurchaseAmountLimit 等,重複呼叫 GetRealBalaceReport,甚至只會顯示 SK_ERROR_QUERY_IN_PROCESSING, 不論是間隔多久,都不會再回傳正確資料。

pythonnet範例

2017年8月1日 星期二

comtypes error 'tuple' object has no attribute '__ctypes_from_outparam__'

最近使用comtypes 需要使用到pythoncom功能,可以用
pip install pypiwin32

這樣就會附帶安裝pythoncom了

#2018年更新,這個問題在 comtypes 1.1.4 版中已經被修正了
使用comtypes 有時候會出現

comtypes 裡的 __init__.py  第 658 行程式碼做些修改可以修正這個bug
路徑大概類似這樣
~python\Lib\site-packages\comtypes\__init__.py
    657             # be iterable.
    658             if len(outargs) == 1:  # rescode is not iterable
--> 659                 return rescode.__ctypes_from_outparam__()
    660 
    661             rescode = list(rescode)
改成
            # be iterable.
            if len(outargs) == 1:  # rescode is not iterable
                try:
                    return rescode.__ctypes_from_outparam__()
                except: #somtimes rescode is tuple
                    return rescode

            rescode = list(rescode)

即可修正


Ref:



2017年7月31日 星期一

群益 API in pyhton 報價範例

群益api更新至2.13.8後,改一下順序,先呼叫 SKReplyLib_ConnectByID,再 EnterMonitor,才會可以收到 OnConnection 3003 的回報,然後就可以求報價了。本範例示範如何用python 接群益api的報價,希望能拋磚引玉, 讓更多能手參與。小弟python自學,COM 元件的運作也是一知半解,東拼西湊下還算可用,程式碼很醜請見諒。過程遇到一些錯誤,很多我無法解決,以下是我試誤各種組合後, 可以運作的一組方法,,若有更好的解法,請各位大大多加分享。

另外是事件處理,我用comtypes 中的 showevents 也有錯誤產生,於是用 pyhoncom.PumpWaitingMessages() 來代替

使用comtpyes 若出現
"comtypes com object's method returns: 'tuple' object has no attribute 'ctypes_from_outparam'",需要做一些修正 ,請參考
https://easontseng.blogspot.com/2017/07/install-pythoncom-tuple-object-has-no.html
#這個bug好像在最新版的comtypes中被修正了

弄個FB社團,大家來討論好了。
https://www.facebook.com/groups/1805224676441902/

運行環境:
winXP, Anaconda python3.4 32 bit, CapitalAPI 2.13.8, pywin32 build221, comtypes 1.1.3

參考文獻:
#pythoncom 是安裝 pywin32 附帶的
https://sourceforge.net/projects/pywin32/
#用pywin makepy utility 找 GUID
https://easontseng.blogspot.tw/2017/07/pywin32-com-makpy-utiltiy-api-com.html
#python 元大期貨 api,
http://hlfutures.blogspot.tw/2016/08/api-in-python_8.html

#comtpyes:
https://pythonhosted.org/comtypes/
http://starship.python.net/crew/theller/comtypes/
https://easontseng.blogspot.tw/2017_07_07_archive.html

###########################################################
# pythoncom 是安裝 pywin32 附的,如果 event 沒反應才需要用到
# 第一次使用cc.GetModule, 會在 comtypes\gen\ 下產生幾個.py檔,像是
# _75AAD71C_8F4F_4F1F_9AEE_3D41A8C9BA5E_0_1_0.py
# 如果有 dll 錯誤產生,請檢查 SKCOM.dll 路徑有沒有錯誤
# 如果發生 gen 下沒有 SKCOMLib 錯誤訊息,請將python關閉
# AttributeError: module 'comtypes.gen.SKCOMLib' has no attribute 'SKCenterLib'
# 重新啟動python後,應該就可以找到了
# 如果還是不行,請重新安裝 capital API 元件,注意 API 與python 版本要一致,
# API安裝要以管理員權限執行

# 20190506,程式碼簡化
import pythoncom, time
import comtypes.client as cc

cc.GetModule('C:\\SKCOM\\x86\\SKCOM.dll')
import comtypes.gen.SKCOMLib as sk

ts=sk.SKSTOCK()

skC=cc.CreateObject(sk.SKCenterLib,interface=sk.ISKCenterLib)
skQ=cc.CreateObject(sk.SKQuoteLib,interface=sk.ISKQuoteLib)

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

#想取得報價的股票代碼
strStocks='TSEA'

#define functions
def getStock(nMarket, nIndex, ts):
    skQ.SKQuoteLib_GetStockByIndex(nMarket, nIndex, ts)
    print(ts.bstrStockName, ts.bstrStockNo,  ts.nClose/10**ts.sDecimal)

#建立事件類別
class skQ_events:
    def OnConnection(self, nKind, nCode):
        if nCode == 0 :
            if nKind == 3001 :
                print("skQ OnConnection, nkind= ", nKind)
            elif (nKind == 3003):
                #等到回報3003 確定連線報價伺服器成功後,才登陸要報價的股票
                skQ.SKQuoteLib_RequestStocks(1, strStocks)
                print("skQ OnConnection, request stocks, nkind= ", nKind)
    def OnNotifyQuote(self, sMarketNo, sStockIdx):
        getStock(sMarketNo, sStockIdx, ts)

#Event sink
EventQ=skQ_events()
#make connection to event sink
ConnectionQ = cc.GetEvents(skQ, EventQ)        

#Login
print("Login,", skC.SKCenterLib_GetReturnCodeMessage(skC.SKCenterLib_Login(ID,PW)))
time.sleep(1)

#登錄報價伺服器
print("EnterMonitor,", skC.SKCenterLib_GetReturnCodeMessage(skQ.SKQuoteLib_EnterMonitor()))
#每秒 pump event 一次,這裡示範15秒

for i in range(15):
    time.sleep(1)
    pythoncom.PumpWaitingMessages()