使用 pip 安裝
# ZeroMQ
pip install zmq
透過 ZeroMQ 的 Pub-Sub 模式實現交易與策略模組分離。
quote.py
import json
import os
import re
import sys
# 停用 loguru 的 stdout 輸出
os.environ['LOGURU_AUTOINIT'] = 'False'
import ctypes
import comtypes
import comtypes.client
import datetime
import dateutil.relativedelta
import loguru
import wcwidth
import wx
import zmq
import zmq.error
class YuantaQuoteAXCtrl:
def __init__(self, parent):
self.parent = parent
# 建立一個 ZeroMQ Publisher 用以讓其他程式註冊接收行情訊號
self.pub = None
try:
loguru.logger.info(f'ZMQ Publisher is starting...\n')
self.pub = zmq.Context().socket(zmq.PUB)
self.pub.bind('tcp://*:10000')
loguru.logger.success(f'ZMQ Publisher is started.\n')
except zmq.error.ZMQError as e:
loguru.logger.error(f'ZMQ Publisher is stopped ({e}).\n')
container = ctypes.POINTER(comtypes.IUnknown)()
control = ctypes.POINTER(comtypes.IUnknown)()
guid = comtypes.GUID()
sink = ctypes.POINTER(comtypes.IUnknown)()
ctypes.windll.atl.AtlAxCreateControlEx(
'YUANTAQUOTE.YuantaQuoteCtrl.1',
self.parent.Handle,
None,
ctypes.byref(container),
ctypes.byref(control),
ctypes.byref(guid),
sink
)
self.ctrl = comtypes.client.GetBestInterface(control)
self.sink = comtypes.client.GetEvents(self.ctrl, self)
self.Host = None
self.Port = None
self.Username = None
self.Password = None
# 儲存繪圖點位
self.Series = []
def Config(self, host, port, username, password):
self.Host = host
self.Port = port
self.Username = username
self.Password = password
def XXF(self):
today = datetime.date.today()
day = datetime.date.today().replace(day=1)
while day.weekday() != 2:
day = day + datetime.timedelta(days=1)
day = day + dateutil.relativedelta.relativedelta(days=14)
if day < today:
day = day + dateutil.relativedelta.relativedelta(months=1)
codes = [
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L'
]
y = day.year % 10
m = codes[day.month - 1]
return f'{m}{y}'
def TXF(self):
return f'TXF{self.XXF()}'
def Logon(self):
self.ctrl.SetMktLogon(
self.Username,
self.Password,
self.Host,
self.Port,
1,
0
)
# 省略未使用事件函數
def OnGetMktAll(self,
symbol,
refPri,
openPri,
highPri,
lowPri,
upPri,
dnPri,
matchTime,
matchPri,
matchQty,
tolMatchQty,
bestBuyQty,
bestBuyPri,
bestSellQty,
bestSellPri,
fdbPri,
fdbQty,
fdsPri,
fdsQty,
reqType):
# 如果 ZeroMQ Publisher 存在,那就將行情訊號發送至 Yuanta.Quote 這個 Topic
if self.pub is not None:
topic = 'Yuanta.Quote'
payload = json.dumps({
'symbol': symbol,
'refPri': refPri,
'openPri': openPri,
'highPri': highPri,
'lowPri': lowPri,
'upPri': upPri,
'dnPri': dnPri,
'matchTime': matchTime,
'matchPri': matchPri,
'matchQty': matchQty,
'tolMatchQty': tolMatchQty,
'bestBuyQty': bestBuyQty,
'bestBuyPri': bestBuyPri,
'bestSellQty': bestSellQty,
'bestSellPri': bestSellPri,
'fdbPri': fdbPri,
'fdbQty': fdbQty,
'fdsPri': fdsPri,
'fdsQty': fdsQty,
'reqType': reqType
})
self.pub.send_multipart([
topic.encode('utf-8'),
payload.encode('utf-8')
])
self.Series.append(float(matchPri))
if len(self.Series) > 165:
self.Series = self.Series[1:]
now = datetime.datetime.now()
clX = f' {now:%H:%M:%S.%f} '
cl01 = f' {symbol: >6} '
cl02 = f' {reqType: >4} '
cl03 = f' {refPri: >10} '
cl04 = f' {openPri: >10} '
cl05 = f' {highPri: >10} '
cl06 = f' {lowPri: >10} '
cl07 = f' {upPri: >10} '
cl08 = f' {dnPri: >10} '
cl09 = f' {matchTime: >15} '
if matchTime:
cl09 = f' {matchTime[:2]}:{matchTime[2:4]}:{matchTime[4:6]}.{matchTime[6:]} '
cl10 = f' {matchPri: >10} '
cl11 = f' {matchQty: >8} '
cl12 = f' {tolMatchQty: >8} '
date = f'{datetime.date.today():%Y-%m-%d}'
title = ' MatchPrice '
chart = ''
if len(set(self.Series)) > 1:
chart = asciichartpy.plot(self.Series, {
'width': 116,
'height': 30
})
sys.stdout.write(
'\033[2J' +
f' 元大期貨 { date }\n' +
f' ----------------------------------------------------------------------------------------------------------------------------------------------------------------------\n' +
f' | 接收時間 | 代碼 | 盤別 | 參考價 | 開盤價 | 最高價 | 最低價 | 漲停價 | 跌停價 | 成交時間 | 成交價位 | 成交數量 | 總成交量 |\n' +
f' ----------------------------------------------------------------------------------------------------------------------------------------------------------------------\n' +
f' |{ cl00}|{ cl01}|{cl02}|{ cl03}|{ cl04}|{ cl05}|{ cl06}|{ cl07}|{ cl08}|{ cl09}|{ cl10}|{ cl11}|{ cl12}|\n' +
f' ----------------------------------------------------------------------------------------------------------------------------------------------------------------------\n' +
f'\n' +
f'{title}\n' +
f'\n' +
f'{chart}\n'
)
def OnMktStatusChange(self,
status,
msg,
reqType):
code = ''
find = re.match(r'^(?P<code>\d).+$', msg)
if find:
code = msg[0]
msg = msg[1:]
if msg.endswith('!'):
msg = msg[:-1]
msg = msg + ' ' * (50 - wcwidth.wcswidth(msg))
clX = f' {datetime.datetime.now():%H:%M:%S.%f} '
cl01 = f' {reqType: >7} '
cl02 = f' {status: >6} '
cl03 = f' {code: >4} '
cl04 = f' {msg} '
loguru.logger.info(
f'OnMktStatusChange\n' +
f'--------------------------------------------------------------------------------------------------\n' +
f'| | reqType | status | code | msg |\n' +
f'--------------------------------------------------------------------------------------------------\n' +
f'|{ clX}|{ cl01}|{ cl02}|{cl03}|{cl04 }|\n' +
f'--------------------------------------------------------------------------------------------------\n'
)
if status != 2:
return
code = self.TXF()
loguru.logger.info(f'OnMktStatusChange: AddMktReg(code={code}, 4, reqType={reqType}, 0)\n')
result = self.ctrl.AddMktReg(code, 4, reqType, 0)
loguru.logger.info(f'OnMktStatusChange: AddMktReg(code={code}, 4, reqType={reqType}, 0) => {result}\n')
def OnGetTimePack(self,
strTradeType,
strTime,
reqType):
clX = f' {datetime.datetime.now():%H:%M:%S.%f} '
cl01 = f' {reqType: >7} '
cl02 = f' {strTime[:2]}:{strTime[2:4]}:{strTime[4:6]}.{strTime[6:]} '
cl03 = f' {strTradeType: >12} '
loguru.logger.info(
f'OnGetTimePack\n' +
f'--------------------------------------------------------------\n' +
f'| | reqType | strTime | strTradeType |\n' +
f'--------------------------------------------------------------\n' +
f'|{ clX}|{ cl01}|{ cl02}|{ cl03}|\n' +
f'--------------------------------------------------------------\n'
)
# 省略未使用事件函數
def main():
app = wx.App()
frame = wx.Frame(
parent=None,
id=wx.ID_ANY,
title='Yuanta.Quote'
)
frame.Hide()
quote = YuantaQuoteAXCtrl(frame)
quote.Config(
host='<Host>',
port='<Port>',
username='<Username>',
password='<Password>'
)
quote.Logon()
app.MainLoop()
if __name__ == '__main__':
loguru.logger.add(
f'{datetime.date.today():%Y%m%d}.log',
rotation='1 day',
retention='7 days',
level='DEBUG'
)
main()
signal.py
import datetime
import os
import loguru
import zmq
def main():
# 建立一個 ZeroMQ Subscriber 用以註冊接收行情訊號
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect('tcp://localhost:10000')
socket.setsockopt(zmq.SUBSCRIBE, b'Yuanta.Quote')
loguru.logger.debug(f'main: receiving...')
while True:
message = socket.recv()
message = message.decode('utf-8')
loguru.logger.debug(f'main: receive message{os.linesep}{message}')
if message == 'Yuanta.Quote':
continue
# 進行策略判斷與下單行為
if __name__ == '__main__':
loguru.logger.add(
f'{datetime.date.today():%Y%m%d}.log',
rotation='1 day',
retention='7 days',
level='DEBUG'
)
main()
團隊系列文:
CSScoke - 金魚都能懂的這個網頁畫面怎麼切 - 金魚都能懂了你還怕學不會嗎
Clarence - LINE bot 好好玩 30 天玩轉 LINE API
Hina Hina - 陣列大亂鬥
King Tzeng - IoT沒那麼難!新手用JavaScript入門做自己的玩具
Vita Ora - 好 Js 不學嗎 !? JavaScript 入門中的入門。
TaTaMo - 用Python開發的網頁不能放到Github上?Lektor說可以!!
您好
感謝您的技術文章分享
已成功根據您的步驟取得tick資料
後續想在 signal.py 內的
#進行策略判斷與下單行為
嘗試取得 message 的 matchPri 及 matchQty 進行運算
請問我該往哪個方向涉略???
目前因為隨便寫進一行像是 a=1
程式便無法執行而閃退
再次感謝您的分享