admin
2023-04-28 c082b525b5e501dfa24038e3a85e444d88bfb1d0
开盘啦采集工具/看盘页面优化
14个文件已添加
9个文件已修改
1986 ■■■■■ 已修改文件
cnn/cnn.py 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnn/cnn_test.py 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnn/model.pt 补丁 | 查看 | 原始文档 | blame | 历史
cnn/model.py 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code_data_manager.py 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
data_spider.py 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jinja2_test.py 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/gui.py 309 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/gui.spec 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/kpl_api.py 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/tool.py 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
log.py 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 317 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.spec 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ocr_util.py 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
opencv_util.py 240 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/kpl.js 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/setting.conf 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/template.html 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
setting.py 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test.py 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tool.py 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
win32_util.py 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnn/cnn.py
New file
@@ -0,0 +1,50 @@
import torch
import torch.nn as nn
print(torch.__version__)
print(torch.cuda.is_available())
# 创建网络
class CNN(torch.nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv = nn.Sequential(
            # [BATCH_SIZE, 1, 28, 28]
            # (通道大小,特征图数量,卷积核大小,步长,边缘填充像素)
            nn.Conv2d(1, 32, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(2),
            # [BATCH_SIZE, 32, 14, 14]
            nn.Conv2d(32, 64, 5, 1, 2),
            # [BATCH_SIZE, 64, 14, 14]
            nn.ReLU(),
            nn.MaxPool2d(2)
            # [BATCH_SIZE, 64, 7, 7]
        )
        self.fc = nn.Linear(64 * 7 * 7, 10)
    def forward(self, x):
        x = self.conv(x)
        x = x.view(x.size(0), -1)
        y = self.fc(x)
        return y
# 评估函数
def accuracy(predictions, labels):
    pred = torch.max(predictions.data, 1)[1]
    rights = pred.eq(labels.data.view_as(pred)).sum()
    return rights, len(labels)
def __train():
    net = CNN()
    # 损失函数
    criterion = nn.CrossEntropyLoss()
    #优化器
    optimizer = torch.optim.Adam(net.parameters(),lr=0.001)
    num_epochs= 10
    # 开始循环训练
    for epoch in range(num_epochs):
        pass
cnn/cnn_test.py
New file
@@ -0,0 +1,52 @@
import os
import time
from PIL import Image
import torch
from torchvision import transforms
import cnn
import matplotlib.pyplot as plt
# 分隔图片
def __split_img_for_cnn(img):
    # 切割图片
    pass
if __name__ == '__main__':
    path = 'C:/Users/Administrator/Desktop/ocr/codes/'
    imgs = []
    labels = []
    for name in sorted(os.listdir(path)):
        if name.find(".png") < 0:
            continue
        img = Image.open(path + name).convert('L')
        img = transforms.ToTensor()(img)
        imgs.append(img)
        labels.append(int(name[0]))
    imgs = torch.stack(imgs, 0)
    # %% 加载模型
    model = cnn.CNN()
    model.load_state_dict(torch.load('model.pt', map_location=torch.device('cpu')))
    model.eval()
    for i in range(0, 10):
        start_time = time.time()
        with torch.no_grad():
            output = model(imgs)
        # %% 打印结果
        pred = output.argmax(1)
        true = torch.LongTensor(labels)
        print(pred)
        print(true)
        print("识别时间:", (time.time() - start_time) * 1000)
        # %% 结果显示
        # plt.figure(figsize=(10, 4))
        # for i in range(len(imgs)):
        #     plt.subplot(2, 5, i + 1)
        #     plt.title(f'pred {pred[i]} | true {true[i]}')
        #     plt.axis('off')
        #     plt.imshow(imgs[i].squeeze(0), cmap='gray')
        # # plt.savefig('test.png')
        # plt.show()
cnn/model.pt
Binary files differ
cnn/model.py
New file
@@ -0,0 +1,70 @@
import torch
import torch.nn as nn
import torchvision.datasets as dataset
import torchvision.transforms as transforms
import torch.utils.data as data_utils
import matplotlib.pyplot as plt
print(torch.__version__)
print(torch.cuda.is_available())
# 获取数据集
train_data = dataset.MNIST(root="D:\\ml",
                           train=True,
                           transform=transforms.ToTensor(),
                           download=True
                           )
test_data = dataset.MNIST(root="D:\\ml",
                          train=False,
                          transform=transforms.ToTensor(),
                          download=False
                          )
train_loader = data_utils.DataLoader(dataset=train_data, batch_size=100, shuffle=True)
test_loader = data_utils.DataLoader(dataset=test_data, batch_size=100, shuffle=True)
cnn = CNN()
# cnn = cnn.cuda()
# 损失函数
los = torch.nn.CrossEntropyLoss()
# 优化函数
optime = torch.optim.Adam(cnn.parameters(), lr=0.01)
train_targets = train_data.targets
plt.figure(figsize=(9, 9))
for i in range(9):
    plt.subplot(3, 3, i + 1)
    plt.title(train_targets[i].numpy())
    plt.axis('off')
    plt.imshow(train_data.data[i], cmap='gray')
plt.show()
# 训练模型
for epo in range(10):
    for i, (images, lab) in enumerate(train_loader):
        # images = images.cuda()
        # lab = lab.cuda()
        out = cnn(images)
        loss = los(out, lab)
        optime.zero_grad()
        loss.backward()
        optime.step()
        print("epo:{},i:{},loss:{}".format(epo + 1, i, loss))
# 测试模型
loss_test = 0
accuracy = 0
with torch.no_grad():
    for j, (images_test, lab_test) in enumerate(test_loader):
        # images_test = images_test.cuda()
        # lab_test = lab_test.cuda()
        out1 = cnn(images_test)
        loss_test += los(out1, lab_test)
        loss_test = loss_test / (len(test_data) // 100)
        _, p = out1.max(1)
        accuracy += (p == lab_test).sum().item()
        accuracy = accuracy / len(test_data)
        print("loss_test:{},accuracy:{}".format(loss_test, accuracy))
code_data_manager.py
@@ -38,29 +38,30 @@
        datas = sorted(datas, key=lambda tup: int(tup["created_at"].split(" ")[1].replace(":", "")))
        return datas
    def get_lack_datas_time_range(self, datas):
    def get_lack_datas_time_range(self, datas, min_time=None):
        def is_trade_time(time_):
            if int("092500") <= int(time_.replace(":", "")) <= int("113000") or int("130000") <= int(
                    last_time.replace(":", "")) <= int("150000"):
                    time_.replace(":", "")) <= int("150000"):
                return True
            return False
        ranges = []
        # 只获取18分钟内的数据
        now_time_str = tool.get_now_time_str()
        if tool.trade_time_sub(now_time_str, "15:00:00") > 0:
            now_time_str = "15:00:00"
        if tool.trade_time_sub(now_time_str, "13:00:00") < 0 and tool.trade_time_sub(now_time_str, "11:30:00") > 0 :
        if tool.trade_time_sub(now_time_str, "13:00:00") < 0 and tool.trade_time_sub(now_time_str, "11:30:00") > 0:
            now_time_str = "11:30:00"
        # 还没到时间
        if tool.trade_time_sub(now_time_str, "09:25:00") < 0:
            return ranges
        last_time = tool.trade_time_add_second(now_time_str, 0 - 18 * 60)
        if tool.trade_time_sub(last_time, "09:25:00") < 0:
            last_time = "09:25:00"
        last_time = "09:25:01" if min_time is None else min_time
        # tool.trade_time_add_second(now_time_str, 0 - 18 * 60)
        # if tool.trade_time_sub(last_time, "09:25:00") < 0:
        #    last_time = "09:25:00"
        if len(datas) == 0:
            ranges.append((last_time, now_time_str))
data_spider.py
New file
@@ -0,0 +1,29 @@
import cv2
import opencv_util
import win32_util
import ocr_util
def save_code_img():
    img = win32_util.window_capture(0x00D118FC, (40, 150, 90, 165))
    gray_img = opencv_util.gray_img(img)
    code = ocr_util.recognize_code(gray_img)
    # 保存文件
    cv2.imwrite(f"datas/test3/{code}.png", gray_img)
    print(code)
def save_l2_select_img():
    x = 1500 + 67
    y = 400 + 4
    img = win32_util.window_capture(0x00010BA2, (x, y, x + 45, y + 16))
    gray_img = opencv_util.gray_img(img)
    code = ocr_util.recognize_num(gray_img)
    # 保存文件
    cv2.imwrite(f"datas/test4/{code}.png", gray_img)
    print(code)
if __name__ == "__main__":
    save_l2_select_img()
jinja2_test.py
New file
@@ -0,0 +1,68 @@
from jinja2 import Environment, FileSystemLoader
def render(params):
    env = Environment(loader=FileSystemLoader('D:/nginx-1.13.6/html/kp'))
    css = env.get_template('index.css')
    params["css"] = css.render()
    template = env.get_template('index.html')
    html_content = template.render(params)
    return html_content
if __name__ == '__main__':
    datas = {
        "base_url": "http://192.168.3.122/kp/",
        "code_name": "天域生态 002255",
        "score_data": {"volume": {"score": 20, "now": "12", "high": {"num": "56", "date": "2023-04-13"}},
                       "bidding": {"score": 10, "money": "4563"},
                       "deal_big_money": {"score": 10, "money": 0, "base_money": 456.5,
                                          "start": {"time": "09:00:00", "num": 1456},
                                          "end": {"time": "09:00:00", "num": 1456}},
                       "k_form": {"score": 10, "datas": ["破前高", "超跌补涨", "涨幅过高"]},
                       "code_nature": {"score": 10, "data_desc": "涨停次数2次"},
                       "hot_block": {"score": 10, "block_name": "影视", "limit_up_count": 10, "open_limit_up_count": 2},
                       "limit_up_time": {"score": 10, "time": "09:56:00"},
                       "zyltgb": {"score": 10, "value": "12.5"},
                       "limit_up_price": {"score": 10, "price": "6.35"},
                       "total_score": "210"
                       },
        "trade_data": {"star": {"desc": "被动买入", "count": 0},
                       "safe_count": {"base": 12, "now": 2},
                       "m": {"base": 1200, "now": 1000},
                       "big_num": {"base": 10, "now": 2},
                       "trade_progress": {"time": "09:12:12", "num": 1111, "money": "12.56"},
                       "buy_single": {"time": "09:12:12", "num": 1111, "money": "12.56"},
                       "buy_exec": {"time": "09:12:12", "num": 1111, "money": "12.56"}
                       },
        "xgb_code_infos": [{"date": "今天", "blocks": [
            {"name": "影视", "limit_up_count": 2, "index": 1, "price": "12.00", "rate": "+10.00%"},
            {"name": "文旅", "limit_up_count": 3, "index": 2, "price": "12.00", "rate": "+10.00%"},
        ]}],
        "initiative_buy_codes": [
            {"name": "测试1", "code": "000123", "score": 125, "limit_up": True, "open_limit_up": True},
            {"name": "测试2", "code": "000123", "score": 125, "limit_up": False, "open_limit_up": True},
            {"name": "测试2", "code": "000123", "score": 125, "limit_up": False, "open_limit_up": True},
            {"name": "测试2", "code": "000123", "score": 125, "limit_up": False, "open_limit_up": False}
            ],
        "passive_buy_codes": [{"name": "测试1", "code": "000123", "score": 125},
                              {"name": "测试2", "code": "000123", "score": 125},
                              {"name": "测试2", "code": "000123", "score": 125},
                              {"name": "测试2", "code": "000123", "score": 125}
                              ],
        "trade_record": {"open_limit_up": "10:00:03", "records": [
            {"type": 1, "index": 1, "time": "11:00:00", "score": 12},
            {"type": 0, "time": "11:26:00", "desc": "H撤撤单"}
        ]},
        "xgb_infos": [{"block": {"name": "测试1", "rate": "+12.00%", "limit_up_count": 10},
                       "codes": [
                           {"limit_up": True, "name": "测试代码", "code": "000654"},
                           {"limit_up": True, "name": "测试代码", "code": "000654"},
                           {"limit_up": True, "name": "测试代码", "code": "000654"},
                           {"limit_up": False, "name": "测试代码", "code": "000654"},
                       ]}]
    }
    result = (render(datas))
    with open("D:/nginx-1.13.6/html/kp/index_test.html", "w", encoding="utf-8") as f:
        f.write(result)
kpl/gui.py
New file
@@ -0,0 +1,309 @@
# 悬浮框
import json
import logging
import os
import sys
import threading
import time
import requests
import wx
import tool
import kpl_api
def show_warning(content, click):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_DEFAULT | wx.ICON_WARNING)
    if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
        if click is not None:
            click()
        toastone.Destroy()
def show_info(content, click):
    toastone = wx.MessageDialog(None, content, "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
    if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
        click()
        toastone.Destroy()
class MyLogHandler(logging.Handler):
    def __init__(self, obj):
        logging.Handler.__init__(self)
        self.setLevel(logging.DEBUG)
        self.Object = obj
    def emit(self, record):
        if record.levelno:
            if len(self.Object.GetValue()) > 1024:
                self.Object.SetValue('')
            tstr = time.strftime('%Y-%m-%d_%H:%M:%S')
            self.Object.AppendText("[%s][%s] %s\n" % (tstr, record.levelname, record.getMessage()))
class KPLDataCaptureFrame(wx.Frame):
    def __create_item(self, name, checked=True, trade_time=True, circulate=True):
        btn = wx.Button(self, label=f"{'开启' if circulate else ''}{name}", size=(120, 28))
        check = wx.CheckBox(self, size=(-1, -1))
        if checked:
            check.SetValue(True)
        else:
            check.SetValue(False)
        label = wx.StaticText(self, label=f"{'是否交易时间采集' if trade_time else ''}")
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        bs1.Add(label)
        bs1.Add(check, 0, wx.LEFT, 10)
        bs2 = wx.BoxSizer(wx.VERTICAL)
        bs2.Add(bs1)
        bs2.Add(btn)
        if not trade_time:
            check.Hide()
        return bs2, btn, check
    # 初始化采集线程
    def __init_capture_threads(self):
        def create_thread(target):
            t1 = threading.Thread(target=target)
            t1.setDaemon(True)
            t1.start()
        def create_circulate_task(target):
            while True:
                try:
                    target()
                except Exception as e:
                    print(str(e))
                time.sleep(3)
        create_thread(lambda: create_circulate_task(self.__exec_limit_up))
        time.sleep(0.5)
        create_thread(lambda: create_circulate_task(self.__exec_open_limit_up))
        time.sleep(0.5)
        create_thread(lambda: create_circulate_task(self.__exec_limit_down))
        time.sleep(0.5)
        create_thread(lambda: create_circulate_task(self.__exec_ever_limit_down))
        time.sleep(0.5)
        create_thread(lambda: create_circulate_task(self.__exec_fengkou))
        time.sleep(0.5)
        create_thread(lambda: create_circulate_task(self.__exec_fengkou_best))
        time.sleep(0.5)
        create_thread(lambda: create_circulate_task(self.__exec_fengxiang))
        time.sleep(0.5)
        create_thread(lambda: create_circulate_task(self.__exec_industry_rank))
    def __init__(self):
        self.__init_data()
        wx.Frame.__init__(self, None, -1, "开盘啦数据采集客户端",
                          style=wx.CAPTION ^ wx.MINIMIZE_BOX ^ wx.CLOSE_BOX ^ wx.STAY_ON_TOP,
                          size=(800, 400))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        boxsier = wx.FlexGridSizer(5, 2, 10, 20)
        self.item_bidding = self.__create_item("竞价采集", trade_time=False, circulate=False)
        boxsier.Add(self.item_bidding[0], 0, wx.TOP, 5)
        self.items = []
        names = ["涨停采集", "炸板采集", "跌停采集", "曾跌停采集", "市场风口采集", "最强风口采集", "风向标采集", "行业涨幅"]
        for name in names:
            self.items.append(self.__create_item(name))
            boxsier.Add(self.items[-1][0], 0, wx.TOP, 5)
        # 绑定事件
        self.item_bidding[1].Bind(wx.EVT_BUTTON, self.__capture_bidding)
        for i in range(0, len(names)):
            self.items[i][1].Bind(wx.EVT_BUTTON, lambda e, x=i: self.__capture_btn_click(x, e))
            self.items[i][2].Bind(wx.EVT_CHECKBOX, lambda e, x=i: self.__capture_btn_check(x, e))
        root_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        left_boxsier = wx.BoxSizer(wx.VERTICAL)
        left_boxsier.Add(boxsier)
        whole_boxsier = wx.BoxSizer(wx.HORIZONTAL)
        self.start_all_btn = wx.Button(self, label="开启全部任务", size=(120, 28))
        self.start_all_btn.SetForegroundColour("#008040")
        self.start_all_btn.Bind(wx.EVT_BUTTON, self.__start_all_task)
        self.stop_all_btn = wx.Button(self, label="关闭全部任务", size=(120, 28))
        self.stop_all_btn.SetForegroundColour("#FF0000")
        self.stop_all_btn.Bind(wx.EVT_BUTTON, self.__stop_all_task)
        whole_boxsier.Add(self.stop_all_btn)
        whole_boxsier.Add(self.start_all_btn, 1, wx.LEFT, 25)
        left_boxsier.Add(whole_boxsier, 1, wx.TOP, 30)
        root_boxsier.Add(left_boxsier, 1, wx.LEFT, 10)
        self.text_log = wx.TextCtrl(parent=self, size=(500, self.Size[1]), style=wx.TE_MULTILINE)
        self.text_log.SetEditable(False)
        self.text_log.SetBackgroundColour("#EEE")
        self.text_log.SetForegroundColour("#333")
        handler = MyLogHandler(self.text_log)
        self.logger = logging.getLogger(__name__)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.DEBUG)
        root_boxsier.Add(self.text_log, 1, wx.LEFT, 10)
        self.SetSizer(root_boxsier)
        self.timer = wx.Timer(self)  # 创建定时器
        # self.Bind(wx.EVT_TIMER, self.clear_msg, self.timer)
        self.logger.info("初始化成功")
        self.__init_capture_threads()
    def __init_data(self):
        self.capture_status = {}
        self.keys = ["limit_up", "open_limit_up", "limit_down", "ever_limit_down", "feng_kou", "best_feng_kou",
                     "feng_xiang", "industry_rank"]
        for key in self.keys:
            self.capture_status[key] = [False, True]
    def __set_btn_text(self, controls):
        text = controls[1].GetLabelText()
        if text.find("开启") == 0:
            text = text.replace("开启", "关闭")
        else:
            text = text.replace("关闭", "开启")
        controls[1].SetLabelText(text)
    def __capture_bidding(self, event):
        results = kpl_api.daBanList(kpl_api.DABAN_TYPE_BIDDING)
        result = json.loads(results)
        self.logger.info(f"获取到竞价数据:{len(result['list'])}")
        self.__upload_data("biddings", result)
        # show_info("采集成功", None)
    def __capture_btn_click(self, key_index, event):
        self.__set_btn_text(self.items[key_index])
        self.capture_status[self.keys[key_index]][0] = not self.capture_status[self.keys[key_index]][0]
    def __capture_btn_check(self, key_index, event):
        self.capture_status[self.keys[key_index]][1] = not self.capture_status[self.keys[key_index]][1]
    def __can_capture(self, key_index):
        if not self.capture_status[self.keys[key_index]][0]:
            return False
        if self.capture_status[self.keys[key_index]][1] and not tool.is_trade_time():
            return False
        return True
    def __exec_limit_up(self):
        if not self.__can_capture(0):
            return
        results = kpl_api.daBanList(kpl_api.DABAN_TYPE_LIMIT_UP)
        result = json.loads(results)
        self.logger.info(f"涨停代码数量:{len(result['list'])}")
        self.__upload_data(self.keys[0], result)
    def __exec_open_limit_up(self):
        if not self.__can_capture(1):
            return
        results = kpl_api.daBanList(kpl_api.DABAN_TYPE_OPEN_LIMIT_UP)
        result = json.loads(results)
        self.logger.info(f"炸板代码数量:{len(result['list'])}")
        self.__upload_data(self.keys[1], result)
    def __exec_limit_down(self):
        if not self.__can_capture(2):
            return
        results = kpl_api.daBanList(kpl_api.DABAN_TYPE_LIMIT_DOWN)
        result = json.loads(results)
        self.logger.info(f"跌停代码数量:{len(result['list'])}")
        self.__upload_data(self.keys[2], result)
    def __exec_ever_limit_down(self):
        if not self.__can_capture(3):
            return
        results = kpl_api.daBanList(kpl_api.DABAN_TYPE_EVER_LIMIT_DOWN)
        result = json.loads(results)
        self.logger.info(f"曾经跌停代码数量:{len(result['list'])}")
        self.__upload_data(self.keys[3], result)
    def __exec_fengkou(self):
        if not self.__can_capture(4):
            return
        results = kpl_api.getFengKouList(tool.get_now_date_str())
        result = json.loads(results)
        self.logger.info(f"市场风口代码数量:{len(result['List'])}")
        self.__upload_data(self.keys[4], result)
    def __exec_fengkou_best(self):
        if not self.__can_capture(5):
            return
        if not os.path.exists("D:/kpl/GetFengKListBest_0_0.log"):
            return
        with open("D:/kpl/GetFengKListBest_0_0.log", mode='r', encoding="utf-16") as f:
            lines = f.readlines()
            if lines:
                data = json.loads(lines[0])
                self.logger.info(f"最强风口代码数量:{data['Count']}")
                self.__upload_data(self.keys[5], data)
    def __exec_fengxiang(self):
        if not self.__can_capture(6):
            return
        self.logger.info(f"开始风向标采集")
        try:
            results = kpl_api.getFengXiangBiao()
            result = json.loads(results)
            self.logger.info(f"风向标代码数量:{len(result['list'])}")
            self.__upload_data(self.keys[6], result)
        except Exception as e:
            self.logger.error(str(e))
    def __exec_industry_rank(self):
        if not self.__can_capture(7):
            return
        results = kpl_api.getIndustryRealRankingInfo()
        result = json.loads(results)
        self.logger.info(f"行业涨幅排行代码数量:{len(result['list'])}")
        self.__upload_data(self.keys[7], result)
    # 开始所有的任务
    def __start_all_task(self, event):
        for i in range(0, len(self.items)):
            # 是否已经开启
            if not self.capture_status[self.keys[i]][0]:
                self.__capture_btn_click(i, None)
        # 开始所有的任务
    def __stop_all_task(self, event):
        for i in range(0, len(self.items)):
            # 是否已经开启
            if self.capture_status[self.keys[i]][0]:
                self.__capture_btn_click(i, None)
    def __upload_data(self, type, datas):
        root_data = {
            "type": type,
            "data": datas
        }
        requests.post("http://192.168.3.252:9004/upload_kpl_data", json.dumps(root_data))
    def OnExit(self, e):
        sys.exit(0)
class mainApp(wx.App):
    def OnInit(self):
        self.SetAppName("开盘啦")
        # 传递代码设置成功的回调
        self.kpl = KPLDataCaptureFrame()
        self.kpl.Show()
        return True
# cd D:\workspace\GP\trade_desk
# D:\workspace\GP\trade_desk\dist\env\pk_env\Scripts\pyinstaller.exe kpl/gui.spec
#
if __name__ == "__main__":
    app = mainApp(redirect=True, filename="logs/output.log")
    app.MainLoop()
kpl/gui.spec
New file
@@ -0,0 +1,50 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
    ['gui.py'],
    pathex=[],
    binaries=[],
    datas=[('../venv/Lib/site-packages/dateutil','dateutil')],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
    pyz,
    a.scripts,
    [],
    exclude_binaries=True,
    name='开盘啦',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None
)
coll = COLLECT(
    exe,
    a.binaries,
    a.zipfiles,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='开盘啦',
)
kpl/kpl_api.py
New file
@@ -0,0 +1,111 @@
import datetime
import logging
import time
import pandas as pd
import requests
import json
import requests
import tool
# 竞价
DABAN_TYPE_BIDDING = 8
# 涨停
DABAN_TYPE_LIMIT_UP = 1
# 炸板
DABAN_TYPE_OPEN_LIMIT_UP = 2
# 跌停
DABAN_TYPE_LIMIT_DOWN = 3
# 曾跌停
DABAN_TYPE_EVER_LIMIT_DOWN = 5
def __base_request(url, data):
    headers = {
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "User-Agent": "Dalvik / 2.1.0(Linux;U;Android 6.0.1;MuMu Build/V417IR)"
    }
    # proxies={'https': '192.168.3.251:9002'}
    # 禁止代理,不然会走本地代理
    response = requests.post(url, data=data, headers=headers, proxies={"http": None, "https": None})
    return response
def daBanList(pidType):
    data = "Order=1&a=DaBanList&st=100&c=HomeDingPan&PhoneOSNew=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23" \
           f"&VerSion=5.8.0.2&Index=0&Is_st=1&PidType={pidType}&apiv=w32&Type=4&FilterMotherboard=0&Filter=0&FilterTIB=0" \
           "&FilterGem=0 "
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php", data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
def getFengKouList(day):
    data = f"c=StockFengKData&a=GetFengKList&Index=0&st=500&Order=17&Day={day}&Time=&DeviceID=a38adabd-99ef-3116" \
           "-8bb9-6d893c846e23 "
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php?apiv=w32&PhoneOSNew=1&VerSion=5.8.0.2",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
def getFengXiangBiao():
    data = f"Order=1&a=ZhiShuStockList_W8&st=30&c=ZhiShuRanking&PhoneOSNew=1&old=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&IsZZ=0&Token=0&Index=0&apiv=w32&Type=6&IsKZZType=0&UserID=0&PlateID=801225&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
def getWeiTuo_W14(code, index):
    data = f"a=GetWeiTuo_W14&st=200&c=StockL2Data&PhoneOSNew=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&Token=0&Index={index}&Tur=30&apiv=w32&Type=3&Vol=500&StockID={code}&VType=1&UserID=0&VOrder=0&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
# 获取行业涨幅
def getIndustryRealRankingInfo():
    data = f"Order=1&a=RealRankingInfo&st=20&apiv=w32&Type=2&c=ZhiShuRanking&PhoneOSNew=1&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&Index=0&ZSType=4&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
# 获取代码的概念
def getStockIDPlate(code):
    data = f"a=GetStockIDPlate_New&apiv=w32&c=StockL2Data&StockID={code}&PhoneOSNew=1&UserID=0&DeviceID=a38adabd-99ef-3116-8bb9-6d893c846e23&VerSion=5.8.0.2&Token=0&"
    response = __base_request("https://apphq.longhuvip.com/w1/api/index.php",
                              data=data)
    if response.status_code != 200:
        raise Exception("请求出错")
    return response.text
def test_l2():
    code = "600981"
    count = 0
    while True:
        result = getWeiTuo_W14(code, count)
        result = json.loads(result)
        new_count = result["Count"]
        list_ = result["List"]
        if new_count != count:
            print(tool.get_now_time_str(), len(list_), result)
        count = new_count
if __name__ == '__main__':
    result = (daBanList(3))
    result = json.loads(result)
    print(len(result["list"]))
    tool.get_now_time_str()
    for d in result["list"]:
        print(d)
    print("行业涨幅", getStockIDPlate("000333"))
kpl/tool.py
New file
@@ -0,0 +1,86 @@
import datetime
import decimal
from threading import Thread
def async_call(fn):
    def wrapper(*args, **kwargs):
        Thread(target=fn, args=args, kwargs=kwargs).start()
    return wrapper
# 获取涨停价
def get_limit_up_price(price):
    price = decimal.Decimal(str(price)) * decimal.Decimal("1.1")
    price = price.quantize(decimal.Decimal("0.00"), decimal.ROUND_HALF_UP)
    return price
# 获取跌停价
def get_limit_down_price(price):
    return decimal.Decimal(str(price)) * decimal.Decimal("0.9").quantize(decimal.Decimal("0.00"), decimal.ROUND_HALF_UP)
def get_time_as_second(time_str):
    ts = time_str.split(":")
    return int(ts[0]) * 3600 + int(ts[1]) * 60 + int(ts[2])
def trade_time_sub(time_str_1, time_str_2):
    split_time = get_time_as_second("11:30:00")
    time_1 = get_time_as_second(time_str_1)
    time_2 = get_time_as_second(time_str_2)
    if time_1 > split_time >= time_2:
        time_2 += 90 * 60
    else:
        if time_1 < split_time <= time_2:
            time_2 -= 90 * 60
    return time_1 - time_2
# 将秒数格式化为时间
def time_seconds_format(seconds):
    h = seconds // 3600
    m = seconds % 3600 // 60
    s = seconds % 60
    return "{0:0>2}:{1:0>2}:{2:0>2}".format(h, m, s)
# 交易时间加几s
def trade_time_add_second(time_str, second):
    ts = time_str.split(":")
    s_ = int(ts[0]) * 3600 + int(ts[1]) * 60 + int(ts[2])
    s = s_ + second
    # 结果在11:30到1点之间
    if 13 * 3600 > s >= 11 * 3600 + 30 * 60:
        if second > 0:
            s += 90 * 60
        else:
            s -= 90 * 60
    return time_seconds_format(s)
def is_trade_time(time_str=None):
    if time_str is None:
        time_str = get_now_time_str()
    if (int("092500") <= int(time_str.replace(":", "")) <= int("113000")) or (
            int("130000") <= int(time_str.replace(":", "")) <= int("150000")):
        return True
    return False
def get_now_time_str():
    return datetime.datetime.now().strftime("%H:%M:%S")
    # return "10:20:00"
def get_now_date_str():
    return datetime.datetime.now().strftime("%Y-%m-%d")
if __name__ == "__main__":
    print(trade_time_sub("13:00:00", "11:30:00"))
    print(trade_time_sub("13:00:00", "11:29:00"))
    print(trade_time_sub("11:29:00", "13:00:00"))
log.py
New file
@@ -0,0 +1,50 @@
import logging
import os
def get_logger(log_filename, level=logging.INFO, when='D', back_count=0):
    """
    :brief  日志记录
    :param log_filename: 日志名称
    :param level: 日志等级
    :param when: 间隔时间:
        S:秒
        M:分
        H:小时
        D:天
        W:每星期(interval==0时代表星期一)
        midnight: 每天凌晨
    :param back_count: 备份文件的个数,若超过该值,就会自动删除
    :return: logger
    """
    # 创建一个日志器。提供了应用程序接口
    logger = logging.getLogger(log_filename)
    # 设置日志输出的最低等级,低于当前等级则会被忽略
    logger.setLevel(level)
    # 创建格式器
    formatter = logging.Formatter('%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s')
    # 创建处理器:ch为控制台处理器,fh为文件处理器
    ch = logging.StreamHandler()
    ch.setLevel(level)
    # 输出到文件
    fh = logging.FileHandler(
        filename=log_filename,
        encoding='utf-8')
    fh.setLevel(level)
    # 设置日志输出格式
    fh.setFormatter(formatter)
    ch.setFormatter(formatter)
    # 将处理器,添加至日志器中
    logger.addHandler(fh)
    logger.addHandler(ch)
    return logger
debug_logger = get_logger("logs/debug.log",logging.DEBUG)
debug_logger.setLevel(logging.DEBUG)
if __name__ == '__main__':
    debug_logger.info("test234")
main.py
@@ -5,8 +5,11 @@
import time
from multiprocessing import freeze_support
import cv2
import torch
import win32con
import win32gui
from matplotlib.lines import Line2D
import ocr_util
import opencv_util
@@ -23,17 +26,20 @@
from matplotlib.widgets import Button
import wx
import wx.html
import wx.html2 as webview
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import code_data_manager
import juejin_core
import tool
import dateutil
import requests
APP_TITLE = "卖票看板"
APP_ICON = ""
SERVER_HOST = "192.168.3.252"
SOCKET_PORT = 9001
HTTP_PORT = 9004
def show_warning(content, click):
@@ -184,7 +190,7 @@
    def __request(self, codes):
        client = socket.socket()  # 生成socket,连接server
        ip_port = ("192.168.3.252", 9001)  # server地址和端口号(最好是10000以后)
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 201, "data": {"codes": codes}}
        client.send(json.dumps(data).encode("utf-8"))
@@ -292,6 +298,10 @@
            toastone.Destroy()
last_ocr_code = ["000000"]
import log
def ocr_ths_code():
    hwnd = ths_util.get_ths_main_content_hwnd()
    if not hwnd:
@@ -301,21 +311,34 @@
    # hwnd_width = (rect[2] - rect[0]) * 15 // 10
    rect_ = setting.get_ths_auto_code_rect()
    img = win32_util.window_capture(hwnd, (0, rect_[0], rect_[1], rect_[0] + rect_[2]))
    code = ocr_util.recognize_code(opencv_util.clip_ths_code_area(img))
    clip_img, details = opencv_util.clip_ths_code_area(img)
    start_time = time.time()
    code = ocr_util.recognize_code(clip_img)
    use_time =round( (time.time()-start_time)*1000)
    if code is None:
        code = ocr_util.recognize_code(img)
    if code != last_ocr_code[0]:
        print("保存图片", code)
        log.debug_logger.info(f"代码识别结果:{code} 识别时间:{use_time}ms")
        last_ocr_code[0] = code
        cv2.imwrite(f"datas/test/{code}.png", opencv_util.gray_img(img))
    return code
# 悬浮框
class FloatFrame(wx.Frame):
    def __init__(self, position):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "悬浮盯盘", style=wx.CAPTION ^ wx.MINIMIZE_BOX ^ wx.CLOSE_BOX ^ wx.STAY_ON_TOP,
                          size=(320, 195))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        self.SetTransparent(230)
        if position:
            self.SetPosition(wx.Point(position[0], position[1]))
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        # 读取配置信息
        window_info = setting.get_float_watch_window_info()
        if window_info:
            self.SetPosition(wx.Point(window_info[0], window_info[1]))
            self.Size = wx.Size(window_info[2], window_info[3])
        boxsier = wx.FlexGridSizer(5, 2, 2, 5)
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_remove_black = wx.Button(self, label="移除黑名单", size=(70, 30))
@@ -364,7 +387,7 @@
        boxsier.Add(self.edit_code)
        self.notify_text = wx.StaticText(self, label="", size=(80, -1))
        boxsier.Add(self.notify_text)
        boxsier.Add(self.notify_text, 0, wx.LEFT, -45)
        # 代码
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
@@ -466,8 +489,12 @@
        try:
            code = self.__get_code()
            print("加入白名单", code)
            self.__request([code], 202)
            self.show_info(f"{code}加入白名单成功")
            result = self.__request([code], 202)
            result = json.loads(result)
            if result["code"] == 0:
                self.show_info(f"{code}加入白名单成功")
            else:
                self.show_warning(f"加入失败:{result['msg']}")
            self.edit_code.SetValue("")
        except Exception as e:
            self.show_warning(str(e))
@@ -563,6 +590,22 @@
            self.check_auto_refresh.SetValue(True)
        else:
            self.check_auto_refresh.SetValue(False)
        self.__init_trade_state()
    def __init_trade_state(self):
        # 获取交易状态
        try:
            result = self.__request_buy_state()
            result = json.loads(result)
            if result["code"] == 0:
                if result["data"]["can_buy"]:
                    self.btn_open_buy.SetLabelText("开启交易*")
                    self.btn_close_buy.SetLabelText("关闭交易")
                else:
                    self.btn_open_buy.SetLabelText("开启交易")
                    self.btn_close_buy.SetLabelText("关闭交易*")
        except:
            pass
    def __auto_click_check(self, event):
        if event.Selection:
@@ -578,15 +621,17 @@
    def __request(self, codes, type):
        client = socket.socket()  # 生成socket,连接server
        ip_port = ("192.168.3.252", 9001)  # server地址和端口号(最好是10000以后)
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {"codes": codes}}
        client.send(json.dumps(data).encode("utf-8"))
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
    def __request_list(self, type):
        client = socket.socket()  # 生成socket,连接server
        ip_port = ("192.168.3.252", 9001)  # server地址和端口号(最好是10000以后)
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {}}
        client.send(json.dumps(data).encode("utf-8"))
@@ -597,12 +642,24 @@
    def __request_buy(self, is_open):
        client = socket.socket()  # 生成socket,连接server
        ip_port = ("192.168.3.252", 9001)  # server地址和端口号(最好是10000以后)
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 501, "data": {"open": is_open}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(10240)
        client.close()
        return result.decode("gbk")
    # 获取买入状态
    def __request_buy_state(self):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": 502, "data": {}}
        client.send(json.dumps(data).encode("utf-8"))
        # 读取内容
        result = client.recv(1024)
        client.close()
        return result.decode("gbk")
@@ -613,6 +670,7 @@
                    result = self.__request_buy(True)
                    msg = json.loads(result)["msg"]
                    show_info(msg, None)
                    self.__init_trade_state()
                except Exception as e:
                    show_warning(str(e), None)
@@ -625,15 +683,23 @@
                    result = self.__request_buy(False)
                    msg = json.loads(result)["msg"]
                    show_info(msg, None)
                    self.__init_trade_state()
                except Exception as e:
                    show_warning(str(e), None)
        show_sure("是否关闭交易", close_buy)
    def OnExit(self, e):
        try:
            setting.set_float_watch_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        self.Hide()
# 代码数据
class CodeDataFrame(wx.Frame):
    def __init__(self, set_codes_success_callback):
    def __init__(self, set_codes_success_callback, show_float_callback, show_main_callback):
        # style=wx.CAPTION |
        wx.Frame.__init__(self, None, -1, "数据",
                          style=wx.CAPTION | wx.STAY_ON_TOP | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.CLOSE_BOX | wx.RESIZE_BORDER,
@@ -642,12 +708,36 @@
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        # self.SetTransparent(230)
        boxsier = wx.BoxSizer(wx.VERTICAL)
        self.data_text = wx.html.HtmlWindow(self, size=wx.Size(self.Size[0], self.Size[
            1]))
        self.data_text.SetBackgroundColour(wx.Colour(224, 224, 224))
        # ---配置浏览器----
        webview.WebView.MSWSetEmulationLevel()
        backend = None
        backends = [
            (webview.WebViewBackendEdge, 'WebViewBackendEdge'),
            (webview.WebViewBackendIE, 'WebViewBackendIE'),
            (webview.WebViewBackendWebKit, 'WebViewBackendWebKit'),
            (webview.WebViewBackendDefault, 'WebViewBackendDefault'),
        ]
        for id, name in backends:
            available = webview.WebView.IsBackendAvailable(id)
            if available and backend is None:
                backend = id
        self.wv = webview.WebView.New(self, size=wx.Size(self.Size[0], self.Size[
            1]), backend=backend)
        self.wv.SetZoomType(wx.html2.WEBVIEW_ZOOM_TYPE_TEXT)
        self.Bind(webview.EVT_WEBVIEW_NAVIGATING, self.OnWebViewNavigating, self.wv)
        self.Bind(webview.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.wv)
        # 执行定时更新
        self.timer = wx.Timer(self)  # 创建定时器
        self.Bind(wx.EVT_TIMER, self.__update_kpl, self.timer)
        self.timer.Start(3000)
        self.count = 0
        self.wv.SetBackgroundColour(wx.Colour(224, 224, 224))
        if "gtk2" in wx.PlatformInfo:
            self.data_text.SetStandardFonts()
        boxsier.Add(self.data_text)
            self.wv.SetStandardFonts()
        boxsier.Add(self.wv, 1, wx.EXPAND)
        self.latest_code = ''
        self.SetSizer(boxsier)
        window_info = setting.get_xgb_window_info()
@@ -656,11 +746,66 @@
            self.Size = wx.Size(window_info[2], window_info[3])
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        self.Bind(wx.EVT_SIZE, self.OnResize)
        self.data_text.SetPage("")
        self.wv.SetPage("", "")
        self.__menu()
        self.show_float_callback = show_float_callback
        self.show_main_callback = show_main_callback
    def OnWebViewNavigating(self, evt):
        # this event happens prior to trying to get a resource
        if evt.GetURL().find("cancel_order://") >= 0:
            code__ = evt.GetURL().split("cancel_order://")[1]
            # if wx.MessageBox("是否要撤单?",
            #                  style=wx.YES_NO | wx.ICON_QUESTION) == wx.YES:
            self.__cancel_order(code__)
            evt.Veto()
    def OnWebViewLoaded(self, evt):
        # pass
        # 加载开盘啦数据
        self.__update_kpl(None)
    # 更新开盘啦数据
    def __update_kpl(self, evt):
        response = requests.get(f"http://{SERVER_HOST}:{HTTP_PORT}/get_kpl_data")
        if response.status_code == 200:
            print(response.text)
            with open("res/kpl.js", mode="r", encoding="utf-8") as f:
                lines = f.readlines()
                script = "\n".join(lines)
                script = script.replace("{}", response.text)
                self.wv.RunScript(script)
    def __cancel_order(self, code):
        client = socket.socket()  # 生成socket,连接server
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        try:
            client.connect(ip_port)
            data = {"type": 80, "data": {"code": code}}
            client.send(json.dumps(data).encode("utf-8"))
            result = client.recv(1024)
            result = result.decode("utf-8")
            result = json.loads(result)
            if result["code"] == 0:
                # show_info("撤单处理成功", None)
                # 刷新数据
                self.__init_data(self.latest_code)
            else:
                show_warning(result["msg"], None)
            client.close()
        except Exception as e:
            show_warning(str(e), None)
    def OnWebViewError(self, evt):
        # The full document has loaded
        print("网页出错")
    def handleMessage(self, event):
        print("数据回调")
    def set_data(self, code, data):
        self.data_text.SetPage(data)
        self.wv.SetPage(data, "")
    # 设置代码
    def set_code(self, code):
@@ -684,7 +829,7 @@
    def __request(self, code, type=71):
        client = socket.socket()  # 生成socket,连接server
        ip_port = ("192.168.3.252", 9001)  # server地址和端口号(最好是10000以后)
        ip_port = (SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        data = {"type": type, "data": {"code": code}}
        client.send(json.dumps(data).encode("utf-8"))
@@ -703,7 +848,7 @@
    def OnResize(self, e):
        print("变化后的尺寸", e.Size)
        self.data_text.Size = e.Size
        self.wv.Size = e.Size
    def __menu(self):
        self.mainmenu = wx.MenuBar()
@@ -715,19 +860,36 @@
        setting_.AppendSeparator()
        setting_.Append(103, '&代码管理', 'Open a new document')
        setting_.Append(102, '&下载涨停代码', 'Open a new document')
        setting_.AppendSeparator()
        setting_.Append(104, '&GPU检测', 'Open a new document')
        self.mainmenu.Append(setting_, '&设置')
        auto_help = wx.Menu()
        auto_help.Append(202, '&同花顺设置', '')
        self.mainmenu.Append(auto_help, '&自动化')
        view = wx.Menu()
        view.Append(301, '&打开悬浮盯盘', '')
        view.Append(302, '&打开分时看盘', '')
        self.mainmenu.Append(view, '&视图')
        # 设置事件
        self.Bind(wx.EVT_MENU, self.__set_juejin_params, id=101)
        self.Bind(wx.EVT_MENU, self.__download_codes, id=102)
        self.Bind(wx.EVT_MENU, self.__manage_code, id=103)
        self.Bind(wx.EVT_MENU, self.__gpu_is_avaiable, id=104)
        self.Bind(wx.EVT_MENU, self.__manage_ths_pos, id=202)
        self.Bind(wx.EVT_MENU, self.__show_float_callback, id=301)
        self.Bind(wx.EVT_MENU, self.__show_main_callback, id=302)
        self.SetMenuBar(self.mainmenu)
    def __gpu_is_avaiable(self,event):
        if torch.cuda.is_available():
            show_info("GPU可用", None)
        else:
            show_warning("GPU不可用", None)
    def __set_juejin_params(self, event):
        ps = self.GetPosition()
@@ -764,8 +926,14 @@
        size = self.GetSize()
        THSPositionSettingFrame((ps[0] + size[0] / 2, ps[1] + size[1] / 2)).Show()
    def __show_float_callback(self, event):
        self.show_float_callback()
class MainFrame(wx.Frame):
    def __show_main_callback(self, event):
        self.show_main_callback()
class TickFrame(wx.Frame):
    def __init__(self):
        '''构造函数'''
        wx.Frame.__init__(self, None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE,
@@ -773,7 +941,12 @@
        # ^ wx.RESIZE_BORDER ^ wx.STAY_ON_TOP
        # 默认style是下列项的组合:wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN
        self.SetBackgroundColour(wx.Colour(0, 0, 0))
        self.Center()
        win_info = setting.get_tick_window_info()
        if win_info:
            self.SetPosition(wx.Point(win_info[0], win_info[1]))
            self.Size = wx.Size(win_info[2], win_info[3])
        else:
            self.Center()
        # 以下代码处理图标
        # if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
@@ -789,7 +962,7 @@
        self.scroll = None
        self.mark_lines = {}
        self.col = 1
        self.ratio = 0.76
        self.ratio = 0.63
        self.__re_draw()
        self.timer = wx.Timer(self)  # 创建定时器
@@ -802,15 +975,6 @@
        # boxsier.Add(mainBoxsier, 1, wx.EXPAND | wx.ALL, 5)
        # self.SetSizer(boxsier)
        # mainBoxsier.Fit(self)
        window_info = setting.get_window_info()
        float_pos = None
        if window_info:
            float_pos = window_info[1]
            self.SetPosition(wx.Point(window_info[0][0], window_info[0][1]))
            self.Size = wx.Size(window_info[0][2], window_info[0][3])
        self.floatFrame = FloatFrame(float_pos)
        self.floatFrame.Show()
        if setting.is_stay_on_top():
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.STAY_ON_TOP
@@ -894,8 +1058,9 @@
            print("关闭", title)
            close_callback(title)
        width_dpi = self.Size[0] / (100 * self.col)
        figure_score = Figure(figsize=(width_dpi, round(width_dpi * (self.ratio), 2)))
        dpi = 100
        width_dpi = self.Size[0] / (dpi * self.col)
        figure_score = Figure(figsize=(width_dpi, round(width_dpi * (self.ratio), 2)), dpi=dpi)
        # 设置外边距
        right_padding_px = 85
        right = round((self.Size[0] - right_padding_px) / self.Size[0], 4)
@@ -950,11 +1115,11 @@
        max_rate = round((limit_up_price - price) / price, 4) * 100
        print("涨停最大比例", max_rate)
        yticks2 = []
        for i in range(0, 21):
            if i >= 10:
                yticks2.append(0 - round(max_rate * (10 - i) / 10, 4))
        for i in range(0, 41):
            if i >= 20:
                yticks2.append(0 - round(max_rate * (20 - i) / 20, 4))
            else:
                yticks2.append(round(max_rate * (i - 10) / 10, 4))
                yticks2.append(round(max_rate * (i - 20) / 20, 4))
        yticks2_labels = []
        yticks = []
        yticks_labels = []
@@ -968,7 +1133,7 @@
            if i % 2 == 0:
                yticks_labels.append("")
                # yticks_labels.append(round(yticks[i], 2))
                if i == 10:
                if i == 20:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=2)
                else:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=1.2)
@@ -985,17 +1150,19 @@
        axes.set_yticks(yticks)
        axes.set_yticklabels(yticks_labels)
        # 设置纵坐标数值颜色
        for i in range(0, 9):
        for i in range(0, 19):
            axes.get_yticklabels()[i].set_color("green")
            axes2.get_yticklabels()[i].set_color("green")
        for i in range(10, 10):
        for i in range(20, 20):
            axes.get_yticklabels()[i].set_color("white")
            axes2.get_yticklabels()[i].set_color("white")
        for i in range(11, 21):
        for i in range(21, 41):
            axes.get_yticklabels()[i].set_color("red")
            axes2.get_yticklabels()[i].set_color("red")
        line = axes2.plot([], [], color='white', linewidth=1)
        average_line = axes2.plot([], [], color='yellow', linewidth=1)
        average_line = axes2.plot([], [], color='green', linewidth=1, linestyle='-')
        average_line_1m = axes2.plot([], [], color='yellow', linewidth=1, linestyle='-')
        # axes2.legend(loc='upper left')
        cannvas = FigureCanvas(pannel, -1, figure_score)
        axes2.text(1, 11.5, r'现价:0.0 涨幅:0.00% \n留格局:0%', fontsize=10, color='white')
        axes2.text(-1, -11.5, r'现价:0.0 涨幅:0.00% \n留格局:0%', fontsize=10, color='white')
@@ -1004,7 +1171,7 @@
        axes.spines['top'].set_visible(False)
        axes2.spines['bottom'].set_visible(False)
        axes.spines['bottom'].set_visible(False)
        return (axes2, line, average_line)
        return (axes2, line, average_line_1m, average_line)
    def __show_top(self, event):
        if event.Selection:
@@ -1016,14 +1183,10 @@
    def OnExit(self, e):
        try:
            self.floatFrame.Close(True)
            setting.set_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]), (
                self.floatFrame.Position[0], self.floatFrame.Position[1], self.floatFrame.Size[0],
                self.floatFrame.Size[1]))
            setting.set_tick_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        jueJinProcess.terminate()
        sys.exit(0)
        self.Hide()
    def post_redraw(self, evt):
@@ -1053,6 +1216,8 @@
# 绘图管理器
class DrawManager:
    X_RANGE_MINIUTES = 60
    X_DATA_MINIUTES = 56
    def __load_lack_datas(self, code, time_ranges):
        codeDataManager = code_data_manager.CodeDataManager()
@@ -1100,7 +1265,15 @@
                code_datas[code] = []
                old_datas = codeDataManager.get_datas(code)
                # 获取缺失的数据
                ranges = codeDataManager.get_lack_datas_time_range(old_datas)
                min_time = tool.get_now_time_str()
                if int(min_time.replace(":", "")) > int("150000"):
                    min_time = "15:00:00"
                elif int("113000") < int(min_time.replace(":", "")) < int("130000"):
                    min_time = "11:30:00"
                min_time = tool.trade_time_add_second(min_time, 0 - DrawManager.X_DATA_MINIUTES * 60)
                ranges = codeDataManager.get_lack_datas_time_range(old_datas, min_time)
                if len(ranges) > 0:
                    self.__load_lack_datas(code, ranges)
                    old_datas = codeDataManager.get_datas(code)
@@ -1136,29 +1309,31 @@
        # 删除9:30以前的数据
        for i in range(0, len(datas)):
            time_ = datas[i]["created_at"][-8:]
            if int(time_.replace(":", "")) >= int("093000"):
            if int(time_.replace(":", "")) >= int("092600"):
                datas = datas[i:]
                break
        # 取最近12分钟的数据
        # 取最近14分钟的数据
        for i in range(len(datas) - 1, -1, -1):
            time_ = datas[i]["created_at"][-8:]
            if tool.trade_time_sub(datas[-1]["created_at"][-8:], time_) >= 14 * 60:
            if tool.trade_time_sub(datas[-1]["created_at"][-8:], time_) >= self.X_DATA_MINIUTES * 60:
                datas = datas[i:]
                break
        xs = []
        ys_rate = []
        ys_average_rate_1m = []
        ys_average_rate = []
        for data in datas:
            xs.append(get_time_as_seconds(data["created_at"]))
            ys_rate.append(data["rate"] * 100)
            ys_average_rate_1m.append(data["average_rate"] * 100 + 1.5)
            ys_average_rate.append(data["average_rate"] * 100)
        xticks = []
        xticklabels = []
        # 设置X轴范围为09:30:00 到15:00:00
        # x轴范围为0-15分钟
        end_x = "0000-00-00 " + tool.trade_time_add_second(datas[0]["created_at"][-8:], 15 * 60)
        end_x = "0000-00-00 " + tool.trade_time_add_second(datas[0]["created_at"][-8:], self.X_RANGE_MINIUTES * 60)
        axes[0].set_xlim(get_time_as_seconds(datas[0]["created_at"]), get_time_as_seconds(end_x))
        # if len(xs) < 2 or xs[-1] - xs[0] < 30 * 60:
@@ -1176,7 +1351,9 @@
        axes[0].set_xticklabels(xticklabels)
        axes[1][0].set_data(xs, ys_rate)
        axes[2][0].set_data(xs, ys_average_rate)
        axes[2][0].set_data(xs, ys_average_rate_1m)
        if axes[3]:
            axes[3][0].set_data(xs, ys_average_rate)
        texts = axes[0].texts
        texts[0].set_text("{}% \n留格局:0%".format(round(datas[-1]["rate"] * 100, 2)))
        texts[1].set_text("{}".format(datas[-1]["created_at"].split(" ")[1]))
@@ -1228,7 +1405,7 @@
                    min_rate = rate
        # 展示最近的600个
        datas = datas[-450:]
        datas = datas[0 - self.X_DATA_MINIUTES * 20:]
        # 修正量数据
        last_info = None
        for i in range(1, len(datas)):
@@ -1237,7 +1414,8 @@
                last_info = datas[i]
            if int(datas[i]["created_at"][-2:]) - int(datas[i - 1]["created_at"][-2:]) < 0:
                last_info = datas[i]
            datas[i]['average_rate'] = last_info['average_rate']
            datas[i]['average_rate_1m'] = last_info['average_rate']
        datas[0]['average_rate_1m'] = datas[0]['average_rate']
        for i in range(0, len(self.codes_info)):
            if self.codes_info[i][0] == code:
@@ -1266,19 +1444,25 @@
                    last_time = round(time.time())
                for index in range(0, len(codes)):
                    if codes[index] == code:
                        self.Frame.scrollTo(index)
                        self.frame.scrollTo(index)
                        break
            except Exception as e:
                # print(str(e))
                pass
            time.sleep(0.005)
    def __show_float_frame(self):
        self.floatFrame.Show()
    def __show_main_frame(self):
        self.frame.Show()
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.frame = MainFrame()
        self.frame.Show()
        self.frame = TickFrame()
        self.floatFrame = FloatFrame()
        # 传递代码设置成功的回调
        self.xgb = CodeDataFrame(self.frame.set_codes_success)
        self.xgb = CodeDataFrame(self.frame.set_codes_success, self.__show_float_frame, self.__show_main_frame)
        self.xgb.Show()
        t1 = threading.Thread(target=lambda: self.__refresh())
        # 后台运行
@@ -1364,8 +1548,7 @@
if __name__ == "__main__1":
    code = ocr_util.recognize_code("C:\\Users\\Administrator\\Desktop\\test.png.bmp")
    print(code)
    print(webview.WebView.IsBackendAvailable(webview.WebViewBackendEdge))
# 打包命令
# cd D:\workspace\GP\trade_desk
main.spec
@@ -47,5 +47,5 @@
    strip=False,
    upx=True,
    upx_exclude=[],
    name='分时',
    name='分时1',
)
ocr_util.py
@@ -16,19 +16,21 @@
# 数字识别
def recognize_code(img):
    results = num_reader.readtext(img, detail=0, mag_ratio=1.5)
    print("识别结果", results)
    # print("识别结果", results)
    if results:
        result = results[0].strip()
        code = result[-6:]
        if code.isnumeric():
            return code
        for result in results:
            result = result.strip()
            if len(result) >= 6:
                code = result[-6:]
                if code.isnumeric():
                    return code
    return None
# 数字识别
def recognize_num(img):
    results = reader.readtext(img, detail=0, mag_ratio=2)
    print("识别结果", results)
    results = num_reader.readtext(img, detail=0, mag_ratio=1.5)
    # print("识别结果", results)
    if results:
        result = results[0].strip()
        if result.isnumeric():
@@ -51,6 +53,9 @@
if __name__ == '__main__':
    start_time = time.time()
    img = "C:\\Users\\Administrator\\Desktop\\ocr\\codes\\000021_23850.png"
    results = num_reader.readtext(img, detail=1, mag_ratio=2)
    print(results)
    # results = kpl_util.recognize_datas("C:\\Users\\Administrator\\Desktop\\test.png")
    # print(results)
opencv_util.py
@@ -1,69 +1,245 @@
# 二值化图像
import os
import random
import cv2
import matplotlib.pyplot as plt
import numpy
SHOW_PLT = False
def gray_img(img):
    if img.ndim == 2:
        return img
    result = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return result
def is_col_empty(img, col, thresh_hold=0):
    for r in img:
        if r[col] > thresh_hold:
def is_col_empty(img, col, start_row, end_row, thresh_hold=0):
    for r in range(start_row, end_row + 1):
        if img[r][col] > thresh_hold:
            return False
    return True
def is_col_full(img, col, thresh_hold_start=64, thresh_hold_end=255):
def is_col_full(img, col, start_row, end_row, thresh_hold_start=64, thresh_hold_end=255, thresh_empty_count=1):
    rows, cols = img.shape
    for r in img:
        if r[col] < thresh_hold_start or r[col] > thresh_hold_end:
    empty_count = 0
    for r in range(start_row, end_row + 1):
        if img[r][col] < thresh_hold_start or img[r][col] > thresh_hold_end:
            empty_count += 1
            if empty_count > thresh_empty_count:
                return False
    return True
# 行是否是满的
def is_row_full(img, row, start_col, end_col, thresh_hold_start=64, thresh_hold_end=255):
    rows, cols = img.shape
    for c in range(start_col, end_col + 1):
        if img[row][c] < thresh_hold_start or img[row][c] > thresh_hold_end:
            return False
    return True
def format_num_img(img):
    # 先调整为方形
    rows, cols = img.shape
    img_new = numpy.zeros((max(rows, cols), max(rows, cols)), numpy.uint8)
    img_new.fill(0)
    start_col = (max(rows, cols) - cols) // 2
    if rows > cols:
        for r in range(rows):
            for c in range(cols):
                img_new[r][c + start_col] = img[r][c]
    else:
        pass
    # 设置为28X28像素
    return cv2.resize(img_new, (28, 28), interpolation=cv2.INTER_AREA)
# 分隔数字
def split_nums_img(img):
    # 获取代码的最大色值
    rows, cols = img.shape
    max_color = 0
    for r in range(0, rows):
        for c in range(0, cols):
            if img[r][c] > max_color:
                max_color = img[r][c]
    start_index = -1
    end_index = -1
    codes_pos = []
    for col in range(0, cols):
        if not is_col_empty(img, col, 0, rows - 1, max_color * 2 // 3):
            if start_index < 0:
                start_index = col
            end_index = col
        else:
            if end_index >= 0 and start_index >= 0:
                codes_pos.append((start_index, end_index + 1))
                end_index = -1
                start_index = -1
    img_detail = []
    for i in range(len(codes_pos)):
        temp_img = img[0:rows - 1, codes_pos[i][0]:codes_pos[i][1]]
        temp_img = format_num_img(temp_img)
        img_detail.append(temp_img)
    return img_detail
# 分离同花顺的代码
def clip_ths_code_area(img):
    img = gray_img(img)
    rows, cols = img.shape
    end_col = cols - 1
    for col in range(0, cols, 1):
        if is_col_full(img, col, 38, 38):
    # 行分隔
    full_row = -1
    start_row = -1
    end_row = -1
    for r in range(0, rows, 1):
        if is_row_full(img, r, 0, cols // 2, 38, 38):
            # print("找到分隔:", col)
            end_col = col - 1
            full_row = r
            if start_row >= 0 and r - start_row > 10:
                end_row = r - 1
                break
        else:
            if full_row > -1:
                full_row = -1
                if start_row < 0:
                    start_row = r
    if end_row < 0:
        end_row = rows - 1
    if start_row < 0:
        raise Exception("没找到上分割线")
    end_col = cols - 1
    for c in range(cols - 1, -1, -1):
        if is_col_full(img, c, start_row, end_row, 38, 38, 2):
            # print("找到分隔:", col)
            end_col = c - 1
            break
    # 往前找数字分隔
    content_start = -1
    empty_start = -1
    empty_end = -1
    start_index = 0
    end_index = end_col
    start_index = -1
    end_index = -1
    codes_pos = []
    # 获取代码的最大色值
    max_color = 0
    for r in range(start_row, end_row + 1):
        for c in range(end_col, end_col // 2, -1):
            if img[r][c] > max_color:
                max_color = img[r][c]
    for col in range(end_col, 0, -1):
        if not is_col_empty(img, col):
            if content_start < 0:
                content_start = col
            empty_start = -1
            empty_end = -1
        if not is_col_empty(img, col, start_row, end_row, max_color * 2 // 3):
            if start_index < 0:
                start_index = col
            end_index = col
        else:
            if empty_start < 0:
                empty_start = col
                empty_end = col
            else:
                empty_end = col
            if empty_start - empty_end > 2 and content_start > 0:
                start_index = col + (empty_start - empty_end + 1)
                end_index = content_start
                # print("代码范围:", start_index, end_index)
                break
    clip_img = img[0:rows, start_index:end_index]
            if end_index >= 0 and start_index >= 0:
                codes_pos.append((end_index - 1, start_index + 1))
                end_index = -1
                start_index = -1
    codes_pos = codes_pos[:6]
    codes_pos.sort(key=lambda x: x[0])
    if SHOW_PLT:
        plt.figure(figsize=(10, 4))
    img_detail = []
    for i in range(len(codes_pos)):
        if SHOW_PLT:
            plt.subplot(2, 5, i + 1)
            plt.title(f'pred {i}')
            plt.axis('off')
        temp_img = img[start_row:end_row, codes_pos[i][0]:codes_pos[i][1]]
        temp_img = format_num_img(temp_img)
        img_detail.append(temp_img)
        if SHOW_PLT:
            plt.imshow(temp_img, cmap='gray')
    if SHOW_PLT:
        plt.show()
    clip_img = img[start_row:end_row, codes_pos[0][0]:codes_pos[-1][1]]
    # cv2.imwrite("test1.png", clip_img)
    return clip_img
    return clip_img, img_detail
    # print(clip_img.shape)
    # ret1, p1 = cv2.threshold(src=clip_img, thresh=100, maxval=255, type=cv2.THRESH_BINARY)
    # cv2.imwrite("D:/workspace/GP/trade_desk/test3.png", p1)
if __name__ == "__main__":
    img = gray_img(cv2.imread("C:\\Users\\Administrator\\Desktop\\test.png"))
    cv2.imwrite("C:\\Users\\Administrator\\Desktop\\test_gray.png", img)
def __test4():
    files = os.listdir("datas/test4/")
    for file in files:
        code = file[:-4]
        img = cv2.imread(f"datas/test4/{file}", cv2.IMREAD_GRAYSCALE)
        img_details = split_nums_img(img)
        for d in range(0, len(img_details)):
            cv2.imwrite(f"C:/Users/Administrator/Desktop/ocr/codes/{code}_{random.randint(0, 100000)}.png",
                        img_details[d])
        plt.figure(figsize=(10, 4))
        for i in range(0, len(img_details)):
            plt.subplot(2, 5, i + 1)
            plt.title(f'pred {i}')
            plt.axis('off')
            plt.imshow(img_details[i], cmap='gray')
        plt.show()
def __test3():
    files = os.listdir("datas/test3/")
    for file in files:
        code = file[:-4]
        img = cv2.imread(f"datas/test3/{file}", cv2.IMREAD_GRAYSCALE)
        rows, cols = img.shape
        for r in range(rows):
            for c in range(cols):
                img[r][c] = 255 - img[r][c]
        img_details = split_nums_img(img)
        for d in range(0, len(img_details)):
            cv2.imwrite(f"C:/Users/Administrator/Desktop/ocr/codes/{code}_{random.randint(0, 100000)}.png",
                        img_details[d])
        plt.figure(figsize=(10, 4))
        for i in range(0, len(img_details)):
            plt.subplot(2, 5, i + 1)
            plt.title(f'pred {i}')
            plt.axis('off')
            plt.imshow(img_details[i], cmap='gray')
        plt.show()
if __name__ == '__main__':
    __test4()
if __name__ == "__main__1":
    #
    files = os.listdir("datas/test/")
    for file in files:
        code = file[:6]
        img = cv2.imread(f"datas/test/{file}", cv2.IMREAD_GRAYSCALE)
        img, img_details = clip_ths_code_area(img)
        for d in range(0, len(img_details)):
            cv2.imwrite(f"C:/Users/Administrator/Desktop/ocr/codes/{code}_{random.randint(0, 100000)}.png",
                        img_details[d])
    if SHOW_PLT:
        plt.figure(figsize=(1, 1))
        plt.subplot(1, 1, 1)
        plt.title(f"test")
        plt.axis('off')
        plt.imshow(img)
        plt.show()
    pass
    # img = gray_img(cv2.imread("C:\\Users\\Administrator\\Desktop\\ocr\\code_test.png"))
    # h = img.shape[0]
    # w = img.shape[1]
    # img = cv2.resize(img, (int(w * 2), int(h * 2)), interpolation=cv2.INTER_AREA)
    # cv2.imwrite("C:\\Users\\Administrator\\Desktop\\ocr\\code_test_gray.png", img)
res/kpl.js
New file
@@ -0,0 +1,188 @@
function money_desc(money) {
            if (Math.abs(money) > 100000000) {
                return (money / 100000000.0).toFixed(2) + "亿";
            } else if (Math.abs(money) > 10000000){
                return (money / 10000).toFixed(0) + "万";
            }else if (Math.abs(money) > 1000000){
                return (money / 10000).toFixed(1) + "万";
            }else{
                return (money / 10000).toFixed(2) + "万";
            }
        };
        function fill_best_feng_kou(best_feng_kou) {
            var html = "";
            //最强风口
            for (var i = 0; i < best_feng_kou.length; i++) {
                var item = best_feng_kou[i];
                var tr = "<tr>";
                if (item[0].indexOf("00")==0||item[0].indexOf("60")==0)
                {
                    tr += "<td>" + item[1] + " <br>" + item[0] + " </td>";
                }else{
                    tr += "<td><span class='color-not-buy'>" + item[1] + "</span> <br>" + item[0] + " </td>";
                }
                tr += "<td>" + item[2] + "</td>";
                if (item[3] >= 9) {
                    tr += "<td class='purple'>" + item[3] + "%</td>";
                } else {
                    tr += "<td >" + item[3] + "%</td>";
                }
                //取板块中的前2个板块
                var blocks = item[5].split("、");
                if (blocks.length > 2) {
                    blocks = blocks.slice(0, 2);
                }
                var hot_blocks = item[4].split("、");
                tr += "<td class='bule'>";
                for (var n = 0; n < blocks.length; n++) {
                    var hot = 0;
                    for (var j = 0; j < hot_blocks.length; j++) {
                        if (hot_blocks[j] == blocks[n]) {
                            hot = 1;
                            break;
                        }
                    }
                    if (hot) {
                        tr += "<span class='red'>" + blocks[n] + "</span>";
                    } else {
                        tr += blocks[n];
                    }
                    if (n != blocks.length - 1) {
                        tr += "<br>";
                    }
                }
                tr += "</td>";
                tr += "</tr>";
                html += tr;
            }
            try{
                table =  document.getElementById('kpl-best_feng_kou');
                if(table!=undefined&&table!=null)
                {
                    table.getElementsByTagName("tbody")[0].innerHTML = html;
                }
            }catch(e){
            };
        };
        function fill_feng_kou(feng_kou) {
            var html = "";
            for (var i = 0; i < feng_kou.length; i++) {
                var item = feng_kou[i];
                var tr = "<tr>";
                if (item[0].indexOf("00")==0||item[0].indexOf("60")==0)
                {
                    tr += "<td>" + item[1] + " <br>" + item[0] + " </td>";
                }else{
                    tr += "<td><span class='color-not-buy'>" + item[1] + "</span> <br>" + item[0] + " </td>";
                }
                tr += "<td>" + money_desc(item[3]) + "</td>";
                if (parseFloat(item[2]) >= 9) {
                    tr += "<td class='purple'>" + item[2] + "%</td>";
                } else {
                    tr += "<td >" + item[2] + "%</td>";
                }
                //取板块中的前2个板块
                var blocks = item[4].split(",");
                if (blocks.length > 2) {
                    blocks = blocks.slice(0, 2);
                }
                tr += "<td class='bule'>";
                for (var n = 0; n < blocks.length; n++) {
                    tr += blocks[n];
                    if (n != blocks.length - 1) {
                        tr += "<br>";
                    }
                }
                tr += "</td>";
                tr += "</tr>";
                html += tr;
            }
            table = document.getElementById('kpl-feng_kou');
            if(table!=undefined&&table!=null)
                {
                    table.getElementsByTagName("tbody")[0].innerHTML = html;
                }
        };
        function fill_feng_xiang(feng_xiang) {
            var html = "";
            for (var i = 0; i < feng_xiang.length; i++) {
                var item = feng_xiang[i];
                var tr = "<tr>";
                if (item[0].indexOf("00")==0||item[0].indexOf("60")==0)
                {
                    tr += "<td>" + item[1] + " <br>" + item[0] + " </td>";
                }else{
                    tr += "<td><span class='color-not-buy'>" + item[1] + "</span> <br>" + item[0] + " </td>";
                }
                if (parseFloat(item[3]) >= 9) {
                    tr += "<td class='purple'>" + item[3] + "%</td>";
                } else {
                    tr += "<td >" + item[3] + "%</td>";
                }
                //取板块中的前2个板块
                var blocks = item[4].split("、");
                if (blocks.length > 2) {
                    blocks = blocks.slice(0, 2);
                }
                tr += "<td class='bule'>";
                for (var n = 0; n < blocks.length; n++) {
                    tr += blocks[n];
                    if (n != blocks.length - 1) {
                        tr += "<br>";
                    }
                }
                tr += "</td>";
                tr += "<td >" + money_desc(item[5]) + "</td>";
                tr += "<td >" + money_desc(item[6]) + "</td>";
                tr += "</tr>";
                html += tr;
            }
            table = document.getElementById('kpl-feng_xiang');
            if(table!=undefined&&table!=null)
                {
                    table.getElementsByTagName("tbody")[0].innerHTML = html;
                }
        };
        function fill_industry_rank(industry_rank) {
            var html = "";
            for (var i = 0; i < industry_rank.length; i++) {
                var item = industry_rank[i];
                var tr = "<tr>";
                tr += "<td>" + item[1] + " <br>" + item[0] + " </td>";
                tr += "<td >" + money_desc(item[2]) + "</td>";
                if (parseFloat(item[3]) >= 9) {
                    tr += "<td class='purple'>" + item[3] + "%</td>";
                } else {
                    tr += "<td >" + item[3] + "%</td>";
                }
                tr += "</tr>";
                html += tr;
            }
            table = document.getElementById('kpl-industry_rank');
            if(table!=undefined&&table!=null)
            {
                table.getElementsByTagName("tbody")[0].innerHTML = html;
            }
        };
var data = JSON.parse('{}');
            if (data.code == 0) {
                fill_best_feng_kou( data.data.best_feng_kou);
                fill_feng_kou( data.data.feng_kou);
                fill_feng_xiang(data.data.feng_xiang);
                fill_industry_rank(data.data.industry_rank);
            };
res/setting.conf
@@ -1,6 +1,9 @@
[config]
stay_on_top = 1
window_info = [[-1268, 136, 800, 502], [1386, 638, 320, 195]]
window_info = [[-1711, 194, 1280, 800], [1473, 621, 320, 195]]
xgb_window_info = [-8, -8, 1936, 1056]
window_watch_float_info = [961, 489, 320, 195]
window_tick_info = [-1310, 226, 986, 645]
[juejin]
strategy_id = 95a982ce-fc2d-11ec-8ff3-0a0027000010
@@ -12,5 +15,5 @@
ths_click_time_space = 2000
ths_trade_auto_refresh = 0
ths_refresh_time_space = 1500
ths_code_rect = [30,200,20]
ths_code_rect = [25,200,30]
res/template.html
New file
@@ -0,0 +1,15 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>{{title}}</title>
</head>
<body>
<ul>
    {% for k in list1 %}
        {% if k.index % 2 == 0 %}<li>{{k.index}}</li>{% endif %}
    {%endfor%}
</ul>
</body>
</html>
setting.py
@@ -157,16 +157,33 @@
        return None
# 记录上次的窗口位置与大小
def set_window_info(mainWindow, floatWindow):
# 设置分时看盘
def set_tick_window_info(win_info):
    cp = __read_setting()
    cp.set("config", "window_info", json.dumps((mainWindow, floatWindow)))
    cp.set("config", "window_tick_info", json.dumps(win_info))
    __write_setting(cp)
def get_window_info():
def get_tick_window_info():
    cp = __read_setting()
    val = __get_setting(cp, "config", "window_info")
    val = __get_setting(cp, "config", "window_tick_info")
    if val is None:
        return None
    else:
        val = json.loads(val)
        return val
# 设置悬浮盯盘
def set_float_watch_window_info(win_info):
    cp = __read_setting()
    cp.set("config", "window_watch_float_info", json.dumps(win_info))
    __write_setting(cp)
def get_float_watch_window_info():
    cp = __read_setting()
    val = __get_setting(cp, "config", "window_watch_float_info")
    if val is None:
        return None
    else:
test.py
New file
@@ -0,0 +1,258 @@
import json
import random
import xlwt
# k_form=(15个交易日是否涨幅24.9%,是否破前高,是否超跌,是否接近前高,是否N)
# code_nature = (是否有涨停,是否有溢价)
# hot_block(板块中涨停票个数(包含自己),板块炸板票个数)
# zyltgb自由流通市值是否大于250亿
# limit_price 涨停价是否大于100块
# limit_up_time 是否10点之前涨停
def getscore(bidding_money, big_money, volume, k_form, code_nature, hot_block, zyltgb, limit_price, limit_up_time):
    score = 0
    score_list = []
    # 开盘前竞价
    if bidding_money:
        score += 200
        score_list.append(200)
    else:
        score_list.append(0)
    # 大单成交
    if big_money:
        score += 150
        score_list.append(150)
    else:
        score_list.append(0)
    # 量
    volume_score = [0, 200, 250, 200, 150, 100, -500, -1000]
    score += volume_score[volume]
    score_list.append(volume_score[volume])
    # 15个交易日是否涨幅24.9%
    if k_form[0]:
        score += -1000
        score_list.append(-1000)
    else:
        score_list.append(0)
    # 是否破前高
    if k_form[1]:
        score += 150
        score_list.append(150)
    else:
        score_list.append(0)
    # 是否超跌
    if k_form[2]:
        score += 170
        score_list.append(170)
    else:
        score_list.append(0)
    # 是否接近前高
    if k_form[3]:
        score += -1000
        score_list.append(-1000)
    else:
        score_list.append(0)
    # 是否N
    if k_form[4]:
        score += 160
        score_list.append(160)
    else:
        score_list.append(0)
    if not code_nature[0]:
        score += 50
        score_list.append(50)
    else:
        score_list.append(0)
    if code_nature[1]:
        score += 100
        score_list.append(100)
    else:
        score_list.append(0)
    if hot_block[0] >= 2:
        score += 200
        score_list.append(200)
    else:
        score += 150
        score_list.append(150)
    if hot_block[1] > 0:
        score += 50
        score_list.append(50)
    else:
        score_list.append(0)
    if zyltgb:
        score += -500
        score_list.append(-500)
    else:
        score_list.append(0)
    if limit_price:
        score += -1000
        score_list.append(-1000)
    else:
        score_list.append(0)
    if limit_up_time:
        score += 150
        score_list.append(150)
    else:
        score_list.append(0)
    return score, score_list
if __name__ == "__main__":
    bidding_money = [True, False]
    bidding_money_desc = ["Y", "N"]
    big_money = [True, False]
    big_money_desc = ["Y", "N"]
    volume = [0, 1, 2, 3, 4, 5, 6, 7]
    volume_desc = ["换手量<49.9%。", "64.9%>换手量≥49.9%。", "79.9%>换手量≥64.9%。", "94.9%>换手量≥79.9%。", "109.9%>换手量≥94.9%。",
                   "124.9%>换手量≥109.9%。", "139.9%>换手量≥124.9%。", "换手量≥139.9%。"]
    k_form = []
    k_form_str = []
    for i in range(0, 5):
        k_form.append([True, False])
        k_form_str.append(["Y", "N"])
    code_nature = []
    code_nature_str = []
    for i in range(0, 2):
        code_nature.append([True, False])
        code_nature_str.append(["Y", "N"])
    hot_block = []
    hot_block.append([1, 2])
    hot_block.append([0, 1])
    zyltgb = [True, False]
    limit_price = [True, False]
    limit_up_time = [True, False]
    total_count = 0
    params = {}
    for i0 in range(0, len(bidding_money)):
        for i1 in range(0, len(big_money)):
            for i2 in range(0, len(volume)):
                for i3 in range(0, len(k_form)):
                    ks = []
                    for i in range(0, len(k_form)):
                        ks.append(0)
                    for n in range(0, 2):
                        ks[0] = k_form[0][n]
                        for n1 in range(0, 2):
                            ks[1] = k_form[1][n1]
                            for n2 in range(0, 2):
                                ks[2] = k_form[2][n2]
                                for n3 in range(0, 2):
                                    ks[3] = k_form[3][n3]
                                    for n4 in range(0, 2):
                                        ks[4] = k_form[4][n4]
                                        cns = []
                                        for i in range(0, len(code_nature)):
                                            cns.append(0)
                                        for j in range(0, 2):
                                            cns[0] = code_nature[0][j]
                                            for j1 in range(0, 2):
                                                cns[1] = code_nature[1][j1]
                                                hbs = []
                                                for i in range(0, len(hot_block)):
                                                    hbs.append(0)
                                                for h in range(0, 2):
                                                    hbs[0] = hot_block[0][h]
                                                    for h1 in range(0, 2):
                                                        hbs[1] = hot_block[1][h1]
                                                        for i10 in range(0, len(zyltgb)):
                                                            for i11 in range(0, len(limit_price)):
                                                                for i12 in range(0, len(limit_up_time)):
                                                                    param = (bidding_money[i0], big_money[i1],
                                                                             volume[i2], ks, cns, hbs,
                                                                             zyltgb[i10], limit_price[i11],
                                                                             limit_up_time[i12])
                                                                    score, score_list = getscore(bidding_money[i0],
                                                                                                 big_money[i1],
                                                                                                 volume[i2], ks, cns,
                                                                                                 hbs,
                                                                                                 zyltgb[i10],
                                                                                                 limit_price[i11],
                                                                                                 limit_up_time[i12])
                                                                    params[json.dumps(param)] = (score, score_list)
    titles = ["竞价强度", "资金力度", "换手量能", ["K线-涨幅24.9%", "K线-是否破前高", "K线-是否超跌", "K线-是否接近前高", "K线-是否N"],
              ["股性-无涨停", "股性-有溢价"],
              ["板块-涨停票数", "板块-炸板票数"], "股价>100", "自由流通市值>250亿", "10点前涨停", "得分"]
    # 写入excel
    datas = []
    for key in params:
        param = json.loads(key)
        score = params[key]
        # 15个交易日是否涨幅24.9 %, 是否破前高,是否超跌,是否接近前高,是否N
        if param[3][1:2] == [True, True]:
            continue
        if param[3][1] == True and param[3][3] == True:
            continue
        if param[4][0] == True and param[4][1] == True:
            continue
        datas.append((param, score))
    print("数量", len(datas))
    random.shuffle(datas)
    datas = datas[:1000]
    if True:
        print("总数", len(datas))
        file_name = "D:/test__.xls"
        wb = xlwt.Workbook()
        ws = wb.add_sheet('sheet1')
        ws.write(0, 0, '序号')
        index = 1
        for title in titles:
            if type(title) != str:
                for t in title:
                    ws.write(0, index, t)
                    index += 1
            else:
                ws.write(0, index, title)
                index += 1
        index = 0
        for data in datas:
            index += 1
            col = 1
            ws.write(index, 0, index)
            for d in data[0]:
                col_data = ""
                if type(d) == list:
                    for dd in d:
                        if type(dd) == bool:
                            col_data = "Y" if dd else "N"
                        else:
                            col_data = str(dd)
                        ws.write(index, col, f"{col_data}({data[1][1][col - 1]})")
                        col += 1
                else:
                    if type(d) == bool:
                        col_data = "Y" if d else "N"
                    else:
                        if col == 3:
                            col_data = volume_desc[d]
                        else:
                            col_data = str(d)
                    print(col)
                    ws.write(index, col, f"{col_data}({data[1][1][col - 1]})")
                    col += 1
            ws.write(index, col, data[1][0])
        wb.save(file_name)
tool.py
@@ -62,8 +62,18 @@
    return time_seconds_format(s)
def is_trade_time(time_str=None):
    if time_str is None:
        time_str = get_now_time_str()
    if (int("092500") <= int(time_str.replace(":", "")) <= int("113000")) or (
            int("130000") <= int(time_str.replace(":", "")) <= int("150000")):
        return True
    return False
def get_now_time_str():
    return datetime.datetime.now().strftime("%H:%M:%S")
    # return "10:20:00"
def get_now_date_str():
win32_util.py
@@ -25,7 +25,6 @@
            str_ = getText(temp)
            if str_.find(title) > -1:
                hwnds.append(temp)
                print(str_)
        temp = win32gui.FindWindowEx(hwnd, temp, None, None)
        if not temp:
            break
@@ -67,8 +66,6 @@
    signedIntsArray = saveBitMap.GetBitmapBits(True)
    # 内存释放
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
@@ -82,5 +79,9 @@
if __name__ == "__main__":
    print(search_window("副屏1"))
    visual_click(0x00152876, (110, 90))
    # print(search_window("副屏1"))
    # visual_click(0x00152876, (110, 90))
    x = 1500 +67
    y = 400 + 4
    window_capture(0x00010BA2,(x,y,x +45,y+16))
    # pass