admin
2024-05-07 582b3f0e67e5bf2f5806b70600faaa89f44215a6
可转债
15个文件已修改
3个文件已添加
9108 ■■■■■ 已修改文件
cb.py 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
convertible_bonds/main.py 573 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
gui_wx.py 331 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
juejin_core.py 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/codes_list.html 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/css/banshuping.css 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/css/index23-05-04.css 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/index23-05-04.html 215 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/code_list.js 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/http.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/md5.min.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/page.js 76 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/test.html 7605 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
network_delegate_manager.py 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/codes.txt 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/setting.conf 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/tool.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cb.py
New file
@@ -0,0 +1,7 @@
"""
可转债分析
"""
from convertible_bonds import main
if __name__ == '__main__':
    main.run()
convertible_bonds/main.py
New file
@@ -0,0 +1,573 @@
import logging
import queue
import time
import wx
import matplotlib.pyplot as plt
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import juejin_core
from juejin_core import JueJinApi
from utils import tool
APP_TITLE = "可转债分析"
class JueJinUtil:
    @classmethod
    def get_all_convertible_bonds_codes(cls):
        """
        获取所有的可转债代码
        :return: 所有代码合集[((可转债代码,可转债名称),股票代码)]
        """
        results = JueJinApi.get_codes(8)
        count = 0
        symbols = []
        for result in results:
            if int(tool.get_now_date_str("%Y%m%d")) < int(result['delisted_date'].strftime("%Y%m%d")):
                count += 1
                symbols.append(((result['symbol'], result['sec_name']), result['underlying_symbol']))
        return symbols
# 绘图管理器
class DrawManager:
    X_RANGE_MINIUTES = 335
    X_DATA_MINIUTES = 335
    h_lines_dict = {}
    cost_mark_dict = {}
    last_max_rate_dict = {}
    cost_rate_dict = {}
    sell_points_dict = {}
    @classmethod
    def get_x_time_as_seconds(cls, created_at_str):
        time_ = created_at_str[-8:]
        if tool.get_time_as_second("13:00:00") > tool.get_time_as_second(time_) > tool.get_time_as_second(
                "11:30:00"):
            time_ = "11:30:00"
        time_s = int(time_.split(":")[0]) * 3600 + int(time_.split(":")[1]) * 60 + int(
            time_.split(":")[2]) - 9 * 3600 - 60 * 30
        if int(time_.replace(":", "")) > int("11:30:00".replace(":", "")):
            time_s -= 90 * 60
        return time_s
    @classmethod
    def set_cost_rate(cls, code, rate):
        cls.cost_rate_dict[code] = rate
    @classmethod
    def __format_max_rate(cls, max_rate):
        PERCENT_RATE = 1
        line_count = 0
        if max_rate % PERCENT_RATE < 0.0001:
            line_count = int(round(max_rate // PERCENT_RATE))
        else:
            line_count = int(round(max_rate // PERCENT_RATE)) + 1
        rate = line_count * PERCENT_RATE
        # 还未接近涨停多增加PERCENT_RATE
        if rate + PERCENT_RATE < 9.8:
            rate += PERCENT_RATE
        return rate, PERCENT_RATE
    @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, max_rate)
        axes.set_ylim(0 - max_rate, max_rate)
        axes2.set_yticks(yticks2)
        axes2.set_yticklabels(yticks2_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, pre_price, y_max_rate=10.5, cost_rate=None):
        def on_pick(event):
            ind = event.ind[0]
            offset = event.artist.get_offsets()[ind]
            x = offset[0]
            y = offset[1]
            TickCompareFrame.set_mouse_data(x, y, code, axes[4])
            print(f"Clicked on point ({x}, {y})")
        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:]
            if int(time_.replace(":", "")) >= int("092600"):
                datas = datas[i:]
                break
        # 取最近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_) >= 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(self.get_x_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:], self.X_RANGE_MINIUTES * 60)
        # axes[0].set_xlim(self.get_x_time_as_seconds(datas[0]["created_at"]), self.get_x_time_as_seconds(end_x))
        xms = axes[0].get_xlim()
        yms = axes[0].get_ylim()
        # 暂时不设置X坐标
        # step = (int(xms[1]) - int(xms[0])) // 30
        # for i in range(int(xms[0]), int(xms[1] + 1), step):
        #     xticks.append(i)
        #     xticklabels.append("")
        # axes[0].set_xticks(xticks)
        # axes[0].set_xticklabels(xticklabels)
        axes[1][0].set_data(xs, ys_rate)
        point_x = xs[0] + 1
        point_y = ys_rate[0]
        if code not in self.sell_points_dict:
            self.sell_points_dict[code] = queue.Queue()
        if code in self.sell_points_dict:
            if not self.sell_points_dict[code].empty():
                item = self.sell_points_dict[code].get_nowait()
                if item:
                    points = axes[0].scatter(item[0], item[1], s=15, c='green', zorder=2, picker=5)
                    # 设置点击事件
                    axes[0].figure.canvas.mpl_connect('pick_event', on_pick)
        # 不需要一分钟均线
        # 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("{}% ".format(round(datas[-1]["rate"] * 100, 2)))
        if datas[-1]["rate"] > 0:
            texts[0].set_color('red')
        elif datas[-1]["rate"] == 0:
            texts[0].set_color('white')
        else:
            texts[0].set_color('green')
        # texts[1].set_text("{}".format(datas[-1]["created_at"].split(" ")[1]))
        texts[1].set_text("")
        texts[0].set_x(xms[1] - 130)
        texts[0].set_y(yms[1] + yms[1] / 20)
        texts[1].set_x(xms[0])
        texts[1].set_y(yms[0] - yms[1] / 9)
        # 删除之前
        if code in self.lines:
            for key in self.lines[code]:
                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}
        axes[0].figure.canvas.draw()
        axes[0].figure.canvas.flush_events()
    def __init__(self, axes_list, codes_info):
        self.axes_list = axes_list
        self.codes_info = codes_info
        self.lines = {}
    def update(self, code, pre_price, datas):
        __start_time = time.time()
        # 获取当前的坐标范围
        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:]
        # 修正量数据
        last_info = None
        for i in range(1, len(datas)):
            # 如果在
            if not last_info:
                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_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:
                try:
                    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)
class TickCompareFrame(wx.Frame):
    # 可转债代码数据
    __cb_datas = []
    mark_lines = {}
    @classmethod
    def show_warning(cls, content, click=None):
        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()
    @classmethod
    def show_info(cls, content, click):
        toastone = wx.MessageDialog(None, content, "提示", wx.YES_DEFAULT | wx.ICON_INFORMATION)
        if toastone.ShowModal() == wx.ID_YES:  # 如果点击了提示框的确定按钮
            click()
            toastone.Destroy()
    def __init__(self):
        super().__init__(None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE,
                         size=(800, 1000))
        self.SetBackgroundColour(wx.Colour(0, 0, 0))
        self.Center()
        # 拉取数据线程
        self.cost_price_threads = {}
        # 定义窗口关闭
        # self.Bind(wx.EVT_SIZE, self.OnResize)
        self.panels = []
        self.col = 1
        self.ratio = 0.55
        self.panel = wx.Panel(self, size=(-1, 50))
        self.panel.SetBackgroundColour(wx.Colour(255, 255, 255))
        bs1 = wx.BoxSizer(wx.HORIZONTAL)
        self.edit_code1 = wx.TextCtrl(self.panel, size=(100, 25))
        self.btn_load_data = wx.Button(self.panel, label="加载数据", size=(80, 25))
        bs1.Add(wx.StaticText(self.panel, label="股票代码:"), proportion=0, flag=wx.ALL, border=12)
        bs1.Add(self.edit_code1, proportion=0, flag=wx.ALL, border=10)
        bs1.Add(self.btn_load_data, proportion=0, flag=wx.ALL, border=10)
        self.panel.SetSizer(bs1)
        # 初始化
        #  self.scroll = wx.ScrolledWindow(self, -1, size=(800, 1000))
        root_boxsier = wx.BoxSizer(wx.VERTICAL)
        root_boxsier.Add(self.panel, proportion=0, flag=wx.EXPAND)
        self.panel1 = wx.Panel(self, size=(-1, 400))
        self.panel2 = wx.Panel(self, size=(-1, 400))
        self.panel1.SetBackgroundColour(wx.Colour(255, 0, 0))
        self.panel2.SetBackgroundColour(wx.Colour(0, 255, 0))
        root_boxsier.Add(self.panel1, proportion=0, flag=wx.EXPAND)
        root_boxsier.Add(self.panel2, proportion=0, flag=wx.EXPAND)
        self.SetSizer(root_boxsier)
        self.Layout()
        self.btn_load_data.Bind(wx.EVT_BUTTON, lambda e: self.__load_data(e))
    def __load_data(self, event):
        code = self.edit_code1.GetValue()
        # 获取可转债
        if not self.__cb_datas:
            self.__cb_datas = JueJinUtil.get_all_convertible_bonds_codes()
        cb_codes = None
        for b in self.__cb_datas:
            if b[1].find(code) >= 0:
                cb_codes = b
                break
        if not cb_codes:
            self.show_warning("无可转债")
            return
        # 获取基础信息
        base_infos = JueJinApi.get_gp_latest_info([cb_codes[0], cb_codes[1]], fields="symbol,sec_id,sec_name,pre_close")
        fdatas = []
        for b in base_infos:
            # 加载数据,获取tick
            start_time, end_time = f"{tool.get_now_date_str()} 09:30:00", f"{tool.get_now_date_str()} {tool.get_now_time_str()}"
            results = JueJinApi.get_history_tick(b['symbol'], start_time, end_time, frequency="tick")
            datas = []
            for data in results:
                data['created_at'] = data["created_at"].strftime("%Y-%m-%d %H:%M:%S")
                datas.append(juejin_core.parse_tick(data))
            # (代码,代码名称,收盘价)
            fdatas.append((b['sec_id'], b['sec_name'], b['pre_close'], datas))
        panels = [self.panel1, self.panel2]
        axes_list = []
        codes_name = []
        for i in range(len(fdatas)):
            d = fdatas[i]
            axes = self.__create_canvas(panels[i], d[0], d[1], d[2], d[3])
            axes_list.append(axes)
            codes_name.append(d[1])
        drawManager = DrawManager(axes_list, codes_name)
        for d in fdatas:
            drawManager.update(d[0], d[2], d[3])
    @classmethod
    def set_mouse_data(cls, xdata, ydata, code, axes2):
        try:
            if code not in cls.mark_lines:
                cls.mark_lines[code] = {}
            if "mouse" not in cls.mark_lines[code]:
                line_h = axes2.axhline(ydata, linestyle='-', color='white', lw=0.5, zorder=3)
                line_v = axes2.axvline(xdata, linestyle='-', color='white', lw=0.5, zorder=3)
                text_h = axes2.text(axes2.get_xlim()[1], ydata, r'', fontsize=10, color='white',
                                    verticalalignment="center", bbox=dict(facecolor='white', alpha=0.9), zorder=3)
                text_v = axes2.text(xdata, axes2.get_ylim()[0], r'', fontsize=10, color='red',
                                    verticalalignment='top', horizontalalignment='center',
                                    bbox=dict(facecolor='white', alpha=0.9), zorder=3)
                cls.mark_lines[code]["mouse"] = (line_h, line_v, text_h, text_v)
            line = cls.mark_lines.get(code).get("mouse")
            x = round(xdata)
            y = round(ydata, 2)
            line[0].set_ydata(ydata)
            line[1].set_xdata(xdata)
            line[2].set_text(f"{y}%")
            line[3].set_text(f"{tool.trade_time_add_second('09:30:00', x)}")
            line[2].set_x(axes2.get_xlim()[1] * (1 + 0.005))
            line[2].set_y(y)
            line[3].set_x(x)
            line[3].set_y(axes2.get_ylim()[0] * (1 + 0.02))
            if y >= 0:
                line[2].set_color('red')
            else:
                line[2].set_color('green')
            axes2.figure.canvas.draw()
        except Exception as e:
            pass
    def __create_canvas(self, pannel, code, title, price, datas, close_callback=None):
        def show_mouse_line(event):
            # 删除之前的线
            self.set_mouse_data(event.xdata, event.ydata, code, axes)
        def clear_mouse_line(event):
            print("clear_mouse_line")
            if code in self.mark_lines:
                if self.mark_lines.get(code):
                    line = self.mark_lines.get(code).get("mouse")
                    if line is not None:
                        for l in line:
                            l.remove()
                        self.mark_lines.get(code).pop("mouse")
                        axes.figure.canvas.draw()
        def close_canvas(event):
            print("关闭", title)
            close_callback(title)
        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)
        figure_score.subplots_adjust(left=0.01, bottom=0.15, top=0.92,
                                     right=right)
        # 设置字体颜色
        plt.rcParams["text.color"] = "red"
        plt.rcParams["axes.labelcolor"] = "red"
        # 设置坐标轴数字颜色
        plt.rcParams["xtick.color"] = "white"
        plt.rcParams["ytick.color"] = "white"
        # 设置坐标轴颜色
        plt.rcParams["axes.edgecolor"] = "firebrick"
        # 解决中文乱码问题
        plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置字体
        plt.rcParams["font.serif"] = ["SimHei"]
        plt.rcParams["axes.unicode_minus"] = False  # 该语句解决图像中的“-”负号的乱码问题
        axes = figure_score.add_subplot(1, 1, 1)
        axes.autoscale(True)
        # axes_score.plot(t_score, s_score, 'ro', t_score, s_score, 'k')
        axes.set_title(title)
        axes.grid(False)
        axes.set_xlabel(f'时间({title})')
        axes.dist = 0
        # axes.set_ylabel(u'价格')
        # 获取平开价
        extra = 0  # (tool.get_limit_up_price(price)-decimal.Decimal(price))*decimal.Decimal(0.02)
        axes.patch.set_facecolor('black')
        figure_score.patch.set_facecolor('black')
        # 设置横坐标9:25-15:00
        xs_str = ["09:25", "09:30", "10:30", "11:30", "14:00", "15:00"]
        xs_int = [DrawManager.get_x_time_as_seconds(f"0000-00-00 {x}:00") for x in xs_str]
        axes.set_xticks(xs_int[1:])
        axes.set_xticklabels(xs_str[1:])
        axes.set_xlim([xs_int[0], xs_int[-1]])
        axes2 = axes.twinx()
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        axes2.grid(color='firebrick', ls='-', lw=0.5)
        # 鼠标在画布移动
        # axes2.figure.canvas.mpl_connect('motion_notify_event', show_mouse_line)
        # # 鼠标离开画布
        # axes2.figure.canvas.mpl_connect('axes_leave_event', clear_mouse_line)
        # 设置纵坐标轴
        limit_up_price = float(tool.get_limit_up_price(price))
        max_rate = round((limit_up_price - price) / price, 4) * 100
        print("涨停最大比例", max_rate)
        # 设置纵坐标信息
        # max_rate = 2
        DrawManager.set_y_info(code, max_rate, axes, axes2, price)
        line = axes2.plot([], [], color='white', linewidth=1)
        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'', fontsize=15, color='red')
        axes2.text(-1, -11.5, r'', fontsize=15, color='red')
        axes2.spines['top'].set_visible(False)
        axes.spines['top'].set_visible(False)
        axes2.spines['bottom'].set_visible(True)
        axes.spines['bottom'].set_visible(False)
        # 中轴线加粗
        hline = axes2.axhline(0, linestyle='-', color='firebrick', lw=2, zorder=1)
        # 设置坐标轴标记点为黑色
        axes.tick_params(axis='x', colors='firebrick')
        axes.tick_params(axis='y', colors='black')
        axes2.tick_params(axis='x', colors='firebrick')
        axes2.tick_params(axis='y', colors='black')
        def update_data(i):
            print("更新数据:", i)
        return axes2, line, average_line_1m, average_line, axes
    def OnResize(self, e):
        print("变化后的尺寸", e.Size)
class mainApp(wx.App):
    def __show_main_frame(self):
        self.frame.Show()
    def __init_data(self):
        try:
            pass
        except:
            pass
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.__init_data()
        self.frame = TickCompareFrame()
        self.__show_main_frame()
        return True
def run():
    JueJinUtil.get_all_convertible_bonds_codes()
    app = mainApp()
    app.MainLoop()
gui_wx.py
@@ -1486,6 +1486,335 @@
        return t1
class TickCompareFrame(wx.Frame):
    mark_lines = {}
    def __init__(self):
        '''构造函数'''
        wx.Frame.__init__(self, None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE,
                          size=(800, 1000))
        self.SetBackgroundColour(wx.Colour(0, 0, 0))
        self.Center()
        # 拉取数据线程
        self.cost_price_threads = {}
        # 以下代码处理图标
        # if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
        #     exeName = win32api.GetModuleFileName(win32api.GetModuleHandle(None))
        #     icon = wx.Icon(exeName, wx.BITMAP_TYPE_ICO)
        # else:
        #     icon = wx.Icon(APP_ICON, wx.BITMAP_TYPE_ICO)
        # self.SetIcon(icon)
        # 定义窗口关闭
        self.Bind(wx.EVT_CLOSE, self.OnExit)
        self.Bind(wx.EVT_SIZE, self.OnResize)
        self.panels = []
        self.scroll = None
        self.col = 1
        self.ratio = 0.55
        self.__re_draw()
        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 = wx.ScrolledWindow(self, -1, size=(800, 1000))
        self.boxsier = wx.BoxSizer(wx.VERTICAL)
        boxSizer1 = wx.BoxSizer(wx.HORIZONTAL)
        self.edit_code1 = wx.TextCtrl(self, size=(100, 30))
        self.edit_code2 = wx.TextCtrl(self, size=(100, 30))
        self.btn_load_data = wx.Button(self, label="加载数据", size=(100, 30))
        boxSizer1.Add(self.edit_code1)
        boxSizer1.Add(self.edit_code2)
        boxSizer1.Add(self.btn_load_data, 1, wx.LEFT|wx.TOP, 2)
        self.boxsier.Add(boxSizer1)
        # self.scroll.EnableScrolling(False, True)
        # self.boxsier.Add(wx.FlexGridSizer(rows, self.col, space, space))
        # self.scroll.SetSizer(self.boxsier)
        # self.scroll.Layout()
        # self.boxsier.Fit(self.scroll)
        # boxsier.Add(mainBoxsier, 1, wx.EXPAND | wx.ALL, 5)
        # self.SetSizer(boxsier)
        # mainBoxsier.Fit(self)
        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
    def __re_draw(self):
        return
        codes_name = juejin_core.GPCodeManager().get_codes_with_names()
        rows = len(codes_name)
        if rows % self.col == 0:
            rows = rows / self.col
        else:
            rows = rows / self.col + 1
        space = 0
        if not self.scroll:
            return
        if self.panels:
            for p in self.panels:
                p.Destroy()
        self.boxsier.Clear()
        pannel_height = round((self.Size[0] - (self.col - 1) * space) / self.col * self.ratio)
        self.scroll.SetScrollbars(1, 1, 0, pannel_height * rows)
        self.scroll.SetScrollRate(0, pannel_height)
        global drawManager
        axes_list = []
        self.panels = []
        for i in range(0, len(codes_name)):
            # pos=(0, i * pannel_height)
            pannel = wx.Panel(self.scroll, size=(-1, pannel_height))
            pannel.BackgroundColour = wx.Colour(0, 0, 0)
            self.panels.append(pannel)
            self.boxsier.Add(pannel)
            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()
        # self.boxsier.Fit(self.scroll)
        #
        # 初始化数据
        drawManager = DrawManager(axes_list, codes_name)
        t1 = threading.Thread(target=lambda: drawManager.init_code_datas())
        # 后台运行
        t1.setDaemon(True)
        t1.start()
    @classmethod
    def set_mouse_data(cls, xdata, ydata, code, axes2):
        try:
            if code not in cls.mark_lines:
                cls.mark_lines[code] = {}
            if "mouse" not in cls.mark_lines[code]:
                line_h = axes2.axhline(ydata, linestyle='-', color='white', lw=0.5, zorder=3)
                line_v = axes2.axvline(xdata, linestyle='-', color='white', lw=0.5, zorder=3)
                text_h = axes2.text(axes2.get_xlim()[1], ydata, r'', fontsize=10, color='white',
                                    verticalalignment="center", bbox=dict(facecolor='white', alpha=0.9), zorder=3)
                text_v = axes2.text(xdata, axes2.get_ylim()[0], r'', fontsize=10, color='red',
                                    verticalalignment='top', horizontalalignment='center',
                                    bbox=dict(facecolor='white', alpha=0.9), zorder=3)
                cls.mark_lines[code]["mouse"] = (line_h, line_v, text_h, text_v)
            line = cls.mark_lines.get(code).get("mouse")
            x = round(xdata)
            y = round(ydata, 2)
            line[0].set_ydata(ydata)
            line[1].set_xdata(xdata)
            line[2].set_text(f"{y}%")
            line[3].set_text(f"{tool.trade_time_add_second('09:30:00', x)}")
            line[2].set_x(axes2.get_xlim()[1] * (1 + 0.005))
            line[2].set_y(y)
            line[3].set_x(x)
            line[3].set_y(axes2.get_ylim()[0] * (1 + 0.02))
            if y >= 0:
                line[2].set_color('red')
            else:
                line[2].set_color('green')
            axes2.figure.canvas.draw()
        except Exception as e:
            pass
    def __create_canvas(self, pannel, code, title, name, price, close_callback=None):
        TickDataProcess.clear(code)
        def show_mouse_line(event):
            # 删除之前的线
            self.set_mouse_data(event.xdata, event.ydata, code, axes)
        def clear_mouse_line(event):
            print("clear_mouse_line")
            if code in self.mark_lines:
                if self.mark_lines.get(code):
                    line = self.mark_lines.get(code).get("mouse")
                    if line is not None:
                        for l in line:
                            l.remove()
                        self.mark_lines.get(code).pop("mouse")
                        axes.figure.canvas.draw()
        def close_canvas(event):
            print("关闭", title)
            close_callback(title)
        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)
        figure_score.subplots_adjust(left=0.01, bottom=0.15, top=0.92,
                                     right=right)
        # 设置字体颜色
        plt.rcParams["text.color"] = "red"
        plt.rcParams["axes.labelcolor"] = "red"
        # 设置坐标轴数字颜色
        plt.rcParams["xtick.color"] = "white"
        plt.rcParams["ytick.color"] = "white"
        # 设置坐标轴颜色
        plt.rcParams["axes.edgecolor"] = "firebrick"
        # 解决中文乱码问题
        plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置字体
        plt.rcParams["font.serif"] = ["SimHei"]
        plt.rcParams["axes.unicode_minus"] = False  # 该语句解决图像中的“-”负号的乱码问题
        # 测试
        buttonaxe = plt.axes([0.94, 0.5, 0.1, 0.1])
        button1 = Button(buttonaxe, '关闭', color='white', hovercolor='yellow')
        axes = figure_score.add_subplot(1, 1, 1)
        axes.autoscale(True)
        # axes_score.plot(t_score, s_score, 'ro', t_score, s_score, 'k')
        axes.set_title(title)
        axes.grid(False)
        axes.set_xlabel(f'时间({name})')
        axes.dist = 0
        # axes.set_ylabel(u'价格')
        # 获取平开价
        extra = 0  # (tool.get_limit_up_price(price)-decimal.Decimal(price))*decimal.Decimal(0.02)
        axes.patch.set_facecolor('black')
        figure_score.patch.set_facecolor('black')
        # 设置横坐标9:25-15:00
        xs_str = ["09:25", "09:30", "10:30", "11:30", "14:00", "15:00"]
        xs_int = [DrawManager.get_x_time_as_seconds(f"0000-00-00 {x}:00") for x in xs_str]
        axes.set_xticks(xs_int[1:])
        axes.set_xticklabels(xs_str[1:])
        axes.set_xlim([xs_int[0], xs_int[-1]])
        axes2 = axes.twinx()
        # axes2.grid(color='firebrick', ls='-', lw=0.5)
        axes2.grid(color='firebrick', ls='-', lw=0.5)
        # 鼠标在画布移动
        # axes2.figure.canvas.mpl_connect('motion_notify_event', show_mouse_line)
        # # 鼠标离开画布
        # axes2.figure.canvas.mpl_connect('axes_leave_event', clear_mouse_line)
        # 设置纵坐标轴
        limit_up_price = float(tool.get_limit_up_price(price))
        max_rate = round((limit_up_price - price) / price, 4) * 100
        print("涨停最大比例", max_rate)
        # 设置纵坐标信息
        # max_rate = 2
        DrawManager.set_y_info(code, max_rate, axes, axes2, price)
        line = axes2.plot([], [], color='white', linewidth=1)
        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'', fontsize=15, color='red')
        axes2.text(-1, -11.5, r'', fontsize=15, color='red')
        axes2.spines['top'].set_visible(False)
        axes.spines['top'].set_visible(False)
        axes2.spines['bottom'].set_visible(True)
        axes.spines['bottom'].set_visible(False)
        # 中轴线加粗
        hline = axes2.axhline(0, linestyle='-', color='firebrick', lw=2, zorder=1)
        # 设置坐标轴标记点为黑色
        axes.tick_params(axis='x', colors='firebrick')
        axes.tick_params(axis='y', colors='black')
        axes2.tick_params(axis='x', colors='firebrick')
        axes2.tick_params(axis='y', colors='black')
        def update_data(i):
            print("更新数据:", i)
        return axes2, line, average_line_1m, average_line, axes
    def __show_top(self, event):
        if event.Selection:
            setting.set_stay_on_top(1)
            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
        else:
            setting.set_stay_on_top(0)
            self.WindowStyle = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN
    def OnExit(self, e):
        try:
            setting.set_tick_window_info((self.Position[0], self.Position[1], self.Size[0], self.Size[1]))
        except Exception as e:
            print("")
        self.Hide()
    def post_redraw(self, evt):
        if abs(self.last_size[0] - self.Size[0]) > 20:
            print("--------post_redraw--------")
            self.last_size = (self.Size[0], self.Size[1])
            self.__re_draw()
    def OnResize(self, e):
        print("变化后的尺寸", e.Size)
        # 留出滚动条,留出上边距
        if self.scroll:
            self.scroll.Size = (e.Size[0] - 15, e.Size[1] - 60)
            for p in self.panels:
                p_height = round(e.Size[0] * (450 / 800))
                p.Size = (e.Size[0], p_height)
        self.timer.Stop()
        self.timer.StartOnce(1000)
        # 降低重绘频率
        # self.__re_draw()
    def set_codes_success(self):
        print("设置代码成功回调")
        p2.send("resub")
        self.__re_draw()
        self.timer.Stop()
        self.timer.StartOnce(1000)
        self.Size = wx.Size(self.Size[0], self.Size[1] + 10)
        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 = 335
@@ -1894,7 +2223,7 @@
    def OnInit(self):
        self.SetAppName(APP_TITLE)
        self.__init_data()
        self.frame = TickFrame()
        self.frame = TickCompareFrame()
        # self.floatFrame = FloatFrame()
        global_datas["tickFrame"] = self.frame
        # global_datas["floatFrame"] = self.floatFrame
juejin_core.py
@@ -1,4 +1,5 @@
# 策略中必须有init方法
import datetime
import json
import threading
import time
@@ -279,7 +280,9 @@
    def get_juejin_code_list_with_prefix(cls, codes):
        list = []
        for d in codes:
            if d[0:2] == '00':
            if d.find(".") > 0:
                list.append(d)
            elif d[0:2] == '00':
                list.append("SZSE.{}".format(d))
            elif d[0:2] == '60':
                list.append("SHSE.{}".format(d))
@@ -297,13 +300,24 @@
        return data
    @classmethod
    def get_history_tick_n(cls, code, count, fields=None):
    def get_history_tick_n(cls, code, count, fields=None, frequency="1d"):
        symbols = cls.get_juejin_code_list_with_prefix([code])
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        # 前除权
        results = gmapi.history_n(symbol=symbols[0], frequency="1d", count=count, adjust=1, fields=fields)
        results = gmapi.history_n(symbol=symbols[0], frequency=frequency, count=count, adjust=1, fields=fields)
        return results
    @classmethod
    def get_history_tick(cls, code, start_time, end_time, fields=None, frequency="1d"):
        symbols = cls.get_juejin_code_list_with_prefix([code])
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        # 前除权
        results = gmapi.history(symbol=symbols[0], frequency=frequency, start_time=start_time, end_time=end_time,
                                adjust=1, fields=fields)
        return results
    @classmethod
@@ -353,3 +367,70 @@
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        return gmapi.get_trading_dates("SHSE", start_date, end_date)
    @classmethod
    def get_codes(cls, sec_type):
        """
        代码获取
        :param sec_type: 8:可转债 1:股票
        :return:
        """
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        return gmapi.get_instrumentinfos(exchanges="SHSE,SZSE", sec_types=[sec_type])
    @classmethod
    def get_underlying_code(cls, symbols):
        """
        获取可转债正股代码
        :param symbols: 如:[SZSE.128144]
        :return: 正股代码,如:[SZSE.002734]
        """
        account_id, s_id, token = JueJinApi.getJueJinAccountInfo()
        gmapi.set_token(token)
        datas = gmapi.get_instruments(symbols=",".join(symbols), sec_types="8",
                                      fields="symbol, sec_type, sec_id,sec_name, underlying_symbol")
        fdatas = {}
        for d in datas:
            fdatas[d['symbol']] = d['underlying_symbol']
        return fdatas
if __name__ == "__main__":
    # print(JueJinApi.get_gp_latest_info(["SZSE.128144"], "sec_id, underlying_symbol"))
    # account_id, s_id, token = JueJinApi.getJueJinAccountInfo()
    # gmapi.set_token(token)
    # data = gmapi.get_instruments(symbols=",".join(["SZSE.128144"]), sec_types="8", fields="symbol, sec_type, sec_id,sec_name, underlying_symbol")
    # print(data)
    # 获取所有的可转债
    # results = JueJinApi.get_codes(8)
    # count = 0
    # with open("datas/convertible_bonds_codes.txt", mode='w',encoding='utf-8') as f:
    #     symbols = []
    #     for result in results:
    #         if int(tool.get_now_date_str("%Y%m%d")) < int(result['delisted_date'].strftime("%Y%m%d")):
    #             count += 1
    #             symbols.append(result['symbol'])
    #     fdata = JueJinApi.get_underlying_code(symbols)
    #     for k in fdata:
    #         item = (k, fdata[k])
    #         print(item)
    #         f.write(f"{item}\n")
    # print(count)
    # 加载涨停代码
    with open("D:\\文件传输\\交易\\日志文件\\2024-04-26_limit_up.log", mode='r', encoding='utf-8') as f:
        data = f.readline()
        datas = eval(data)
        limit_up_codes = "000421,000559,000560,000608,000657,000698,000705,000799,000949,000952,000980,001301,001366,001376,002037,002305,002331,002339,002367,002455,002616,002633,002748,002759,002790,002795,002827,002862,002895,002910,002946,002968,002972,300175,300407,300758,300778,300829,300905,301209,301226,301301,301393,600211,600223,600230,600326,600580,600686,600727,600743,600763,600789,600805,600844,600865,600866,600984,601233,601333,603079,603127,603219,603225,603231,603311,603580,603659,603711,603739,603778,603823,603928,603968,605055,605169,605298"
    with open("datas/convertible_bonds_codes.txt", mode='r', encoding='utf-8') as f:
        lines = f.readlines()
        for line in lines:
            if line:
                data = eval(line)
                code = data[1][-6:]
                if code in limit_up_codes:
                    print(data[0], data[1])
kp_html/kp/codes_list.html
@@ -540,8 +540,17 @@
                </div>
            </div>
            <div id="l2_screen">
                <div> <span>量:</span> <input id="min-volume" /> - <input id="max-volume" /> </div>
                <table style="width: auto;">
                    <tr>
                        <td>量:</td>
                        <td> <input id="min-volume" /> - <input id="max-volume" /></td>
                    </tr>
                    <tr>
                        <td>索引:</td>
                        <td><textarea id="watch_indexes" style="border: #BBB solid 1px;" placeholder="索引用逗号分隔"></textarea></td>
                    </tr>
                </table>
                <div> <button class="btn" @click="l2_screen_click">确定</button> </div>
kp_html/kp/css/banshuping.css
@@ -592,8 +592,23 @@
#l2_screen input{
    border: solid 1px #BBB;
    border-radius: 4px;
    border-radius: 2px;
    width: 50px;
    padding: 2px 5px;
}
#l2_screen table{
    width: auto;
}
#l2_screen textarea{
    padding: 2px 5px;
    width: 300px;
    height: 50px;
    border-radius: 2px;
}
#l2_screen  .btn{
    margin-top: 10px;
    margin-left: 315px;
}
kp_html/kp/css/index23-05-04.css
@@ -724,4 +724,58 @@
#date{
    width: 100px;
    font-size: 14px;
}
.cb_list_top{
    display: flex;
    overflow-wrap: break-word;
    word-wrap: break-word;
    white-space: normal;
    width: 97.4%;
    flex-wrap: wrap;
    padding: 10px;
    background-color: black;
    color: white;
}
.cb_list_top > div{
    display: block;
    width: 180px;
    margin-bottom: 5px;
    cursor: pointer;
}
.cb_list_top button{
    border-radius: 5px;
    font-size: 12px;
    padding: 1px 4px;
    cursor: pointer;
}
.cb_list{
    margin: 10px 10px;
    width: 100%;
}
.cb_list table{
    border: solid 1px #EEE;
}
.cb_list table tr{
}
.cb_list table tr td{
    border: none;
    padding: 2px;
}
.cb_list button{
    border-radius: 5px;
    font-size: 12px;
    padding: 1px 4px;
    cursor: pointer;
}
kp_html/kp/index23-05-04.html
@@ -14,6 +14,7 @@
        <script src="js/vconsole.min.js"></script>
        <script src="js/chart.js"></script>
        <script src="js/chartjs-plugin-datalabels.min.js"></script>
        <script src="js/md5.min.js"></script>
        <style>
        </style>
@@ -126,9 +127,10 @@
                            <tr>
                                <td><span class="bold">板块</span></td>
                                <td class="budinggundong">
                                    <div class="scroll-y" style="height: 93px;;">
                                    <div class="scroll-y" style="height: 93px;">
                                        <span v-for="(item,i) in kpl_code_info.plate" class="label-style"
                                            :class="{'red':item[4]}">{{item[1]}}</span>
                                            :class="{'red':item[4]!=null}">{{item[1]}}<span
                                                v-if="item[4]!=null">({{item[4][1]}}&{{item[4][2]}})</span></span>
                                    </div>
                                </td>
                            </tr>
@@ -319,191 +321,25 @@
                <!-- 右侧 -->
                <div style="float: right; width:72%;" v-if="choose_code_info">
                    <!-- 右侧固定位 -->
                    <div>
                        <table
                            style="float: right; width:100%; background: #EEE;height: 130px; max-height: 130px;min-height: 130px; overflow: hidden;">
                            <thead></thead>
                            <tbody>
                                <tr style="display: none;">
                                    <td style="width: 10%;"><span class="bold">强度</span></td>
                                    <td colspan="3">
                                        <div class="scroll-x" style="width: 670px;">
                                            <div style="display: inline-block; margin-right: 16px;"
                                                v-for="(item,index) in choose_code_info.plat_strength"><span
                                                    class="num-style">{{item[1]}}</span><br><span
                                                    class="num-style">{{item[2]}}</span>
                                            </div>
                                        </div>
                                    </td>
                                </tr>
                                <tr>
                                    <td><span class="bold">今天</span></td>
                                    <td><span v-if="choose_code_info.today">
                                            <span class="red">【{{choose_code_info.today[0]}}】</span>|&nbsp;<span
                                                class="num-style">{{choose_code_info.today[2]}}</span>
                                            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;二级:
                                            <span class="num-style">
                                                {{choose_code_info.industry}}
                                            </span>
                                    </td>
                                </tr>
                                <tr>
                                    <td><span class="bold">历史</span></td>
                                    <td>
                                        <span v-for="(item1,i1) in choose_code_info.code_records">
                                            <span class="num-style red">【{{item1[0]}}】</span>|&nbsp;<span
                                                class="num-style">{{item1[2]}}</span><span
                                                class="num-style">({{item1[1]}})</span>
                                            &nbsp;&nbsp;&nbsp;&nbsp;
                                        </span>
                                    </td>
                                </tr>
                                <tr>
                                    <td><span class="bold">板块</span></td>
                                    <td>
                                        <div class="scroll-x" style="width: 670px;">
                                            <div v-for="(item,i) in choose_code_info.plate" class="item">
                                                <span class="num-style line">{{item[1]}}</span>
                                                <span class="num-style line">{{item[2]}}%</span>
                                            </div>
                                        </div>
                                    </td>
                                </tr>
                            </tbody>
                            <tfoot></tfoot>
                        </table>
                        <div style="float: right; width:100%;background: #cecece;height: 10px;"></div>
                    </div>
                    <!-- 右侧滚动表格 -->
                    <div class="outer-container scroll-y" style="float: right;">
                        <div class="daixuan" v-for="(item,i) in choose_code_info.code_list_info"
                            @click="select_code_level_2($event,i)">
                            <!-- 一条信息 -->
                            <table :class="{'active':choose_code.second==item.code_info[0]}">
                                <thead></thead>
                                <tbody>
                                    <div>
                                        <tr>
                                            <td style="width: 10%;"><span class="bold"
                                                    :class="{'purple':item.code_info[11]>8}">{{item.code_info[11]}}%</span>
                                            </td>
                                            <td colspan="3">
                                                <div>
                                                    <span v-if="item.code_info[2]==1"><img
                                                            src="./images/limit_up.png"></span>
                                                    <span v-if="item.code_info[2]==2"><img
                                                            src="./images/stop_up.png"></span>
                                                    <span class="num-style bold"
                                                        :class="{'green': item.code_info[0].indexOf('00')!=0&&item.code_info[0].indexOf('60')!=0 }">{{item.code_info[1]}}</span><span
                                                        class="num-card-red"
                                                        v-if="item.code_info[3]">{{item.code_info[3]}}</span><span
                                                        class="num-card-bule"
                                                        v-if="item.code_info[4]">{{item.code_info[4]}}</span>
                                                    <span v-if="item.code_info[5]"
                                                        class="score-card-color yellow">{{item.code_info[5]}}</span><span
                                                        class="num-card-black"
                                                        v-if="item.code_info[13]">{{item.code_info[13]}}</span>
                                                    <span class="num-card-pink"
                                                        v-if="item.code_info[14]">{{item.code_info[14]}}</span>
                                                </div>
                                                <img src="./images/delete.png" alt=""
                                                    style="float: right;margin-right: -2px;"
                                                    @click="del_code_level_2(i)">
                                                <div><span
                                                        v-if="item.code_info[6]&& item.code_info[6]!='00:00:00'">涨停时间:<span
                                                            class="num-style red">{{item.code_info[6]}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span>代码:<span
                                                        class="num-style">{{item.code_info[0]}}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;现价:<span
                                                        class="num-style red">{{item.code_info[12]}}元</span>&nbsp;&nbsp;&nbsp;&nbsp;自由市值:<span
                                                        class="num-style red">{{item.code_info[9]}}</span></div>
                                                <div>主力净值:<span
                                                        class="num-style">{{item.code_info[15]}}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;300W:<span
                                                        class="num-style">{{item.code_info[16]}}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第一季度机构增仓:<span
                                                        class="num-style">{{item.code_info[17]}}</span></div>
                                            </td>
                                        </tr>
                                        <tr
                                            v-if="item.code_info[0].indexOf('00')==0||item.code_info[0].indexOf('60')==0">
                                            <td><span class="bold">今天</span></td>
                                            <td>
                                                <span v-if="item.today">
                                                    <span class="red">【{{item.today[0]}}】</span>|&nbsp;<span
                                                        class="num-style">{{item.today[2]}}</span>
                                                    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                                                </span>
                                                二级:
                                                <span class="num-style">
                                                    {{item.industry}}
                                                </span>
                                            </td>
                                        </tr>
                                        <tr
                                            v-if="item.code_info[0].indexOf('00')==0||item.code_info[0].indexOf('60')==0">
                                            <td><span class="bold">历史</span></td>
                                            <td style="max-width: 650px;">
                                                <div style="display: flex;flex-wrap: wrap;border-width: 1px;">
                                                    <span v-for="(item1,i1) in item.code_records">
                                                        <span class="num-style red">【{{item1[0][0]}}】</span>|&nbsp;
                                                        <span v-for="(item2,i2) in item1[2]">
                                                            <span>{{item2[0]}}</span><span
                                                                v-if="i2!=item1[2].length -1">、</span>
                                                        </span>
                                                        <span class="num-style">({{item1[1]}})</span>
                                                        &nbsp;&nbsp;&nbsp;&nbsp;
                                                    </span>
                                                </div>
                                            </td>
                                        </tr>
                                        <tr
                                            v-if="item.code_info[0].indexOf('00')==0||item.code_info[0].indexOf('60')==0">
                                            <td><span class="bold">板块</span></td>
                                            <td>
                                                <div class="scroll-x" style="width: 650px;">
                                                    <div v-for="(item1,i1) in item.plate" class="item">
                                                        <span class="num-style line"
                                                            style="display: block;">{{item1[1]}}</span>
                                                        <span class="num-style line"
                                                            style="display: block;">{{item1[2]}}%</span>
                                                    </div>
                                                </div>
                                            </td>
                                        </tr>
                                    </div>
                                </tbody>
                                <tfoot></tfoot>
                            </table>
                <div style="float: right; width:72%;">
                    <div class="cb_list_top">
                        <div v-for="item in cb_list_top" v-on:click="select_cb(item)" :class="{'red': cb_selected_code == item[0][0]}">
                            <img v-if="item[3]" style="margin-right:5px;" src="./images/stop_up.png">
                            <span>{{item[0][1]}}</span>
                            <span>({{item[0][0]}})</span>
                            <button v-on:click="add_to_ths(item[0][0])">查看</button>
                        </div>
                    </div>
                    <div v-for="item in cb_list" class="cb_list" :class="{'red': cb_selected_code == item[0][0]}">
                        <table>
                            <tr><td>正股名称:{{item[1][1]}}({{item[1][0]}}) <button v-on:click="add_to_ths(item[1][0])">查看</button></td><td >转债名称:<span class="green">{{item[0][1]}}({{item[0][0]}})</span> &nbsp;<button v-on:click="add_to_ths(item[0][0])">查看</button></td></tr>
                            <tr><td>涨停时间:{{item[2]}}</td><td>---</td></tr>
                            <tr><td>最新涨幅:{{item[1][2][1]}}%</td><td>最新涨幅:{{item[0][2][1]}}%</td></tr>
                        </table>
                    </div>
                </div>
@@ -548,10 +384,14 @@
                    <div v-for="(item,i) in want_codes" class="item"
                        style="display: flex;width: 335px;margin-bottom: 15px;" v-if="item[8]==1">
                        <div><img :style="{'visibility':item[3]==1?'visiable':'hidden'}" style="margin-right:5px;"
                        <div>
                            <input type="checkbox" :checked="item[9]==1"
                                v-on:click="add_or_remove_forbidden($event,item[0])" />
                            <img :style="{'visibility':item[3]==1?'visiable':'hidden'}" style="margin-right:5px;"
                                src="./images/stop_up.png"><span
                                :class="{'red': item[5],'green': item[0].indexOf('00')!=0&&item[0].indexOf('60')!=0 }">{{item[1]}}({{item[0]}})-{{item[2]}}-{{item[6]}}-{{item[7]}}
                            </span><span v-if="item[3]==1">(炸)</span> <span class="red" v-if="item[4]">*</span> </div>
                            </span><span v-if="item[3]==1">(炸)</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>
@@ -559,7 +399,8 @@
                    <div v-for="(item,i) in want_codes" class="item"
                        style="display: flex;width: 335px;margin-bottom: 15px;" v-if="item[8]==0">
                        <div><img :style="{'visibility':item[3]==1?'visiable':'hidden'}" style="margin-right:5px;"
                        <div><input type="checkbox" :checked="item[9]==1" /><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]}}-{{item[6]}}-{{item[7]}}
                            </span><span v-if="item[3]==1">(炸)</span> <span class="red" v-if="item[4]">*</span> </div>
kp_html/kp/js/code_list.js
@@ -73,7 +73,9 @@
            kpl_open_limit_up_count_rank:[],
            // 大单买卖数量
            big_buy_order_count:0,
            big_sell_order_count:0
            big_sell_order_count:0,
            // 过滤的索引
            filter_indexes:[]
            
        },
        watch: {
@@ -168,6 +170,7 @@
                }
                http_util.get_l2_h_cancel_datas(app.code, app.operate_index, function(res) {
                    res = JSON.parse(res);
                    console.log("H撤數據",res)
                    if (res.code == 0) {
                        app.h_cancel_indexes = res.data;
                    }
@@ -459,9 +462,21 @@
            },
            need_show: function(item) {
                
                if(app.filter_indexes.length>0){
                    if(app.filter_indexes.indexOf(item[2][0])>=0)
                    {
                         return true;
                    }else{
                        return false;
                    }
                }
                if(app.real_order_indexes.includes( item[2][0])){
                    return true;
                }
                
                if (app.hidden_canceled && (item[2][6].indexOf('买撤') >= 0 || (item[2][8] != null &&
                        item[2][8].indexOf("-") > 0))) {
@@ -552,6 +567,7 @@
                            app.refresh_l2_data();
                        }
                    });
                    app.get_l2_cant_buy_reasons(app.origin_code);
                    http_util.get_open_limit_up_count_rank(null,function(res){
                        res = JSON.parse(res);
@@ -616,6 +632,18 @@
                }else{
                    app.l2_max_volume=null;
                }
                watch_indexes_str = $("#watch_indexes").val();
                if(watch_indexes_str!=null&&watch_indexes_str.length>0){
                    var final_indexes=[];
                    var indexes = watch_indexes_str.split(",");
                    indexes.forEach(function(e){
                      final_indexes.push(parseInt(e));
                    });
                    app.filter_indexes = final_indexes;
                    console.log("选中索引:",final_indexes);
                }else{
                    app.filter_indexes =[];
                }
            },
            fordbidden_buy:function(code){
kp_html/kp/js/http.js
@@ -134,16 +134,19 @@
        console.log("socket请求", data)
        var sign = http_util.get_sign(data);
        data['sign'] = sign;
        console.log("参数:",data);
        pyjs.socket_request(JSON.stringify(data), function(result) {
            console.log(result);
        http_util.socket_request(JSON.stringify(data), function(result) {
            console.log("撤单结果",result)
            result = JSON.parse(result);
            if (result.code == 0) {
                // pyjs.show_info(type_desc +"成功");
                layer.msg(type_desc + "成功")
            if(result.code ==0 ){
                layer.msg(type_desc + "成功");
            }
            callback(result);
        });
    },
    get_want_codes: function(plate, callback) {
@@ -179,7 +182,7 @@
        
        console.log(params_str)
        
         md5Hash = crypto.createHash("md5").update(params_str).digest("hex");
         md5Hash = md5(params_str);
         return md5Hash;
    },
    cancel_order: function(code, success) {
@@ -313,6 +316,10 @@
        var params={}
        http_util.http_request("/get_kpl_market_feelings", params, callback);
    },
    get_cb_list(callback){
        var params={}
        http_util.http_request("/get_cb_list", params, callback);
    },
    
    
    
kp_html/kp/js/md5.min.js
New file
@@ -0,0 +1,2 @@
!function(n){"use strict";function d(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function f(n,t,r,e,o,u){return d((u=d(d(t,n),d(e,u)))<<o|u>>>32-o,r)}function l(n,t,r,e,o,u,c){return f(t&r|~t&e,n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t&e|r&~e,n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function c(n,t){var r,e,o,u;n[t>>5]|=128<<t%32,n[14+(t+64>>>9<<4)]=t;for(var c=1732584193,f=-271733879,i=-1732584194,a=271733878,h=0;h<n.length;h+=16)c=l(r=c,e=f,o=i,u=a,n[h],7,-680876936),a=l(a,c,f,i,n[h+1],12,-389564586),i=l(i,a,c,f,n[h+2],17,606105819),f=l(f,i,a,c,n[h+3],22,-1044525330),c=l(c,f,i,a,n[h+4],7,-176418897),a=l(a,c,f,i,n[h+5],12,1200080426),i=l(i,a,c,f,n[h+6],17,-1473231341),f=l(f,i,a,c,n[h+7],22,-45705983),c=l(c,f,i,a,n[h+8],7,1770035416),a=l(a,c,f,i,n[h+9],12,-1958414417),i=l(i,a,c,f,n[h+10],17,-42063),f=l(f,i,a,c,n[h+11],22,-1990404162),c=l(c,f,i,a,n[h+12],7,1804603682),a=l(a,c,f,i,n[h+13],12,-40341101),i=l(i,a,c,f,n[h+14],17,-1502002290),c=g(c,f=l(f,i,a,c,n[h+15],22,1236535329),i,a,n[h+1],5,-165796510),a=g(a,c,f,i,n[h+6],9,-1069501632),i=g(i,a,c,f,n[h+11],14,643717713),f=g(f,i,a,c,n[h],20,-373897302),c=g(c,f,i,a,n[h+5],5,-701558691),a=g(a,c,f,i,n[h+10],9,38016083),i=g(i,a,c,f,n[h+15],14,-660478335),f=g(f,i,a,c,n[h+4],20,-405537848),c=g(c,f,i,a,n[h+9],5,568446438),a=g(a,c,f,i,n[h+14],9,-1019803690),i=g(i,a,c,f,n[h+3],14,-187363961),f=g(f,i,a,c,n[h+8],20,1163531501),c=g(c,f,i,a,n[h+13],5,-1444681467),a=g(a,c,f,i,n[h+2],9,-51403784),i=g(i,a,c,f,n[h+7],14,1735328473),c=v(c,f=g(f,i,a,c,n[h+12],20,-1926607734),i,a,n[h+5],4,-378558),a=v(a,c,f,i,n[h+8],11,-2022574463),i=v(i,a,c,f,n[h+11],16,1839030562),f=v(f,i,a,c,n[h+14],23,-35309556),c=v(c,f,i,a,n[h+1],4,-1530992060),a=v(a,c,f,i,n[h+4],11,1272893353),i=v(i,a,c,f,n[h+7],16,-155497632),f=v(f,i,a,c,n[h+10],23,-1094730640),c=v(c,f,i,a,n[h+13],4,681279174),a=v(a,c,f,i,n[h],11,-358537222),i=v(i,a,c,f,n[h+3],16,-722521979),f=v(f,i,a,c,n[h+6],23,76029189),c=v(c,f,i,a,n[h+9],4,-640364487),a=v(a,c,f,i,n[h+12],11,-421815835),i=v(i,a,c,f,n[h+15],16,530742520),c=m(c,f=v(f,i,a,c,n[h+2],23,-995338651),i,a,n[h],6,-198630844),a=m(a,c,f,i,n[h+7],10,1126891415),i=m(i,a,c,f,n[h+14],15,-1416354905),f=m(f,i,a,c,n[h+5],21,-57434055),c=m(c,f,i,a,n[h+12],6,1700485571),a=m(a,c,f,i,n[h+3],10,-1894986606),i=m(i,a,c,f,n[h+10],15,-1051523),f=m(f,i,a,c,n[h+1],21,-2054922799),c=m(c,f,i,a,n[h+8],6,1873313359),a=m(a,c,f,i,n[h+15],10,-30611744),i=m(i,a,c,f,n[h+6],15,-1560198380),f=m(f,i,a,c,n[h+13],21,1309151649),c=m(c,f,i,a,n[h+4],6,-145523070),a=m(a,c,f,i,n[h+11],10,-1120210379),i=m(i,a,c,f,n[h+2],15,718787259),f=m(f,i,a,c,n[h+9],21,-343485551),c=d(c,r),f=d(f,e),i=d(i,o),a=d(a,u);return[c,f,i,a]}function i(n){for(var t="",r=32*n.length,e=0;e<r;e+=8)t+=String.fromCharCode(n[e>>5]>>>e%32&255);return t}function a(n){var t=[];for(t[(n.length>>2)-1]=void 0,e=0;e<t.length;e+=1)t[e]=0;for(var r=8*n.length,e=0;e<r;e+=8)t[e>>5]|=(255&n.charCodeAt(e/8))<<e%32;return t}function e(n){for(var t,r="0123456789abcdef",e="",o=0;o<n.length;o+=1)t=n.charCodeAt(o),e+=r.charAt(t>>>4&15)+r.charAt(15&t);return e}function r(n){return unescape(encodeURIComponent(n))}function o(n){return i(c(a(n=r(n)),8*n.length))}function u(n,t){return function(n,t){var r,e=a(n),o=[],u=[];for(o[15]=u[15]=void 0,16<e.length&&(e=c(e,8*n.length)),r=0;r<16;r+=1)o[r]=909522486^e[r],u[r]=1549556828^e[r];return t=c(o.concat(a(t)),512+8*t.length),i(c(u.concat(t),640))}(r(n),r(t))}function t(n,t,r){return t?r?u(t,n):e(u(t,n)):r?o(n):e(o(n))}"function"==typeof define&&define.amd?define(function(){return t}):"object"==typeof module&&module.exports?module.exports=t:n.md5=t}(this);
//# sourceMappingURL=md5.min.js.map
kp_html/kp/js/page.js
@@ -80,6 +80,12 @@
            app.get_kpl_market_feelings();
        }
    }, 3000, 3000);
    setInterval(function() {
        if (is_trade_time()||true) {
            app.get_cb_list();
        }
    }, 3000, 3000);
@@ -98,6 +104,9 @@
                trade_data: {
                    order: 0
                },
                cb_list:[],// 可转债列表
                cb_list_top:[],
                cb_selected_code:'',
                // 代码的开盘啦涨停原因
                kpl_limit_up_reason: '',
                default_score_data: {
@@ -458,15 +467,14 @@
                                app.trade_data = res.data.trade_data
                            }
                            res.data.kpl_code_info.plate.forEach(function(e) {
                                var s = false;
                                var s = null;
                                if (app.first_info.limit_up_reason_statistic) {
                                    for (var i = 0; i < app.first_info
                                        .limit_up_reason_statistic
                                        .length; i++) {
                                        if (app.first_info
                                            .limit_up_reason_statistic[
                                                i][0] == e[1]) {
                                            s = true;
                                        var limit_up_info = app.first_info.limit_up_reason_statistic[i];
                                        if (limit_up_info[0] == e[1]) {
                                            s = limit_up_info;
                                            break;
                                        }
                                    }
@@ -679,7 +687,7 @@
                            console.log("板块请求结果:", res);
                            if (res.code == 0) {
                                res.data.plate.forEach(function(e) {
                                    var s = false;
                                    var s = null;
                                    if (app.first_info.limit_up_reason_statistic) {
                                        for (var i = 0; i < app.first_info
                                            .limit_up_reason_statistic
@@ -687,7 +695,7 @@
                                            if (app.first_info
                                                .limit_up_reason_statistic[
                                                    i][0] == e[1]) {
                                                s = true;
                                                s = app.first_info.limit_up_reason_statistic[i];
                                                break;
                                            }
                                        }
@@ -829,8 +837,59 @@
                        }
                    });
                },
                add_or_remove_forbidden:function(event,code){
                    var el = event.currentTarget;
                    var checked = $(el).is(':checked');
                    console.log(code, checked);
                    if(checked){
                        http_util.do_action_for_code(code,'',0,function(res){
                        });
                    }else{
                        http_util.do_action_for_code(code,'',1,function(res){
                        });
                    }
                },
                get_cb_list:function(){
                    http_util.get_cb_list(function(res){
                        res = JSON.parse(res);
                        if(res.code==0){
                            app.cb_list = res.data;
                            var arr= new Array();
                            arr = arr.concat(res.data);
                            app.cb_list_top = arr;
                            app.set_selected_cb_top();
                        }
                    })
                },
                set_selected_cb_top:function(){
                    var temp_list=app.cb_list;
                    for(var i=0;i<temp_list.length;i++){
                        if(temp_list[i][0][0] == app.cb_selected_code){
                            var data = temp_list[i];
                            temp_list.splice(i, 1);
                            temp_list.unshift(data);
                            app.cb_list = temp_list;
                            break;
                        }
                    }
                },
                select_cb:function(data){
                    app.cb_selected_code = data[0][0];
                    this.set_selected_cb_top();
                },
                add_to_ths:function(code){
                    pyjs.add_code_to_ths(code);
                }
            }
             }
        });
    }
@@ -839,6 +898,7 @@
        app.get_jingxuan_data(app.jingxuan_data_type);
        app.get_limit_up_list(app.code, false);
        app.get_kpl_market_feelings();
        app.get_cb_list();
    }
    $(".market .child-title").click(function() {
kp_html/kp/test.html
Diff too large
main.py
@@ -59,6 +59,7 @@
    def __socket_request(self, text, callback_info):
        try:
            print("socket請求:", text)
            result = network_util.socket_request(text)
            print("请求结果:", result)
            self.signal_request.emit(callback_info[0], callback_info[1], result)
network_delegate_manager.py
@@ -7,6 +7,7 @@
import time
from urllib.parse import parse_qs, urlparse
from convertible_bonds.main import JueJinUtil
from juejin_core import JueJinApi
from kpl import kpl_util, kpl_api
from kpl.kpl_data_manager import KPLLimitUpDataManager
@@ -37,6 +38,9 @@
        elif url.startswith("/get_kpl_market_feelings"):
            return cls.__get_kpl_market_feelings()
        elif url.startswith("/get_cb_list"):
            # 获取可转债的列表
            return cls.get_cb_list()
        return None, False
@@ -300,9 +304,63 @@
        return json.dumps(
            {"code": 0, "data": fdata}), True
    __cb_codes_list = None  # 所有可转债数据
    __pre_price_dict = {}  # 昨日收盘价
    # 获取可转债的列表
    @classmethod
    def get_cb_list(cls):
        # 获取所有可转债列表
        if not cls.__cb_codes_list:
            cls.__cb_codes_list = JueJinUtil.get_all_convertible_bonds_codes()
            # 获取昨日收盘价
            symbols = [x[0][0] for x in cls.__cb_codes_list]
            symbols.extend([x[1] for x in cls.__cb_codes_list])
            # 获取现价
            results = JueJinApi.get_gp_latest_info(symbols, fields='symbol,pre_close')
            cls.__pre_price_dict = {x['symbol']: x['pre_close'] for x in results}
        # 获取涨停列表
        records = KPLLimitUpDataManager().get_limit_up_history_datas()
        currents = KPLLimitUpDataManager().get_limit_up_current_datas()
        current_codes = [d[0] for d in currents]
        record_codes = [d[0] for d in records]
        record_map = {}
        for r in records:
            record_map[r[0]] = r
        # 格式[([转债代码,转债名称, (现价,涨幅)],[正股代码,正股名称,(现价,涨幅)],正股涨停时间,是否炸板)]
        datas = []
        for b in cls.__cb_codes_list:
            code = b[1].split(".")[1]
            if code in record_codes:
                if record_map[code][4] != '首板':
                    continue
                datas.append(
                    [[b[0][0], b[0][1], None], [b[1], record_map[code][1], None], tool.time_format(record_map[code][2]),
                     code not in current_codes])
        # 获取所有的涨幅
        symbols = []
        for d in datas:
            symbols.append(d[0][0])
            symbols.append(d[1][0])
        # 获取当前
        current_infos = JueJinApi.get_gp_current_info(symbols, fields='symbol,price')
        rate_dict = {x['symbol']: (x['price'], round(
            (x['price'] - cls.__pre_price_dict[x['symbol']]) * 100 / cls.__pre_price_dict[x['symbol']], 2)) for x in
                     current_infos}
        for d in datas:
            d[0][2] = rate_dict[d[0][0]]
            d[1][2] = rate_dict[d[1][0]]
            d[0][0] = d[0][0].split('.')[1]
            d[1][0] = d[1][0].split('.')[1]
        datas.sort(key=lambda x: int(x[2].replace(":","")), reverse=True)
        # 获取今日正股涨停的可转债列表
        return json.dumps(
            {"code": 0, "data": datas}), True
if __name__ == "__main__":
    codes = ["002523", "603095"]
    datas = LocalKanPanNetworkDelegate.get_kpl_market_feelings(codes)
    datas = LocalKanPanNetworkDelegate.get_cb_list()
    print(datas)
res/codes.txt
@@ -1 +1,4 @@
601766
600866
600984
000705
000980
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 = [-71, 121, 1046, 885]
xgb_window_info = [-2354, 86, 1920, 1017]
window_watch_float_info = [146, 419, 435, 220]
window_tick_info = [-1799, 324, 1223, 665]
kp_second_window_info = [130, 295, 730, 974]
window_tick_info = [280, 81, 800, 1000]
kp_second_window_info = [-1907, -8, 1920, 1017]
code_attribute_window_info = [-650, 315, 291, 278]
client = hxh
float_frame_auto_focus = 1
utils/tool.py
@@ -12,8 +12,8 @@
# 获取涨停价
def get_limit_up_price(price):
    price = decimal.Decimal(str(price)) * decimal.Decimal("1.1")
def get_limit_up_price(price, max_rate=0.1):
    price = decimal.Decimal(str(price)) * decimal.Decimal(f"{round(1 + max_rate, 1)}")
    price = price.quantize(decimal.Decimal("0.00"), decimal.ROUND_HALF_UP)
    return price
@@ -78,7 +78,6 @@
def get_now_date_str(format="%Y-%m-%d"):
    # TODO 测试
    return datetime.datetime.now().strftime(format)