Administrator
昨天 2f00a0565dcf8d652b8bb5c4caefbce1c2c92d62
bug修复/策略完善
9个文件已修改
160 ■■■■ 已修改文件
strategy/data_analyzer.py 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/low_suction_strategy.py 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/strategy_manager.py 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/strategy_params_settings.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/strategy_script_v6.py 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/strategy_variable.py 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/strategy_variable_factory.py 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/test.py 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/time_series_backtest.py 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
strategy/data_analyzer.py
@@ -280,8 +280,12 @@
        @param pre_close:
        @return:
        """
        return abs(close - cls.calculate_upper_limit_price(code,
                                                           pre_close)) < 0.01
        return round(abs(close - cls.calculate_upper_limit_price(code,
                                                                 pre_close)), 4) < 0.01
    @classmethod
    def is_limit_up(cls, code, close, pre_close):
        return cls.__is_limit_up(code, close, pre_close)
    @classmethod
    def get_third_limit_up_days(cls, k_data, days):
@@ -422,7 +426,7 @@
        return count
    @classmethod
    def is_too_high_and_not_relase_volume(cls, k_data):
    def is_too_high_and_not_release_volume(cls, k_data):
        """
        长得太高且没放量:30个交易日内,出现过最低价(最高价之前的交易日)到最高价之间的涨幅≥35%的票,且今日距离最高价那日无涨停/无炸板且>=3板且必须有2连板
        @param k_data: K线数据列表(近150个交易日,不包含当前交易日,时间倒序)
@@ -440,12 +444,13 @@
            # 从最高价日期向前最多看15个交易日
            before_datas = before_datas[:15]
        min_close_price_data = min(before_datas, key=lambda x: x["close"])
        if (max_high_price_data['high'] - min_close_price_data['close']) / min_close_price_data['close'] < 0.35:
        rate = (max_high_price_data['high'] - min_close_price_data['close']) / min_close_price_data['close']
        rate = round(rate, 4)
        if rate < 0.35:
            # 涨幅小于35%
            return False
        before_k_datas = [d for d in k_data if min_close_price_data['bob'] <= d['bob'] <= max_high_price_data['bob']]
        before_k_datas.sort(key=lambda x: x['bob'])
        # [最低价-最高价]日期内有3个板且有两连扳
        continue_2_limit_up_date = None
@@ -469,10 +474,40 @@
        k_data = [d for d in k_data if d['bob'] > max_high_price_data['bob']]
        # 判断是否涨停过
        if len([d for d in k_data if cls.__is_limit_up(code, d["high"], d["pre_close"])]) > 0 or len(after_datas) >= 10:
        threshold_day_count = min(int(20*rate + 3), 30)
        if len([d for d in k_data if cls.__is_limit_up(code, d["high"], d["pre_close"])]) > 0 or len(after_datas) >= threshold_day_count:
            # 最高价之后有过涨停或者是最高价后10个交易日
            return False
        return True, f"高价日期:{max_high_price_data['bob'][:10]},低价日期:{min_close_price_data['bob'][:10]},两连扳日期:{continue_2_limit_up_date}"
    @classmethod
    def is_latest_limit_up_with_no_release_volume(cls, k_data, days_count=7):
        """
        最近7个交易日内有炸板/首板涨停次日无溢价,且炸板/涨停那日距今日的最高价无法超过炸板那日的最高价
        @param days_count:
        @param k_data: K线数据列表(近150个交易日,不包含当前交易日,时间倒序)
        @return: 四跌停及以上天数
        """
        k_data = k_data[:days_count]
        code = k_data[0]["sec_id"]
        # 找到最近的涨停/炸板
        latest_limited_up_data = None
        for item in k_data:
            if cls.__is_limit_up(code, item['high'], item['pre_close']):
                latest_limited_up_data = item
                break
        if not latest_limited_up_data:
            # 最近没有涨停/炸板
            return False
        after_datas = [x for x in k_data if x['bob'] > latest_limited_up_data['bob']]
        if not after_datas:
            # 炸板之后没有数据
            return False
        after_max_price_data = max(after_datas, key=lambda x: x["high"])
        if after_max_price_data['high'] > latest_limited_up_data['high']:
            # 有最高价覆盖炸板/涨停那日最高价
            return False
        return True, f"炸板/涨停日期:{latest_limited_up_data['bob'][:10]}"
class K60SLineAnalyzer:
@@ -601,3 +636,10 @@
                        block_days[reason].add(date)
            return set([b for b in block_days if len(block_days[b]) == len(days_list)])
        return set()
if __name__ == "__main__":
    item = {'sec_id': '000037', 'open': 9.11, 'high': 9.91, 'low': 9.07, 'close': 9.25, 'volume': 34540400,
            'pre_close': 9.02,
            'bob': '2025-06-13 00:00:00', 'amount': 326110864}
    print(KTickLineAnalyzer.is_limit_up(item['sec_id'], item['high'], item['pre_close']))
strategy/low_suction_strategy.py
@@ -7,6 +7,7 @@
import constant
from db.mysql_data_delegate import Mysqldb
from strategy import strategy_variable
from utils import huaxin_util, tool
class BackTest:
@@ -171,7 +172,7 @@
            return eval(line)
        return None
    def export_big_order_deal(self, min_money=299e4):
    def export_big_order_deal(self, min_money=299e4, max_deal_space=1000):
        """
        大单成交
        @return: {"代码":[(买单号, 量, 金额, 时间, 最终成交价)]}
@@ -187,6 +188,10 @@
                    continue
                if data[2][2] < min_money:
                    continue
                if len(data[2]) > 6:
                    if tool.trade_time_sub(huaxin_util.convert_time(data[2][3]),
                                           huaxin_util.convert_time(data[2][5])) > max_deal_space:
                        continue
                if data[0] not in fdatas:
                    fdatas[data[0]] = []
                fdatas[data[0]].append(data[2])
strategy/strategy_manager.py
@@ -192,7 +192,7 @@
        """
        IS_BY_BIG_ORDER = False
        BIG_ORDER_MONEY_THRESHOLD = 200e4
        big_order_deals = self.__LowSuctionOriginDataExportManager.export_big_order_deal(BIG_ORDER_MONEY_THRESHOLD)
        big_order_deals = self.__LowSuctionOriginDataExportManager.export_big_order_deal(BIG_ORDER_MONEY_THRESHOLD, max_deal_space = 3)
        if not big_order_deals or IS_BY_BIG_ORDER:
            big_order_deals = self.__LowSuctionOriginDataExportManager.export_big_order_deal_by(
                BIG_ORDER_MONEY_THRESHOLD)
@@ -406,6 +406,8 @@
        }
        exec(self.scripts, global_dict)
        compute_result = global_dict["compute_result"]
        async_log_util.info(logger_trade, f"{code}:{compute_result}")
        if compute_result[0]:
            if code in sv.成交代码:
                return
strategy/strategy_params_settings.py
@@ -20,6 +20,9 @@
        self.buy_money = 2000
        # 最大买入票的数量
        self.max_buy_codes_count = 100
        # 每个板块最多买入的代码数量
        self.max_buy_codes_count_per_plate = 1
        # 价格区间
        self.price_range = (3, 60)
        # 老题材涨停数
strategy/strategy_script_v6.py
@@ -15,7 +15,7 @@
    return f"{huaxin_timestamp[0:2]}:{huaxin_timestamp[2: 4]}:{huaxin_timestamp[4: 6]}"
def can_buy():
def __can_buy():
    """
    @return: 是否可买, 不能买的原因/可买的板块, 是否量够
    """
@@ -81,6 +81,8 @@
    if sv.今日开盘价 and (sv.今日开盘价 - sv.昨日收盘价) / sv.昨日收盘价 < settings.min_open_rate:
        return False, f"开盘涨幅小于{settings.min_open_rate}"
    rate = (sv.当前价 - sv.昨日收盘价) / sv.昨日收盘价
    if rate >= settings.avaiable_rates[1] or rate < settings.avaiable_rates[0]:
        return False, f"涨幅不在区间内({settings.avaiable_rates[0]}-{settings.avaiable_rates[1]}):{rate}"
@@ -95,7 +97,10 @@
        return False, f"10个交易日有>=3连板"
    if sv.涨得高未放量:
        return False, f"涨得高未放量"
        return False, f"30个交易日涨得高未放量"
    if sv.涨停过未放量:
        return False, f"7个交易日内有涨停/炸板,未出现过高价"
    # if sv.当前价 > sv.昨日最低价 * 1.1:
    #     return False, f"买入时的价格必须≤昨日最低价*110%"
@@ -117,9 +122,9 @@
    # 目标票板块涨停个数>=2
    # 板块只能买入一个代码
    # 板块只能买入2个代码
    if sv.板块成交代码:
        can_buy_plates -= set(sv.板块成交代码.keys())
        can_buy_plates -= set([p for p in sv.板块成交代码 if len(sv.板块成交代码[p]) >= settings.max_buy_codes_count_per_plate])
    if not can_buy_plates:
        return False, f"没有涨停的板块: {[(plate, sv.开盘啦最正板块涨停.get(plate)) for plate in sv.代码板块 if sv.开盘啦最正板块涨停]}  连续老题材:{sv.连续老题材}"
@@ -167,5 +172,4 @@
    return True, f" \n\t大单信息:{round(big_order_money / 1e4, 2)}万(买:{round(big_order_money / 1e4, 2)}万 卖:{round(big_sell_order_money / 1e4, 2)}万)/{round(threshold_money / 1e4, 2)}万  \n\t量够信息:{sv.今日量够信息}\n\t今日最高价:{sv.今日最高价信息} \n\t5日最高价:{sv.日最高价_5}", f"\n\t板块信息:{[(p, sv.开盘啦最正板块涨停.get(p)) for p in can_buy_plates]}", can_buy_plates
compute_result = can_buy()
compute_result = __can_buy()
strategy/strategy_variable.py
@@ -214,6 +214,7 @@
        self.领涨板块信息 = set()
        self.连续老题材 = set()
        self.涨得高未放量 = False
        self.涨停过未放量 = False
    def replace_variables(self, expression):
        """
strategy/strategy_variable_factory.py
@@ -343,6 +343,8 @@
                    continue
                if re.match(r"风险|立案", r[2]):
                    continue
                if re.match(r"退市", r[1]):
                    continue
                if r[1].find("ST") >= 0:
                    continue
                if r[1].find("S") >= 0:
@@ -381,12 +383,16 @@
                        if not is_for_buy:
                            if r[3] < 1:
                                continue
                            if re.match(r"退市", r[1]):
                                continue
                            if r[1].find("ST") >= 0:
                                continue
                            if r[1].find("S") >= 0:
                                continue
                        else:
                            if re.match(r"风险|立案", r[2]):
                                continue
                            if re.match(r"退市", r[1]):
                                continue
                            if r[1].find("ST") >= 0:
                                continue
@@ -603,10 +609,15 @@
            fdata = K60SLineAnalyzer.get_close_price_of_max_volume(kline_data_60s)
            instance.__setattr__(f"昨日分时最高量价", fdata)
        if KTickLineAnalyzer.is_too_high_and_not_relase_volume(kline_data_1d):
        if KTickLineAnalyzer.is_too_high_and_not_release_volume(kline_data_1d):
            instance.涨得高未放量 = True
        else:
            instance.涨得高未放量 = False
        if KTickLineAnalyzer.is_latest_limit_up_with_no_release_volume(kline_data_1d):
            instance.涨停过未放量 = True
        else:
            instance.涨停过未放量 = False
        return instance
@@ -661,7 +672,7 @@
    results = __DataLoader.load_target_plate_and_codes()
    # for k in results:
    #     print(k, results[k])
    plates = ["天然气", "军工"]
    plates = ["锂电池", "化工"]
    print("==========新题材=======")
    for p in plates:
        codes = [x for x in results.get(p)]  # if get_zylt(x) < 31e8
@@ -674,7 +685,7 @@
    # __load_target_codes_v1()
    __DataLoader = DataLoader("2025-06-04")
    __DataLoader = DataLoader("2025-06-19")
    # __test_jx_blocks(__DataLoader)
    # instance = StockVariables()
@@ -692,7 +703,7 @@
    results = __DataLoader.load_target_plate_and_codes()
    # for k in results:
    #     print(k, results[k])
    plates = ["锂电池"]
    plates = ["锂电池", "化工"]
    print("==========新题材=======")
    for p in plates:
        print(p, results.get(p))
strategy/test.py
@@ -44,14 +44,28 @@
    #     if len(blocks & target_block) == len(target_block):
    #         print(code, blocks)
    __DataLoader = DataLoader("2025-06-18")
    __DataLoader = DataLoader("2025-06-19")
    kline_datas = __DataLoader.load_kline_data()
    codes = []
    codes1 = []
    codes2 = []
    for code in kline_datas:
        # if code !='003010':
        #     continue
        result = data_analyzer.KTickLineAnalyzer.is_too_high_and_not_relase_volume(kline_datas[code])
        if result:
            print("未放量", code, result[1])
            codes.append(code)
    print(len(codes))
        if code != '002846':
            continue
        result = data_analyzer.KTickLineAnalyzer.is_latest_limit_up_with_no_release_volume(kline_datas[code])
        print("最近涨停没放量", result)
        result = data_analyzer.KTickLineAnalyzer.is_too_high_and_not_release_volume(kline_datas[code])
        print("30个交易日长得太高没放量", result)
        # if result:
        #     print("最近涨停没放量", code, result[1])
        #     codes1.append(code)
        # result = data_analyzer.KTickLineAnalyzer.is_latest_limit_up_with_no_release_volume(kline_datas[code],
        #                                                                                    days_count=5)
        # if result:
        #     print("无高价", code, result[1])
        #     codes2.append(code)
    print(len(codes1), len(codes2))
    for d in set(codes1) - set(codes2):
        print(d)
strategy/time_series_backtest.py
@@ -122,15 +122,15 @@
        :return: 按时间排序的数据列表
        """
        if self.day >= '2025-05-26':
            IS_BY_BIG_ORDER = True
        else:
            IS_BY_BIG_ORDER = False
        else:
            IS_BY_BIG_ORDER = True
        day = self.day
        fdata = {}
        __LowSuctionOriginDataExportManager = LowSuctionOriginDataExportManager(day)
        all_limit_up_list = __LowSuctionOriginDataExportManager.export_limit_up_list()
        fdata["limit_up_list"] = {d[0][:8]: d[1] for d in all_limit_up_list}
        big_order_deals = __LowSuctionOriginDataExportManager.export_big_order_deal(BIG_ORDER_MONEY_THRESHOLD)
        big_order_deals = __LowSuctionOriginDataExportManager.export_big_order_deal(BIG_ORDER_MONEY_THRESHOLD, max_deal_space=3)
        if not big_order_deals or IS_BY_BIG_ORDER:
            big_order_deals = __LowSuctionOriginDataExportManager.export_big_order_deal_by(BIG_ORDER_MONEY_THRESHOLD)
        # 转换格式为:{时间: [("代码", (买单号, 量, 金额, 时间, 最终成交价))]
@@ -358,7 +358,7 @@
            # self.fcodes, self.head_rise_code_blocks = self.__get_target_codes_v3()  # __filter_codes(current_data, timeline_data)
            self.fcodes, self.head_rise_code_blocks = self.__get_target_codes_v4(), {}
        print(len(self.fcodes), self.fcodes)
        print(len(self.fcodes))
        if not self.current_tick_data:
            try:
                self.current_tick_data = self.load_current_tick_datas(self.data_loader)
@@ -700,6 +700,8 @@
                    code = big_order[0]
                    if code not in self.fcodes:
                        continue
                    if DEBUG_CODES and code not in DEBUG_CODES:
                        continue
                    self.init_stock_variables(code, self.timeline_data, self.current_data)
                    stock_variables: StockVariables = self.stock_variables_dict.get(code)
                    if plate_limit_up_codes_info is not None:
@@ -724,6 +726,7 @@
                        self.__process_test_result(code, stock_variables, next_trade_day, big_order[1][4],
                                                   huaxin_util.convert_time(big_order[1][3]), compute_result)
                    except Exception as e:
                        print(time_str)
                        logging.exception(e)
        print("可买题材:", all_new_plates)
@@ -794,7 +797,9 @@
            stock_variables.板块成交代码 = self.deal_block_codes
# DEBUG_CODES = ['603579', '300884']
# 锂电池 ['002882', '002667', '002846', '300530', '002074', '301662', '002580', '300584', '603399', '601515']
# 化工 ['600610', '002427', '002165', '002809', '000565', '002365', '603192', '600370', '600800', '603188']
# DEBUG_CODES = ['600610', '002427', '002165', '002809', '000565', '002365', '603192', '600370', '600800', '603188']
DEBUG_CODES = []
VOLUME_LOG_ENABLE = False
@@ -807,11 +812,12 @@
if __name__ == "__main__":
    back_test_dict = {}
    # days = ["2025-05-12", "2025-05-13", "2025-05-14", "2025-05-15", "2025-05-16", "2025-05-19", "2025-05-20",
    #         "2025-05-21", "2025-05-22", "2025-05-23", "2025-05-26", "2025-05-27", "2025-05-28", "2025-05-29",
    #         "2025-05-30", "2025-06-03"]
    days = ["2025-05-12", "2025-05-13", "2025-05-14", "2025-05-15", "2025-05-16", "2025-05-19", "2025-05-20",
            "2025-05-21", "2025-05-22", "2025-05-23", "2025-05-26", "2025-05-27", "2025-05-28", "2025-05-29",
            "2025-05-30", "2025-06-03"]
    days = ["2025-06-03", "2025-06-04", "2025-06-05", "2025-06-06", "2025-06-09", "2025-06-10",
            "2025-06-11", "2025-06-12", "2025-06-13", "2025-06-16", "2025-06-17", "2025-06-18"]
            "2025-06-11", "2025-06-12", "2025-06-13", "2025-06-16", "2025-06-17", "2025-06-18", "2025-06-19",
            "2025-06-20"]
    # days = ["2025-05-23"]