admin
2024-01-11 5a2ef3a696ddccbc1faef1e2e90f5b535ec24a0d
看盘网页修改/接口本地化代理
1个文件已删除
5 文件已重命名
20个文件已修改
6个文件已添加
1340 ■■■■ 已修改文件
code_data_manager.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
gui_wx.py 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
juejin_core.py 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
juejin_data_export.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/codes_list.html 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/css/banshuping.css 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/css/index23-05-04.css 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/index23-05-04.html 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/banshuping.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/code_list.js 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/http.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kp_html/kp/js/page.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/gui.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/kpl_api.py 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/kpl_data_manager.py 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/kpl_util.py 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
kpl/tool.py 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.py 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
network_delegate_manager.py 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/codes.txt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
res/setting.conf 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sell_processor.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
socket_util.py 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test_kpl.py 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/log_util.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/network_util.py 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/ocr_util.py 补丁 | 查看 | 原始文档 | blame | 历史
utils/opencv_util.py 补丁 | 查看 | 原始文档 | blame | 历史
utils/socket_util.py 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/ths_util.py 补丁 | 查看 | 原始文档 | blame | 历史
utils/tool.py 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/xgb_api.py 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code_data_manager.py
@@ -3,7 +3,7 @@
import json
import os
import tool
from utils import tool
from utils import log_util
gui_wx.py
@@ -11,15 +11,14 @@
import cv2
import win32con
import win32gui
from matplotlib.animation import FuncAnimation
import constant
import juejin_data_export
import ocr_util
import opencv_util
from utils import ocr_util
from utils import opencv_util
import setting
import socket_util
import ths_util
from utils import socket_util
from utils import ths_util
import win32_util
# freeze_support()
@@ -37,7 +36,7 @@
import code_data_manager
import juejin_core
import tool
from utils import tool
import requests
from sell_processor import TickDataProcess
@@ -2042,17 +2041,12 @@
                t1.start()
            elif type_ == "juejin_tick_download":
                wx.CallAfter(lambda: JueJinTickDataDownloadFrame().Show())
            elif type_ == "exit":
                try:
                    jueJinProcess.terminate()
                except:
                    pass
                wx.CallAfter(lambda: sys.exit())
        except Exception as e:
            logging.exception(e)
juejin_core.py
@@ -7,7 +7,7 @@
import xlwt
import setting
import tool
from utils import tool
def init(context):
@@ -92,7 +92,7 @@
def on_tick(context, tick):
    print(tick)
    if int(tick['created_at'].strftime("%H:%M:%S").replace(":","")) < int("092500"):
    if int(tick['created_at'].strftime("%H:%M:%S").replace(":", "")) < int("092500"):
        return
    data = parse_tick(tick)
    data = {"type": 0, "data": data}
@@ -120,8 +120,6 @@
    #           serv_addr='', backtest_match_mode=0,
    #           token=token)
    subscript()
class GPCodeManager:
@@ -268,3 +266,90 @@
                f.write("\n")
class JueJinApi:
    __previous_trading_date_cache = {}
    # 获取掘金参数
    @classmethod
    def getJueJinAccountInfo(cls):
        strategy_id, token = setting.get_juejin_params()
        return "", strategy_id, token
    @classmethod
    def get_juejin_code_list_with_prefix(cls, codes):
        list = []
        for d in codes:
            if d[0:2] == '00':
                list.append("SZSE.{}".format(d))
            elif d[0:2] == '60':
                list.append("SHSE.{}".format(d))
        return list
    @classmethod
    def get_gp_latest_info(cls, codes, fields=None):
        if not codes:
            return []
        symbols = cls.get_juejin_code_list_with_prefix(codes)
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        data = gmapi.get_instruments(symbols=",".join(symbols), fields=fields)
        return data
    @classmethod
    def get_history_tick_n(cls, code, count, fields=None):
        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)
        return results
    @classmethod
    def get_gp_current_info(cls, codes, fields=''):
        if not codes:
            return []
        symbols = cls.get_juejin_code_list_with_prefix(codes)
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        data = gmapi.current(symbols=",".join(symbols), fields=fields)
        return data
    # 获取交易所的代码
    @classmethod
    def get_exchanges_codes(cls, exchanges):
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        return gmapi.get_instruments(exchanges=exchanges, sec_types=[1], skip_suspended=True, skip_st=True,
                                     fields="symbol,sec_type,sec_id,sec_name,listed_date,sec_level,is_suspended,pre_close")
    @classmethod
    def get_previous_trading_date(cls, date):
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        return gmapi.get_previous_trading_date("SHSE", date)
    @classmethod
    def get_previous_trading_date_cache(cls, date):
        if date not in cls.__previous_trading_date_cache:
            pre_date = cls.get_previous_trading_date(date)
            if pre_date:
                cls.__previous_trading_date_cache[date] = pre_date
        return cls.__previous_trading_date_cache.get(date)
    # 返回指定日期的下个交易日
    @classmethod
    def get_next_trading_date(cls, date):
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        return gmapi.get_next_trading_date("SHSE", date)
    @classmethod
    def get_trading_dates(cls, start_date, end_date):
        account_id, s_id, token = cls.getJueJinAccountInfo()
        gmapi.set_token(token)
        return gmapi.get_trading_dates("SHSE", start_date, end_date)
juejin_data_export.py
@@ -1,6 +1,6 @@
import xlwt
import tool
from utils import tool
from juejin_core import GPCodeManager
kp_html/kp/codes_list.html
@@ -4,6 +4,7 @@
        <meta charset="utf-8">
        <title>看盘端</title>
        <link rel="stylesheet" type="text/css" href="./css/banshuping.css" />
        <link rel="stylesheet" type="text/css" href="layui/css/layui.css" />
        <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
        <script src="http://cdn.yeshitv.com/js/vue.min.js"></script>
        <script src="js/vconsole.min.js"></script>
@@ -11,6 +12,7 @@
        <script src="js/qwebchannel.js"></script>
        <script src="js/kpl.js"></script>
        <script src="js/http.js"></script>
        <script src="layui/layui.js"></script>
        <style>
        </style>
@@ -56,8 +58,46 @@
                    <!-- 交易参数 -->
                    <table style="position: relative;" >
                        <caption class="table-name">
                            <span>
                            买入预期
                            </span>
                            <button style="position: absolute;right: 5px;top: 16px;font-size: 14px;" class="btn" v-on:click="get_delegated_buy_code_infos">刷新</button>
                        </caption>
                        <thead>
                            <tr v-if="delegated_buy_code_infos.length>0">
                                <td>
                                    <div class="delegated_codes_info scroll-y">
                                        <div v-for="(item,index) in delegated_buy_code_infos">
                                            <div>
                                                <div>{{item.code_info[0]}}</div>
                                                <div>{{item.code_info[1]}}</div>
                                            </div>
                                            <div>
                                                <div class="layui-progress" style="background-color: #BBB" :id="'progress_'+item.code_info[0]">
                                                  <div class="layui-progress-bar" lay-percent="{{item.percent+'%'}}" ></div>
                                                </div>
                                                <div>
                                                    还有<span :class="{'red':item.left_count <=4}">{{item.left_count}}</span>笔,还有<span>{{item.left_money}}</span>成交到我们
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </td>
                            </tr>
                        </thead>
                    </table>
                    <table v-if="trade_data">
                        <caption class="table-name">交易参数</caption>
                        <caption class="table-name"><span>交易参数</span></caption>
                        <tr>
                            <td>是否可以买入</td>
                            <td v-if="trade_data.can_buy_info">
@@ -109,6 +149,15 @@
                                </span>
                            </td>
                        </tr>
                        <tr>
                            <td>辨识度信息</td>
                            <td><span v-if="trade_data.special_info">
                                        <span class="num-style ">{{trade_data.special_info[1]}}</span>
                                </span>
                            </td>
                        </tr>
                    </table>
kp_html/kp/css/banshuping.css
@@ -552,3 +552,25 @@
}
.delegated_codes_info{
    max-height: 200px;
}
.delegated_codes_info>div{
    display: flex;
    margin-bottom: 10px;
    align-items: center;
}
.delegated_codes_info>div>div:first-child{
    width: 60px;
}
.delegated_codes_info>div>div:last-child{
    position: relative;
    width: 410px;
}
.delegated_codes_info .layui-progress{
    margin-bottom: 5px;
}
kp_html/kp/css/index23-05-04.css
@@ -307,6 +307,9 @@
.purple {
    color: #ff80ff;
}
.white{
    color: #fff;
}
.num-card-color1 {
    background: #ed96f3;
@@ -527,7 +530,7 @@
.market-container {
    width: 520px;
    height: 178px;
    height: 175px;
    word-wrap: break-word;
    display: flex;
    flex-wrap: wrap;
@@ -641,6 +644,7 @@
    padding-left: 10px;
    box-sizing: border-box;
    flex-wrap: wrap;
    z-index: 100;
}
@@ -664,5 +668,10 @@
    margin-right: 5px;
    margin-bottom: 3px;
    width: auto;
    max-width: 150px;
    max-width: 200px;
}
.xgb_info{
    max-height: 40px;
    padding: 5px;
}
kp_html/kp/index23-05-04.html
@@ -31,6 +31,15 @@
                </span>
            </div>
            <div class="xgb_info" >
                <div v-if="xgb_limit_up_reasons">
                <span class="red">{{xgb_limit_up_reasons[0]}}</span>:<span>{{xgb_limit_up_reasons[1]}}</span>
                </div>
            </div>
            <!-- 文档流 第一块 -->
            <div>
                <!-- 右侧板块 -->
@@ -98,6 +107,7 @@
                                    <td><span  class="bold">昨日涨停原因</span></td>
                                    <td>
                                        <span class="red">{{yesterday_block_info.reason}}</span>
                                        <span :class="{'red': yesterday_block_info.reason_rate>0,'green':yesterday_block_info.reason_rate<0,'white':yesterday_block_info.reason_rate==0}">({{yesterday_block_info.reason_rate}}%)</span>
                                    </td>
                                </tr>
                                
@@ -106,7 +116,8 @@
                                    <td class="budinggundong">
                                        <div>
                                            <span v-for="(item,i) in yesterday_block_info.data.yesterday"
                                                class="label-style" >{{item[1]}}({{item[0]}})</span>
                                                class="label-style" >{{item[1]}}({{item[0]}})(<span :class="{'red': item[2]>0,'green':item[2]<0,'white':item[2]==0}">{{item[2]}}%</span>)
                                                </span>
                                        </div>
                                    </td>
                                </tr>
@@ -200,7 +211,6 @@
                        v-on:click="show_want_codes(item[0])">
                        <span class="num-style red">{{item[0]}}({{item[1]}}&{{item[2]}})</span>&nbsp;&nbsp;&nbsp;&nbsp;
                    </span>
                </div>
                <!-- 左侧 -->
                <div style="float: left;width: 27.5%;margin-right: 0.4%;" class="scroll-y">
@@ -227,14 +237,12 @@
                                    <div style="display:flex;justify-content:space-between;align-items:center">
                                        <div>
                                            <span v-if="item[2]==1"><img src="./images/limit_up.png"></span>
                                            <span v-else-if="item[2]==2"><img src="./images/stop_up.png"></span>
                                            <span v-else style="visibility: hidden;"><img
                                                    src="./images/stop_up.png"></span>
                                            <span v-else-if="item[2]==2">(炸板)</span>
                                            <span class="num-style"
                                                :class="{'green': item[0].indexOf('00')!=0&&item[0].indexOf('60')!=0 }">{{item[1]}}</span><span
                                                class="num-card-red" v-if="item[3]">{{item[3]}}</span><span
                                                :class="{'green': item[0].indexOf('00')!=0&&item[0].indexOf('60')!=0 }">{{item[1]}}</span>
                                            <span class="num-card-red" v-if="item[3]">{{item[3]}}</span><span
                                                class="num-card-bule" v-if="item[4]">{{item[4]}}</span><span
                                                class="score-card-color yellow" v-if="item[5]">{{item[5]}}</span>
                                        </div>
@@ -502,12 +510,23 @@
            <!-- 弹出框 -->
            <div id="want_code_dialog" style="display: none;">
                <div v-if="want_codes" class="want_code_content"
                    style="display: flex;width: 600px;justify-content: space-between;">
                    style="display: flex;width: 676px;justify-content: space-between;">
                    <div v-for="(item,i) in want_codes" class="item"
                        style="display: flex;width: 298px;margin-bottom: 15px;">
                        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;"
                                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>
                        <img v-if="item[2]==1" class="delete" src="./images/delete.png"
                            @click="delete_from_want_codes(i)">
                    </div>
                    <div style="width: 100%; height: 1px;background-color: #000;margin-bottom: 5px;"></div>
                    <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;"
                                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>
                        <img v-if="item[2]==1" class="delete" src="./images/delete.png"
kp_html/kp/js/banshuping.js
@@ -55,8 +55,11 @@
                }
            }
        }
    })
    });
    // 获取数据
    http_util.get_delegated_buy_code_infos(function(result){
        if (result.code == 0) {
            console.log("已挂买单数据:",result.data);
        }
    });
});
kp_html/kp/js/code_list.js
@@ -57,7 +57,29 @@
            h_cancel_indexes: [],
            buy_single_indexes: [],
            l2_code_name: '',
            operate_index: -1
            operate_index: -1,
            // 已经委托的买代码信息
            delegated_buy_code_infos:[],
            layui_element:null
        },
        watch:{
            delegated_buy_code_infos:function(){
                if(app.layui_element){
                    setTimeout(()=>{
                        app.layui_element.render();
                    })
                }
            }
        },
        mounted:function(){
             layui.use('element', function(){
                var element = layui.element;
                app.layui_element=element;
              });
            setInterval(function(){
                app.get_delegated_buy_code_infos();
            },1000*600*5);
        },
        methods: {
            change_data_type: function(type) {
@@ -157,6 +179,24 @@
                });
            },
            get_delegated_buy_code_infos:function(){
                // 获取数据
                http_util.get_delegated_buy_code_infos(function(result){
                    if (result.code == 0) {
                        console.log("已挂买单数据:",result.data);
                        result.data.forEach(function(e){
                            if(e.total_num>0){
                                e.percent = e.finish_num*100/e.total_num;
                            }else{
                                e.percent = 0;
                            }
                        });
                        app.delegated_buy_code_infos = result.data
                    }
                });
            },
            clear_cancel_mark: function() {
                //清除撤单标记
                app.l_up_cancel_indexes = [];
kp_html/kp/js/http.js
@@ -22,6 +22,11 @@
        pyjs.http_request(path, JSON.stringify(data), JSON.stringify(["http_util.http_request_result", key]));
    },
    socket_request: function(data, callback) {
        key = "http_callback_" + new Date().getTime() + "_" + Math.round(Math.random() * 100000000);
        http_util.request_callback[key] = callback;
        pyjs.socket_request(data, JSON.stringify(["http_util.http_request_result", key]));
    },
    get_score_data: function(code, name, callback) {
@@ -212,5 +217,19 @@
        var params={code:code}
        http_util.http_request("/get_kpl_block_info", params, callback);
    },
    // 获取已挂买单的信息
    get_delegated_buy_code_infos:function(callback){
        data = {type:"common", data: {ctype:"get_delegated_buy_code_infos"},sign:''}
        http_util.socket_request(JSON.stringify(data), function(result) {
            result = JSON.parse(result);
            callback(result);
        });
    },
    get_xgb_limit_up_reasons:function(code,callback){
        var params={code:code}
        http_util.http_request("/get_xgb_limit_up_reasons", params, callback);
    },
     
};
kp_html/kp/js/page.js
@@ -13,14 +13,14 @@
var app;
$(function() {
    function _resize() {
        var code_info_height = 480
        var code_info_height = 450
        console.log("总高", $(window).height())
        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)
        $("#body>div:nth-child(3)>div:nth-child(2)").css("height", bottom_height - 10)
        $("#body>div:nth-child(3)>div:nth-child(3)").css("height", bottom_height - 60)
        $("#body>div:nth-child(3)>div").css("height", code_info_height).css("max-height", code_info_height);
        $("#body>div:nth-child(4)").css("height", bottom_height)
        $("#body>div:nth-child(4)>div:nth-child(2)").css("height", bottom_height - 10)
        $("#body>div:nth-child(4)>div:nth-child(3)").css("height", bottom_height - 60)
        $(".outer-container").css("height", bottom_height - 180)
    }
    //针对窗口高度做调整
@@ -365,7 +365,8 @@
                },
                industry_data_type: 0,
                jingxuan_data_type: 0,
                // 选股宝涨停原因
                xgb_limit_up_reasons:["大金融","1、珠海国企,实控人珠海国资委,珠海金控旗下唯一控股的上市平台,参股优必选等人工智能独角兽公司; 2、公司持有华金证券1.4493%股权"],
            },
            watch: {
                want_codes: function() {
@@ -464,13 +465,13 @@
                        res = JSON.parse(res)
                        if (res.code == 0) {
                            app.first_info = res.data
                            if (app.choose_code.first) {
                                app.get_plate_info(app.choose_code.first, is_auto_refresh);
                            } else if (app.first_info.limit_up_codes) {
                                app.choose_code.first = app.first_info.limit_up_codes[0][0];
                                app.get_plate_info(app.first_info.limit_up_codes[0][0],
                                    is_auto_refresh);
                            }
                            // if (app.choose_code.first) {
                            //     app.get_plate_info(app.choose_code.first, is_auto_refresh);
                            // } else if (app.first_info.limit_up_codes) {
                            //     app.choose_code.first = app.first_info.limit_up_codes[0][0];
                            //     app.get_plate_info(app.first_info.limit_up_codes[0][0],
                            //         is_auto_refresh);
                            // }
                        }
                    });
                },
@@ -603,15 +604,32 @@
                    app.origin_code = code;
                    app.code = code;
                    app.code_name = code;
                    //请求板块
                    http_util.get_kpl_block_info(code,function(res){
                        console.log("板块请求结果:",res);
                        res = JSON.parse(res)
                        if(res.code==0){
                            app.kpl_code_info = res.data;
                    app.first_info.limit_up_codes.forEach(function(e){
                        if(e[0] == code){
                            app.code_name = e[1] + " " + e[0];
                        }
                    });
                    app.get_last_trade_day_reasons(code);
                    http_util.get_xgb_limit_up_reasons(code,function(res){
                        console.log("选股宝请求结果:",res);
                        res = JSON.parse(res)
                        if(res.code==0){
                            app.xgb_limit_up_reasons = res.data;
                        }else{
                            app.xgb_limit_up_reasons = null;
                        }
                    });
                    //请求板块
                    setTimeout(function(){
                        http_util.get_kpl_block_info(code,function(res){
                            console.log("板块请求结果:",res);
                            res = JSON.parse(res)
                            if(res.code==0){
                                app.kpl_code_info = res.data;
                            }
                        });
                        app.get_last_trade_day_reasons(code);
                    },10);
                    //init_data();
                },
                show_want_codes: function(plate) {
@@ -646,6 +664,7 @@
                    
                    http_util.get_last_trade_day_reasons(code,function(res){
                        res = JSON.parse(res);
                        console.log("板块今日表现:",res)
                        if(res.code == 0){
                            app.yesterday_block_info = res.data;
                        }else{
kpl/gui.py
@@ -10,7 +10,7 @@
import schedule
import wx
import tool
from utils import tool
import kpl_api
kpl/kpl_api.py
@@ -7,9 +7,11 @@
import json
import requests
import tool
from utils import tool
# 竞价
from kpl import kpl_util
DABAN_TYPE_BIDDING = 8
# 涨停
DABAN_TYPE_LIMIT_UP = 1
@@ -138,6 +140,39 @@
    return response.text
def __getLimitUpInfo(pidType, page, pageSize):
    data = f"Order=0&a=DailyLimitPerformance&st={pageSize}&apiv=w35&Type=4&c=HomeDingPan&PhoneOSNew=1&DeviceID=a38adabb-99ef-3116-8bb9-6d893c846e24&VerSion=5.13.0.0&Index={(page - 1) * pageSize}&PidType={pidType}&"
    result = __base_request("https://apphq.longhuvip.com/w1/api/index.php", data=data)
    return result.text
def getLimitUpInfoNew():
    pids = [(1, "首板"), (2, "2连板"), (3, "3连板"), (4, "4连板"), (5, "")]
    fresults = []
    for pid_info in pids:
        results = []
        for i in range(10):
            start_time = time.time()
            result = __getLimitUpInfo(pid_info[0], i + 1, 20)
            print("请求用时", time.time() - start_time)
            result = json.loads(result)
            datas = result["info"][0]
            results.extend(datas)
            day = result["info"][1]
            if len(datas) < 20:
                break
        for r in results:
            if not r[18] and pid_info[1]:
                r[18] = pid_info[1]
            # 替换掉板块名称
            for i in range(len(r)):
                if type(r[i]) == str:
                    r[i] = kpl_util.filter_block(r[i])
        fresults.extend(results)
    return json.dumps({"errcode": 0, "list": fresults})
def test_l2():
    code = "600981"
    count = 0
kpl/kpl_data_manager.py
New file
@@ -0,0 +1,154 @@
# 开盘啦涨停数据管理
import json
import os
import threading
import time
from utils import tool
from kpl import kpl_util
from kpl import kpl_api
class KPLLimitUpDataManager:
    __instance = None
    limit_up_records_dict = {}
    limit_up_current_dict = {}
    __limit_up_history_datas_cache = {}
    __limit_up_current_datas_cache = {}
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(KPLLimitUpDataManager, cls).__new__(cls, *args, **kwargs)
            cls.__load_data()
        return cls.__instance
    @classmethod
    def __load_data(cls):
        record_apth = cls.__get_record_path()
        current_path = cls.__get_current_path()
        # 获取历史涨停
        if os.path.exists(record_apth):
            children = os.listdir(record_apth)
            for c in children:
                with open(f"{record_apth}\\{c}", mode="r") as f:
                    line = f.readline().strip()
                    if line:
                        data = json.loads(line)
                        cls.limit_up_records_dict[data[0]] = data
        # 获取实时涨停
        if os.path.exists(current_path):
            with open(current_path, mode="r") as f:
                line = f.readline().strip()
                if line:
                    datas = json.loads(line)
                    for d in datas:
                        cls.limit_up_current_dict[d[0]] = d
    def __save_data(self, limit_up_datas):
        record_path = self.__get_record_path()
        current_path = self.__get_current_path()
        if not os.path.exists(record_path):
            os.makedirs(record_path)
        for d in limit_up_datas:
            code = d[0]
            # 保存记录数据
            with open(f"{record_path}\\{code}.log", mode='w') as f:
                f.write(json.dumps(d))
        with open(current_path, mode='w') as f:
            f.write(json.dumps(limit_up_datas))
    def add_data(self, limit_up_datas):
        temp_limit_up_dict = {}
        for d in limit_up_datas:
            temp_limit_up_dict[d[0]] = d
            self.limit_up_records_dict[d[0]] = d
        self.limit_up_current_dict = temp_limit_up_dict
        # 保存文件
        threading.Thread(target=self.__save_data, args=(limit_up_datas,), daemon=True).start()
    def get_limit_up_current_datas(self):
        temps = [self.limit_up_current_dict[k] for k in self.limit_up_current_dict]
        temps.sort(key=lambda x: x[2], reverse=True)
        return temps
    def get_limit_up_history_datas(self):
        temps = [self.limit_up_records_dict[k] for k in self.limit_up_records_dict]
        temps.sort(key=lambda x: x[2], reverse=True)
        return temps
    # 从缓存文件获取某天的涨停数据
    def get_limit_up_current_datas_from_file(self, day):
        current_path = self.__get_current_path(day)
        # 获取实时涨停
        if os.path.exists(current_path):
            with open(current_path, mode="r") as f:
                line = f.readline().strip()
                if line:
                    datas = json.loads(line)
                    return datas
        return None
    # 从缓存文件获取某天的历史涨停数据
    def get_limit_up_history_datas__from_file(self, day):
        record_apth = self.__get_record_path(day)
        if os.path.exists(record_apth):
            fdatas = []
            children = os.listdir(record_apth)
            for c in children:
                with open(f"{record_apth}\\{c}", mode="r") as f:
                    line = f.readline().strip()
                    if line:
                        data = json.loads(line)
                        fdatas.append(data)
            return fdatas
        return None
    def get_limit_up_history_datas_cache(self, day):
        if day not in self.__limit_up_history_datas_cache:
            results = self.get_limit_up_history_datas__from_file(day)
            if results:
                self.__limit_up_history_datas_cache[day] = results
        return self.__limit_up_history_datas_cache.get(day)
    def get_limit_up_current_datas_cache(self, day):
        if day not in self.__limit_up_current_datas_cache:
            results = self.get_limit_up_current_datas_from_file(day)
            if results:
                self.__limit_up_current_datas_cache[day] = results
        return self.__limit_up_current_datas_cache.get(day)
    @classmethod
    def __get_record_path(cls, day=None):
        base_path = cls.__get_base_path(day)
        return f"{base_path}\\record"
    @classmethod
    def __get_current_path(cls, day=None):
        base_path = cls.__get_base_path(day)
        return f"{base_path}\\limit_up.log"
    @classmethod
    def __get_base_path(cls, day=None):
        if not day:
            day = tool.get_now_date_str()
        return f"{os.getcwd()}\\datas\\{day}"
    def run(self):
        while True:
            try:
                if not tool.is_trade_time(tool.get_now_date_str()):
                    continue
                result = kpl_api.getLimitUpInfoNew()
                limit_up_datas = kpl_util.parseLimitUpData(result)
                KPLLimitUpDataManager().add_data(limit_up_datas)
            except:
                pass
            finally:
                time.sleep(3)
if __name__ == '__main__':
    print(KPLLimitUpDataManager().get_limit_up_current_datas())
    print(KPLLimitUpDataManager().get_limit_up_history_datas())
    input()
kpl/kpl_util.py
New file
@@ -0,0 +1,52 @@
import json
def __parseLimitUpItemData(data):
    # (代码, 名称, 首次涨停时间, 最近涨停时间, 几板, 涨停原因, 板块, 实际流通, 主力净额, 涨停原因代码, 涨停原因代码数量)
    return data[0], data[1], data[4], data[4], data[18], data[5], data[12], data[13], data[8], data[19], data[20]
def parseLimitUpData(data):
    if type(data) == str:
        data = json.loads(data)
    if int(data["errcode"]) != 0:
        raise Exception(f"解析数据出错,errcode:{data['errcode']}")
    list_ = data["list"]
    fresult_ = []
    for d in list_:
        pdata = __parseLimitUpItemData(d)
        if pdata:
            fresult_.append(pdata)
    return fresult_
def parseMarketIndustry(data):
    if type(data) == str:
        data = json.loads(data)
    if int(data["errcode"]) != 0:
        raise Exception(f"解析数据出错,errcode:{data['errcode']}")
    list_ = data["list"]
    fresult_ = []
    for d in list_:
        # (代码,名称,涨幅,主力净额)
        fresult_.append((d[0], d[1], d[3], d[6]))
    return fresult_
def parseMarketJingXuan(data):
    if type(data) == str:
        data = json.loads(data)
    if int(data["errcode"]) != 0:
        raise Exception(f"解析数据出错,errcode:{data['errcode']}")
    list_ = data["list"]
    fresult_ = []
    for d in list_:
        # (代码,名称,强度,主力净额)
        fresult_.append((d[0], d[1], d[2], d[6]))
    return fresult_
def filter_block(block):
    if not block:
        return block
    return block.replace("概念", "")
kpl/tool.py
@@ -1,5 +1,6 @@
import datetime
import decimal
import time
from threading import Thread
@@ -80,6 +81,18 @@
    return datetime.datetime.now().strftime("%Y-%m-%d")
def money_desc(money):
    if abs(money) > 100000000:
        return f"{round(money / 100000000, 2)}亿"
    else:
        return f"{round(money / 10000, 2)}万"
def time_format(timestamp):
    if timestamp:
        return time.strftime("%H:%M:%S", time.localtime(int(timestamp)))
    return ""
if __name__ == "__main__":
    print(trade_time_sub("13:00:00", "11:30:00"))
    print(trade_time_sub("13:00:00", "11:29:00"))
main.py
@@ -6,6 +6,8 @@
import time
from multiprocessing import freeze_support
import sys
from urllib.parse import urlparse, parse_qs
import torch
import win32gui
from PyQt5.QtGui import QFont, QPalette, QColor, QTextOption
@@ -19,10 +21,14 @@
import constant
import gui_wx
import network_util
from network_delegate_manager import LocalKanPanNetworkDelegate
from utils import network_util
import setting
import tool
from utils  import tool
import win32_util
from juejin_core import JueJinApi
from kpl import kpl_util, kpl_api
from kpl.kpl_data_manager import KPLLimitUpDataManager
freeze_support()
@@ -41,9 +47,23 @@
        self.__webview = webview
        self.signal_request.connect(self.__request_result_callback)
    def __http_request(self, url, callback_info):
        try:
            result = network_util.http_get(url)
            # 代理请求结果
            result, need_delegate = LocalKanPanNetworkDelegate.http_delegate_request(url)
            if not need_delegate:
                result = network_util.http_get(url)
            print("请求结果:", result)
            self.signal_request.emit(callback_info[0], callback_info[1], result)
            return result
        except Exception as e:
            logging.exception(e)
    def __socket_request(self, text, callback_info):
        try:
            result = network_util.socket_request(text)
            print("请求结果:", result)
            self.signal_request.emit(callback_info[0], callback_info[1], result)
            return result
@@ -62,10 +82,13 @@
        t1.setDaemon(True)
        t1.start()
    @pyqtSlot(str, result=str)
    def socket_request(self, text):
    @pyqtSlot(str, str)
    def socket_request(self, text, callback_info):
        print("socket_request", text)
        return network_util.socket_request(json.loads(text))
        callback_info = json.loads(callback_info)
        t1 = threading.Thread(target=lambda: self.__socket_request(text, callback_info))
        t1.setDaemon(True)
        t1.start()
    # 获取客户端ID
    @pyqtSlot(result=str)
@@ -236,7 +259,8 @@
        self.signal_update_kpl.emit(datas)
    def set_target_code(self, code):
        print("set_target_code")
        print("set_target_code", code)
        # 测试
        self.webview.page().runJavaScript(f"app.set_target_code('{code}')")
    def closeEvent(self, event):
@@ -382,7 +406,6 @@
        view_ = menubar.addMenu('&打开分时')
        view_.aboutToShow.connect(__show_main_callback)
    def __init__(self, wx_pipe):
        super().__init__()
        self.wx_pipe = wx_pipe
@@ -431,6 +454,7 @@
        # 后台运行
        t1.setDaemon(True)
        t1.start()
        threading.Thread(target=KPLLimitUpDataManager().run, daemon=True).start()
    def closeEvent(self, event):
        event.accept()
network_delegate_manager.py
New file
@@ -0,0 +1,246 @@
"""
网络代理管理器
"""
# 本地看盘网络代理
import decimal
import json
import time
from urllib.parse import parse_qs, urlparse
from juejin_core import JueJinApi
from kpl import kpl_util, kpl_api
from kpl.kpl_data_manager import KPLLimitUpDataManager
from utils import tool, xgb_api
class LocalKanPanNetworkDelegate:
    # 保存代码涨幅
    __code_limit_rate_dict = {}
    # HTTP代理请求
    @classmethod
    def http_delegate_request(cls, url):
        if url.startswith("/kpl/get_limit_up_list"):
            return cls.__get_limit_up_list(url)
        elif url.startswith("/kpl/get_market_data"):
            return cls.__get_market_data(url)
        elif url.startswith("/get_last_trade_day_reasons"):
            return cls.__get_last_trade_day_reasons(url)
        elif url.startswith("/get_xgb_limit_up_reasons"):
            return cls.__get_xgb_limit_up_reasons(url)
        return None, False
    @classmethod
    def __get_limit_up_list(cls, url):
        # (代码, 名称, 首次涨停时间, 最近涨停时间, 几板, 涨停原因, 板块, 实际流通, 主力净额, 涨停原因代码, 涨停原因代码数量)
        records = KPLLimitUpDataManager().get_limit_up_history_datas()
        currents = KPLLimitUpDataManager().get_limit_up_current_datas()
        current_codes = [d[0] for d in currents]
        fresult = []
        # 计算涨停时间排序
        record_reason_dict = {}
        current_reason_dict = {}
        for r in records:
            if r[5] not in record_reason_dict:
                record_reason_dict[r[5]] = []
            record_reason_dict[r[5]].append(r)
        for r in currents:
            if r[5] not in current_reason_dict:
                current_reason_dict[r[5]] = []
            current_reason_dict[r[5]].append(r)
        for k in record_reason_dict:
            record_reason_dict[k].sort(key=lambda x: x[2])
        for r in records:
            if r[0] in current_codes:
                limit_up_state = 1
            else:
                limit_up_state = 2
            rank = 0
            for i in range(len(record_reason_dict[r[5]])):
                if record_reason_dict[r[5]][i][0] == r[0]:
                    rank = i
            # (代码, 名称, 涨停状态(0 - 无状态 1-涨停 2-炸板), 龙几, 首板, 分值, 涨停时间, 原因, 相同原因代码数量, 自由流通, 涨停原因是否变化,涨停原因的流入净额,下单简介)
            fresult.append((r[0], r[1], limit_up_state, f"龙{rank + 1}", r[4], 0,
                            tool.time_format(int(r[2])), r[5], r[10], tool.money_desc(r[7]),
                            '', '', ''))
        # (板块名称,涨停代码数量,炸板数量,涨停时间)
        limit_up_reason_statistic_info = [(k, len(record_reason_dict[k]), len(record_reason_dict[k]) - len(
            current_reason_dict.get(k) if k in current_reason_dict else []),
                                           record_reason_dict[k][0][5]) for k in record_reason_dict]
        limit_up_reason_statistic_info.sort(key=lambda x: x[1] - x[2])
        limit_up_reason_statistic_info.reverse()
        result = json.dumps({"code": 0, "data": {"limit_up_count": len(currents),
                                                 "open_limit_up_count": len(records) - len(currents),
                                                 "limit_up_reason_statistic": limit_up_reason_statistic_info,
                                                 "limit_up_codes": fresult}})
        return result, True
    @classmethod
    def __get_market_data(cls, url):
        ps_dict = dict([(k, v[0]) for k, v in parse_qs(urlparse(url).query).items()])
        type_ = int(ps_dict['type'])
        result = []
        if type_ == 0:
            # 行业,主力净额倒序
            result = kpl_api.getMarketIndustryRealRankingInfo(True)
            result = kpl_util.parseMarketIndustry(result)
        elif type_ == 1:
            # 行业,主力净额顺序
            result = kpl_api.getMarketIndustryRealRankingInfo(False)
            result = kpl_util.parseMarketIndustry(result)
        elif type_ == 2:
            # 精选,主力净额倒序
            result = kpl_api.getMarketJingXuanRealRankingInfo(True)
            result = kpl_util.parseMarketJingXuan(result)
        elif type_ == 3:
            # 精选,主力净额顺序
            result = kpl_api.getMarketJingXuanRealRankingInfo(False)
            result = kpl_util.parseMarketJingXuan(result)
        fresult = []
        forbidden_plates = []
        for d in result:
            d = list(d)
            d.append(1 if d[1] in forbidden_plates else 0)
            fresult.append(d)
        response_data = json.dumps({"code": 0, "data": fresult})
        return response_data, True
    @classmethod
    def get_codes_limit_rate(cls, codes):
        return cls.__get_codes_limit_rate(codes)
    # 获取昨日收盘价
    __pre_close_cache = {}
    @classmethod
    def __get_codes_limit_rate(cls, codes):
        latest_info_codes = set()
        for code in codes:
            if code not in cls.__pre_close_cache:
                latest_info_codes.add(code)
        if latest_info_codes:
            datas = JueJinApi.get_gp_latest_info(latest_info_codes, fields="sec_id,pre_close")
            for data in datas:
                code = data["sec_id"]
                pre_close = data['pre_close']
                cls.__pre_close_cache[code] = pre_close
        current_info = JueJinApi.get_gp_current_info(codes, fields="symbol,price")
        now_prices = [(c["symbol"].split(".")[1], c["price"]) for c in current_info]
        f_results = []
        for data in now_prices:
            code = data[0]
            price = data[1]
            pre_price = float(cls.__pre_close_cache.get(code))
            rate = round((price - pre_price) * 100 / pre_price, 2)
            f_results.append((code, rate))
        f_results.sort(key=lambda tup: tup[1])
        f_results.reverse()
        return f_results
    @classmethod
    def __get_limit_rate_list(cls, codes):
        if not codes:
            return []
        need_request_codes = set()
        if tool.trade_time_sub(tool.get_now_time_str(), "09:30:00") < 0:
            need_request_codes |= set(codes)
        else:
            now_time = time.time()
            for c in codes:
                if c not in cls.__code_limit_rate_dict:
                    need_request_codes.add(c)
                elif now_time - cls.__code_limit_rate_dict[c][1] > 60:
                    need_request_codes.add(c)
        if need_request_codes:
            _limit_rate_list = cls.__get_codes_limit_rate(list(need_request_codes))
            for d in _limit_rate_list:
                cls.__code_limit_rate_dict[d[0]] = (d[1], time.time())
        return [(c_, cls.__code_limit_rate_dict[c_][0]) for c_ in codes]
    @classmethod
    def __get_last_trade_day_reasons(cls, url):
        # 获取 昨日涨停数据
        ps_dict = dict([(k, v[0]) for k, v in parse_qs(urlparse(url).query).items()])
        code = ps_dict["code"]
        # 获取昨日涨停数据
        day = JueJinApi.get_previous_trading_date_cache(tool.get_now_date_str())
        # (代码, 名称, 首次涨停时间, 最近涨停时间, 几板, 涨停原因, 板块, 实际流通, 主力净额, 涨停原因代码, 涨停原因代码数量)
        limit_up_records = KPLLimitUpDataManager().get_limit_up_history_datas_cache(day)
        if not limit_up_records:
            limit_up_records = []
        reasons = []
        for d in limit_up_records:
            if d[0] == code:
                reasons.append(d)
        # 获取代码的原因
        if reasons:
            reasons = list(reasons)
            reasons.sort(key=lambda x: x[9])
            reason = reasons[-1][5]
            # 获取涨停数据
            datas = KPLLimitUpDataManager().get_limit_up_current_datas_cache(day)
            # (代码,名称,首次涨停时间,最近涨停时间,几板,涨停原因,板块,实际流通,主力净额,涨停原因代码,涨停原因代码数量)
            yesterday_result_list = []
            percent_rate = 0
            if datas:
                yesterday_codes = set()
                for d in datas:
                    if d[5] == reason:
                        yesterday_codes.add(d[0])
                # 获取涨幅
                limit_rate_list = cls.__get_limit_rate_list(yesterday_codes)
                limit_rate_dict = {}
                if limit_rate_list:
                    total_rate = 0
                    for d in limit_rate_list:
                        limit_rate_dict[d[0]] = d[1]
                        total_rate += d[1]
                    percent_rate = round(total_rate / len(limit_rate_list), 2)
                for d in datas:
                    if d[5] == reason:
                        yesterday_codes.add(d[0])
                        if d[0] != code:
                            # (代码,名称, 涨幅)
                            yesterday_result_list.append((d[0], d[1], limit_rate_dict.get(d[0])))
            current_limit_up_list = KPLLimitUpDataManager().get_limit_up_current_datas()
            current_result_list = []
            if current_limit_up_list:
                for c in current_limit_up_list:
                    if c[5] == reason and c[0] != code:
                        current_result_list.append((c[0], c[1]))
            response_data = json.dumps({"code": 0, "data": {"reason": reason, "reason_rate": percent_rate,
                                                            "data": {"yesterday": yesterday_result_list,
                                                                     "current": current_result_list}}})
        else:
            response_data = json.dumps({"code": 1, "msg": "昨日未涨停"})
        return response_data, True
    @classmethod
    def __get_xgb_limit_up_reasons(cls, url):
        # 获取 昨日涨停数据
        ps_dict = dict([(k, v[0]) for k, v in parse_qs(urlparse(url).query).items()])
        code = ps_dict["code"]
        reasons = xgb_api.get_code_limit_up_reasons(code)
        if reasons:
            response_data = json.dumps({"code": 0, "data": ("、".join(reasons[0]), reasons[1])})
        else:
            response_data = json.dumps({"code": 1, "msg": "尚未获取到涨停原因"})
        return response_data, True
if __name__ == "__main__":
    codes = ["002523", "603095"]
    datas = LocalKanPanNetworkDelegate.get_codes_limit_rate(codes)
    datas = LocalKanPanNetworkDelegate.get_codes_limit_rate(codes)
    print(datas)
res/codes.txt
@@ -1 +1,3 @@
002445
600228
603787
002186
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 = [60, 80, 1115, 858]
xgb_window_info = [-1742, 419, 1291, 912]
window_watch_float_info = [146, 419, 435, 220]
window_tick_info = [-1371, 218, 1227, 661]
kp_second_window_info = [397, 140, 721, 843]
window_tick_info = [-1497, 356, 1227, 661]
kp_second_window_info = [-960, 127, 678, 971]
code_attribute_window_info = [-650, 315, 291, 278]
client = hxh
float_frame_auto_focus = 1
sell_processor.py
@@ -1,5 +1,5 @@
import juejin_core
import tool
from utils import tool
class TickDataProcess:
socket_util.py
File was deleted
test_kpl.py
New file
@@ -0,0 +1,59 @@
import base64
import ssl
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
def decrypt_ssl():
    context = ssl.SSLContext(ssl.PROTOCOL_TLS)
    # 加载证书和密钥
    context.load_cert_chain(certfile='D:/文件传输/交易/接口解析/com.aiyu.kaipanla.cert.pem', keyfile='D:/文件传输/交易/接口解析/PrivateKey1.pem')
    # 使用 SSL 上下文创建一个解密器对象
    decrypter = context.decryptor()
    # 加密的文本字符串
    encrypted_data = b'\xb0\x1d\x96\x82\xcd\x7f\xf2Q'
    # 解码加密的文本
    decrypted_data = decrypter.update(encrypted_data) + decrypter.finalize()
    # 打印解码后的文本
    print(decrypted_data)
if __name__ == '__main__':
    k =[1,0]
    is_s = True if k and k[1] else False
    print(is_s)
if __name__ == '__main__1':
    with open("D:\\文件传输\\交易\\接口解析\\PrivateKey1.pem", "rb") as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
        )
        print(private_key)
    with open("D:\\文件传输\\交易\\接口解析\\PublicKey.pem", "rb") as key_file:
        public_key = serialization.load_pem_public_key(
            key_file.read(),
        )
        print(public_key)
    content = b""
    for i in range(10):
        content += b"Hello World 123123"
    encrypted = public_key.encrypt(content, padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    ))
    print(encrypted)
    decrypted = private_key.decrypt(encrypted, padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    ))
    print(decrypted)
utils/log_util.py
@@ -3,7 +3,7 @@
from loguru import logger
import tool
from utils import tool
class MyLogger:
utils/network_util.py
File was renamed from network_util.py
@@ -4,10 +4,13 @@
import requests
import constant
from utils import socket_util
SOCKET_PORT = 9001
SOCKET_PORT = 11008
# B类
HTTP_PORT = 9004
# A类
# HTTP_PORT = 9005
@@ -16,11 +19,15 @@
    client = socket.socket()  # 生成socket,连接server
    ip_port = (constant.SERVER_HOST, SOCKET_PORT)  # server地址和端口号(最好是10000以后)
    client.connect(ip_port)
    client.send(json.dumps(data).encode("utf-8"))
    if type(data) != str:
        client.send(socket_util.load_header(json.dumps(data).encode("utf-8")))
    else:
        client.send(socket_util.load_header(data.encode("utf-8")))
    # 读取内容
    result = client.recv(102400)
    result, header = socket_util.recv_data(client)
    print("socket_request结果:", result)
    client.close()
    return result.decode("gbk")
    return result
def http_get(path):
utils/ocr_util.py
utils/opencv_util.py
utils/socket_util.py
New file
@@ -0,0 +1,182 @@
"""
socket工具类
"""
# 添加数据头
import base64
import hashlib
import json
import socket
import ssl
import rsa
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
def test_rsa():
    def format_rsa(private_key_bytes):
        private_key_bytes = private_key_bytes.replace(b"\r\n", b"").replace(b"\n", b"")
        fbytes = []
        row = len(private_key_bytes) // 64
        if len(private_key_bytes) % 64 > 0:
            row += 1
        for i in range(0, row):
            if i != row - 1:
                fbytes.append(private_key_bytes[i * 64:(i + 1) * 64])
            else:
                fbytes.append(private_key_bytes[i * 64:])
        fbs = b'-----BEGIN RSA PRIVATE KEY-----\n'+b"\n".join(fbytes)+b'\n-----END RSA PRIVATE KEY-----\n'
        return fbs
    # with open("D:\\项目\\三方工具\\dubbo\\dubbo-admin\\dubbo-admin-ui\\node_modules\\public-encrypt\\test\\test_rsa_privkey.pem", mode='rb') as f:
    #     private_key_data = f.read()
    #     print(private_key_data)
    with open("D:\\文件传输\\交易\\接口解析\\PrivateKey", mode='rb') as f:
        private_key_data = f.read()
        private_key_data = format_rsa(private_key_data)
    results = []
    with open("D:\\文件传输\\交易\\日志文件\\hex.txt", mode='r') as f:
        lines = f.readlines()
        for line in lines:
            if not line:
                continue
            results.append(line[8:].strip())
    hex_bytes = bytes.fromhex("".join(results))
    print(private_key_data.decode())
    # 将私钥字符串解码为 RSA 私钥对象
    private_key = rsa.PrivateKey._load_pkcs1_der(private_key_data)
    # 将 base64 编码的字符串解码为 bytes,并进行 RSA 解密
    encrypted_data = hex_bytes
    decrypted_data = rsa.decrypt(
        encrypted_data,
        private_key
    )
    # 将解密后的 bytes 对象转换为字符串
    decrypted_data_str = decrypted_data.decode()
    print(decrypted_data_str)
if __name__ == "__main__":
    test_rsa()
if __name__ == "__main__1":
    results = []
    with open("D:\\文件传输\\交易\\日志文件\\hex.txt", mode='r') as f:
        lines = f.readlines()
        for line in lines:
            if not line:
                continue
            results.append(line[8:].strip())
    hex_bytes = bytes.fromhex("".join(results))
    # SSL解码
    context = ssl.SSLContext(ssl.PROTOCOL_TLS)
    with open("D:/文件传输/交易/接口解析/com.aiyu.kaipanla.cert.pem", mode='r') as f:
        print(f.readlines())
    context.load_cert_chain(certfile='D:/文件传输/交易/接口解析/com.aiyu.kaipanla.cert.pem')
    decrypter = context.decryptor()
    decrypted_data = decrypter.update(hex_bytes) + decrypter.finalize()
    # 打印解码后的文本
    print(decrypted_data)
    # client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 生成socket,连接server
    # client.connect(("appsockapp.longhuvip.com", 14000))
    # client.send(hex_bytes)
    # print("返回数据", client.recv(10240))
utils/ths_util.py
utils/tool.py
File was renamed from tool.py
@@ -1,5 +1,6 @@
import datetime
import decimal
import time
from threading import Thread
@@ -76,9 +77,22 @@
    # return "10:20:00"
def get_now_date_str():
def get_now_date_str(format="%Y-%m-%d"):
    # TODO 测试
    return datetime.datetime.now().strftime("%Y-%m-%d")
    return datetime.datetime.now().strftime(format)
def money_desc(money):
    if abs(money) > 100000000:
        return f"{round(money / 100000000, 2)}亿"
    else:
        return f"{round(money / 10000, 2)}万"
def time_format(timestamp):
    if timestamp:
        return time.strftime("%H:%M:%S", time.localtime(int(timestamp)))
    return ""
if __name__ == "__main__":
utils/xgb_api.py
New file
@@ -0,0 +1,39 @@
import json
import time
import requests
from utils import tool
def get_limit_up_codes_infos():
    url = f"https://flash-api.xuangubao.cn/api/surge_stock/stocks?date={tool.get_now_date_str('%Y%m%d')}&normal=true&uplimit=true"
    response = requests.get(url)
    print(response.text)
    return response.text
__limit_up_codes_infos_cache = None
# 获取涨停列表的原因
def get_code_limit_up_reasons(code):
    global __limit_up_codes_infos_cache
    if __limit_up_codes_infos_cache is None or time.time() - __limit_up_codes_infos_cache[1] > 10:
        result = get_limit_up_codes_infos()
        result = json.loads(result)
        if result["code"] == 20000:
            items = result["data"]["items"]
            __limit_up_codes_infos_cache = (items, time.time())
    if __limit_up_codes_infos_cache:
        for item in __limit_up_codes_infos_cache[0]:
            if item[0].split(".")[0] == code:
                reasons = [x["name"] for x in item[8]]
                return reasons, item[5]
    return None
if __name__ == '__main__':
    print(get_code_limit_up_reasons("002641"))
    print(get_code_limit_up_reasons("002610"))