admin
2023-09-11 5b17c0b74c3b9c7cfb6bdf861fe532a6eb9e7e4b
分时看盘调整
7个文件已修改
2个文件已添加
624 ■■■■ 已修改文件
gui_wx.py 406 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
juejin_core.py 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
juejin_data_export.py 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/index23-05-04.html 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/page.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/codes.txt 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/setting.conf 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
socket_util.py 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gui_wx.py
@@ -11,9 +11,11 @@
import win32gui
import constant
import juejin_data_export
import ocr_util
import opencv_util
import setting
import socket_util
import ths_util
import win32_util
@@ -65,6 +67,78 @@
    elif result == wx.ID_NO:
        callback(False)
        toastone.Destroy()
# 掘金分时下载
class JueJinTickDataDownloadFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "掘金分时数据下载", style=wx.SYSTEM_MENU ^ wx.CLOSE_BOX ^ wx.CAPTION ^ wx.STAY_ON_TOP,
                          size=(350, 250))
        self.SetBackgroundColour(wx.Colour(224, 224, 224))
        boxsier = wx.BoxSizer()
        flex = wx.FlexGridSizer(rows=4, cols=2, vgap=10, hgap=10)
        # 策略
        label = wx.StaticText(self, label="代码:")
        flex.Add(label, flag=wx.ALIGN_RIGHT)
        self.edit_code = wx.TextCtrl(self, size=(100, -1))
        flex.Add(self.edit_code)
        # 目录
        label = wx.StaticText(self, label="目录选择:")
        flex.Add(label)
        bs1 = wx.BoxSizer(wx.VERTICAL)
        self.btn_dir = wx.Button(self, label="选择目录")
        bs1.Add(self.btn_dir)
        self.label_path = wx.StaticText(self, label="")
        bs1.Add(self.label_path)
        flex.Add(bs1)
        label = wx.StaticText(self, label="时间选择:")
        flex.Add(label)
        self.edit_start_time = wx.TextCtrl(self, size=(100, -1))
        self.edit_end_time = wx.TextCtrl(self, size=(100, -1))
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        bs1.Add(self.edit_start_time, 1, wx.RIGHT, 10)
        label = wx.StaticText(self, label="-")
        bs1.Add(label)
        bs1.Add(self.edit_end_time, 1, wx.LEFT, 10)
        flex.Add(bs1)
        # 占位
        flex.Add(wx.StaticText(self, label=""))
        # 确定按钮
        ID_SURE = wx.NewId()
        self.btn_sure = wx.Button(self, label='确定', id=ID_SURE)
        self.btn_sure.Bind(wx.EVT_BUTTON, self.__sure_btn)
        self.btn_dir.Bind(wx.EVT_BUTTON, self.__choose_dir)
        flex.Add(self.btn_sure, 1, wx.TOP | wx.LEFT, 20)
        boxsier.Add(flex, 1, wx.TOP | wx.LEFT, 20)
        self.SetSizer(boxsier)
        # 赋初值
        self.edit_start_time.SetValue("09:30:00")
        self.edit_end_time.SetValue("10:00:00")
    def __choose_dir(self, event):
        dialog = wx.DirDialog(None, "选择保存目录:")
        if dialog.ShowModal() == wx.ID_OK:
            path = dialog.GetPath()
            self.label_path.SetLabel(path)
        dialog.Destroy()
    def __sure_btn(self, event):
        code = self.edit_code.GetValue()
        start_time = self.edit_start_time.GetValue()
        end_time = self.edit_end_time.GetValue()
        dir_path = self.label_path.GetLabel()
        if code and start_time and end_time and dir_path:
            juejin_data_export.export_tick_data(code, start_time, end_time, dir_path)
            show_info("导出成功", None)
        else:
            show_warning("参数不完整", None)
class JueJinSettingFrame(wx.Frame):
@@ -1009,6 +1083,28 @@
        self.Hide()
class SocketApiUtil:
    @classmethod
    def __request(cls, data_json):
        socket_util.encryp_client_params_sign(data_json)
        client = socket.socket()  # 生成socket,连接server
        ip_port = (constant.SERVER_HOST, 10009)  # server地址和端口号(最好是10000以后)
        client.connect(ip_port)
        client.send(socket_util.load_header(json.dumps(data_json).encode("utf-8")))
        result_str, header = socket_util.recv_data(client)
        return result_str
    @classmethod
    def get_cost_price(cls, code):
        result_str = cls.__request({"type": "get_cost_price", "data": {"code": code}})
        print(result_str)
        result = json.loads(result_str)
        if result['code'] == 0:
            return result['data']['price']
        raise Exception(result['msg'])
class TickFrame(wx.Frame):
    def __init__(self):
        '''构造函数'''
@@ -1023,6 +1119,9 @@
            self.Size = wx.Size(win_info[2], win_info[3])
        else:
            self.Center()
        # 拉取数据线程
        self.cost_price_threads = {}
        # 以下代码处理图标
        # if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
@@ -1044,6 +1143,8 @@
        self.timer = wx.Timer(self)  # 创建定时器
        self.Bind(wx.EVT_TIMER, self.post_redraw, self.timer)  # 绑定一个定时器事件
        self.last_size = (self.Size[0], self.Size[1])
        # self.scroll.Layout()
        # self.boxsier.Fit(self.scroll)
@@ -1088,8 +1189,13 @@
            pannel.BackgroundColour = wx.Colour(0, 0, 0)
            self.panels.append(pannel)
            self.boxsier.Add(pannel)
            axes = self.__create_canvas(pannel, "{}({})".format(codes_name[i][0], codes_name[i][1]), codes_name[i][1],
            code =  codes_name[i][0]
            axes = self.__create_canvas(pannel, code, "{}({})".format(codes_name[i][0], codes_name[i][1]),
                                        codes_name[i][1],
                                        codes_name[i][2])
            if code not in self.cost_price_threads:
                t1 = self.run_get_cost_price(codes_name[i][0])
                self.cost_price_threads[code] = t1
            axes_list.append(axes)
        self.scroll.Layout()
@@ -1102,7 +1208,7 @@
        t1.setDaemon(True)
        t1.start()
    def __create_canvas(self, pannel, title, name, price, close_callback=None):
    def __create_canvas(self, pannel, code, title, name, price, close_callback=None):
        def show_mouse_line(event):
            # 删除之前的线
            if title in self.mark_lines:
@@ -1165,7 +1271,7 @@
        # axes_score.plot(t_score, s_score, 'ro', t_score, s_score, 'k')
        axes.set_title(title)
        axes.grid(color='firebrick', ls='-', lw=0.5)
        axes.grid(False)
        axes.set_xlabel(f'时间({name})')
        axes.dist = 0
@@ -1179,8 +1285,7 @@
        axes2 = axes.twinx()
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        axes2.grid(False)
        axes2.grid(color='firebrick', ls='-', lw=0.5)
        # 鼠标在画布移动
        axes2.figure.canvas.mpl_connect('motion_notify_event', show_mouse_line)
        # 鼠标离开画布
@@ -1190,64 +1295,33 @@
        limit_up_price = float(tool.get_limit_up_price(price))
        max_rate = round((limit_up_price - price) / price, 4) * 100
        print("涨停最大比例", max_rate)
        yticks2 = []
        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 - 20) / 20, 4))
        yticks2_labels = []
        yticks = []
        yticks_labels = []
        for i in range(0, len(yticks2)):
            if i % 2 == 0:
                yticks2_labels.append("{}%".format(abs(round(yticks2[i], 2))))
            else:
                yticks2_labels.append("")
            price_ = round((1 + yticks2[i] / 100) * price, 2)
            yticks.append(price_)
            if i % 2 == 0:
                yticks_labels.append("")
                # yticks_labels.append(round(yticks[i], 2))
                if i == 20:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=2)
                else:
                    axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=1.2)
            else:
                # axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=0.5)
                yticks_labels.append("")
        # 加粗中轴线
        # 设置纵坐标信息
        # max_rate = 2
        DrawManager.set_y_info(code, max_rate, axes, axes2, price)
        axes2.set_ylabel(u'')
        # 设置纵轴的值的范围
        axes2.set_ylim(0 - max_rate * (1), max_rate * (1))
        axes2.set_yticks(yticks2)
        axes2.set_yticklabels(yticks2_labels)
        axes.set_yticks(yticks)
        axes.set_yticklabels(yticks_labels)
        # 设置纵坐标数值颜色
        for i in range(0, 19):
            axes.get_yticklabels()[i].set_color("green")
            axes2.get_yticklabels()[i].set_color("green")
        for i in range(20, 20):
            axes.get_yticklabels()[i].set_color("white")
            axes2.get_yticklabels()[i].set_color("white")
        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='green', linewidth=1, linestyle='-')
        average_line = axes2.plot([], [], color='yellow', 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')
        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')
        axes2.spines['top'].set_visible(False)
        axes.spines['top'].set_visible(False)
        axes2.spines['bottom'].set_visible(False)
        axes2.spines['bottom'].set_visible(True)
        axes.spines['bottom'].set_visible(False)
        return (axes2, line, average_line_1m, average_line)
        # 中轴线加粗
        hline = axes2.axhline(0, linestyle='-', color='firebrick', lw=2)
        # 设置坐标轴标记点为黑色
        axes.tick_params(axis='x', colors='black')
        axes.tick_params(axis='y', colors='black')
        axes2.tick_params(axis='x', colors='black')
        axes2.tick_params(axis='y', colors='black')
        return (axes2, line, average_line_1m, average_line, axes)
    def __show_top(self, event):
        if event.Selection:
@@ -1295,11 +1369,41 @@
        time.sleep(0.1)
        self.Size = wx.Size(self.Size[0], self.Size[1] - 10)
    def run_get_cost_price(self, code):
        def request(code):
            while True:
                try:
                    price = SocketApiUtil.get_cost_price(code)
                    pre_price = juejin_core.GPCodeManager().get_pre_prices(code)
                    rate = round((price - pre_price) * 100 / pre_price, 2)
                    DrawManager.set_cost_rate(code, rate)
                    # wx.CallAfter(lambda :)
                except Exception as e:
                    DrawManager.set_cost_rate(code, None)
                    # wx.CallAfter(lambda: str(e))
                except:
                    pass
                finally:
                    time.sleep(3)
        t1 =  threading.Thread(target=lambda: request(code), daemon=True)
        t1.start()
        return t1
# 绘图管理器
class DrawManager:
    X_RANGE_MINIUTES = 60
    X_DATA_MINIUTES = 56
    h_lines_dict = {}
    cost_mark_dict = {}
    last_max_rate_dict = {}
    cost_rate_dict = {}
    @classmethod
    def set_cost_rate(cls, code, rate):
        cls.cost_rate_dict[code] = rate
    def __load_lack_datas(self, code, time_ranges):
        codeDataManager = code_data_manager.CodeDataManager()
@@ -1311,7 +1415,7 @@
            for data in results:
                datas.append(juejin_core.parse_tick(data))
            # 保存数据
            last_time = time_range[0]
            for data in datas:
                # 09:25:00之前的数据不保存
                created_at = data["created_at"].strftime("%Y-%m-%d %H:%M:%S")
@@ -1326,6 +1430,9 @@
                # 不是今天的数据不保存
                if day != created_at[:10]:
                    continue
                # 每隔15s保存一次
                if last_time and tool.trade_time_sub(created_at[-8:], last_time) >= 15:
                    last_time = created_at[-8:]
                codeDataManager.save_data(data)
    def init_code_datas(self):
@@ -1356,17 +1463,112 @@
                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)
                # TODO 测试注释
                # if len(ranges) > 0:
                #     self.__load_lack_datas(code, ranges)
                #     old_datas = codeDataManager.get_datas(code)
                old_datas = []
                if old_datas:
                    code_datas[code].extend(old_datas)
                    # self.update(code, code_datas[code])
                    wx.CallAfter(self.update, code, code_datas[code])
    @classmethod
    def __format_max_rate(cls, max_rate):
        line_count = 0
        if max_rate % 0.6 < 0.0001:
            line_count = int(round(max_rate // 0.6))
        else:
            line_count = int(round(max_rate // 0.6)) + 1
        return line_count * 0.6, 0.6
    @classmethod
    def set_y_info(cls, code, max_rate, axes, axes2, price):
        # 设置y轴数据
        max_rate, amplitude = cls.__format_max_rate(max_rate)
        line_count = int(round(max_rate / amplitude))
        print("line_count", line_count)
        yticks2 = []
        for i in range(0, line_count * 2 + 1):
            if i >= line_count:
                yticks2.append(0 - round(max_rate * (line_count - i) / line_count, 4))
            else:
                yticks2.append(round(max_rate * (i - line_count) / line_count, 4))
        yticks2_labels = []
        yticks = []
        yticks_labels = []
        if code not in cls.h_lines_dict:
            cls.h_lines_dict[code] = []
        for line in cls.h_lines_dict[code]:
            line.remove()
        cls.h_lines_dict[code].clear()
        for i in range(0, len(yticks2)):
            # if i % 2 == 0:
            yticks2_labels.append("{}%".format(abs(round(yticks2[i], 2))))
            # else:
            #     yticks2_labels.append("")
            price_ = round((1 + yticks2[i] / 100) * price, 2)
            yticks.append(price_)
            if i % 2 == 0 and False:
                yticks_labels.append("")
                # yticks_labels.append(round(yticks[i], 2))
                if i == line_count:
                    hline = axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=2)
                    cls.h_lines_dict[code].append(hline)
                else:
                    # hline = axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=1.2)
                    # cls.h_lines_dict[code].append(hline)
                    pass
            else:
                # axes2.axhline(yticks2[i], linestyle='-', color='firebrick', lw=0.5)
                yticks_labels.append("")
        axes2.set_ylabel(u'')
        # 设置纵轴的值的范围
        axes2.set_ylim(0 - max_rate * (1), max_rate * (1))
        axes.set_ylim((100 - max_rate) * price / 100, (100 + max_rate) * price / 100)
        axes2.set_yticks(yticks2)
        axes2.set_yticklabels(yticks2_labels)
        axes.set_yticks(yticks)
        axes.set_yticklabels(yticks_labels)
        # axes.axhline(0, color='firebrick', linewidth=2)
        # 设置纵坐标数值颜色
        for i in range(0, line_count):
            axes.get_yticklabels()[i].set_color("green")
            axes2.get_yticklabels()[i].set_color("green")
        for i in range(line_count, line_count):
            axes.get_yticklabels()[i].set_color("white")
            axes2.get_yticklabels()[i].set_color("white")
        for i in range(line_count + 1, line_count * 2 + 1):
            axes.get_yticklabels()[i].set_color("red")
            axes2.get_yticklabels()[i].set_color("red")
    @classmethod
    def mark_code_info(cls, code, max_rate, axes2, cost_rate):
        ## 标记代码相关的信息
        # 设置成本线
        if code in cls.cost_mark_dict:
            cls.cost_mark_dict[code].remove()
        if cost_rate is None:
            return
        mark_x = axes2.get_xlim()
        mark_y = axes2.get_ylim()
        if cost_rate > max_rate:
            scatter_cost = axes2.scatter((mark_x[0] + mark_x[1]) // 2, mark_y[1] - mark_y[1] / 25, c='#159EEC',
                                         marker='^', s=50)
            cls.cost_mark_dict[code] = scatter_cost
        elif cost_rate < 0 and abs(cost_rate) > max_rate:
            scatter_cost = axes2.scatter((mark_x[0] + mark_x[1]) // 2, 0 - mark_y[1] + mark_y[1] / 25, c='#159EEC',
                                         marker='v', s=50)
            cls.cost_mark_dict[code] = scatter_cost
        else:
            line1 = axes2.axhline(cost_rate, linestyle='--', color='#FF8020', lw=1)
            cls.cost_mark_dict[code] = line1
    # 更新数据
    def __update_data(self, code, axes, datas, min_rate, max_rate):
    def __update_data(self, code, axes, datas, pre_price, y_max_rate=10.5, cost_rate=None):
        def get_time_as_seconds(created_at):
            time_ = created_at[-8:]
            if tool.get_time_as_second("13:00:00") > tool.get_time_as_second(time_) > tool.get_time_as_second(
@@ -1388,6 +1590,14 @@
            s = seconds % 60
            return "{0:0>2}:{1:0>2}:{2:0>2}".format(h, m, s)
        y_max_rate, a = self.__format_max_rate(y_max_rate)
        if code not in self.last_max_rate_dict or self.last_max_rate_dict[code] < y_max_rate:
            self.last_max_rate_dict[code] = y_max_rate
            # 设置纵坐标的信息
            self.set_y_info(code, y_max_rate, axes[4], axes[0], pre_price)
        # 标记代码相关的信息
        self.mark_code_info(code, self.last_max_rate_dict[code], axes[0], cost_rate)
        # 删除9:30以前的数据
        for i in range(0, len(datas)):
            time_ = datas[i]["created_at"][-8:]
@@ -1408,6 +1618,7 @@
        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)
@@ -1433,11 +1644,13 @@
        axes[0].set_xticklabels(xticklabels)
        axes[1][0].set_data(xs, ys_rate)
        axes[2][0].set_data(xs, ys_average_rate_1m)
        # 不需要一分钟均线
        # 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[0].set_text("{}% \n留:{}".format(round(datas[-1]["rate"] * 100, 2), '无持仓' if self.cost_rate_dict.get(
            code) is None else f"{self.cost_rate_dict.get(code)}%"))
        texts[1].set_text("{}".format(datas[-1]["created_at"].split(" ")[1]))
        texts[0].set_x(xms[1] - 80)
        texts[0].set_y(yms[1] + 0.5)
@@ -1450,10 +1663,10 @@
                line = self.lines[code][key]
                line.remove()
            self.lines.pop(code)
        # 绘制最大最小坐标
        line_min = axes[0].axhline(min_rate, linestyle='--', color='yellow', lw=0.5)
        line_max = axes[0].axhline(max_rate, linestyle='--', color='yellow', lw=0.5)
        self.lines[code] = {"min": line_min, "max": line_max}
        # 暂时不需要 绘制最大最小坐标
        # line_min = axes[0].axhline(min_rate, linestyle='--', color='yellow', lw=0.5)
        # line_max = axes[0].axhline(max_rate, linestyle='--', color='yellow', lw=0.5)
        # self.lines[code] = {"min": line_min, "max": line_max}
        axes[0].figure.canvas.draw()
        axes[0].figure.canvas.flush_events()
@@ -1464,28 +1677,13 @@
    def update(self, code, datas):
        __start_time = time.time()
        # 获取前高和前低
        max_rate = None
        min_rate = None
        if code in max_min_prices:
            pre_price = juejin_core.GPCodeManager().get_pre_prices(code)
            min_rate = round((max_min_prices[code][0] - pre_price) / pre_price, 4)
            max_rate = round((max_min_prices[code][1] - pre_price) / pre_price, 4)
        for data in datas:
            rate = data["rate"] * 100
            created_at = data["created_at"][-8:]
            if tool.get_time_as_second("15:00:00") >= tool.get_time_as_second(created_at) >= tool.get_time_as_second(
                    "09:30:00"):
                if max_rate is None:
                    max_rate = rate
                if min_rate is None:
                    min_rate = rate
                if rate > max_rate:
                    max_rate = rate
                if rate < min_rate:
                    min_rate = rate
        # 获取当前的坐标范围
        max_price = pre_price
        for d in datas:
            if abs(d['price'] - pre_price) > abs(max_price - pre_price):
                max_price = d['price']
        y_max_rate = round(abs(max_price - pre_price) * 100 / pre_price, 2)
        # 展示最近的600个
        datas = datas[0 - self.X_DATA_MINIUTES * 20:]
        # 修正量数据
@@ -1502,10 +1700,11 @@
        for i in range(0, len(self.codes_info)):
            if self.codes_info[i][0] == code:
                try:
                    self.__update_data(code, self.axes_list[i], datas, min_rate, max_rate)
                    self.__update_data(code, self.axes_list[i], datas, pre_price, y_max_rate,
                                       self.cost_rate_dict.get(code))
                except Exception as e:
                    logging.exception(e)
        print("绘图花费时间:", time.time() - __start_time)
        # print("绘图花费时间:", time.time() - __start_time)
class mainApp(wx.App):
@@ -1545,7 +1744,6 @@
        self.floatFrame = FloatFrame()
        global_datas["tickFrame"] = self.frame
        global_datas["floatFrame"] = self.floatFrame
        # # 测试
        # global_datas["floatFrame"].Show()
@@ -1562,19 +1760,36 @@
    while True:
        data = pipe.recv()
        if data:
            type = data["type"]
            if type == 0:
            _type = data["type"]
            if _type == 0:
                # tick数据
                data = data["data"]
                code = data["code"]
                try:
                if code not in code_datas:
                    code_datas[code] = []
                except:
                    continue
                create_time = data['created_at'].strftime("%H:%M:%S")
                last_time = None
                if code_datas[code]:
                    if type(code_datas[code][-1]["created_at"]) == str:
                        last_time = code_datas[code][-1]["created_at"].split(" ")[1]
                    else:
                        last_time = code_datas[code][-1]["created_at"].strftime("%H:%M:%S")
                # 如果相差15s就添加进去
                if last_time is None or tool.trade_time_sub(create_time, last_time) >= 15:
                code_datas[code].append(data)
                codeDataManager.save_data(data)
                else:
                    code_datas[code][-1]['rate'] = data['rate']
                    code_datas[code][-1]['price'] = data['price']
                    code_datas[code][-1]['average_price'] = data['average_price']
                    code_datas[code][-1]['average_rate'] = data['average_rate']
                # 更新数据
                try:
                    drawManager.update(code, code_datas[code])
                    print("接受到的tick数据:", data)
                    # print("接受到的tick数据:", data)
                except:
                    pass
@@ -1666,6 +1881,9 @@
                # 后台运行
                t1.setDaemon(True)
                t1.start()
            elif type_ == "juejin_tick_download":
                wx.CallAfter(lambda: JueJinTickDataDownloadFrame().Show())
            elif type_ == "exit":
                try:
@@ -1716,4 +1934,4 @@
    # app = mainApp()
    # app.MainLoop()
    # ocr_ths_code()
    print(int(1.5))
    SocketApiUtil.get_cost_price("002207")
juejin_core.py
@@ -4,6 +4,7 @@
import time
import gm.api as gmapi
import xlwt
import setting
import tool
@@ -33,7 +34,7 @@
def subscript():
    # 清除收盘价
    GPCodeManager.pre_prices.clear()
    __get_latest_price()
    # __get_latest_price()
    # t1 = threading.Thread(target=lambda: __get_latest_price())
    # # 后台运行
@@ -41,13 +42,13 @@
    # t1.start()
    # # 读取订阅代码
    # codes = gpCodeManager.get_codes()
    # gpcode_list = GPCodeManager.get_gp_list_with_prefix(codes)
    # if gpcode_list:
    #     codes_str = ",".join(gpcode_list)
    #     print("订阅:", codes_str)
    #     gmapi.subscribe(symbols=codes_str, frequency='tick', count=2, wait_group=False, wait_group_timeout='10s',
    #                     unsubscribe_previous=True)
    codes = gpCodeManager.get_codes()
    gpcode_list = GPCodeManager.get_gp_list_with_prefix(codes)
    if gpcode_list:
        codes_str = ",".join(gpcode_list)
        print("订阅:", codes_str)
        gmapi.subscribe(symbols=codes_str, frequency='tick', count=2, wait_group=False, wait_group_timeout='10s',
                        unsubscribe_previous=True)
# 获取最新的现价
@@ -72,7 +73,7 @@
                        on_tick(None, data)
        except:
            pass
        time.sleep(1)
        time.sleep(15)
def parse_tick(tick):
@@ -91,6 +92,8 @@
def on_tick(context, tick):
    print(tick)
    if int(tick['created_at'].strftime("%H:%M:%S").replace(":","")) < int("092500"):
        return
    data = parse_tick(tick)
    data = {"type": 0, "data": data}
    __pipe.send(data)
@@ -105,19 +108,22 @@
    #           filename='juejin_core.py',
    #           mode=gmapi.MODE_LIVE,
    #           token=token)
    # 测试订阅
    gmapi.run(strategy_id=strategy_id,
              filename='juejin_core.py',
              mode=gmapi.MODE_BACKTEST,
              backtest_start_time="2023-09-11 09:25:00",
              backtest_end_time="2023-09-11 11:25:00",
              backtest_initial_cash=1000000,
              backtest_transaction_ratio=1, backtest_commission_ratio=0,
              backtest_slippage_ratio=0, backtest_adjust=gmapi.ADJUST_NONE, backtest_check_cache=0,
              serv_addr='', backtest_match_mode=0,
              token=token)
    subscript()
    # gmapi.run(strategy_id=strategy_id,
    #           filename='juejin_core.py',
    #           mode=gmapi.MODE_BACKTEST,
    #           backtest_start_time="2023-03-13 09:25:00",
    #           backtest_end_time="2023-03-13 09:35:06",
    #           backtest_initial_cash=1000000,
    #           backtest_transaction_ratio=1, backtest_commission_ratio=0,
    #           backtest_slippage_ratio=0, backtest_adjust=gmapi.ADJUST_NONE, backtest_check_cache=0,
    #           serv_addr='', backtest_match_mode=0,
    #           token=token)
class GPCodeManager:
@@ -181,10 +187,10 @@
            self.pre_prices[code] = price
        return price
    def get_history_tick_n(self, code, count):
    def get_history_tick_n(self, code, count, frequency="1d", end_time=None):
        symbols = self.get_gp_list_with_prefix([code])
        gmapi.set_token(self.token)
        results = gmapi.history_n(symbol=symbols[0], frequency="1d", count=count)
        results = gmapi.history_n(symbol=symbols[0], frequency=frequency, count=count, end_time=end_time)
        last_data = None
        for result in results:
            try:
@@ -264,5 +270,3 @@
                f.write("\n")
if __name__ == "__main__":
    __get_latest_price()
juejin_data_export.py
New file
@@ -0,0 +1,39 @@
import xlwt
import tool
from juejin_core import GPCodeManager
def __export_excel(datas, path):
    wb = xlwt.Workbook(encoding="utf-8")
    ws = wb.add_sheet('sheet1')
    ws.write(0, 0, '时间')
    ws.write(0, 1, '价格')
    ws.write(0, 2, '涨幅')
    for i in range(len(datas)):
        ws.write(i + 1, 0, datas[i][0])
        ws.write(i + 1, 1, datas[i][1])
        ws.write(i + 1, 2, datas[i][2])
    wb.save(path)
def export_tick_data(code, start_time, end_time, dir_path):
    seconds = tool.trade_time_sub(end_time, start_time)
    count = seconds // 3
    datas = GPCodeManager().get_history_tick_n(code, count, frequency="tick",
                                               end_time=f"{tool.get_now_date_str()} {end_time}")
    # 获取上个交易日的收盘价
    pre_price = GPCodeManager().get_pre_prices(code)
    datas = [(x['created_at'].strftime("%H:%M:%S"), x['price']) for x in datas]
    new_datas = []
    for d in datas:
        if tool.trade_time_sub(d[0], start_time) < 0:
            continue
        new_datas.append((d[0], d[1], round((d[1] - pre_price) * 100 / pre_price, 2)))
    __export_excel(new_datas, f"{dir_path}\\分时_{code}.xls")
if __name__ == "__main__":
    code = "002073"
    export_tick_data(code, "09:30:00", "13:05:00", "D:\\文件传输\\新建文件夹\\L2导出文件")
kp_html/kp/index23-05-04.html
@@ -309,7 +309,7 @@
                
                
                <div class="table-name plate-container"
                    style="float: left;width: 100%;text-align: center; background: #000;height:60px">
                    style="float: left;width: 100%;text-align: center; background: #000;height:90px">
                    <span v-for="(item,i) in first_info.limit_up_reason_statistic"
                        v-on:click="show_want_codes(item[0])">
@@ -580,7 +580,7 @@
            </div>
            <div style="position: fixed;bottom: 10px;right: 10px;">
            <div style="position: fixed;bottom: 10px;right: 10px;display: none;">
                <div style="display: flex;">
                    <span id="" class="button-normal" style="display: inline-block;"
                        @click="do_action_for_code(0)">加入黑名单
@@ -616,7 +616,7 @@
                <div v-if="want_codes" class="want_code_content" style="display: flex;width: 540px;justify-content: space-between;">
                    <div v-for="(item,i) in want_codes" class="item" style="display: flex;width: 220px;margin-bottom: 15px;">
                        <div><img :style="{'visibility':item[3]?'visiable':'hidden'}" style="margin-right:5px;" src="./images/stop_up.png" ><span :class="{'red': item[5]}">{{item[1]}}({{item[0]}})-{{item[2]}} </span><span class="red" v-if="item[4]">*</span> </div>
                        <div><img :style="{'visibility':item[3]==1?'visiable':'hidden'}" style="margin-right:5px;" src="./images/stop_up.png" ><span :class="{'red': item[5]}">{{item[1]}}({{item[0]}})-{{item[2]}} </span><span class="red" v-if="item[4]">*</span> </div>
                        <img v-if="item[2]==1" class="delete" src="./images/delete.png"
                            @click="delete_from_want_codes(i)">
                    </div>
kp_html/kp/js/page.js
@@ -15,7 +15,7 @@
    function _resize() {
        var code_info_height = 480
        console.log("总高", $(window).height())
        var bottom_height = $(window).height() - code_info_height - 110;
        var bottom_height = $(window).height() - code_info_height - 140;
        console.log("底部高", bottom_height)
        $("#body>div:nth-child(2)>div").css("height", code_info_height).css("max-height", code_info_height);
        $("#body>div:nth-child(3)").css("height", bottom_height)
main.py
@@ -254,6 +254,8 @@
        self.wx_pipe.send(json.dumps({"type": "set_code", "code": code}))
        self.webview.page().runJavaScript(f"app.set_target_code('{code}')")
    # 菜单及菜单点击事件
    def __menu(self):
        def __gpu_is_avaiable():
@@ -266,6 +268,9 @@
            ps = (self.x(), self.y())
            size = (self.width(), self.height())
            self.wx_pipe.send(json.dumps({"type": "juejin_setting", "pos": (ps[0] + size[0] / 2, ps[1] + size[1] / 2)}))
        def __juejin_tick_download():
            self.wx_pipe.send(json.dumps({"type": "juejin_tick_download"}))
        def __download_codes():
            try:
@@ -338,6 +343,10 @@
        action.triggered.connect(__clear_webview_cache)
        setting_.addAction(action)
        action = QAction("&掘金分时数据下载", self)
        action.triggered.connect(__juejin_tick_download)
        setting_.addAction(action)
        auto_ = menubar.addMenu('&自动化')
        action = QAction("&同花顺设置", self)
        action.triggered.connect(__manage_ths_pos)
res/codes.txt
@@ -1,4 +1,2 @@
002387
002813
001330
603286
002528
601136
res/setting.conf
@@ -1,10 +1,10 @@
[config]
stay_on_top = 1
window_info = [[-1711, 194, 1280, 800], [1473, 621, 320, 195]]
xgb_window_info = [-1921, -8, 1920, 1017]
xgb_window_info = [-554, 369, 1918, 1008]
window_watch_float_info = [-628, 346, 435, 220]
window_tick_info = [-1420, 79, 1027, 643]
kp_second_window_info = [-2320, 119, 1920, 1017]
window_tick_info = [-1575, 199, 1181, 737]
kp_second_window_info = [-2274, 283, 1920, 1017]
code_attribute_window_info = [-650, 315, 291, 278]
client = hxh
float_frame_auto_focus = 1
socket_util.py
New file
@@ -0,0 +1,100 @@
"""
socket工具类
"""
# 添加数据头
import hashlib
import json
import socket
def md5_encrypt(value):
    md5 = hashlib.md5()
    md5.update(value.encode('utf-8'))
    return md5.hexdigest()
def create_socket(addr, port):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 生成socket,连接server
    client.connect((addr, port))
    return client
def load_header(data_bytes):
    slen = '##%08d' % len(data_bytes)
    return slen.encode("utf-8") + data_bytes
# 接收数据,去除头
def recv_data(sk):
    data = ""
    header_size = 10
    buf = sk.recv(1024)
    header_str = buf[:header_size]
    if buf:
        buf = buf.decode('utf-8')
        if buf.startswith("##"):
            content_length = int(buf[2:10])
            received_size = 0
            # 加上读取头的数据
            received_size += len(buf[header_size:])
            data += buf[header_size:]
            while not received_size == content_length:
                r_data = sk.recv(10240)
                received_size += len(r_data)
                data += r_data.decode('utf-8')
        else:
            data = sk.recv(1024 * 1024)
            data = buf + data.decode('utf-8')
    return data, header_str
# 客户端参数加密
def encryp_client_params_sign(dataJson):
    if type(dataJson) != dict:
        return dataJson
    str_list = []
    for k in dataJson:
        if type(dataJson[k]) == dict:
            str_list.append(f"{k}={json.dumps(dataJson[k], separators=(',', ':'))}")
        else:
            str_list.append(f"{k}={dataJson[k]}")
    str_list.sort()
    str_list.append("%Yeshi2014@#.")
    dataJson["sign"] = md5_encrypt("&".join(str_list))
    return dataJson
# 客户端密码加密验证
def is_client_params_sign_right(dataJson):
    if type(dataJson) != dict:
        return False
    sign = dataJson["sign"]
    dataJson.pop("sign")
    str_list = []
    for k in dataJson:
        if type(dataJson[k]) == dict:
            str_list.append(f"{k}={json.dumps(dataJson[k], separators=(',', ':'))}")
        else:
            str_list.append(f"{k}={dataJson[k]}")
    str_list.sort()
    str_list.append("%Yeshi2014@#.")
    new_sign = md5_encrypt("&".join(str_list))
    # print("加密前字符串","&".join(str_list))
    if sign == new_sign:
        return True
    else:
        return False
# 端口是否被占用
def is_port_bind(port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex(('127.0.0.1', port))
    if result == 0:
        return True
    else:
        return False
if __name__ == "__main__":
    pass