Administrator
2025-05-26 b51b2ae184fad5aaf37a78903987e064f192d430
third_data/code_plate_key_manager.py
@@ -4,40 +4,77 @@
# 涨停代码关键词板块管理
import copy
import datetime
import itertools
import json
import time
import constant
from code_attribute import code_nature_analyse
from code_attribute import gpcode_manager
from db.redis_manager_delegate import RedisUtils
from third_data import kpl_block_util, kpl_api, kpl_util
from trade.trade_manager import MarketSituationManager
from settings.trade_setting import MarketSituationManager
from third_data.history_k_data_manager import HistoryKDataManager
from third_data.history_k_data_util import HistoryKDatasUtils
from third_data.kpl_data_constant import LimitUpCodesBlockRecordManager, ContainsLimitupCodesBlocksManager
from third_data.third_blocks_manager import BlockMapManager
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 import async_log_util
from db import redis_manager_delegate as redis_manager, mysql_data_delegate as mysql_data
from log_module.log import logger_kpl_block_can_buy
from log_module.log import logger_kpl_block_can_buy, logger_kpl_jx_out, logger_kpl_jx_in, logger_debug, \
    logger_kpl_latest_gaobiao
from third_data.kpl_util import KPLPlatManager
from trade import trade_manager, l2_trade_util
from trade import l2_trade_util, trade_constant
# 代码精选板块管理
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)
            try:
                cls.__load_data()
            except Exception as e:
                logger_debug.exception(e)
        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 +95,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:
@@ -93,7 +120,7 @@
            # logger_kpl_block_can_buy.info(f"准备更新精选板块:{code}-{buy_1_price}-{limit_up_price}")
            if limit_up_price and buy_1_price:
                # 处理买1,卖1信息
                pre_close_price = round(float(limit_up_price) / 1.1, 2)
                pre_close_price = round(float(limit_up_price) / tool.get_limit_up_rate(code), 2)
                # 如果涨幅大于7%就读取板块
                price_rate = (buy_1_price - pre_close_price) / pre_close_price
                if price_rate > 0.07:
@@ -104,6 +131,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 +153,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,15 +163,82 @@
                        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))
# 禁止下单的板块
class ForbiddenBlockManager:
    __db = 3
    __redisManager = redis_manager.RedisManager(3)
    __instance = None
    __forbidden_blocks = set()
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(ForbiddenBlockManager, cls).__new__(cls, *args, **kwargs)
            cls.__load_data()
        return cls.__instance
    @classmethod
    def __get_redis(cls):
        return cls.__redisManager.getRedis()
    # 加载数据
    @classmethod
    def __load_data(cls):
        blocks = cls.__get_redis().smembers("forbidden_blocks")
        if blocks:
            for b in blocks:
                cls.__forbidden_blocks.add(b)
    def add(self, block):
        self.__forbidden_blocks.add(block)
        RedisUtils.sadd_async(self.__db, "forbidden_blocks", block)
        RedisUtils.expire_async(self.__db, "forbidden_blocks", tool.get_expire())
    def remove(self, block):
        if block in self.__forbidden_blocks:
            self.__forbidden_blocks.remove(block)
        RedisUtils.srem_async(self.__db, "forbidden_blocks", block)
    def get_blocks(self):
        return copy.deepcopy(self.__forbidden_blocks)
    def is_in(self, block):
        return block in self.__forbidden_blocks
# 开盘啦禁止交易板块管理
class KPLPlateForbiddenManager:
    __redisManager = redis_manager.RedisManager(3)
    """
    不能买的板块管理
    """
    __redis_manager = redis_manager.RedisManager(3)
    __kpl_forbidden_plates_cache = set()
    # 已经删除了的板块
    __deleted_kpl_forbidden_plates_cache = set()
    # 监控的高标板块代码字典:{"板块":{"代码1","代码2"}}
    __watch_block_high_codes = {}
    # 高标代码
    __watch_high_codes = set()
    __instance = None
@@ -156,24 +252,164 @@
    def __load_datas(cls):
        __redis = cls.__get_redis()
        try:
            __kpl_forbidden_plates_cache = RedisUtils.smembers(__redis, "kpl_forbidden_plates")
            cls.__kpl_forbidden_plates_cache = RedisUtils.smembers(__redis, "kpl_forbidden_plates")
            cls.__deleted_kpl_forbidden_plates_cache = RedisUtils.smembers(__redis, "deleted_kpl_forbidden_plates")
        finally:
            RedisUtils.realse(__redis)
        cls.__load_latest_gb()
    @classmethod
    def __get_redis(cls):
        return cls.__redisManager.getRedis()
        return cls.__redis_manager.getRedis()
    def save_plate(self, plate):
        self.__kpl_forbidden_plates_cache.add(plate)
        RedisUtils.sadd(self.__get_redis(), "kpl_forbidden_plates", plate)
        RedisUtils.expire(self.__get_redis(), "kpl_forbidden_plates", tool.get_expire())
        self.__deleted_kpl_forbidden_plates_cache.discard(plate)
        RedisUtils.srem(self.__get_redis(), "deleted_kpl_forbidden_plates", plate)
        RedisUtils.expire(self.__get_redis(), "deleted_kpl_forbidden_plates", tool.get_expire())
    def delete_plate(self, plate):
        self.__kpl_forbidden_plates_cache.discard(plate)
        RedisUtils.srem(self.__get_redis(), "kpl_forbidden_plates", plate)
        RedisUtils.expire(self.__get_redis(), "kpl_forbidden_plates", tool.get_expire())
        self.__deleted_kpl_forbidden_plates_cache.add(plate)
        RedisUtils.sadd(self.__get_redis(), "deleted_kpl_forbidden_plates", plate)
        RedisUtils.expire(self.__get_redis(), "deleted_kpl_forbidden_plates", tool.get_expire())
    def list_all(self):
        return RedisUtils.smembers(self.__get_redis(), "kpl_forbidden_plates")
    def list_all_cache(self):
        return self.__kpl_forbidden_plates_cache
    def list_all_deleted_cache(self):
        return self.__deleted_kpl_forbidden_plates_cache
    def is_in_cache(self, plate):
        if self.__kpl_forbidden_plates_cache and plate in self.__kpl_forbidden_plates_cache:
            return True
        return False
    @classmethod
    def __load_latest_gb(cls):
        """
        加载最近的市场高标
        @return:
        """
        # 获取最近10个交易日涨停的涨停数据
        dates = HistoryKDatasUtils.get_latest_trading_date_cache(10)
        if not dates:
            return
        min_date = dates[-1]
        sql = f"SELECT r.`_code`, r.`_hot_block_name`, r.`_day`, r.`_open` FROM `kpl_limit_up_record` r WHERE r.`_day`>='{min_date}'"
        mysqldb = mysql_data.Mysqldb()
        results = mysqldb.select_all(sql)
        code_days_map = {}
        # 每炸板
        f_code_days_map = {}
        for r in results:
            if r[0] not in code_days_map:
                code_days_map[r[0]] = set()
            code_days_map[r[0]].add(r[2])
            if not r[3]:
                if r[0] not in f_code_days_map:
                    f_code_days_map[r[0]] = set()
                f_code_days_map[r[0]].add(r[2])
        # 过滤涨停次数>=3次的数据
        target_codes = set()
        for code in code_days_map:
            if f_code_days_map.get(code) and (len(f_code_days_map.get(code)) >= 4 or (
                    tool.is_ge_code(code) and len(f_code_days_map.get(code)) >= 2)):
                # 且有3天属于连续涨停
                day_list = list(code_days_map[code])
                day_list.sort(reverse=True)
                step = 3
                has_continue = False
                for i in range(0, len(day_list) - step + 1):
                    item_list = day_list[i:i + step]
                    # 是否属于连续涨停
                    is_sub = False
                    for j in range(0, len(dates) - step):
                        if f"{dates[j:j + step]}" == f"{item_list}":
                            is_sub = True
                            break
                    if is_sub:
                        has_continue = True
                        break
                if not has_continue:
                    continue
                has_big_deal = False
                # 最近10个交易日的成交额要大于10亿
                volumes_data = HistoryKDataManager().get_history_bars(code, dates[0])
                if volumes_data:
                    for d in volumes_data[:10]:
                        if d["amount"] > 10e8:
                            has_big_deal = True
                            break
                if not has_big_deal:
                    continue
                target_codes.add(code)
        # 代码对应的板块
        code_blocks = {}
        for r in results:
            if r[0] not in target_codes:
                continue
            if r[0] not in code_blocks:
                code_blocks[r[0]] = set()
            code_blocks[r[0]].add(kpl_util.filter_block(r[1]))
        # 所有板块对应的代码集合
        block_codes = {}
        for code in code_blocks:
            for b in code_blocks[code]:
                if b in constant.KPL_INVALID_BLOCKS:
                    continue
                if b not in block_codes:
                    block_codes[b] = set()
                block_codes[b].add(code)
        print(block_codes)
        cls.__watch_block_high_codes = block_codes
        logger_kpl_latest_gaobiao.info(f"{block_codes}")
        cls.__watch_high_codes.clear()
        for b in block_codes:
            cls.__watch_high_codes |= block_codes[b]
        for k in block_codes:
            print(k, [(x, gpcode_manager.get_code_name(x)) for x in block_codes[k]])
    def get_watch_high_codes(self):
        return self.__watch_high_codes
    def get_watch_high_codes_by_block(self, b):
        return self.__watch_block_high_codes.get(b)
    def compute(self, code_rate_dict: dict):
        """
        根据比例计算需要拉黑的代码
        @param code_rate_dict: 涨幅百分数
        @return:
        """
        try:
            if self.__watch_block_high_codes:
                forbidden_blocks = set()
                for b in self.__watch_block_high_codes:
                    total_rate = 0
                    for code in self.__watch_block_high_codes[b]:
                        if code in code_rate_dict:
                            total_rate += code_rate_dict.get(code)
                    average_rate = total_rate / len(self.__watch_block_high_codes[b])
                    if average_rate < 1:
                        forbidden_blocks.add(b)
                    # async_log_util.info(logger_debug, f"板块平均涨幅 {b}-{average_rate}")
                self.__kpl_forbidden_plates_cache = forbidden_blocks
                async_log_util.info(logger_debug, f"拉黑板块:{forbidden_blocks}")
        except Exception as e:
            logger_debug.exception(e)
class LimitUpCodesPlateKeyManager:
@@ -211,12 +447,6 @@
    @classmethod
    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):
@@ -258,8 +488,10 @@
# 实时开盘啦市场数据
class RealTimeKplMarketData:
    # 精选前5
    top_5_reason_list = []
    # 流入缓存 [ID, 板块名称, 板块涨幅, 流入金额]
    top_in_list_cache = []
    # 流出缓存
    top_out_list_cache = []
    # 行业前5
    top_5_industry_list = []
    #
@@ -269,31 +501,156 @@
    __KPLPlateForbiddenManager = KPLPlateForbiddenManager()
    __LimitUpCodesPlateKeyManager = LimitUpCodesPlateKeyManager()
    __KPLPlatManager = KPLPlatManager()
    # 精选流入前几
    __top_jx_blocks = []
    # 精选流出前几
    __top_jx_out_blocks = []
    # 精选板块流入金额
    __jx_blocks_in_money_dict = {}
    # 市场行情热度,默认为60
    __market_strong = 60
    @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 get_jingxuan_in_block_threshold_count(cls):
        """
        获取买精选流入前几
        @return:
        """
        score = 60
        if cls.__market_strong is not None:
            score = int(cls.__market_strong)
        for info in constant.RADICAL_BUY_TOP_IN_COUNT_BY_MARKET_STRONG:
            if info[0] <= score < info[1]:
                return info[2]
        return 10
        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_blocks(cls, datas):
        """
        设置精选流入数据
        @param datas:[(板块编号,板块名称,涨幅, 板块流入金额)]
        @return:
        """
        # 流入阈值
        # THRESHOLD_MONEY = 50 * (tool.trade_time_sub(tool.get_now_time_str(), "09:30:00") // 60) + 1000
        # THRESHOLD_MONEY = min(THRESHOLD_MONEY, 10000)
        # THRESHOLD_MONEY = max(THRESHOLD_MONEY, 1000)
        # THRESHOLD_MONEY = THRESHOLD_MONEY * 10000
        THRESHOLD_MONEY = 0
        # 最大数量
        # MAX_COUNT = cls.get_jingxuan_in_block_threshold_count()
        cls.top_in_list_cache = datas
        blocks = set()
        count = 0
        fblock_money = {}
        for data in datas:
            cls.__jx_blocks_in_money_dict[data[1]] = data[3]
            if data[1] in constant.KPL_INVALID_BLOCKS:
                continue
            if data[3] < THRESHOLD_MONEY:
                continue
            # 过滤出来为同一个板块就只算1个数量
            fb = BlockMapManager().filter_blocks({data[1]})
            if blocks & fb:
                continue
            for b in fb:
                fblock_money[b] = data[3]
            blocks |= fb
            # 如果该原因没有涨停票要往后移一位
            has_code = False
            for b in fb:
                if ContainsLimitupCodesBlocksManager().get_block_codes(b):
                    has_code = True
                    break
            if has_code:
                count += 1
                if count == 10:
                    strong = cls.get_market_strong()
                    if strong is None:
                        strong = 60
                    if data[3] > 3e7:
                        # 大于3千万
                        THRESHOLD_MONEY = int((1 - strong / 200) * data[3])
                    else:
                        THRESHOLD_MONEY = data[3]
            # if count >= MAX_COUNT:
            #     break
        # 记录精选流出日志
        async_log_util.info(logger_kpl_jx_in, f"原数据:{datas[:50]} 板块:{blocks}")
        blocks = list(blocks)
        blocks.sort(key=lambda x: fblock_money.get(x), reverse=True)
        cls.__top_jx_blocks = blocks
    @classmethod
    def set_market_jingxuan_out_blocks(cls, datas):
        """
        设置精选流出数据
        @param datas:
        @return:
        """
        cls.top_out_list_cache = datas
        count = 0
        blocks = set()
        for data in datas:
            cls.__jx_blocks_in_money_dict[data[1]] = data[3]
            if data[1] in constant.KPL_INVALID_BLOCKS:
                continue
            if data[3] > -5e7:
                # 过滤5千万以上的
                break
            # 过滤出来为同一个板块就只算1个数量
            fb = BlockMapManager().filter_blocks({data[1]})
            if blocks & fb:
                continue
            blocks |= fb
            count += 1
            if count >= 10:
                break
        # 记录精选流出日志
        async_log_util.info(logger_kpl_jx_out, f"原数据:{datas[:10]} 板块:{blocks}")
        cls.__top_jx_out_blocks = list(blocks)
    @classmethod
    def set_market_strong(cls, strong):
        """
        设置市场行情强度
        @param strong:
        @return:
        """
        cls.__market_strong = strong
    @classmethod
    def is_ignore_block_in_money(cls):
        if cls.__market_strong and cls.__market_strong >= constant.IGNORE_BLOCK_IN_MONEY_MARKET_STRONG:
            return True
        return False
    @classmethod
    def get_market_strong(cls):
        return cls.__market_strong
    @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 get_block_info_at_block_in(cls, b):
        """
        获取板块的净流入情况
        @param b:
        @return: (板块名称,身位,流入金额)
        """
        for i in range(0, len(cls.top_in_list_cache)):
            if cls.top_in_list_cache[i][1] == b:
                return b, i, cls.top_in_list_cache[i][3]
        return b, -1, 0
    @classmethod
    def set_top_5_industry(cls, datas):
@@ -326,35 +683,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()
@@ -366,6 +694,10 @@
        else:
            return False, None
    @classmethod
    def get_jx_block_in_money(cls, block):
        return cls.__jx_blocks_in_money_dict.get(block)
# 代码历史涨停原因与板块管理
class CodesHisReasonAndBlocksManager:
@@ -374,6 +706,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()
@@ -431,6 +771,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:
@@ -442,9 +817,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: (板块关键词集合,今日涨停原因+涨停推荐原因,今日历史涨停原因,历史涨停原因,精选板块)
        """
@@ -459,10 +834,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)
@@ -471,15 +849,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:
@@ -497,92 +882,36 @@
    __TargetCodePlateKeyManager = TargetCodePlateKeyManager()
    __LimitUpCodesPlateKeyManager = LimitUpCodesPlateKeyManager()
    __CodesHisReasonAndBlocksManager = CodesHisReasonAndBlocksManager()
    __CodesTradeStateManager = trade_manager.CodesTradeStateManager()
    __can_buy_compute_result_dict = {}
    # 是否需要积极买
    @classmethod
    def __remove_from_l2(cls, code, msg):
        # 根据身位移除代码
        # return
        # 下过单的代码不移除
        if trade_manager.CodesTradeStateManager().get_trade_state_cache(code) != trade_manager.TRADE_STATE_NOT_TRADE:
            # 只要下过单的就不移除
            return
        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_limit_up_datas, limit_up_record_datas,
                             yesterday_current_limit_up_codes):
    def __is_need_active_buy(cls, code, block, current_rank, open_limit_up_count):
        """
        激进买入规则:主板老大且前面有高位板/20cm
        @param code:
        @param block:
        @param current_limit_up_datas:
        @param limit_up_record_datas:
        @param yesterday_current_limit_up_codes:
        板块是否需要积极买入
        规则:根据身位判断是否需要积极买,根据时间划分
        @param code: 代码
        @param block: 板块名称
        @param current_rank: 目前在板块中的身位,从0开始
        @param open_limit_up_count: 开1的数量
        @return:
        """
        code_limit_up_reason_dict = {}
        for d in limit_up_record_datas:
            code_limit_up_reason_dict[d[3]] = d[2]
        # 前面有高位板/非主板涨停且曾涨停主板排老大则激进买
        block_codes_info = []
        contains_code = False
        for d in current_limit_up_datas:
            reason = kpl_util.get_current_limit_up_reason(d)
            if reason != block:
                continue
            if d[0] == code:
                contains_code = True
            # 格式:[代码,涨停时间,是否高位板, 自由流通市值]
            block_codes_info.append((d[0], d[2], d[0] in yesterday_current_limit_up_codes, d[7]))
        if not contains_code:
            block_codes_info.append((code, int(time.time()), False, global_util.zyltgb_map.get(code)))
        block_codes_info.sort(key=lambda x: x[1])
        # 暂存在当前代码之前涨停的高位板/非主板代码
        before_codes = []
        for info in block_codes_info:
            if info[0] == code:
        real_current_rank = max(current_rank - open_limit_up_count, 0)
        TIME_STR_RANGES = ["10:00:00", "10:30:00", "11:00:00", "13:00:00", "13:30:00", "14:00:00", "14:30:00",
                           "15:00:00"]
        TIME_INT_RANGES = [int(x.replace(':', '')) for x in TIME_STR_RANGES]
        MAX_RANKS = [3, 3, 2, 2, 1, 0, 0, 0]
        now_time_str = tool.get_now_time_str().replace(':', '')
        for i in range(len(TIME_INT_RANGES)):
            if int(now_time_str) <= TIME_INT_RANGES[i]:
                if MAX_RANKS[i] > real_current_rank:
                    return True
                break
            if info[2] or not tool.is_shsz_code(info[0]):
                before_codes.append(info[0])
        if before_codes:
            # 判断历史涨停主板身位
            record_shsz_rank, record_shsz_rank_codes = kpl_block_util.get_code_record_rank(code, block,
                                                                                           limit_up_record_datas,
                                                                                           code_limit_up_reason_dict,
                                                                                           yesterday_current_limit_up_codes,
                                                                                           shsz=True)
            if record_shsz_rank == 0:
                # 历史主板老大
                return True, f"主板老大且前面有非主板/高位板涨停,先于涨停代码:{before_codes}"
        # 排除自己有3个涨停,且这4个中自由流通市值最小,且涨停过的代码中主板排名前4
        if len(block_codes_info) >= 4:
            temp_codes = []
            for info in block_codes_info:
                # 不是高位板且是主板
                if not info[2] and tool.is_shsz_code(info[0]):
                    # 格式:(代码, 自由流通市值)
                    temp_codes.append((info[0], info[3]))
            # 判断是否为主板中自由市值最小
            temp_codes.sort(key=lambda x: x[1])
            if temp_codes[0][0] == code:
                # 自由市值最小
                # 判断历史身位
                record_shsz_rank, record_shsz_rank_codes = kpl_block_util.get_code_record_rank(code, block,
                                                                                               limit_up_record_datas,
                                                                                               code_limit_up_reason_dict,
                                                                                               yesterday_current_limit_up_codes,
                                                                                               shsz=True)
                if record_shsz_rank < 4:
                    return True, f"板块有大于3票涨停,且自由流通市值最小,身位({record_shsz_rank})前4"
        return False
        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,
@@ -595,18 +924,14 @@
        block_codes = current_limit_up_block_codes_dict.get(block)
        if block_codes is None:
            block_codes = set()
        # 计算是否需要激进买入
        active_buy = cls.__is_need_active_buy(code, block, current_limit_up_datas,
                                              limit_up_record_datas,
                                              yesterday_current_limit_up_codes)
        if not block_codes:
            # 高位板泛化板块中无板块
            if not high_level_block_codes.get(block):
                return False, True, f"{block}:板块无涨停", False, active_buy
                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, active_buy
                return False, True, f"{block}:板块只有当前代码涨停", False, False
        # 可以买的最大排名
        # open_limit_up_codes = kpl_block_util.get_shsz_open_limit_up_codes(code, block, limit_up_record_datas,
        #                                                                   code_limit_up_reason_dict)
@@ -669,6 +994,9 @@
                                                                                                    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,
        #                                                                                limit_up_record_datas,
        #                                                                                code_limit_up_reason_dict,
@@ -676,10 +1004,10 @@
        #                                                                                shsz=True)
        if int(tool.get_now_time_str().replace(":", "")) <= int("094000") and is_strong_block:
            # 强势主线加强势10分钟
            return True, False, f"【{block}】:强势主线+强势10分钟", is_strong_block, active_buy
            return True, False, f"【{block}】:强势主线+强势10分钟", is_strong_block, is_active_buy
        if current_shsz_rank < len(current_open_limit_up_codes) + max_rank:
            return True, False, f"【{block}】前排代码:{current_shsz_rank}", is_strong_block, active_buy
            return True, False, f"【{block}】前排代码:{current_shsz_rank}", is_strong_block, is_active_buy
        else:
            # k_format = code_nature_analyse.CodeNatureRecordManager().get_k_format_cache(code)
            # if k_format and k_format[8][0]:
@@ -692,8 +1020,8 @@
                situation = MarketSituationManager().get_situation_cache()
                zylt_threshold_as_yi = buy_condition_util.get_zyltgb_threshold(situation)
                if zyltgb_as_yi and zylt_threshold_as_yi[2] <= zyltgb_as_yi <= zylt_threshold_as_yi[3]:
                    return True, False, f"【{block}】强势板块 自由流通市值({zyltgb_as_yi})大于{zylt_threshold_as_yi[2]}亿 小于{zylt_threshold_as_yi[3]}亿", is_strong_block, active_buy
            return False, False, f"【{block}】前排代码:{front_current_shsz_rank_codes} 超过{len(current_open_limit_up_codes) + max_rank}个", is_strong_block, active_buy
                    return True, False, f"【{block}】强势板块 自由流通市值({zyltgb_as_yi})大于{zylt_threshold_as_yi[2]}亿 小于{zylt_threshold_as_yi[3]}亿", is_strong_block, is_active_buy
            return False, False, f"【{block}】前排代码:{front_current_shsz_rank_codes} 超过{len(current_open_limit_up_codes) + max_rank}个", is_strong_block, is_active_buy
        # 过时的代码
        # if open_limit_up_codes:
@@ -740,18 +1068,20 @@
        block_codes = current_limit_up_block_codes_dict.get(block)
        if block_codes is None:
            block_codes = set()
        # 计算是否需要激进买入
        active_buy = cls.__is_need_active_buy(code, block, current_limit_up_datas,
                                              limit_up_record_datas,
                                              yesterday_current_limit_up_codes)
        # 历史涨停代码
        block_codes_records = set()
        if limit_up_record_datas:
            for k in limit_up_record_datas:
                if block in code_limit_up_reasons_dict.get(k[3]):
                    block_codes_records.add(k[3])
        if not block_codes:
            # 高位板泛化板块中无板块
            if not high_level_block_codes.get(block):
                return False, True, f"{block}:板块无涨停", False, active_buy
                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, active_buy
                return False, True, f"{block}:板块只有当前代码涨停", False, False, 0, 0, 0
        # 可以买的最大排名
        # open_limit_up_codes = kpl_block_util.get_shsz_open_limit_up_codes(code, block, limit_up_record_datas,
        #                                                                   code_limit_up_reason_dict)
@@ -761,7 +1091,7 @@
        is_strong_block = False
        # 最多买老几
        RANKS = [5, 4, 3, 3, 3, 2, 2]
        RANKS = [6, 5, 4, 4, 3, 3, 2]
        RANK_TIMES = ["10:00:00", "10:30:00", "11:00:00", "11:30:00", "13:30:00", "14:00:00", "15:00:00"]
        now_time_str = tool.get_now_time_str()
        max_rank = 2
@@ -796,10 +1126,18 @@
                                                                                                    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))
        if current_shsz_rank < len(current_open_limit_up_codes) + max_rank:
            return True, False, f"【{block}】前排代码:{current_shsz_rank}", is_strong_block, active_buy
            return True, len(block_codes | {
                code}) <= 1, f"【{block}】前排代码:{current_shsz_rank}", is_strong_block, is_active_buy, current_shsz_rank, len(
                block_codes), len(block_codes_records)
        else:
            return False, False, f"【{block}】前排代码:{front_current_shsz_rank_codes} 超过{len(current_open_limit_up_codes) + max_rank}个", is_strong_block, active_buy
            return False, len(block_codes | {
                code}) <= 1, f"【{block}】前排代码:{front_current_shsz_rank_codes} 超过{len(current_open_limit_up_codes) + max_rank}个", is_strong_block, is_active_buy, current_shsz_rank, len(
                block_codes), len(block_codes_records)
    # 获取可以买的板块
    # current_limit_up_datas: 今日实时涨停
@@ -816,21 +1154,31 @@
        # 加载涨停代码的目标板块
        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 = LimitUpCodesBlockRecordManager().get_radical_buy_blocks(code)
        if not keys:
            keys = set()
        keys = BlockMapManager().filter_blocks(keys)
        if keys:
            keys -= constant.KPL_INVALID_BLOCKS
        # log.logger_kpl_debug.info("{}最终关键词:{}", code, keys)
@@ -839,37 +1187,41 @@
        fresults = []
        if not keys:
            return fresults, set()
        code_limit_up_reasons_dict = {}
        load_code_block()
        for block in keys:
            can_buy, unique, msg, is_strong, active_buy_info = cls.__is_block_can_buy_new(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,
                                                                                          high_level_code_blocks=high_level_general_code_blocks,
                                                                                          high_level_block_codes=high_level_general_block_codes)
            fresults.append((block, can_buy, unique, msg, is_strong, active_buy_info))
            can_buy, unique, msg, is_strong, is_active_buy, current_rank, block_limit_up_count, block_limit_up_record_count = cls.__is_block_can_buy_new(
                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,
                high_level_code_blocks=high_level_general_code_blocks,
                high_level_block_codes=high_level_general_block_codes)
            fresults.append((block, can_buy, unique, msg, is_strong, is_active_buy, current_rank, block_limit_up_count,
                             block_limit_up_record_count))
        return fresults, keys
    # 是否可以下单
    # 返回:可以买的板块,是否独苗,消息
    #  可买的板块, 是否独苗, 消息, 可买的强势板块, 关键词, 激进买的板块
    #  可买的板块, 是否独苗, 消息, 可买的强势板块, 关键词, 积极买的板块
    @classmethod
    def can_buy(cls, code):
        if constant.TEST:
            return ["测试"], True, cls.BLOCK_TYPE_NONE, [], set(), ["化工"]
            return [("测试", 0, 1, 1)], True, cls.BLOCK_TYPE_NONE, [], set(), ["化工"]
        # if True:
        #     # 测试
        #     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,
                                 current_limit_up_block_codes_dict, high_level_general_code_blocks):
                                 current_limit_up_block_codes_dict, high_level_general_code_blocks, codes_delegate,
                                 codes_success):
        # 根据代码泛化板块获取泛化板块的代码集合
        high_level_general_block_codes = {}
        for c in high_level_general_code_blocks:
@@ -885,10 +1237,7 @@
                                                             high_level_general_block_codes)
        if not blocks_compute_results:
            return False, True, f"没有找到板块", [], keys, []
        codes_delegate = set(cls.__CodesTradeStateManager.get_codes_by_trade_states_cache(
            {trade_manager.TRADE_STATE_BUY_DELEGATED, trade_manager.TRADE_STATE_BUY_PLACE_ORDER}))
        codes_success = set(cls.__CodesTradeStateManager.get_codes_by_trade_states_cache(
            {trade_manager.TRADE_STATE_BUY_SUCCESS}))
        codes = codes_delegate | codes_success
        # 统计成交代码的板块
        trade_codes_blocks_dict = {}
@@ -928,7 +1277,7 @@
        msg_list = []
        active_buy_blocks = []
        for r in blocks_compute_results:
            # r的数据结构(板块,是否可以买,是否独苗,消息,是否是强势板块, 激进买入信息)
            # r的数据结构(板块,是否可以买,是否独苗,消息,是否是强势板块, 积极买入信息)
            if r[2]:
                # 独苗
                unique_count += 1
@@ -936,21 +1285,24 @@
                # 强势主线最多同时挂3只票,最多成交2只票
                MAX_DELEGATE_COUNT = 3 if r[4] else 2
                MAX_DEAL_COUNT = 2 if r[4] else 1
                if r[0] in trade_success_blocks_count and len(trade_success_blocks_count[r[0]]) >= MAX_DEAL_COUNT:
                    msg_list.append(f"【{r[0]}】有成交代码:{trade_success_blocks_count[r[0]]}")
                    continue
                if r[0] in trade_delegate_blocks_count and len(trade_delegate_blocks_count[r[0]]) >= MAX_DELEGATE_COUNT:
                    msg_list.append(f"【{r[0]}】已挂单:{trade_delegate_blocks_count[r[0]]}")
                    continue
                can_buy_blocks.append(r[0])
                # if r[0] in trade_success_blocks_count and len(trade_success_blocks_count[r[0]]) >= MAX_DEAL_COUNT:
                #     msg_list.append(f"【{r[0]}】有成交代码:{trade_success_blocks_count[r[0]]}")
                #     continue
                # if r[0] in trade_delegate_blocks_count and len(trade_delegate_blocks_count[r[0]]) >= MAX_DELEGATE_COUNT:
                #     msg_list.append(f"【{r[0]}】已挂单:{trade_delegate_blocks_count[r[0]]}")
                #     continue
                if len(r) > 8:
                    can_buy_blocks.append((r[0], r[6], r[7], r[8]))
                else:
                    # (板块名称,身位,板块涨停数量)
                    can_buy_blocks.append((r[0], 0, 1, 1))
                if r[4]:
                    can_buy_strong_blocks.append(r[0])
                if r[3]:
                    msg_list.append(r[3])
                if r[5][0]:
                if r[5]:
                    active_buy_blocks.append(r[0])
                    msg_list.append(f"【{r[0]}】激进买入({r[5][1]})")
                    msg_list.append(f"【{r[0]}】积极买入({r[5]})")
            else:
                if r[3]:
                    msg_list.append(r[3])
@@ -964,7 +1316,7 @@
    @classmethod
    def update_can_buy_blocks(cls, code, current_limit_up_datas, limit_up_record_datas,
                              latest_current_limit_up_records,
                              before_blocks_dict, current_limit_up_block_codes_dict):
                              before_blocks_dict, current_limit_up_block_codes_dict, delegate_codes, deal_codes):
        yesterday_current_limit_up_codes = set()
        yesterday_current_limit_up_records_dict = {}
        yesterday_current_limit_up_records = latest_current_limit_up_records[0][1]
@@ -998,11 +1350,13 @@
                                                                                                                   yesterday_current_limit_up_codes,
                                                                                                                   before_blocks_dict,
                                                                                                                   current_limit_up_block_codes_dict,
                                                                                                                   high_level_general_code_blocks)
                                                                                                                   high_level_general_code_blocks,
                                                                                                                   delegate_codes,
                                                                                                                   deal_codes)
        # 保存板块计算结果
        cls.__can_buy_compute_result_dict[code] = (
            can_buy_blocks, unique, msg, can_buy_strong_blocks, keys, active_buy_blocks)
if __name__ == "__main__":
    pass
    KPLPlateForbiddenManager().compute()