mirror of
https://gitee.com/fasiondog/hikyuu.git
synced 2024-11-30 19:08:48 +08:00
298 lines
12 KiB
C++
298 lines
12 KiB
C++
"""
|
||
扯线木偶界面自动化应用编程接口(Puppet UIAutomation API),是扯线木偶量化框架(Puppet Quant Framework)的一个组件。
|
||
技术群:624585416
|
||
"""
|
||
__author__ = "睿瞳深邃(https://github.com/Raytone-D)"
|
||
__project__ = 'Puppet'
|
||
__version__ = "0.4.12"
|
||
__license__ = 'MIT'
|
||
|
||
# coding: utf-8
|
||
import ctypes
|
||
from functools import reduce
|
||
import time
|
||
import pyperclip
|
||
|
||
MSG = {'WM_SETTEXT': 12,
|
||
'WM_GETTEXT': 13,
|
||
'WM_KEYDOWN': 256,
|
||
'WM_KEYUP': 257,
|
||
'WM_COMMAND': 273,
|
||
'BM_CLICK': 245,
|
||
'CB_GETCOUNT': 326,
|
||
'CB_SETCURSEL': 334,
|
||
'CBN_SELCHANGE': 1,
|
||
'COPY_DATA': 57634}
|
||
|
||
NODE = {'FRAME': (59648, 59649),
|
||
'FORM': (59648, 59649, 1047, 200, 1047),
|
||
'ACCOUNT': (59392, 0, 1711),
|
||
'COMBO': (59392, 0, 2322),
|
||
'BUY': (161, (1032, 1033, 1034), 1006),
|
||
'SELL':(162, (1032, 1033, 1034), 1006),
|
||
'撤单': 163,
|
||
'双向委托': 512,
|
||
'新股申购': 554,
|
||
'中签查询': 1070}
|
||
|
||
TWO_WAY = {'买入代码': 1032,
|
||
'买入价格': 1033,
|
||
'买入数量': 1034,
|
||
'买入': 1006,
|
||
'卖出代码': 1035,
|
||
'卖出价格': 1058,
|
||
'卖出数量': 1039,
|
||
'卖出': 1008,
|
||
'可用余额': 1038,
|
||
'刷新': 32790,
|
||
'全撤': 30001,
|
||
'撤买': 30002,
|
||
'撤卖': 30003,
|
||
'报表': 1047}
|
||
|
||
TAB = {'持仓': ord('W'),
|
||
'成交': ord('E'),
|
||
'委托': ord('R')}
|
||
|
||
SCHEDULE = {'证券代码': '',
|
||
'证券名称': '',
|
||
'实际数量': '',
|
||
'市值': ''}
|
||
|
||
CANCEL = {'全选': 1098,
|
||
'撤单': 1099,
|
||
'全撤': 30001,
|
||
'撤买': 30002,
|
||
'撤卖': 30003,
|
||
'填单': 3348,
|
||
'查单': 3349}
|
||
|
||
NEW = {'新股代码': 1032,
|
||
'新股名称': 1036,
|
||
'申购价格': 1033,
|
||
'可申购数量': 1018,
|
||
'申购数量': 1034,
|
||
'申购': 1006}
|
||
|
||
RAFFLE = ['新股代码', '证券代码', '申购价格', '申购上限']
|
||
#CMD = {'COPY': 57634}
|
||
VKCODE = {'F1': 112,
|
||
'F2': 113,
|
||
'F3': 114,
|
||
'F4': 115,
|
||
'F5': 116,
|
||
'F6': 117}
|
||
|
||
MKT = {'CYB': '3',
|
||
'SH': '7',
|
||
'SZ': '0',
|
||
'创业板': '3',
|
||
'沪市': '7',
|
||
'深市': '0'}
|
||
|
||
op = ctypes.windll.user32
|
||
|
||
def switch_combo(index, idCombo, hCombo):
|
||
op.SendMessageW(hCombo, MSG['CB_SETCURSEL'], index, 0)
|
||
op.SendMessageW(op.GetParent(hCombo), MSG['WM_COMMAND'], MSG['CBN_SELCHANGE']<<16|idCombo, hCombo)
|
||
|
||
class Puppet:
|
||
"""
|
||
界面自动化操控包装类
|
||
# 方法 # '委买': buy(), '委卖': sell(), '撤单': cancel(), '打新': raffle(),
|
||
# 属性 # '帐号': account, '可用余额': balance, '持仓': position, '成交': deals, '可撤委托': cancelable,
|
||
# # '新股': new, '中签': bingo,
|
||
"""
|
||
def __init__(self, main=None, title='网上股票交易系统5.0'):
|
||
|
||
print('我正在热身,稍等一下...')
|
||
self.main = main or op.FindWindowW(0, title)
|
||
self.switch = lambda node: op.SendMessageW(self.main, MSG['WM_COMMAND'], node, 0)
|
||
self._order = []
|
||
for i in (NODE['BUY'],NODE['SELL']):
|
||
node, parts, button = i
|
||
self.switch(node)
|
||
time.sleep(0.3)
|
||
x = reduce(op.GetDlgItem, NODE['FRAME'], self.main)
|
||
self._order.append((tuple(op.GetDlgItem(x, v) for v in parts), button, x))
|
||
|
||
op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['双向委托'], 0) # 切换到交易操作台
|
||
self.wait_a_second = lambda sec=0.2: time.sleep(sec)
|
||
self.wait_a_second() # 可调整区间值(0.01~0.5)
|
||
self.buff = ctypes.create_unicode_buffer(32)
|
||
self.two_way = reduce(op.GetDlgItem, NODE['FRAME'], self.main)
|
||
self.members = {k: op.GetDlgItem(self.two_way, v) for k, v in TWO_WAY.items()}
|
||
print('我准备好了,开干吧!人生巅峰在前面!') if self.main else print("没找到已登录的客户交易端,我先撤了!")
|
||
# 获取登录账号
|
||
self.account = reduce(op.GetDlgItem, NODE['ACCOUNT'], self.main)
|
||
op.SendMessageW(self.account, MSG['WM_GETTEXT'], 32, self.buff)
|
||
self.account = self.buff.value
|
||
self.combo = reduce(op.GetDlgItem, NODE['COMBO'], self.main)
|
||
self.count = op.SendMessageW(self.combo, MSG['CB_GETCOUNT'])
|
||
|
||
def switch_tab(self, hCtrl, keyCode, param=0): # 单击
|
||
op.PostMessageW(hCtrl, MSG['WM_KEYDOWN'], keyCode, param)
|
||
self.wait_a_second(0.5)
|
||
op.PostMessageW(hCtrl, MSG['WM_KEYUP'], keyCode, param)
|
||
|
||
def copy_data(self, key=0): # background mode
|
||
"将CVirtualGridCtrl|Custom<n>的数据复制到剪贴板,默认取当前的表格"
|
||
if key:
|
||
self.switch_tab(self.two_way, key) # 切换到持仓('W')、成交('E')、委托('R')
|
||
print("正在等待实时数据返回,请稍候...")
|
||
pyperclip.copy('')
|
||
# 查到只有列表头的空白数据等3秒...orz
|
||
for i in range(10):
|
||
time.sleep(0.3)
|
||
op.SendMessageW(reduce(op.GetDlgItem, NODE['FORM'], self.main),
|
||
MSG['WM_COMMAND'], MSG['COPY_DATA'], NODE['FORM'][-1])
|
||
if len(pyperclip.paste().splitlines()) > 1:
|
||
break
|
||
return pyperclip.paste()
|
||
|
||
def buy(self, symbol, price, qty):
|
||
self.switch(NODE['BUY'][0])
|
||
tuple(map(lambda hCtrl, arg: op.SendMessageW(
|
||
hCtrl, MSG['WM_SETTEXT'], 0, str(arg)), self._order[0][0], (symbol, price, qty)))
|
||
op.PostMessageW(self._order[0][-1], MSG['WM_COMMAND'], self._order[0][1], 0)
|
||
|
||
def sell(self, symbol, price, qty):
|
||
self.switch(NODE['SELL'][0])
|
||
tuple(map(lambda hCtrl, arg: op.SendMessageW(
|
||
hCtrl, MSG['WM_SETTEXT'], 0, str(arg)), self._order[1][0], (symbol, price, qty)))
|
||
op.PostMessageW(self._order[1][-1], MSG['WM_COMMAND'], self._order[1][1], 0)
|
||
|
||
def buy2(self, symbol, price, qty): # 买入(B)
|
||
self.switch(NODE['双向委托'])
|
||
op.SendMessageW(self.members['买入代码'], MSG['WM_SETTEXT'], 0, str(symbol))
|
||
op.SendMessageW(self.members['买入价格'], MSG['WM_SETTEXT'], 0, str(price))
|
||
op.SendMessageW(self.members['买入数量'], MSG['WM_SETTEXT'], 0, str(qty))
|
||
#op.SendMessageW(self.members['买入'], MSG['BM_CLICK'], 0, 0)
|
||
op.PostMessageW(self.two_way, MSG['WM_COMMAND'], TWO_WAY['买入'], self.members['买入'])
|
||
|
||
def sell2(self, symbol, price, qty): # 卖出(S)
|
||
self.switch(NODE['双向委托'])
|
||
op.SendMessageW(self.members['卖出代码'], MSG['WM_SETTEXT'], 0, str(symbol))
|
||
op.SendMessageW(self.members['卖出价格'], MSG['WM_SETTEXT'], 0, str(price))
|
||
op.SendMessageW(self.members['卖出数量'], MSG['WM_SETTEXT'], 0, str(qty))
|
||
#op.SendMessageW(self.members['卖出'], MSG['BM_CLICK'], 0, 0)
|
||
op.PostMessageW(self.two_way, MSG['WM_COMMAND'], TWO_WAY['卖出'], self.members['卖出'])
|
||
|
||
def refresh(self): # 刷新(F5)
|
||
op.PostMessageW(self.two_way, MSG['WM_COMMAND'], TWO_WAY['刷新'], self.members['刷新'])
|
||
|
||
def cancel(self, symbol=None, way='撤买'):
|
||
|
||
op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['撤单'], 0) # 切换到撤单操作台
|
||
if way and str(symbol).isdecimal():
|
||
#print(self.copy_data())
|
||
self.cancel_c = reduce(op.GetDlgItem, NODE['FRAME'], self.main)
|
||
self.cancel_ctrl = {k: op.GetDlgItem(self.cancel_c, v) for k, v in CANCEL.items()}
|
||
op.SendMessageW(self.cancel_ctrl['填单'], MSG['WM_SETTEXT'], 0, symbol)
|
||
self.wait_a_second()
|
||
op.PostMessageW(self.cancel_c, MSG['WM_COMMAND'], CANCEL['查单'], self.cancel_ctrl['查单'])
|
||
op.PostMessageW(self.cancel_c, MSG['WM_COMMAND'], CANCEL[way], self.cancel_ctrl[way])
|
||
schedule = self.copy_data()
|
||
op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['双向委托'], 0) # 必须返回交易操作台
|
||
return schedule
|
||
|
||
@property
|
||
def balance(self):
|
||
print('可用余额: %s' % ('$'*68))
|
||
op.SendMessageW(self.members['可用余额'], MSG['WM_GETTEXT'], 32, self.buff)
|
||
return self.buff.value
|
||
|
||
@property
|
||
def position(self):
|
||
print('实时持仓: %s' % ('$'*68))
|
||
return self.copy_data(TAB['持仓'])
|
||
|
||
@property
|
||
def market_value(self):
|
||
form = (x.split() for x in self.position.splitlines())
|
||
index = next(form).index('市值')
|
||
return sum((float(row[index]) for row in form))
|
||
|
||
@property
|
||
def deals(self):
|
||
print('当天成交: %s' % ('$'*68))
|
||
return self.copy_data(TAB['成交'])
|
||
|
||
@property
|
||
def cancelable(self):
|
||
print('可撤委托: %s' % ('$'*68))
|
||
return self.cancel(way=False)
|
||
|
||
@property
|
||
def new(self):
|
||
print('新股名单: %s' % ('$'*68))
|
||
return self.raffle(way=False)
|
||
|
||
@property
|
||
def bingo(self):
|
||
print('新股中签: {0}'.format('$'*68))
|
||
op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['中签查询'], 0)
|
||
return self.copy_data()
|
||
|
||
def cancel_all(self): # 全撤(Z)
|
||
op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 30001, self.members[30001])
|
||
|
||
def cancel_buy(self): # 撤买(X)
|
||
op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 30002, self.members[30002])
|
||
|
||
def cancel_sell(self): # 撤卖(C)
|
||
op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 30003, self.members[30003])
|
||
|
||
def cancel_last(self): # 撤最后一笔,仅限华泰定制版有效
|
||
op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 2053, self.members[2053])
|
||
|
||
def cancel_same(self): # 撤相同代码,仅限华泰定制版
|
||
#op.PostMessageW(self.two_way, WM_COMMAND, 30022, self.members[30022])
|
||
pass
|
||
|
||
def raffle(self, skip=None, way=True): # 打新股。
|
||
op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['新股申购'], 0)
|
||
#close_pop() # 弹窗无需关闭,不影响交易。
|
||
schedule = self.copy_data()
|
||
if way:
|
||
print("开始打新股%s" % ('>'*68))
|
||
print(schedule)
|
||
self.raffle_c = reduce(op.GetDlgItem, NODE['FRAME'], self.main)
|
||
self.raffle_ctrl = {k: op.GetDlgItem(self.raffle_c, v) for k, v in NEW.items()}
|
||
new = [x.split() for x in schedule.splitlines()]
|
||
index = [new[0].index(x) for x in RAFFLE if x in new[0]] # 索引映射:代码0, 价格1, 数量2
|
||
new = map(lambda x: [x[y] for y in index], new[1:])
|
||
for symbol, price, qty in new:
|
||
if symbol[0] == skip:
|
||
print({symbol: (qty, "跳过<%s>开头的新股!" % skip)})
|
||
continue
|
||
if qty == '0':
|
||
print({symbol: (qty, "数量为零")})
|
||
continue
|
||
op.SendMessageW(self.raffle_ctrl['新股代码'], MSG['WM_SETTEXT'], 0, symbol)
|
||
self.wait_a_second(1)
|
||
#op.SendMessageW(self.raffle_ctrl['可申购数量'], MSG['WM_GETTEXT'], 32, self.buff)
|
||
#qty = self.buff.value
|
||
op.SendMessageW(self.raffle_ctrl['申购数量'], MSG['WM_SETTEXT'], 0, qty)
|
||
self.wait_a_second()
|
||
op.PostMessageW(self.raffle_c, MSG['WM_COMMAND'], NEW['申购'], self.raffle_ctrl['申购'])
|
||
print({symbol: (qty, "已申购")})
|
||
print(self.cancelable)
|
||
op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['双向委托'], 0) # 切换到交易操作台
|
||
return schedule
|
||
|
||
if __name__ == '__main__':
|
||
|
||
trader = Puppet()
|
||
#trader = Puppet(title='广发证券核新网上交易系统7.60')
|
||
if trader.account:
|
||
print(trader.account) # 帐号
|
||
#print(trader.new) # 查当天新股名单
|
||
#trader.raffle(MKT['创业板']) # 确定打新股,跳过创业板不打。
|
||
#print(trader.balance) # 可用余额
|
||
#print(trader.position) # 实时持仓
|
||
#print(trader.deals) # 当天成交
|
||
#print(trader.cancelable) # 可撤委托
|
||
print(trader.market_value)
|
||
|