third_data/code_plate_key_manager.py
@@ -4,40 +4,73 @@
# 涨停代码关键词板块管理
import copy
import datetime
import json
import time
import constant
from code_attribute import code_nature_analyse
from db.redis_manager_delegate import RedisUtils
from third_data import kpl_block_util, kpl_api, kpl_util
from third_data import kpl_block_util, kpl_api, kpl_util, kpl_data_constant, huaxin_l1_data_manager
from settings.trade_setting import MarketSituationManager
from third_data.kpl_data_constant import LimitUpDataConstant, LimitUpCodesBlockRecordManager
from third_data.third_blocks_manager import BlockMapManager, CodeThirdBlocksManager
from trade.buy_money_count_setting import RadicalBuyBlockCodeCountManager
from trade.order_statistic import DealAndDelegateWithBuyModeDataManager
from trade.radical_buy_data_manager import RadicalBuyDataManager, BlockPlaceOrderRecordManager
from trade.trade_data_manager import RadicalBuyDealCodesManager
from utils import global_util, tool, buy_condition_util
from log_module import log, async_log_util
from db import redis_manager_delegate as redis_manager
from log_module.log import logger_kpl_block_can_buy
from log_module.log import logger_kpl_block_can_buy, logger_debug, logger_kpl_jx_out
from third_data.kpl_util import KPLPlatManager
from trade import trade_manager, l2_trade_util, trade_constant
from trade import trade_manager, l2_trade_util, trade_constant, radical_buy_data_manager
# 代码精选板块管理
from utils.kpl_data_db_util import KPLLimitUpDataUtil
class KPLCodeJXBlockManager:
    __db = 3
    __redisManager = redis_manager.RedisManager(3)
    __code_blocks = {}
    # 备用
    __code_by_blocks = {}
    # 激进买的代码板块
    __code_blocks_for_radical_buy = {}
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(KPLCodeJXBlockManager, cls).__new__(cls, *args, **kwargs)
            cls.__load_data()
        return cls.__instance
    def __get_redis(self):
        return self.__redisManager.getRedis()
    @classmethod
    def __load_data(cls):
        keys = RedisUtils.keys(cls.__get_redis(), "kpl_jx_blocks_by-*")
        if keys:
            for k in keys:
                val = RedisUtils.get(cls.__get_redis(), k)
                val = json.loads(val)
                cls.__code_by_blocks[k.split("-")[1]] = (val, time.time())
        keys = RedisUtils.keys(cls.__get_redis(), "kpl_jx_blocks-*")
        if keys:
            for k in keys:
                val = RedisUtils.get(cls.__get_redis(), k)
                val = json.loads(val)
                cls.__code_blocks[k.split("-")[1]] = (val, time.time())
        keys = RedisUtils.keys(cls.__get_redis(), "kpl_jx_blocks_radical-*")
        if keys:
            for k in keys:
                val = RedisUtils.get(cls.__get_redis(), k)
                val = json.loads(val)
                cls.__code_blocks_for_radical_buy[k.split("-")[1]] = (val, time.time())
    @classmethod
    def __get_redis(cls):
        return cls.__redisManager.getRedis()
    def save_jx_blocks(self, code, blocks: list, current_limit_up_blocks: set, by=False):
        if not blocks:
@@ -58,28 +91,18 @@
            RedisUtils.setex_async(self.__db, f"kpl_jx_blocks-{code}", tool.get_expire(), json.dumps(final_blocks))
            self.__code_blocks[code] = (final_blocks, time.time())
    # 获取精选板块
    def get_jx_blocks(self, code, by=False):
        if by:
            if code in self.__code_by_blocks:
                return self.__code_by_blocks[code]
            val = RedisUtils.get(self.__get_redis(), f"kpl_jx_blocks_by-{code}")
            if val is None:
                return None
            else:
                val = json.loads(val)
                self.__code_by_blocks[code] = val
            return self.__code_by_blocks[code]
        else:
            if code in self.__code_blocks:
                return self.__code_blocks[code]
            val = RedisUtils.get(self.__get_redis(), f"kpl_jx_blocks-{code}")
            if val is None:
                return None
            else:
                val = json.loads(val)
                self.__code_blocks[code] = val
            return self.__code_blocks[code]
    def save_jx_blocks_for_radical_buy(self, code, blocks: list):
        if not blocks:
            return
        RedisUtils.setex_async(self.__db, f"kpl_jx_blocks_radical-{code}", tool.get_expire(), json.dumps(blocks))
        self.__code_blocks_for_radical_buy[code] = (blocks, time.time())
    # 获取精选板块(激进买)
    def get_jx_blocks_radical(self, code):
        blocks_info = self.__code_blocks_for_radical_buy.get(code)
        if blocks_info:
            return set(blocks_info[0])
        return None
    def get_jx_blocks_cache(self, code, by=False):
        if by:
@@ -104,6 +127,8 @@
                        async_log_util.info(logger_kpl_block_can_buy,
                                            f"{code}:获取到精选板块-{blocks}  耗时:{int(time.time() - start_time)}s")
                        self.save_jx_blocks(code, blocks, current_limit_up_blocks)
                        # 跟随精选板块一起更新
                        self.load_jx_blocks_radical(code)
                    else:
                        # 还没涨停的需要更新精选板块 更新精选板块
                        if abs(float(buy_1_price) - float(limit_up_price)) >= 0.001:
@@ -124,8 +149,8 @@
                                async_log_util.info(logger_kpl_block_can_buy,
                                                    f"{code}:获取到精选板块(更新)-{blocks}  耗时:{int(time.time() - start_time)}s")
                                self.save_jx_blocks(code, blocks, current_limit_up_blocks)
                                # 跟随精选板块一起更新
                                self.load_jx_blocks_radical(code)
                elif price_rate > 0.03:
                    # 添加备用板块
                    if not self.get_jx_blocks_cache(code, by=True):
@@ -134,9 +159,24 @@
                        self.save_jx_blocks(code, blocks, current_limit_up_blocks, by=True)
                        async_log_util.info(logger_kpl_block_can_buy,
                                            f"{code}:获取到精选板块(备用)-{blocks}  耗时:{int(time.time() - start_time)}s")
                        # 跟随精选板块一起更新
                        self.load_jx_blocks_radical(code)
                if price_rate > 0.03:
                    if not self.__code_blocks_for_radical_buy.get(code):
                        self.load_jx_blocks_radical(code)
        except Exception as e:
            logger_kpl_block_can_buy.error(f"{code} 获取板块出错")
            logger_kpl_block_can_buy.exception(e)
    def load_jx_blocks_radical(self, code):
        start_time = time.time()
        blocks = kpl_api.getCodeJingXuanBlocks(code, jx=False)
        blocks = set([b[1] for b in blocks])
        # fblocks = BlockMapManager().filter_blocks(blocks)
        async_log_util.info(logger_kpl_block_can_buy,
                            f"{code}:获取到板块(激进买) 过滤前-{blocks} 耗时:{int(time.time() - start_time)}s")
        self.save_jx_blocks_for_radical_buy(code, list(blocks))
# 禁止下单的板块
@@ -254,12 +294,6 @@
    def get_today_limit_up_reason(cls, code):
        return cls.__today_total_limit_up_reason_dict.get(code)
    # 今日涨停原因变化
    def set_today_limit_up_reason_change(self, code, from_reason, to_reason):
        RedisUtils.sadd(self.__get_redis(), f"kpl_limit_up_reason_his-{code}", from_reason)
        RedisUtils.expire(self.__get_redis(), f"kpl_limit_up_reason_his-{code}", tool.get_expire())
        self.__set_total_keys(code)
    # 设置代码的今日涨停原因
    def __set_total_keys(self, code):
        keys = set()
@@ -311,31 +345,54 @@
    __KPLPlateForbiddenManager = KPLPlateForbiddenManager()
    __LimitUpCodesPlateKeyManager = LimitUpCodesPlateKeyManager()
    __KPLPlatManager = KPLPlatManager()
    # 精选流入前几
    __top_jx_blocks = set()
    # 精选流出前几
    __top_jx_out_blocks = set()
    @classmethod
    def set_top_5_reasons(cls, datas):
        temp_list = []
        for d in datas:
            cls.total_reason_dict[d[1]] = d
        # 排序
        for i in range(0, len(datas)):
            if datas[i][1] not in constant.KPL_INVALID_BLOCKS:
                # (名称,净流入金额,排名)
                temp_list.append((datas[i][1], datas[i][3], len(temp_list)))
                # 只获取前10个
                if len(temp_list) > 10:
                    break
                if datas[i][3] < 3 * 10000 * 10000:
                    break
    def set_market_jingxuan_blocks(cls, datas):
        """
        设置精选流入数据
        @param datas:
        @return:
        """
        blocks = set()
        for data in datas:
            if data[3] <= 0:
                break
            blocks.add(data[1])
        cls.__top_jx_blocks = blocks
        for temp in temp_list:
            names = cls.__KPLPlatManager.get_same_plat_names_by_id(temp[0])
            for name in names:
                if name == temp[1]:
                    continue
                temp_list.append((name, temp[1], temp[2]))
        cls.top_5_reason_list = temp_list
        cls.__reset_top_5_dict()
    @classmethod
    def set_market_jingxuan_out_blocks(cls, datas):
        """
        设置精选流出数据
        @param datas:
        @return:
        """
        blocks = set()
        for i in range(0, len(datas)):
            if i >= 10 and int(tool.get_now_time_str().replace(":", "")) < int("100000"):
                # 10点前看前10,十点后不看前10
                break
            data = datas[i]
            if data[3] > 0 - 5e7:
                # 过滤5千万以上的
                break
            blocks.add(kpl_util.filter_block(data[1]))
        # 记录精选流出日志
        async_log_util.info(logger_kpl_jx_out, f"原数据:{datas[:10]} 板块:{blocks}")
        cls.__top_jx_out_blocks = blocks
    @classmethod
    def get_top_market_jingxuan_blocks(cls):
        return cls.__top_jx_blocks
    @classmethod
    def get_top_market_jingxuan_out_blocks(cls):
        return cls.__top_jx_out_blocks
    @classmethod
    def set_top_5_industry(cls, datas):
@@ -368,35 +425,6 @@
        temp_set = cls.top_5_key_dict.keys()
        return temp_set
    # 通过关键字判断能买的代码数量
    @classmethod
    def get_can_buy_codes_count(cls, code, key):
        # 判断行业涨停票数量,除开自己必须大于1个
        temp_codes = LimitUpCodesPlateKeyManager.total_key_codes_dict.get(key)
        if temp_codes is None:
            temp_codes = set()
        else:
            temp_codes = set(temp_codes)
        temp_codes.discard(code)
        if len(temp_codes) < 1:
            # 后排才能挂单
            return 0, "身位不为后排"
        forbidden_plates = cls.__KPLPlateForbiddenManager.list_all_cache()
        if key in forbidden_plates:
            return 0, "不买该板块"
        # 10:30以前可以挂2个单
        if int(tool.get_now_time_str().replace(':', '')) < int("100000"):
            return 2, "10:00以前可以挂2个单"
        # 10:30以后
        if key not in cls.top_5_key_dict:
            return 0, "净流入没在前5"
        if cls.top_5_key_dict[key][1] > 3 * 10000 * 10000:
            return 2, "净流入在前5且大于3亿"
        else:
            return 1, "净流入在前5"
    @classmethod
    def is_in_top(cls, keys):
        reasons = cls.get_can_buy_key_set()
@@ -416,6 +444,14 @@
    __history_limit_up_reason_dict = {}
    # 板块
    __blocks_dict = {}
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(CodesHisReasonAndBlocksManager, cls).__new__(cls, *args, **kwargs)
        return cls.__instance
    def __get_redis(self):
        return self.__redisManager.getRedis()
@@ -473,6 +509,41 @@
            blocks = set()
        return reasons | blocks
    __history_blocks_dict_cache = {}
    def get_history_blocks(self, code):
        """
        获取180天的历史涨停原因
        @param code:
        @return:
        """
        if code in self.__history_blocks_dict_cache:
            return self.__history_blocks_dict_cache.get(code)
        try:
            kpl_results = KPLLimitUpDataUtil.get_latest_block_infos(code=code)
            # 取最近2条数据
            if kpl_results and len(kpl_results) > 2:
                kpl_results = kpl_results[-2:]
            keys = set()
            if kpl_results:
                keys |= set([x[2] for x in kpl_results])
            for r in kpl_results:
                if r[3]:
                    keys |= set(r[3].split("、"))
            self.__history_blocks_dict_cache[code] = keys
            return keys
        except:
            pass
        return set()
    def get_history_blocks_cache(self, code):
        """
        获取180天的历史涨停原因缓存
        @param code:
        @return:
        """
        return self.__history_blocks_dict_cache.get(code)
# 目标代码板块关键词管理
class TargetCodePlateKeyManager:
@@ -484,9 +555,9 @@
        return self.__redisManager.getRedis()
    # 返回key集合(排除无效板块),今日涨停原因,今日历史涨停原因,历史涨停原因,二级,精选板块
    def get_plate_keys(self, code):
    def get_plate_keys(self, code, contains_today=True):
        """
        获取代码的板块
        获取代码的板块: (180天的涨停原因+推荐原因)+今日涨停原因+今日涨停推荐原因+今日推荐原因
        @param code:
        @return: (板块关键词集合,今日涨停原因+涨停推荐原因,今日历史涨停原因,历史涨停原因,精选板块)
        """
@@ -501,10 +572,13 @@
        k2 = self.__CodesPlateKeysManager.get_history_limit_up_reason_cache(code)
        if k2 is None:
            k2 = set()
        k3 = set()
        industry = global_util.code_industry_map.get(code)
        if industry:
            k3 = {industry}
        k3 = self.__CodesPlateKeysManager.get_history_blocks(code)
        if k3:
            keys |= k3
        # industry = global_util.code_industry_map.get(code)
        # if industry:
        #     k3 = {industry}
        k4 = set()
        jingxuan_block_info = self.__KPLCodeJXBlockManager.get_jx_blocks_cache(code)
@@ -513,15 +587,22 @@
        if jingxuan_block_info:
            jingxuan_blocks = jingxuan_block_info[0]
            k4 |= set(jingxuan_blocks)  # set([x[1] for x in jingxuan_blocks])
        if k1:
        if k1 and contains_today:
            # 涨停过
            keys |= k1
            keys = keys - set(constant.KPL_INVALID_BLOCKS)
        if not keys:
            # 获取不到涨停原因
        # 获取不到涨停原因
        if contains_today:
            keys |= k4
            keys = keys - set(constant.KPL_INVALID_BLOCKS)
        keys = keys - set(constant.KPL_INVALID_BLOCKS)
        return keys, k1, k11, k2, k3, k4
    def get_plate_keys_for_radical_buy(self, code):
        """
        激进买入的板块
        @param code:
        @return:
        """
class CodePlateKeyBuyManager:
@@ -553,12 +634,12 @@
        l2_trade_util.forbidden_trade(code, msg=msg)
        logger_kpl_block_can_buy.info(msg)
    # 是否需要激进买
    # 是否需要积极买
    @classmethod
    def __is_need_active_buy(cls, code, block, current_rank, open_limit_up_count):
        """
        板块是否需要激进买入
        规则:根据身位判断是否需要激进买,根据时间划分
        板块是否需要积极买入
        规则:根据身位判断是否需要积极买,根据时间划分
        @param code: 代码
        @param block: 板块名称
        @param current_rank: 目前在板块中的身位,从0开始
@@ -580,7 +661,7 @@
                break
        return False
    # 返回内容(是否可买, 是否为独苗, 描述信息, 是否为强势主线, 是否需要激进买)
    # 返回内容(是否可买, 是否为独苗, 描述信息, 是否为强势主线, 是否需要积极买)
    @classmethod
    def __is_block_can_buy(cls, code, block, current_limit_up_datas, code_limit_up_reasons_dict,
                           yesterday_current_limit_up_codes, limit_up_record_datas, current_limit_up_block_codes_dict,
@@ -597,7 +678,7 @@
        if not block_codes:
            # 高位板泛化板块中无板块
            if not high_level_block_codes.get(block):
                return False, True, f"{block}:板块无涨停", False, False
                return False, True, f"【{block}】:板块无涨停", False, False
        elif len(block_codes) == 1 and code in block_codes:
            if not high_level_block_codes.get(block):
                return False, True, f"{block}:板块只有当前代码涨停", False, False
@@ -663,7 +744,7 @@
                                                                                                    current_open_limit_up_codes),
                                                                                                shsz=True,
                                                                                                limit_up_time=first_limit_up_time)
        # 计算是否需要激进买入
        # 计算是否需要积极买入
        is_active_buy = cls.__is_need_active_buy(code, block, current_shsz_rank, len(current_open_limit_up_codes))
        # record_shsz_rank, record_shsz_rank_codes = kpl_block_util.get_code_record_rank(code, block,
@@ -747,7 +828,7 @@
        if not block_codes:
            # 高位板泛化板块中无板块
            if not high_level_block_codes.get(block):
                return False, True, f"{block}:板块无涨停", False, False, 0, 0, 0
                return False, True, f"【{block}】:板块无涨停", False, False, 0, 0, 0
        elif len(block_codes) == 1 and code in block_codes:
            if not high_level_block_codes.get(block):
                return False, True, f"{block}:板块只有当前代码涨停", False, False, 0, 0, 0
@@ -796,7 +877,7 @@
                                                                                                shsz=True,
                                                                                                limit_up_time=first_limit_up_time)
        # 计算是否需要激进买入
        # 计算是否需要积极买入
        is_active_buy = cls.__is_need_active_buy(code, block, current_shsz_rank, len(current_open_limit_up_codes))
        if current_shsz_rank < len(current_open_limit_up_codes) + max_rank:
@@ -823,21 +904,26 @@
        # 加载涨停代码的目标板块
        def load_code_block():
            if limit_up_record_datas:
                # 获取今日9:30以前的时间
                time_str = datetime.datetime.now().strftime("%Y-%m-%d") + " 09:30:00"
                timestamp = time.mktime(time.strptime(time_str, '%Y-%m-%d %H:%M:%S'))
                for d in limit_up_record_datas:
                    if d[2] in constant.KPL_INVALID_BLOCKS and d[3] in before_blocks_dict:
                        code_limit_up_reasons_dict[d[3]] = {list(before_blocks_dict.get(d[3]))[0]}
                    else:
                        code_limit_up_reasons_dict[d[3]] = {d[2]}
                        # 不包含推荐原因
                        # if d[6]:
                        #     code_limit_up_reasons_dict[d[3]] |= set(d[6].split("、"))
                        # 开1才能包含推荐原因
                        if d[6] and int(d[5]) < timestamp:
                            code_limit_up_reasons_dict[d[3]] |= set(d[6].split("、"))
            return code_limit_up_reasons_dict
        if current_limit_up_datas is None:
            current_limit_up_datas = []
        # 获取目标代码板块
        keys, k1, k11, k2, k3, k4 = cls.__TargetCodePlateKeyManager.get_plate_keys(code)
        # keys, k1, k11, k2, k3, k4 = cls.__TargetCodePlateKeyManager.get_plate_keys(code)
        keys, k1 = RadicalBuyBlockManager.get_code_blocks(code)
        # log.logger_kpl_debug.info("{}最终关键词:{}", code, keys)
@@ -846,6 +932,7 @@
        fresults = []
        if not keys:
            return fresults, set()
        code_limit_up_reasons_dict = {}
        load_code_block()
        for block in keys:
@@ -864,7 +951,7 @@
    # 是否可以下单
    # 返回:可以买的板块,是否独苗,消息
    #  可买的板块, 是否独苗, 消息, 可买的强势板块, 关键词, 激进买的板块
    #  可买的板块, 是否独苗, 消息, 可买的强势板块, 关键词, 积极买的板块
    @classmethod
    def can_buy(cls, code):
        if constant.TEST:
@@ -874,7 +961,7 @@
        #     return True, "不判断板块身位"
        return cls.__can_buy_compute_result_dict.get(code)
    # 返回:(可以买的板块列表, 是否是独苗, 消息简介,可买的强势主线, 激进买入板块列表)
    # 返回:(可以买的板块列表, 是否是独苗, 消息简介,可买的强势主线, 积极买入板块列表)
    @classmethod
    def __compute_can_buy_blocks(cls, code, current_limit_up_datas, limit_up_record_datas,
                                 yesterday_current_limit_up_codes, before_blocks_dict,
@@ -937,7 +1024,7 @@
        msg_list = []
        active_buy_blocks = []
        for r in blocks_compute_results:
            # r的数据结构(板块,是否可以买,是否独苗,消息,是否是强势板块, 激进买入信息)
            # r的数据结构(板块,是否可以买,是否独苗,消息,是否是强势板块, 积极买入信息)
            if r[2]:
                # 独苗
                unique_count += 1
@@ -962,7 +1049,7 @@
                    msg_list.append(r[3])
                if r[5]:
                    active_buy_blocks.append(r[0])
                    msg_list.append(f"【{r[0]}】激进买入({r[5]})")
                    msg_list.append(f"【{r[0]}】积极买入({r[5]})")
            else:
                if r[3]:
                    msg_list.append(r[3])
@@ -1016,5 +1103,614 @@
            can_buy_blocks, unique, msg, can_buy_strong_blocks, keys, active_buy_blocks)
class RadicalBuyBlockManager:
    """
    扫入买板块管理
    """
    __TargetCodePlateKeyManager = TargetCodePlateKeyManager()
    # 上次的涨停代码
    __last_limit_up_codes = set()
    # 记录代码的涨停时间
    __limit_up_time_dict = {}
    # 炸板代码的总涨停时间
    __total_limit_up_space_dict = {}
    # 当前涨停的代码
    __current_limit_up_codes = set()
    @classmethod
    def set_current_limit_up_datas(cls, current_limit_up_datas):
        # 查询当前的涨停代码集合
        codes = set([d[0] for d in current_limit_up_datas])
        cls.__current_limit_up_codes = codes
        try:
            # 炸板代码
            break_limit_up_codes = cls.__last_limit_up_codes - codes
            # 新涨停的代码
            new_limit_up_codes = codes - cls.__last_limit_up_codes
            if new_limit_up_codes:
                for code in new_limit_up_codes:
                    if code not in cls.__limit_up_time_dict:
                        cls.__limit_up_time_dict[code] = time.time()
            if break_limit_up_codes:
                # 记录总涨停时间
                for bc in break_limit_up_codes:
                    if bc in cls.__limit_up_time_dict:
                        space = tool.trade_time_sub(tool.get_now_time_str(),
                                                    tool.to_time_str(cls.__limit_up_time_dict[bc]))
                        if bc not in cls.__total_limit_up_space_dict:
                            cls.__total_limit_up_space_dict[bc] = 0
                        cls.__total_limit_up_space_dict[bc] = cls.__total_limit_up_space_dict[bc] + space
                        logger_debug.info(f"炸板代码涨停时间:{bc}-{cls.__total_limit_up_space_dict[bc]}")
                        cls.__limit_up_time_dict.pop(bc)
        except Exception as e:
            logger_debug.exception(e)
        finally:
            cls.__last_limit_up_codes = codes
        cls.compute_open_limit_up_code_dict_for_radical_buy(current_limit_up_datas)
    @classmethod
    def compute_open_limit_up_code_dict_for_radical_buy(cls, current_limit_up_datas):
        """
        计算开1的代码信息,不包含5板以上的
        @param current_limit_up_datas:
        @return:
        """
        timestamp_start, timestamp_end = kpl_block_util.open_limit_up_time_range
        temp_dict = {}
        for d in current_limit_up_datas:
            code = d[0]
            # d: (代码, 名称, 首次涨停时间, 最近涨停时间, 几板, 涨停原因, 板块, 实际流通, 主力净额,涨停原因代码,涨停原因代码数量)
            # 计算是否开1
            if int(d[2]) >= timestamp_end or int(d[2]) < timestamp_start:
                continue
            buy1_money = huaxin_l1_data_manager.get_buy1_money(code)
            # 买1是否大于5000w
            if not constant.TEST:
                if not buy1_money or buy1_money < 5e7:
                    continue
            if not tool.is_can_buy_code(code):
                continue
            blocks = {d[5]}
            if d[6]:
                blocks |= set(d[6].split("、"))
            blocks -= constant.KPL_INVALID_BLOCKS
            # 过滤
            blocks = BlockMapManager().filter_blocks(blocks)
            # 开1剔除4板以上的
            high_level = kpl_util.get_high_level_count(d[4])
            if high_level >= 4:
                continue
            temp_dict[code] = (high_level, blocks)
        kpl_data_constant.open_limit_up_code_dict_for_radical_buy = temp_dict
    @classmethod
    def __get_current_index(cls, code, block, yesterday_limit_up_codes, exclude_codes=None, limit_up_time=None,
                            ignore_open_limit_up=True):
        """
         获取当前涨停身位
        @param code:
        @param block:
        @param yesterday_limit_up_codes:
        @param exclude_codes:
        @param limit_up_time:
        @param ignore_open_limit_up: 是否忽略开1的代码
        @return: 索引,前排代码信息([(代码, 涨停时间)])
        """
        if exclude_codes is None:
            exclude_codes = set()
        current_index = 0
        block_codes_infos = []
        timestamp_start, timestamp_end = kpl_block_util.open_limit_up_time_range
        if limit_up_time is None:
            limit_up_time = time.time()
        for k in LimitUpDataConstant.current_limit_up_datas:
            _code = k[0]
            if _code in exclude_codes:
                continue
            blocks = LimitUpDataConstant.get_blocks_with_history(_code)
            if not blocks:
                blocks = set()
            if _code == code:
                # 获取当前代码涨停时间
                limit_up_time = int(k[2])
                continue
            # 不是这个板块
            if block not in blocks:
                continue
            # 剔除4板以上的板
            if kpl_util.get_high_level_count(k[4]) >= 4 and len(blocks) > 1:
                continue
            if not tool.is_can_buy_code(_code):
                continue
            # 剔除开1的数据
            if ignore_open_limit_up and timestamp_start <= int(k[2]) < timestamp_end:
                continue
            # 剔除高位板
            if _code in yesterday_limit_up_codes:
                continue
            # 代码.涨停时间
            block_codes_infos.append((_code, int(k[2])))
        block_codes_infos.append((code, limit_up_time))
        block_codes_infos.sort(key=lambda x: x[1])
        before_codes_info = []
        for i in range(0, len(block_codes_infos)):
            if block_codes_infos[i][0] == code:
                current_index = i
                break
            else:
                before_codes_info.append(block_codes_infos[i])
        return current_index, before_codes_info
    @classmethod
    def __get_history_index(cls, code, block, yesterday_limit_up_codes, exclude_codes=None, ignore_open_limit_up=True):
        """
        获取历史涨停身位
        @param code:
        @param block:
        @param yesterday_limit_up_codes:
        @param exclude_codes:
        @param ignore_open_limit_up: 是否忽略开1代码
        @return:
        """
        if exclude_codes is None:
            exclude_codes = set()
        history_index = 0
        block_codes_infos = []
        # 开1时间范围
        timestamp_start, timestamp_end = kpl_block_util.open_limit_up_time_range
        limit_up_time = time.time()
        limit_up_space_ge_60s_codes = set()
        for k in LimitUpDataConstant.history_limit_up_datas:
            _code = k[3]
            if _code in exclude_codes:
                continue
            if _code == code:
                # 获取当前代码涨停时间
                limit_up_time = int(k[5])
                continue
            blocks = LimitUpDataConstant.get_blocks_with_history(_code)
            # 不是这个板块
            if block not in blocks:
                continue
            # 剔除4板以上且板块数量大于1个
            if kpl_util.get_high_level_count(k[12]) >= 4 and len(blocks) > 1:
                continue
            if not tool.is_can_buy_code(_code):
                continue
            # 剔除开1的数据
            if ignore_open_limit_up and timestamp_start <= int(k[5]) < timestamp_end:
                continue
            # 剔除高位板
            if _code in yesterday_limit_up_codes:
                continue
            # 剔除炸板代码持续涨停时间小于1分钟的代码 且 只能用于不排除前2条数据
            if _code not in cls.__current_limit_up_codes and _code in cls.__total_limit_up_space_dict and \
                    cls.__total_limit_up_space_dict[_code] < 60 and not exclude_codes and len(
                limit_up_space_ge_60s_codes) < 3:
                limit_up_space_ge_60s_codes.add(_code)
                continue
            # 代码,涨停时间
            block_codes_infos.append((_code, int(k[5])))
        block_codes_infos.append((code, limit_up_time))
        block_codes_infos.sort(key=lambda x: x[1])
        before_codes_info = []
        for i in range(0, len(block_codes_infos)):
            if block_codes_infos[i][0] == code:
                history_index = i
                break
            else:
                before_codes_info.append(block_codes_infos[i])
        return history_index, before_codes_info
    @classmethod
    def __is_radical_buy_with_open_limitup(cls, code, block, yesterday_limit_up_codes):
        """
        是否需要激进买(某个板块开1)
        1.有>=2个开1买老2
        2.有1个开1的买老3
        @param code:
        @param block:
        @param yesterday_limit_up_codes 昨日涨停代码
        @return:
        """
        # 9:45点之前涨停的才能买入
        # 获取当前代码的涨停时间
        limit_up_timestamp = cls.__get_limit_up_timestamp(code)
        if tool.get_now_time_as_int() > 100000:
            return False, "超过生效时间"
        # 根据板块聚合数据
        open_limit_up_block_codes_dict = {}
        for c in kpl_data_constant.open_limit_up_code_dict_for_radical_buy:
            blocks = kpl_data_constant.open_limit_up_code_dict_for_radical_buy[c][1]
            for b in blocks:
                if b not in open_limit_up_block_codes_dict:
                    open_limit_up_block_codes_dict[b] = set()
                open_limit_up_block_codes_dict[b].add(c)
        if block not in open_limit_up_block_codes_dict:
            return False, "板块未开1"
        open_limit_up_block_codes = list(open_limit_up_block_codes_dict.get(block))
        count = len(open_limit_up_block_codes)
        # ----获取历史身位----
        history_index, history_before_codes_info = cls.__get_history_index(code, block, yesterday_limit_up_codes)
        # ----获取实时身位----
        current_index, current_before_codes_info = cls.__get_current_index(code, block, yesterday_limit_up_codes,
                                                                           limit_up_time=limit_up_timestamp)
        exclude_codes = set()
        # 是否为强势二板开1:该代码所有的慨念必须都为二板开1且开1的只能有他自己
        is_strong_2_level = count == 1 and \
                            kpl_data_constant.open_limit_up_code_dict_for_radical_buy[open_limit_up_block_codes[0]][
                                0] == 2
        if is_strong_2_level:
            _code = open_limit_up_block_codes[0]
            blocks = LimitUpDataConstant.get_blocks_with_history(_code)
            if blocks:
                # 所有的慨念的开1代码必须只有他自己
                for b in blocks:
                    temp_codes = open_limit_up_block_codes_dict.get(b)
                    if temp_codes and len(temp_codes) > 1:
                        is_strong_2_level = False
                        break
        if count >= 2 or is_strong_2_level:
            # 开始数量大于2个或者只有一个2板开1
            exclude_codes.clear()
        else:
            # 只有一个开1
            # 获取包含高位板的身位
            # ----获取历史身位----
            history_index, history_before_codes_info = cls.__get_history_index(code, block, set())
            # ----获取实时身位----
            current_index, current_before_codes_info = cls.__get_current_index(code, block, set(),
                                                                               limit_up_time=limit_up_timestamp)
            if history_before_codes_info and current_before_codes_info and history_before_codes_info[0][0] == \
                    current_before_codes_info[0][0]:
                # 排除第一个非开1数据
                exclude_codes = {history_before_codes_info[0][0]}
            else:
                return False, f"开1数量:{count},历史-{history_index + 1} 实时-{current_index + 1}"
        # 获取主板的身位(已经排除了开1的代码)
        history_index, history_before_codes_info = cls.__get_history_index(code, block,
                                                                           yesterday_limit_up_codes,
                                                                           exclude_codes=exclude_codes)
        # 过滤不正的原因
        history_index, history_before_codes_info = cls.__filter_before_codes(block, history_index,
                                                                             history_before_codes_info,
                                                                             yesterday_limit_up_codes)
        # 买首板老大/老二
        # 首板老大不能买时可买老二
        if history_index > 1:
            return False, f"开1数量:{count},非开1首板身位不匹配:历史-{history_index + 1} 实时-{current_index + 1}"
        if history_index == 1:
            # 当前代码为老2,要判断老大是否可买
            if RadicalBuyDataManager.is_code_can_buy(history_before_codes_info[0][0],
                                                     DealAndDelegateWithBuyModeDataManager().get_deal_codes(),
                                                     is_refered=True)[0]:
                return False, f"开1数量:{count},前排代码可买:{history_before_codes_info[0]}"
            return True, f"开1数量:{count},前排代码不可买:{history_before_codes_info[0]},历史前排-{history_before_codes_info},开1代码-{open_limit_up_block_codes}"
        return True, f"开1数量:{count},历史-{history_index + 1} 实时-{current_index + 1}, 前排代码-{current_before_codes_info}, 开1代码-{open_limit_up_block_codes}"
    @classmethod
    def __filter_before_codes(cls, block, index, before_codes_info, yesterday_codes):
        """
        过滤前排涨停原因不正/不算身位的代码
        @param code:
        @param block:板块
        @param index: 目标代码位置
        @param before_codes_info: [(代码, 涨停时间戳)]
        @return: 过滤之后的 (index, before_codes_info)
        """
        try:
            if index == 0 or not before_codes_info:
                return index, before_codes_info
            temp_index = index
            temp_before_codes_info = []
            deal_codes = RadicalBuyDealCodesManager().get_deal_codes()
            for b in before_codes_info:
                # 当作目标票获取扫入板块
                code_ = b[0]
                blocks = LimitUpCodesBlockRecordManager().get_radical_buy_blocks(code_)
                blocks = BlockMapManager().filter_blocks(blocks)
                need_delete = False
                # 判断原因不正
                if block not in blocks and b[0] not in yesterday_codes:
                    # 首板涨停原因不正
                    need_delete = True
                # 判断是否是不计算身位
                if not need_delete:
                    if radical_buy_data_manager.ExcludeIndexComputeCodesManager.is_in_cache(code_):
                        need_delete = True
                # 判断是不是扫了没有扫进入
                if not need_delete:
                    if code_ in  BlockPlaceOrderRecordManager().get_codes() and code_ not in deal_codes:
                        need_delete = True
                if need_delete:
                    temp_index -= 1
                else:
                    temp_before_codes_info.append(b)
            return temp_index, temp_before_codes_info
        except Exception as e:
            async_log_util.error(logger_debug, f"扫入板块过滤出错:{str(e)}")
            return index, before_codes_info
    @classmethod
    def __is_radical_buy_with_block_up(cls, code, block, yesterday_limit_up_codes):
        """
        是否激进买(板块突然涨起来)
        1.老二和老三的涨停时间相差5分钟内
        2.老三不能买顺位到老四(老四与老三相差10分钟内)
        3.前2个票不能炸板(历史身位与现在身位一致)
        4.除开前两个代码可买老1与老2
        5.买老2的情况:老1不满足买入条件
        @param code:
        @param block:
        @param yesterday_limit_up_codes:
        @return:
        """
        # 获取当前代码的涨停时间
        limit_up_timestamp = cls.__get_limit_up_timestamp(code)
        # 获取当前的板块
        current_index, current_before_codes_info = cls.__get_current_index(code, block, set(),
                                                                           limit_up_time=limit_up_timestamp)
        current_before_codes = [x[0] for x in current_before_codes_info]
        if len(current_before_codes_info) < 2:
            return False, f"前排代码小于2个:{current_before_codes_info}"
        # 当前代码开1不能买
        if limit_up_timestamp < kpl_block_util.open_limit_up_time_range[1]:
            return False, f"当前代码开1"
        if tool.trade_time_sub(tool.timestamp_format(limit_up_timestamp, '%H:%M:%S'),
                               tool.timestamp_format(current_before_codes_info[-1][1], '%H:%M:%S')) >= 10 * 60:
            return False, f"距离上个代码涨停已过去10分钟({current_before_codes_info[-1]})"
        # 包含高位板的整体排序
        all_history_index, all_history_before_codes_info = cls.__get_history_index(code, block, set())
        all_history_before_codes = [x[0] for x in all_history_before_codes_info]
        # 前两个代码是否有炸板
        dif_codes = set(all_history_before_codes[:2]) - set(current_before_codes[:2])
        if dif_codes:
            return False, f"前2代码有炸板:{dif_codes}, 前排代码:{all_history_before_codes}"
        # 前排代码炸板不能>=2个
        dif_codes = set(all_history_before_codes) - set(current_before_codes)
        if len(dif_codes) >= 2:
            return False, f"板块前排有{len(dif_codes)}个炸板"
        exclude_codes = set()
        for x in current_before_codes_info:
            if x[1] < kpl_block_util.open_limit_up_time_range[1]:
                exclude_codes.add(x[0])
        # 除去前二代码与开1代码之后是否为首板老大:所有开1的视为1个
        open_count = len(exclude_codes)
        if open_count > 0 and open_count + 1 <= len(current_before_codes):
            # 前排有开1
            exclude_codes |= set(current_before_codes[open_count:open_count + 1])
        else:
            exclude_codes |= set(current_before_codes[:2])
        open_limit_up_code_dict = kpl_data_constant.open_limit_up_code_dict_for_radical_buy
        if open_limit_up_code_dict:
            exclude_codes |= set(open_limit_up_code_dict.keys())
        history_index, history_before_codes_info = cls.__get_history_index(code, block, yesterday_limit_up_codes,
                                                                           exclude_codes)
        # 过滤不正的原因
        history_index, history_before_codes_info = cls.__filter_before_codes(block, history_index,
                                                                             history_before_codes_info,
                                                                             yesterday_limit_up_codes)
        # 获取本板块买入代码的最大数量
        max_count = RadicalBuyBlockCodeCountManager().get_block_code_count(block)
        if history_index > max_count:
            return False, f"排除前2,目标代码位于历史身位-{history_index + 1},前排代码:{history_before_codes_info}, 板块最多可买{max_count}"
        if max_count == 1:
            if history_index == 1:
                # 首板老2,判断前面的老大是否是属于不能买的范畴
                pre_code = history_before_codes_info[0][0]
                # pre_code不能买,才能买
                if RadicalBuyDataManager.is_code_can_buy(pre_code,
                                                         DealAndDelegateWithBuyModeDataManager().get_deal_codes(),
                                                         is_refered=True)[0]:
                    return False, f"前排代码可买:{pre_code}"
                # 前面一个代码不能买,前一个代码必须与前前个代码涨停时间相差15分钟内
                for i in range(len(all_history_before_codes_info) - 1, -1, -1):
                    if all_history_before_codes_info[i][0] == pre_code:
                        if tool.trade_time_sub(tool.timestamp_format(all_history_before_codes_info[i][1], '%H:%M:%S'),
                                               tool.timestamp_format(all_history_before_codes_info[i - 1][1],
                                                                     '%H:%M:%S')) >= 15 * 60:
                            return False, f"被顺位代码({pre_code}) 与上个代码涨停时间>=15分钟 ({all_history_before_codes_info[i]}##{all_history_before_codes_info[i - 1]})"
                        else:
                            return True, f"被顺位代码({pre_code}) 与上个代码涨停时间<15分钟 ({all_history_before_codes_info[i]}##{all_history_before_codes_info[i - 1]})"
                return False, f"没找到顺位代码({pre_code})的前排代码"
            else:
                # 代码为目标代码
                pass
        else:
            # 代码为目标代码
            pass
        return True, f"满足买入需求: 前排代码-{current_before_codes_info}"
    @classmethod
    def __is_re_limit_up(cls, code, block):
        """
        是否是炸板回封可买
        @param code:
        @param block:
        @return:
        """
        # 获取身位
        current_index, current_before_codes_info = cls.__get_current_index(code, block, set(),
                                                                           limit_up_time=cls.__get_limit_up_timestamp(
                                                                               code))
        history_index, history_before_codes_info = cls.__get_history_index(code, block, set())
        if current_index != history_index:
            return False, f"有其他炸板"
        if current_index > 1:
            return False, f"不是前2的板块"
        history_codes = set()
        # 获取板块炸板情况
        for k in LimitUpDataConstant.history_limit_up_datas:
            _code = k[3]
            blocks = LimitUpDataConstant.get_blocks_with_history(_code)
            blocks = BlockMapManager().filter_blocks(blocks)
            # 不是这个板块
            if block in blocks:
                history_codes.add(_code)
        if len(history_codes) <= 4:
            return False, f"板块历史涨停小于4个:{history_codes}"
        # 获取当前涨停数量
        current_codes = set()
        for k in LimitUpDataConstant.current_limit_up_datas:
            _code = k[0]
            blocks = LimitUpDataConstant.get_blocks_with_history(_code)
            if not blocks:
                blocks = set()
            blocks = BlockMapManager().filter_blocks(blocks)
            # 不是这个板块
            if block in blocks:
                current_codes.add(_code)
        current_codes.add(code)
        diff = history_codes - current_codes
        if diff:
            return False, f"板块炸板不止当前票:{diff}"
        return True, ""
    @classmethod
    def __get_limit_up_timestamp(cls, code):
        """
        获取代码的涨停时间,默认当前时间
        @param code:
        @return:
        """
        limit_up_timestamp = LimitUpDataConstant.get_first_limit_up_time(code)
        if not limit_up_timestamp:
            limit_up_timestamp = time.time()
        return limit_up_timestamp
    @classmethod
    def get_code_kpl_blocks(cls, code):
        blocks = KPLCodeJXBlockManager().get_jx_blocks_radical(code)
        if blocks is None:
            blocks = set()
        # 将获取涨停原因与涨停推荐
        keys = TargetCodePlateKeyManager().get_plate_keys(code, contains_today=True)
        if keys and keys[0]:
            blocks |= set(keys[0])
        return blocks
    @classmethod
    def get_code_blocks(cls, code):
        """
        获取目标代码的板块
        @param code:
        @return: 过滤后的板块,过滤前的板块
        """
        # blocks = cls.get_code_kpl_blocks(code)
        # match_blocks, info = CodeThirdBlocksManager().get_intersection_blocks_info(code, blocks)
        # match_blocks -= constant.KPL_INVALID_BLOCKS
        # fblocks = match_blocks & RealTimeKplMarketData.get_top_market_jingxuan_blocks()
        # if not fblocks:
        #     fblocks = set()
        # match_blocks_3, info = CodeThirdBlocksManager().get_intersection_blocks_info(code, blocks, same_count=3)
        # if match_blocks_3:
        #     match_blocks_3 -= constant.KPL_INVALID_BLOCKS
        #     fblocks |= match_blocks_3
        # # 获取开盘啦历史涨停原因
        # kpl_history_blocks = CodesHisReasonAndBlocksManager().get_history_blocks_cache(code)
        # if kpl_history_blocks:
        #     fblocks |= BlockMapManager().filter_blocks(kpl_history_blocks)
        # jx_out_blocks = RealTimeKplMarketData.get_top_market_jingxuan_out_blocks()
        # if jx_out_blocks:
        #     fblocks -= jx_out_blocks
        #
        # return fblocks, match_blocks
        # 新版本
        before_fblocks = LimitUpCodesBlockRecordManager().get_radical_buy_blocks(code)
        if not before_fblocks:
            before_fblocks = set()
        fblocks = BlockMapManager().filter_blocks(before_fblocks)
        if fblocks:
            fblocks -= constant.KPL_INVALID_BLOCKS
        return fblocks, before_fblocks
    @classmethod
    def is_radical_buy(cls, code, yesterday_limit_up_codes):
        """
        是否是激进买
        @param code:
        @return: {激进买的板块}, 原因
        """
        # 计算
        # 获取开1的板块
        open_limit_up_code_dict = kpl_data_constant.open_limit_up_code_dict_for_radical_buy
        open_limit_up_blocks = set()
        if open_limit_up_code_dict:
            for c in open_limit_up_code_dict:
                open_limit_up_blocks |= open_limit_up_code_dict[c][1]
        # 获取代码的板块
        keys_, info = cls.get_code_blocks(code)
        if not keys_:
            return set(), "没获取到板块交集"
        match_blocks = open_limit_up_blocks & keys_
        can_buy_blocks = set()
        fmsges = []
        msges = []
        for b in match_blocks:
            # 判断板块是否该激进买
            result = cls.__is_radical_buy_with_open_limitup(code, b, yesterday_limit_up_codes)
            if result[0]:
                can_buy_blocks.add(b)
            msges.append(f"【{b}】:{result[1]}")
        fmsges.append("开1判断##" + ",".join(msges))
        if not can_buy_blocks:
            msges.clear()
            for b in keys_:
                # 板块快速启动
                result = cls.__is_radical_buy_with_block_up(code, b, yesterday_limit_up_codes)
                if result[0]:
                    can_buy_blocks.add(b)
                msges.append(f"【{b}】:{result[1]}")
            fmsges.append("板块快速启动判断##" + ",".join(msges))
        if not can_buy_blocks:
            msges.clear()
            for b in keys_:
                result = cls.__is_re_limit_up(code, b)
                if result[0]:
                    can_buy_blocks.add(b)
                    msges.append(f"【{b}】:{result[1]}")
            fmsges.append("板块回封判断##" + ",".join(msges))
        # 如果能扫入就需要获取代码在板块中的身位
        # if can_buy_blocks:
        #     excude_codes = set()
        #     if kpl_data_constant.open_limit_up_code_dict_for_radical_buy:
        #         excude_codes |= kpl_data_constant.open_limit_up_code_dict_for_radical_buy.keys()
        #     # 剔除开1的代码
        #     for b in keys_:
        #         history_index, history_before_codes_info = cls.__get_history_index(code, b, yesterday_limit_up_codes,
        #                                                                            excude_codes)
        #         if history_index >= 4:
        #             return set(), f"在【{b}】中身位({history_index + 1})靠后"
        return can_buy_blocks, " **** ".join(fmsges)
if __name__ == "__main__":
    pass