2017/07/04

群益api– python 串接使用 pythonnet

群益api 版本: 2.13.6
python 版本: 2 or 3 64bit
用pythonnet, 一開始串SKCOM, 結果一直沒有成功, 想說要不要換 ironpython 試試
後來想說改串 Interop.SKCOMLib 沒想到卻成功了

目前遇到的問題是 Event Callback 時候沒辦法下中斷點,
另外在 Event 裡面再去呼叫 Com 的程式,好像會遇到不明的錯誤
所以全部改為使用 queue 在外部實作功能

特別一提的是 呼叫 SKQuoteLib_GetStockByIndex 輸入的的 STOCK 變數,輸出卻是不同一個(?)
所以我就在 SKCOMWapper 裡面也宣告了一個 STOCK, 每次都使用同一個就好了

在測試本程式的時候需要 更改 帳號/密碼, dll位置我是放在同一個目錄下的 dll/x64/
宣告 StockBot 時候,初始要查詢的 股票編號

switch.py 就不另外給了,網路上也是方便可以查詢的到原始碼
本身我 python 也是初學,目前先寫到可以登入、查詢股票
# -*- coding: utf-8 -*-
import sys, os, clr, time
is_py2 = sys.version[0] == '2'
if is_py2:
import Queue as queue
else:
import queue as queue
from switch import *
import logging
logging.basicConfig(level=logging.DEBUG)
q = queue.Queue ()
class Job:
GetTimer = 0
Quote_EnterMonitor = 1
GetStockByIndex = 2
GetStockByIndexResult = 3
OnNotifyQuote = 4
def __init__ (self, do_type):
self.do_type = do_type
class SKCenterLibEvent:
def __init__(self, parent, bot):
self.parent = parent
self.bot = bot
def Bind(self, obj):
obj.OnTimer += self.OnTimer
def OnTimer(self, nTime):
j = Job (Job.GetTimer)
j.Time = str(nTime)
q.put (j)
print ("OnTimer: " + str(nTime))
def OnShowAgreement(self, bstrData):
print ("OnShowAgreement: " + str(bstrData))
class SKReplyLibEvent:
def __init__(self):
self.parent = None
self.bot = None
def OnConnect(bstrUserID, nErrorCode ):
print ("OnConnect: user:" + bstrUserID + " error:" + str(nErrorCode))
class SKQuoteLibEvent:
def __init__(self, parent, bot):
self.parent = parent
self.bot = bot
def Bind(self, obj):
obj.OnConnection += self.OnConnection
obj.OnNotifyQuote += self.OnNotifyQuote
def OnConnection(self, nKind, nCode):
print ("OnConnection: kind:" + str(nKind) + " error:" + str(nCode))
self.bot.SKQuoteLibOnOnConnection (nKind, nCode)
def OnNotifyQuote(self, sMarketNo, sIndex):
j = Job (Job.GetStockByIndexResult)
j.sMarketNo = sMarketNo
j.sIndex = sIndex
q.put (j)
class SKCOMWapper:
def __init__(self, uid, bot):
self.uid = uid
self.bot = bot
sys.path.append(os.path.join(os.getcwd(), "dll", "x64"))
clr.AddReference("Interop.SKCOMLib")
import SKCOMLib as SKCOMLib
self.Template = SKCOMLib
self.SKCenterLib = SKCOMLib.SKCenterLib ()
self.SKCenterLibEvent = SKCenterLibEvent(self, bot)
self.SKCenterLibEvent.Bind (self.SKCenterLib)
self.SKReplyLib = SKCOMLib.SKReplyLib ()
#國內報價物件。
self.SKQuoteLib = SKCOMLib.SKQuoteLib ()
self.SKQuoteLibEvent = SKQuoteLibEvent(self, bot)
self.SKQuoteLibEvent.Bind (self.SKQuoteLib)
self.SKSTOCK = SKCOMLib.SKSTOCK ()
class StockBot:
def __init__(self, stockno):
self.SKCOM = SKCOMWapper (0, self)
self.Stock = {}
self.StockNo = stockno
def DoLogin (self, account, pwd):
ret = self.SKCOM.SKCenterLib.SKCenterLib_Login (account, pwd)
print ("CeterLib Login: " + str (ret))
q.put (Job (Job.Quote_EnterMonitor))
def SKQuoteLibOnOnConnection (self, nKind, nCode):
if nKind == 3003 and nCode == 0:
q.put (Job (Job.GetStockByIndex))
def DoOnNotifyQuote (self, sMarketNo, sIndex):
ret, Stock = self.SKCOM.SKQuoteLib.SKQuoteLib_GetStockByIndex(sMarketNo, sIndex, self.SKCOM.SKSTOCK)
j = Job(Job.OnNotifyQuote)
j.Stock = Stock
q.put (j)
def DoGetStockByIndex (self):
self.SKCOM.SKQuoteLib.SKQuoteLib_RequestStocks (-1, self.StockNo)
def DoEnterMonitor (self):
ret = self.SKCOM.SKQuoteLib.SKQuoteLib_EnterMonitor ()
def DoJob(Bot, x):
for case in switch(x.do_type):
print ("DoJob: " + str (x.do_type))
if case(Job.GetTimer):
print ("GetTimer: " + x.Time)
break
if case(Job.Quote_EnterMonitor):
Bot.DoEnterMonitor ()
break
if case(Job.GetStockByIndex):
Bot.DoGetStockByIndex ()
break
if case(Job.GetStockByIndexResult):
Bot.DoOnNotifyQuote (x.sMarketNo, x.sIndex)
break
if case(Job.OnNotifyQuote):
print ("OnNotifyQuote: " + x.Stock.bstrStockName + " Close: " + str (x.Stock.nClose))
Bot.Stock[x.Stock.bstrStockNo] = x.Stock
break;
if __name__ == "__main__":
Bot = StockBot("TSEA")
Bot.DoLogin('帳號', '密碼')
while 1:
while not q.empty():
next_job = q.get()
DoJob (Bot, next_job)
time.sleep(1)
有些人問了 switch.py, 這裡有一個範例可以使用
https://github.com/soarpenguin/python-scripts/blob/master/switch.py
另外 pythonnet 使用群益的新版目前會遇到問題, 請參考網址, 改用 comtypes
http://easontseng.blogspot.tw/2017/08/api-in-python.html

17 則留言:

Epson 提到...

山豆兒你好

對於用python串接群益API有興趣了解
剛好最近也在學習如何使用python做程式交易
可否跟你請教呢?
方便用email交流嗎?
我的epson0209@gmail.com

謝謝

山豆兒 提到...

你好,我對程式交易 可能連一知半解還不到
但還是歡迎交流,可以寄信到這個信箱
把 _ 換 成 @ 就行了
xblueman_gmail.com
謝謝

yuntsan0323 提到...

您好....山豆兒....我一直在找人教我群益api. 的連結......可以請教您們嗎....
不過我很笨......

我的目的...用群益api回補tick .1分.5分資料.....

可以聊嗎?

yuntsan0323@gmail.com


我叫eric
謝謝.

匿名 提到...

您好,我使用上面的範例來取得報價,
在win 10上測試,
經過數分或是十數分鐘後,
報價就會停止,
請問可能是哪邊的問題呢?

山豆兒 提到...

你好

我使用目前的程式,報價並沒有停止的問題(我的環境是win7)
不過有想到一個問題,
依群益的使用watch的回報方式,是要報價內容有變動,
才會通知更新,所以會不會是這期間沒有資料變動,
所以就不會更新了,建議
Bot = StockBot("6111")
可以改為
Bot = StockBot("TSEA")
使用台灣加權指數來測試報價會不會中斷
以上

匿名 提到...

您好
我是使用TX_05來測試
資料應該是很常變動

因為我是在公司測試
若您測試沒有停止問題的話
那我猜測也許是公司網路不穩定的關係?

若是網路不穩的話
是否有建議的方法來偵測?
再次感謝

山豆兒 提到...

你好
我有更新一下程式
onTimer 是會定時回報 伺服器上目前的時間
你可以試試看會不會卡住
這個應該也是從群益的伺服器回傳的
但如果卡住是不是網路有問題其實也很難確定
建議你可能還是要換個環境
測試一下是平台的問題還是網路的問題

匿名 提到...

您好
我今天測試的結果
如果只用一檔2330的話
目前測了一個半小時都正常

但是我將
Bot = StockBot("2330")
改成一次要多檔資料(目前要30檔)
跑沒幾分鐘就會卡住

我有將所有queue.put和get都加上false讓它不要被block
也有在while裡面放try except看有沒有辦法抓到exception
但它就是卡住了什麼訊息都沒有

山豆兒 提到...

你好
不知道你是否方便將你修改後的程式碼給我測試
我目前是已經沒有使用 pythonnet 而是改用 comtypes

匿名 提到...

您好
目前最新的狀況如下
我把 Bot裡,case(Job.OnNotifyQuote) 裡面的 job
往前拉到 SKQuoteLibEvent 裡的 OnNotifyQuote() 做
就是從OnNotifyQuote之後的所有動作都不要進 queue

目前在兩個環境各測試不同的情境
一個是只抓TX05
一個是抓40檔的個股
目前測了15分鐘都還沒卡住
看起來朝這方向解應該是正確的

在此跟您分享一下我目前的狀況
也再次感謝您這篇的分享
謝謝您

匿名 提到...

您好
改成這樣的做法之後
剛剛跑到收盤都沒過問題
跟您報告一下

山豆兒 提到...

你好,
謝謝你的分享~

匿名 提到...

您好,
請問一下您有使用過 SKOrderLib.SendFutureOrder 來下單嗎?
因為我試了好久無法成功。

我從您的範例去改,
先宣告一個:
class FUTUREORDER(Structure):
_fields_ = [("bstrFullAccount", c_char_p),
("bstrStockNo" , c_char_p),
("sTradeType", c_int),
("sBuySell", c_int),
("sDayTrade", c_int),
("sNewClose", c_int),
("bstrPrice", c_char_p),
("nQty", c_int),
("bstrTrigger", c_char_p),
("bstrMovingPoint", c_char_p),
("sReserved", c_int),
("bstrDealPrice", c_char_p)
]

然後試著呼叫
def DoSendFutureOrder(self, data_dict):
FUTUREORDER.bstrFullAccount = ""
FUTUREORDER.bstrStockNo= "TX06"
FUTUREORDER.sTradeType=0
FUTUREORDER.sBuySell=0
FUTUREORDER.sDayTrade=0
FUTUREORDER.sNewClose=0
FUTUREORDER.bstrPrice="10000"
FUTUREORDER.nQty=1

strMessage = ""
self.SKCOM.SKOrderLib.SendFutureOrder("", 1, FUTUREORDER, strMessage);

會出現 No method matches given arguments

匿名 提到...

補充一下,
我已成功的初始化以下兩個function
SKCenterLib_Login: 0
SKOrderLib_Initialize: 0

匿名 提到...

您好,
因為一直試不出來
我剛剛改用下面這個連結的範例
就可以成功送出了
https://easontseng.blogspot.tw/2017/07/api-in-pyhton.html
跟您報告一下以免您花費時間測試

謝謝您的分享

山豆兒 提到...

你好

使用 pythonnet 由於是使用 Interop.SKCOMLib.dll 來呼叫 COM 元件
但是群益已經很久沒有更新這個DLL了,所以有些函式可能格式不符或是新增卻不能呼叫
之前有網友有說可以自已產出來

不過我後來改用 comtypes 也是使用 eason 大大的方式,
就沒再使用 pythonnet 了
目前 comtypes 都可以正常使用

匿名 提到...

您好!
有一個問題想請教一下,最近參考https://easontseng.blogspot.com/2017/07/api-in-pyhton.html
的方法來抓歷史報價。

但是只要建立class skQ_events:
運行後就會出現Error in sys.excepthook: Original exception was:

想請問一下有可能是哪方面出問題?
謝謝!