Administrator
5 天以前 48fb7a00951f91bdc707e5dd2d196e5bccb752c3
trade/trade_gui.py
@@ -3,6 +3,7 @@
"""
import array
import logging
import threading
import time
import random
@@ -10,11 +11,14 @@
import win32gui
import win32con
import constant
from code_attribute import gpcode_manager
from db.redis_manager_delegate import RedisUtils
from ocr import ocr_util
from trade import l2_trade_util
from db import redis_manager
from log import *
from tool import async_call
from db import redis_manager_delegate as redis_manager
from log_module.log import *
from utils.tool import async_call
from utils import win32_util, capture_util, tool
class THSGuiTrade(object):
@@ -27,16 +31,16 @@
            # 初始化设置
            # 获取交易窗口的锁
            cls.__instance.buy_lock = threading.RLock()
            cls.__instance.buy_cancel_lock = threading.RLock()
            cls.__instance.buy_cancel_locks = {}
            cls.__instance.buy_win_list = cls.get_buy_wins()
            print("交易窗口", cls.__instance.buy_win_list)
            # print("交易窗口", cls.__instance.buy_win_list)
            cls.__instance.using_buy_wins = set()
            cls.__instance.cancel_win = cls.__instance.getCancelBuyWin()
            cls.__instance.cancel_wins = cls.__instance.getCancelBuyWins()
        return cls.__instance
    # 刷新窗口句柄
    def refresh_hwnds(self):
        self.cancel_win = self.__instance.getCancelBuyWin()
        self.cancel_wins = self.__instance.getCancelBuyWins()
        self.buy_win_list = self.get_buy_wins()
    # 打开交易环境
@@ -66,11 +70,11 @@
            raise Exception("下单窗口最低需要10个")
        # 检测撤单窗口
        cancel_trade_win = cls.getCancelBuyWin()
        if cancel_trade_win <= 0:
        cancel_trade_wins = cls.getCancelBuyWins()
        if len(cancel_trade_wins) <= 0:
            raise Exception("委托撤销窗口未打开")
        else:
            pos = win32gui.GetWindowRect(cancel_trade_win)
            pos = win32gui.GetWindowRect(cancel_trade_wins[0])
            width = pos[2] - pos[0]
            height = pos[3] - pos[1]
            if width <= 0 or height <= 0:
@@ -108,7 +112,24 @@
    # 获取撤单窗口
    @classmethod
    def getCancelBuyWin(cls):
    def getCancelBuyWin(cls, code=None):
        # 获取代码位分配的下单窗口
        if code:
            trade_win = THSBuyWinManagerNew.get_distributed_code_win(code)
            if trade_win and win32gui.IsWindowVisible(trade_win):
                top_win = win32_util.get_top_window(trade_win)
                if cls.getText(top_win) == '专业版下单' and win32gui.IsWindowVisible(top_win):
                    return top_win
        wins = cls.getCancelBuyWins()
        if wins:
            return wins[0]
        else:
            return 0
    @classmethod
    def getCancelBuyWins(cls):
        cancel_buy_wins = set()
        hWndList = []
        win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), hWndList)
        for hwnd in hWndList:
@@ -120,10 +141,10 @@
                        width = pos[2] - pos[0]
                        height = pos[3] - pos[1]
                        if width > 200 and height > 200:
                            return hwnd
                            cancel_buy_wins.add(hwnd)
                except:
                    pass
        return 0
        return list(cancel_buy_wins)
    def input_number(self, hwnd, num_str):
        for i in range(10):
@@ -249,10 +270,10 @@
            #     raise Exception(error)
            # TODO 暂时不验证涨停价
            if not constant.TEST:
                if abs(float(limit_up_price_now) - float(limit_up_price)) >= 0.01:
                    error = "涨停价验证出错 {}-{}".format(limit_up_price, limit_up_price_now)
                    raise Exception(error)
            # if not constant.TEST:
            #     if abs(float(limit_up_price_now) - float(limit_up_price)) >= 0.001:
            #         error = "涨停价验证出错 {}-{}".format(limit_up_price, limit_up_price_now)
            #         raise Exception(error)
            # 开始交易,买入按钮ID:0x000003EE
            # buy_hwnd = win32gui.GetDlgItem(win, 0x000003EE)
@@ -326,13 +347,10 @@
                        pass
        return 0
    def __get_code_input(self):
        win = self.cancel_win
        if win <= 0 or not win32gui.IsWindowVisible(win):
            self.cancel_win = self.getCancelBuyWin()
            win = self.cancel_win
            if win <= 0:
                raise Exception("无法找到取消委托窗口")
    def __get_code_input(self, code=None):
        win = self.getCancelBuyWin(code)
        if win <= 0:
            raise Exception("无法找到取消委托窗口")
        t = time.time()
        print(t)
        start = int(round(t * 1000))
@@ -351,9 +369,15 @@
    def cancel_buy(self, code):
        if not constant.TRADE_ENABLE:
            return
        self.buy_cancel_lock.acquire()
        main_win = self.getCancelBuyWin(code)
        if main_win <= 0:
            raise Exception("尚未找到交易窗口")
        if main_win not in self.buy_cancel_locks:
            self.buy_cancel_locks[main_win] = threading.RLock()
        self.buy_cancel_locks[main_win].acquire()
        logger_trade_gui.info("开始获取撤单控件:code-{}".format(code))
        code_input, win = self.__get_code_input()
        code_input, win = self.__get_code_input(code)
        try:
            logger_trade_gui.info("开始撤单:code-{}".format(code))
            start = int(round(time.time() * 1000))
@@ -389,7 +413,7 @@
            logger_trade_gui.info("撤单成功:code-{} 耗时:{}".format(code, end - start))
            time.sleep(0.03)
        finally:
            self.buy_cancel_lock.release()
            self.buy_cancel_locks[main_win].release()
            # 清空代码框
            self.input_number(code_input, "")
            # 再次清除代码框
@@ -414,32 +438,41 @@
    @async_call
    def refresh_data(self):
        # 获取到专业下单页面
        win = self.getCancelBuyWin()
        child_win = None
        refresh_btn = None
        for i in range(0, 20):
            child_win = win32gui.FindWindowEx(win, child_win, "#32770", None)
            if not child_win:
                break
            if not win32gui.IsWindowVisible(child_win):
                continue
            temp = win32gui.FindWindowEx(child_win, None, "Button", "还原")
            if temp:
                refresh_btn = win32gui.GetDlgItem(child_win, 0x00000457)
                break
        if refresh_btn:
            # 点击刷新
            THSGuiUtil.click(refresh_btn)
        wins = self.getCancelBuyWins()
        if wins:
            for win in wins:
                refresh_btn = None
                child_win = None
                for i in range(0, 20):
                    child_win = win32gui.FindWindowEx(win, child_win, "#32770", None)
                    if not child_win:
                        break
                    if not win32gui.IsWindowVisible(child_win):
                        continue
                    temp = win32gui.FindWindowEx(child_win, None, "Button", "还原")
                    if temp:
                        refresh_btn = win32gui.GetDlgItem(child_win, 0x00000457)
                        break
                if refresh_btn:
                    # 点击刷新
                    THSGuiUtil.click(refresh_btn)
            for w in wins:
                if w not in self.buy_cancel_locks:
                    self.buy_cancel_locks[w] = threading.RLock()
class THSGuiUtil:
    @classmethod
    def getText(cls, hwnd):
        bufSize = win32gui.SendMessage(hwnd, win32con.WM_GETTEXTLENGTH, 0, 0) + 1
        buffer = array.array('b', b'\x00\x00' * bufSize)
        win32gui.SendMessage(hwnd, win32con.WM_GETTEXT, bufSize, buffer)
        text = win32gui.PyGetString(buffer.buffer_info()[0], bufSize - 1)
        return text.replace("\x00", "").strip()
        try:
            buffer = array.array('b', b'\x00\x00' * bufSize)
            win32gui.SendMessage(hwnd, win32con.WM_GETTEXT, bufSize, buffer)
            text = win32gui.PyGetString(buffer.buffer_info()[0], bufSize - 1)
            return text.replace("\x00", "").strip()
        except:
            return ""
    # 添加下单窗口
    @classmethod
@@ -524,131 +557,6 @@
        THSGuiTrade().input_number(hwnd1, code)
# 过时 同花顺买入窗口管理器
class __THSBuyWinManager:
    redisManager = redis_manager.RedisManager(2)
    @classmethod
    def __get_redis(cls):
        return cls.redisManager.getRedis()
    # 保存窗口代码分配
    @classmethod
    def __save_code_win(cls, code, win):
        key = "buywin_distribute-{}".format(code)
        cls.__get_redis().setex(key, tool.get_expire(), win)
    # 获取窗口分配的代码
    @classmethod
    def __get_code_win(cls, code):
        key = "buywin_distribute-{}".format(code)
        win = cls.__get_redis().get(key)
        if win is not None:
            return int(win)
        return None
    # 删除代码窗口分配
    @classmethod
    def __del_code_win(cls, code):
        key = "buywin_distribute-{}".format(code)
        cls.__get_redis().delete(key)
    # 获取所有已经分配窗口的代码
    @classmethod
    def __get_distributed_win_codes(cls):
        key = "buywin_distribute-*"
        keys = cls.__get_redis().keys(key)
        codes = []
        for k in keys:
            codes.append(k.replace("buywin_distribute-", ""))
        return codes
    # 获取可用的窗口
    @classmethod
    def __get_available_win(cls):
        # 是否有可用的还未分配的窗口
        key = "buywin_distribute-*"
        keys = cls.__get_redis().keys(key)
        win_list = THSGuiTrade().get_buy_wins()
        if len(win_list) < 1:
            raise Exception("必须要有一个买入窗口")
        win_set = set(win_list)
        for k in keys:
            win = int(cls.__get_redis().get(k))
            if win in win_set:
                win_set.remove(win)
        if len(win_set) > 0:
            return win_set.pop()
        # 没有剩余的窗口,新增加窗口
        win = THSGuiUtil.add_buy_win()
        if win:
            return win
        else:
            raise Exception("新增窗口失败")
    # 为代码分配窗口
    @classmethod
    def distribute_win_for_code(cls, code):
        # 获取是否已经分配
        win = cls.__get_code_win(code)
        if win is not None:
            # 已经分配的窗口是否有效
            if THSGuiUtil.is_win_exist(win):
                # 填充代码
                THSGuiUtil.set_buy_window_code(win, code)
                return win
        # 获取可用的窗口
        win = cls.__get_available_win()
        if win is None:
            raise Exception("窗口已经分配完毕,无可用窗口")
        # 保存窗口分配信息
        cls.__save_code_win(code, win)
        THSGuiUtil.set_buy_window_code(win, code)
        return win
    # 删除代码窗口分配
    @classmethod
    def cancel_distribute_win_for_code(cls, code):
        win = cls.__get_code_win(code)
        if win is not None:
            # 清除代码
            THSGuiUtil.clear_buy_window_code(win)
        cls.__del_code_win(code)
    # 获取代码已经分配的窗口
    @classmethod
    def get_distributed_code_win(cls, code):
        win = cls.__get_code_win(code)
        if not THSGuiUtil.is_win_exist(win):
            # 删除不存在的窗口
            cls.__del_code_win(code)
            return None
        return win
    @classmethod
    def fill_codes(cls, codes):
        codes_ = gpcode_manager.get_gp_list()
        # 先删除没有的代码
        old_codes = cls.__get_distributed_win_codes()
        for code in old_codes:
            if code not in codes_:
                cls.cancel_distribute_win_for_code(code)
        add_codes = codes[0:10]
        del_codes = codes[10:]
        for code in del_codes:
            cls.cancel_distribute_win_for_code(code)
        for code in add_codes:
            # 已经加入进去的不做操作
            if code in old_codes:
                continue
            win = cls.distribute_win_for_code(code)
            print("分配的窗口:", win, THSGuiUtil.is_win_exist(win))
# 同花顺买入窗口管理器
class THSBuyWinManagerNew:
    redisManager = redis_manager.RedisManager(2)
@@ -657,24 +565,24 @@
    def get_buy_wins(cls):
        buy_win_list = []
        hWndList = []
        main_hwnd = None
        main_hwnds = []
        win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), hWndList)
        for hwnd in hWndList:
            if THSGuiUtil.getText(hwnd) == "专业版下单":
                main_hwnd = hwnd
                break
        if not main_hwnd:
            if win32gui.IsWindowVisible(hwnd) and THSGuiUtil.getText(hwnd) == "专业版下单":
                main_hwnds.append(hwnd)
        if not main_hwnds:
            raise Exception("专业版下单未打开")
        child_win = None
        for i in range(0, 20):
            child_win = win32gui.FindWindowEx(main_hwnd, child_win, "#32770", None)
            if not child_win:
                break
            if not win32gui.IsWindowVisible(child_win):
                continue
            temp = win32gui.FindWindowEx(child_win, None, "Button", "撤单")
            if temp:
                buy_win_list.append(child_win)
        for main_hwnd in main_hwnds:
            for i in range(0, 20):
                child_win = win32gui.FindWindowEx(main_hwnd, child_win, "#32770", None)
                if not child_win:
                    break
                if not win32gui.IsWindowVisible(child_win):
                    continue
                temp = win32gui.FindWindowEx(child_win, None, "Button", "撤单")
                if temp:
                    buy_win_list.append(child_win)
        return buy_win_list
    @classmethod
@@ -698,13 +606,13 @@
    @classmethod
    def __save_code_win(cls, code, win):
        key = "buywin_distribute-{}".format(code)
        cls.__get_redis().setex(key, tool.get_expire(), win)
        RedisUtils.setex(cls.__get_redis(), key, tool.get_expire(), win)
    # 获取窗口分配的代码
    @classmethod
    def __get_code_win(cls, code):
        key = "buywin_distribute-{}".format(code)
        win = cls.__get_redis().get(key)
        win = RedisUtils.get(cls.__get_redis(), key)
        if win is not None:
            return int(win)
        return None
@@ -713,13 +621,13 @@
    @classmethod
    def __del_code_win(cls, code):
        key = "buywin_distribute-{}".format(code)
        cls.__get_redis().delete(key)
        RedisUtils.delete(cls.__get_redis(), key)
    # 获取所有已经分配窗口的代码
    @classmethod
    def __get_distributed_win_codes(cls):
        key = "buywin_distribute-*"
        keys = cls.__get_redis().keys(key)
        keys = RedisUtils.keys(cls.__get_redis(), key)
        codes = []
        for k in keys:
            codes.append(k.replace("buywin_distribute-", ""))
@@ -730,17 +638,19 @@
    def __get_available_win(cls):
        # 是否有可用的还未分配的窗口
        key = "buywin_distribute-*"
        keys = cls.__get_redis().keys(key)
        keys = RedisUtils.keys(cls.__get_redis(), key)
        win_list = cls.get_buy_wins()
        if len(win_list) < 1:
            raise Exception("必须要有一个买入窗口")
        win_set = set(win_list)
        for k in keys:
            win = int(cls.__get_redis().get(k))
            win = int(RedisUtils.get(cls.__get_redis(),k))
            if win in win_set:
                win_set.remove(win)
        if len(win_set) > 0:
            return win_set.pop()
            win_list = list(win_set)
            random.shuffle(win_list)
            return win_list[0]
        # 没有剩余的窗口,新增加窗口
        raise Exception("没有剩余窗口")
@@ -760,16 +670,28 @@
        # 获取可用的窗口
        win = cls.__get_available_win()
        if win is None:
            logger_buy_win_distibute.error(f"无可用窗口:{code}")
            raise Exception("窗口已经分配完毕,无可用窗口")
        # 保存窗口分配信息
        cls.__save_code_win(code, win)
        # 设置代码多试几次
        is_success = False
        for i in range(0, 3):
            THSGuiUtil.set_buy_window_code(cls.get_trade_win(win), code)
            time.sleep(0.5)
            code_name_win = cls.__get_code_name(win)
            if code_name == code_name_win:
                break
            if code_name == code_name_win or code_name_win.find(code_name) > -1:
                if cls.__is_buy_limit_up_price(win):
                    is_success = True
                    break
                else:
                    cls.__del_code_win(code)
                    THSGuiUtil.set_buy_window_code(cls.get_trade_win(win), "")
                    raise Exception("不是买涨停价")
        if is_success:
            logger_buy_win_distibute.info(f"新分配窗口成功:{code}-{win}")
        else:
            logger_buy_win_distibute.info(f"新分配窗口失败:{code}-{win}")
        return win
    # 删除代码窗口分配
@@ -794,6 +716,18 @@
            return None
        return win
    # 获取已分配的交易框信息
    @classmethod
    def get_distributed_code_wins(cls):
        key = "buywin_distribute-*"
        keys = RedisUtils.keys(cls.__get_redis(), key)
        results = []
        for k in keys:
            code = k.split("-")[-1]
            win = RedisUtils.get(cls.__get_redis(), k)
            results.append((code, win))
        return results
    # 获取代码名称
    @classmethod
    def __get_code_name(cls, win):
@@ -805,6 +739,18 @@
        if name is not None:
            name = name.replace(" ", "")
        return tool.strQ2B(name)
    # 是否是涨停价
    @classmethod
    def __is_buy_limit_up_price(cls, win):
        trade_win = cls.get_trade_win(win)
        if trade_win is None:
            return None
        price_win = win32gui.GetDlgItem(trade_win, 0x00000409)
        ocr_result = ocr_util.OcrUtil.ocr_with_key(capture_util.window_capture(price_win), "涨停价|张停价")
        if ocr_result:
            return True
        return False
    @classmethod
    def fill_codes(cls, codes):
@@ -826,8 +772,10 @@
            else:
                new_delete_codes.append(code)
        add_codes = new_codes[0:10]
        del_codes = new_codes[10:]
        cancel_wins = THSGuiTrade.getCancelBuyWins()
        add_codes_num = len(cancel_wins) * 10
        add_codes = new_codes[0:add_codes_num]
        del_codes = new_codes[add_codes_num:]
        del_codes.extend(new_delete_codes)
        for code in del_codes:
@@ -846,8 +794,16 @@
                    if name_codes.get(code_name) != code:
                        cls.cancel_distribute_win_for_code(code)
                continue
            win = cls.distribute_win_for_code(code, gpcode_manager.get_code_name(code))
            print("分配的窗口:", win, THSGuiUtil.is_win_exist(win))
            try:
                win = cls.distribute_win_for_code(code, gpcode_manager.get_code_name(code))
                print("分配的窗口:", win, THSGuiUtil.is_win_exist(win))
            except Exception as e:
                logging.exception(e)
# 根据涨幅高低分配交易窗口
def re_distribute_buy_win(codes):
    THSBuyWinManagerNew.fill_codes(codes)
class GUITest:
@@ -877,12 +833,10 @@
if __name__ == '__main__':
    try:
        THSGuiTrade().cancel_buy_again("000637")
    except Exception as e:
        print(e)
    # GUITest().test_distribute()
    # try:
    #     THSGuiUtil.set_buy_window_code(0x000112D0, "000333")
    #     THSGuiTrade().cancel_buy_again("000637")
    # except Exception as e:
    #     print(e)
    print(ocr_util.OcrUtil.ocr_with_key(capture_util.window_capture(0x000314EA), "涨停价|张停价"))
    # THSGuiTrade().buy("600613", 10.29)