From d5d2288ba84f2774935b2f866ca6faa8da0aac66 Mon Sep 17 00:00:00 2001
From: Administrator <admin@example.com>
Date: 星期一, 07 七月 2025 18:38:05 +0800
Subject: [PATCH] bug修复/策略完善

---
 strategy/time_series_backtest.py |  335 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 330 insertions(+), 5 deletions(-)

diff --git a/strategy/time_series_backtest.py b/strategy/time_series_backtest.py
index 66d0fa8..4a14f4c 100644
--- a/strategy/time_series_backtest.py
+++ b/strategy/time_series_backtest.py
@@ -176,7 +176,8 @@
 
         code_plates_dict_for_refer = self.data_loader.load_code_plates_for_refer()
 
-        plate_codes = self.data_loader.load_target_plate_and_codes()
+        # TODO 蹇�熸媺浼搁┍鍔�
+        plate_codes = self.data_loader.load_target_plate_and_codes_v2()
         code_plates_dict_for_buy = {}
         for p in plate_codes:
             for code in plate_codes.get(p):
@@ -704,6 +705,330 @@
         finally:
             self.finish = True
 
+    def run_v2(self):
+        def __compute_can_buy_plates(_current_limit_up_list):
+            # 缁熻浠g爜寮�1鏁伴噺:(娑ㄥ仠鏃堕棿,娑ㄥ仠鍘熷洜, 鏄惁浜屾澘, 鏄惁涓�鏉�)
+            code_info_dict = {x[0]: (tool.to_time_str(x[2]), x[5], 1 if kpl_util.get_high_level_count(x[4]) == 2 else 0,
+                                     1 if kpl_util.get_high_level_count(x[4]) == 1 else 0) for x in
+                              _current_limit_up_list}
+            plate_codes_dict = {}
+            # 鎸夋澘鍧楀垎绫�
+            for code in code_info_dict.keys():
+                plates = code_plates.get(code)
+                if not plates:
+                    plates = {code_info_dict.get(code)[1]}
+                plates -= constant.KPL_INVALID_BLOCKS
+                if plates:
+                    for p in plates:
+                        if p not in plate_codes_dict:
+                            plate_codes_dict[p] = set()
+                        plate_codes_dict[p].add(code)
+            valid_plates = set()
+            for p in plate_codes_dict:
+                codes = list(plate_codes_dict[p])
+                codes.sort(key=lambda x: code_info_dict[x][0])
+                # 寮�1 浜屾澘鏁伴噺
+                open_continue_2_count = sum(
+                    [v[2] for k, v in code_info_dict.items() if k in codes and v[0] < '09:30:00'])
+                # 寮�1 棣栨澘鏁伴噺
+                open_continue_1_count = sum(
+                    [v[3] for k, v in code_info_dict.items() if k in codes and v[0] < '09:30:00'])
+                if open_continue_2_count >= 1 or open_continue_1_count >= 2:
+                    valid_plates.add(p)
+                    continue
+                if len(codes) >= 2 and tool.trade_time_sub(code_info_dict[codes[1]][0],
+                                                           code_info_dict[codes[0]][0]) < 10 * 60:
+                    # 鏈夆墺2涓定鍋滐紝涓斾袱涓定鍋滀箣闂撮棿闅斺墹10鍒嗛挓
+                    valid_plates.add(p)
+                    continue
+            return valid_plates
+
+        # 蹇�熸媺鍗囬┍鍔�
+        try:
+            self.load_data()
+            # print(self.fcodes)
+            limit_up_record_data_dict = {}
+            for limit_up_item in self.timeline_data["limit_up_record_data"]:
+                if limit_up_item[0] not in limit_up_record_data_dict:
+                    limit_up_record_data_dict[limit_up_item[0]] = []
+                limit_up_record_data_dict[limit_up_item[0]].append(limit_up_item)
+            self.timeline_data["limit_up_record_data"] = limit_up_record_data_dict
+            next_trade_day = self.timeline_data["next_trade_day"]
+            start_time, end_time = "09:25:00", "12:00:00"
+            # 鍒嗛挓K绾�
+            minute_bars_dict = {}
+            code_plates = self.current_data["code_plates"]
+            code_plates_for_refer = self.current_data["code_plates_for_refer"]
+
+            # 鏉垮潡娑ㄥ仠浠g爜淇℃伅
+            kpl_plate_limit_up_codes_info = None
+            plate_limit_up_codes_info = None
+            kpl_head_plate_limit_up_codes_info = None
+
+            latest_current_limit_up_list = None
+
+            latest_block_in_datas = None
+
+            latest_can_buy_plates = set()
+
+            # 鏍规嵁鏉垮潡鑾峰彇鐩爣绁�
+            target_plate_codes_infos = {}
+            for code in self.head_rise_code_blocks:
+                for p in self.head_rise_code_blocks[code]:
+                    if p not in target_plate_codes_infos:
+                        target_plate_codes_infos[p] = []
+                    target_plate_codes_infos[p].append(self.head_rise_code_blocks[code][p])
+            for p in target_plate_codes_infos:
+                target_plate_codes_infos[p].sort(key=lambda x: x[1], reverse=True)
+
+            all_new_plates = set()
+
+            for i in range(60 * 60 * 5):
+                if self.finish:
+                    break
+                time_str = tool.trade_time_add_second(start_time, i)
+                # print(f"[{tool.get_now_time_str()}]", time_str)
+                if time_str > end_time:
+                    break
+                self.current_time = time_str
+                ticks = self.current_tick_data.get(time_str) if self.current_tick_data else None
+                # ===============缁熻褰撳墠娑ㄥ仠鏁版嵁
+                origin_current_limit_up_list = self.current_data["limit_up_list"].get(time_str, [])
+                current_limit_up_list = [x for x in origin_current_limit_up_list if
+                                         kpl_util.get_high_level_count(x[4]) < 3]
+
+                if current_limit_up_list:
+                    latest_current_limit_up_list = current_limit_up_list
+
+                if current_limit_up_list:
+                    latest_can_buy_plates = __compute_can_buy_plates(current_limit_up_list)
+                    plate_codes_info = {}
+                    # 缁熻鏉垮潡娑ㄥ仠
+                    for x in current_limit_up_list:
+                        # 鎸変唬鐮佺殑鏉垮潡缁熻娑ㄥ仠鏉垮潡涓殑浠g爜鏁伴噺
+                        # 娑ㄥ仠杩�1鍒嗛挓鎵嶇畻鏈夋晥娑ㄥ仠
+                        if tool.trade_time_sub(time_str, tool.timestamp_format(x[2], "%H:%M:%S")) < 60:
+                            continue
+                        plates = code_plates.get(x[0])
+                        if plates:
+                            for p in plates:
+                                if p not in plate_codes_info:
+                                    plate_codes_info[p] = []
+                                plate_codes_info[p].append((x[0], x[2]))
+                    plate_limit_up_codes_info = plate_codes_info
+
+                    plate_codes_info = {}
+                    for x in current_limit_up_list:
+                        # 鎸夊紑鐩樺暒娑ㄥ仠鍘熷洜缁熻
+                        p = x[5]
+                        if p in constant.KPL_INVALID_BLOCKS:
+                            continue
+                        if p not in plate_codes_info:
+                            plate_codes_info[p] = []
+                        plate_codes_info[p].append((x[0], x[2], x[4]))
+                    kpl_plate_limit_up_codes_info = plate_codes_info
+
+                    # {"浠g爜":[(鏉垮潡浠g爜, 鏉垮潡鍚嶇О)]}
+                    limit_up_plate_names_of_refer_code = self.current_data["limit_up_plate_names_of_refer_code"]
+                    plate_codes_info = {}
+                    for x in current_limit_up_list:
+                        # 鎸夊紑鐩樺暒娑ㄥ仠鍘熷洜缁熻
+                        code = x[0]
+                        # if code not in limit_up_plate_names_of_refer_code:
+                        #     continue
+                        # 濡傛灉璁板綍娑ㄥ仠鏃堕棿杩囧幓20鍒嗛挓灏遍噰鐢ㄦ定鍋滈槦鍒楃殑娑ㄥ仠鍘熷洜
+                        if tool.trade_time_sub(time_str, tool.timestamp_format(x[2], "%H:%M:%S")) < 60 * 20 or True:
+                            plates_infos = limit_up_plate_names_of_refer_code.get(code)
+                            plates = set([d[1] for d in plates_infos if d[1] == x[5]]) if plates_infos else set()
+                        else:
+                            plates = {x[5]}
+
+                        new_plates = set()
+                        for p in plates:
+                            if p in constant.KPL_INVALID_BLOCKS:
+                                continue
+                            new_plates.add(p)
+                        for p in new_plates:
+                            if p not in plate_codes_info:
+                                plate_codes_info[p] = []
+                            plate_codes_info[p].append((x[0], x[2]))
+                    kpl_head_plate_limit_up_codes_info = plate_codes_info
+
+                # ==================娉ㄥ叆鏉垮潡娴佸叆
+                block_in_datas = self.current_data["block_in"].get(time_str)
+                if block_in_datas:
+                    blocks = [x[0] for x in block_in_datas if x[1] > 0]
+                    block_in_datas = blocks[:20]
+                    latest_block_in_datas = block_in_datas
+
+                # ================褰撳墠鏃跺埢澶у崟
+                current_big_orders = self.current_data["big_order"].get(time_str)
+                if current_big_orders:
+                    for big_order in current_big_orders:
+                        # 鏍煎紡 ("浠g爜", (涔板崟鍙�, 閲�, 閲戦, 鏃堕棿, 鏈�缁堟垚浜や环))
+                        self.init_stock_variables(big_order[0], self.timeline_data, self.current_data)
+                        stock_variables: StockVariables = self.stock_variables_dict.get(big_order[0])
+                        if stock_variables.浠婃棩澶у崟鏁版嵁 is None:
+                            stock_variables.浠婃棩澶у崟鏁版嵁 = []
+                        stock_variables.浠婃棩澶у崟鏁版嵁.append(big_order[1])
+                        # 缁熻澶у崟鍧囦环
+                        order_ids = set()
+                        total_money = 0
+                        total_volume = 0
+                        for order in reversed(stock_variables.浠婃棩澶у崟鏁版嵁):
+                            if order[0] in order_ids:
+                                continue
+                            order_ids.add(order[0])
+                            total_money += order[2]
+                            total_volume += order[1]
+                        if total_volume > 0:
+                            stock_variables.浠婃棩澶у崟鍧囦环 = round(total_money / total_volume, 2)
+                        else:
+                            stock_variables.浠婃棩澶у崟鍧囦环 = 0
+                current_big_sell_orders = self.current_data["big_sell_order"].get(time_str)
+                if current_big_sell_orders:
+                    for big_order in current_big_sell_orders:
+                        # 鏍煎紡 ("浠g爜", (涔板崟鍙�, 閲�, 閲戦, 鏃堕棿, 鏈�缁堟垚浜や环))
+                        self.init_stock_variables(big_order[0], self.timeline_data, self.current_data)
+                        stock_variables: StockVariables = self.stock_variables_dict.get(big_order[0])
+                        if stock_variables.浠婃棩鍗栧ぇ鍗曟暟鎹� is None:
+                            stock_variables.浠婃棩鍗栧ぇ鍗曟暟鎹� = []
+                        stock_variables.浠婃棩鍗栧ぇ鍗曟暟鎹�.append(big_order[1])
+
+                # 寮�鐩樺暒鏈�姝f定鍋滃師鍥�
+                most_real_kpl_plate_limit_up_codes_info = {}
+                # 鑾峰彇杩欎釜鏉垮潡鐨勭洰鏍囩エ
+                if kpl_plate_limit_up_codes_info:
+                    current_limit_up_dict = {x[0]: x for x in latest_current_limit_up_list}
+                    codes = set()
+                    for plate in kpl_plate_limit_up_codes_info:
+                        kpl_plate_codes = kpl_plate_limit_up_codes_info.get(plate)
+                        codes |= set([x[0] for x in kpl_plate_codes])
+                    for code in codes:
+                        plates = code_plates.get(code)
+                        if not plates:
+                            plates = {current_limit_up_dict.get(code)[5]}
+                        plates -= constant.KPL_INVALID_BLOCKS
+                        if plates:
+                            for p in plates:
+                                if p not in most_real_kpl_plate_limit_up_codes_info:
+                                    most_real_kpl_plate_limit_up_codes_info[p] = []
+                                most_real_kpl_plate_limit_up_codes_info[p].append(code)
+                if ticks:
+                    for tick in ticks:
+                        code = tick["symbol"][-6:]
+                        if code not in self.fcodes:
+                            continue
+                        if self.target_codes and code not in self.target_codes:
+                            continue
+
+                        if code not in self.stock_variables_dict:
+                            # 鍔犺浇鍩虹鏁版嵁
+                            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:
+                            stock_variables.鏉垮潡娑ㄥ仠 = plate_limit_up_codes_info
+
+                        if kpl_plate_limit_up_codes_info is not None:
+                            stock_variables.寮�鐩樺暒鏉垮潡娑ㄥ仠 = kpl_plate_limit_up_codes_info
+
+                        if kpl_head_plate_limit_up_codes_info is not None:
+                            stock_variables.寮�鐩樺暒棰嗘定鏉垮潡娑ㄥ仠 = kpl_head_plate_limit_up_codes_info
+
+                        stock_variables.鏉垮潡鎴愪氦浠g爜 = self.deal_block_codes
+                        # 鏉垮潡娴佸叆鏁版嵁
+                        if latest_block_in_datas:
+                            stock_variables.璧勯噾娴佸叆鏉垮潡 = latest_block_in_datas
+                        # 鏆傛椂涓嶇敤鍒嗛挓K绾�
+                        # if code not in minute_bars_dict:
+                        #     minute_bars_dict[code] = [tick]
+                        # if minute_bars_dict[code][-1]["created_at"][:-2] == tick["created_at"][:-2]:
+                        #     # 缁熻鍒嗛挓K绾�
+                        #     minute_bars_dict[code][-1] = tick
+                        # else:
+                        #     # 淇濆瓨鍒嗛挓K绾挎渶楂樹环
+                        #     if not stock_variables.浠婃棩鏈�楂樹环:
+                        #         stock_variables.浠婃棩鏈�楂樹环 = minute_bars_dict[code][-1]["price"]
+                        #     if minute_bars_dict[code][-1]["price"] > stock_variables.浠婃棩鏈�楂樹环:
+                        #         stock_variables.浠婃棩鏈�楂樹环 = minute_bars_dict[code][-1]["price"]
+
+                        # 淇濆瓨寮�鐩樹环
+                        if tick["created_at"][-8:] < '09:30:00':
+                            stock_variables.浠婃棩寮�鐩樹环 = tick["price"]
+                            # 浠婃棩寮�鐩樻定骞�
+                            stock_variables.浠婃棩寮�鐩樻定骞� = round(
+                                (tick["price"] - stock_variables.鏄ㄦ棩鏀剁洏浠�) / stock_variables.鏄ㄦ棩鏀剁洏浠�,
+                                4)
+                        stock_variables.浠婃棩鎴愪氦閲� = tick["cum_volume"]
+                        stock_variables.浠婃棩鎴愪氦棰� = tick["cum_amount"]
+                        stock_variables.褰撳墠浠� = tick["price"]
+                        if not stock_variables.浠婃棩閲忓淇℃伅:
+                            if stock_variables.浠婃棩鎴愪氦閲� > stock_variables.鏄ㄦ棩鎴愪氦閲� * 0.8:
+                                stock_variables.浠婃棩閲忓淇℃伅 = (time_str, stock_variables.褰撳墠浠�, round(
+                                    (stock_variables.褰撳墠浠� - stock_variables.鏄ㄦ棩鏀剁洏浠�) * 100 / stock_variables.鏄ㄦ棩鏀剁洏浠�, 2),
+                                                          self.__statistic_big_order_info(stock_variables))
+                                if VOLUME_LOG_ENABLE:
+                                    # 缁熻澶у崟鍑�棰濓紝(50w浠ヤ笂,鍑�棰�,涔板崟涓暟/涔板崟鎬婚噾棰�,鍗栧崟涓暟/鍗栧崟鎬婚噾棰�)
+                                    print("****閲忓", code, stock_variables.浠婃棩閲忓淇℃伅)
+
+                        # 缁熻浠婃棩鏈�楂樹环
+                        # if stock_variables.浠婃棩鏈�楂樹环 and tick["price"] > stock_variables.浠婃棩鏈�楂樹环:
+                        #     print(code, "====绐佺牬鍒嗘椂鏈�楂樹环锛�", tick["created_at"], tick["price"])
+
+                        if not stock_variables.浠婃棩鏈�楂樹环淇℃伅 or tick["price"] > stock_variables.浠婃棩鏈�楂樹环淇℃伅[0]:
+                            stock_variables.浠婃棩鏈�楂樹环淇℃伅 = (tick["price"], time_str)
+
+                        if not stock_variables.浠婃棩鏈�浣庝环 or tick["price"] < stock_variables.浠婃棩鏈�浣庝环:
+                            stock_variables.浠婃棩鏈�浣庝环 = tick["price"]
+                        if most_real_kpl_plate_limit_up_codes_info:
+                            stock_variables.寮�鐩樺暒鏈�姝f澘鍧楁定鍋� = most_real_kpl_plate_limit_up_codes_info
+
+                        # 璁$畻娑ㄩ��, 涓婁竴涓猼ick
+                        if stock_variables.涓婁釜tick and stock_variables.涓婁釜tick['price'] > 0:
+                            try:
+                                time_space = tool.trade_time_sub(time_str,
+                                                                 stock_variables.涓婁釜tick["created_at"][-8:]) // 3
+                                if time_space > 0:
+                                    rate = (tick['price'] - stock_variables.涓婁釜tick[
+                                        'price']) * 100 / stock_variables.鏄ㄦ棩鏀剁洏浠� / time_space
+                                    stock_variables.娑ㄩ�� = round(rate, 2)
+                            except:
+                                print("")
+
+                        if stock_variables.娑ㄩ�� >= 0.8:
+                            if code in self.target_codes:
+                                print(time_str, code, f"蹇�熸媺鍗囷細{stock_variables.娑ㄩ�焳",f"浠g爜鏉垮潡锛歿stock_variables.浠g爜鏉垮潡}", f"鍙拱鏉垮潡锛歿latest_can_buy_plates}")
+                            # 鍑嗗鏁版嵁
+                            if plate_limit_up_codes_info is not None:
+                                stock_variables.鏉垮潡娑ㄥ仠 = plate_limit_up_codes_info
+
+                            if kpl_plate_limit_up_codes_info is not None:
+                                stock_variables.寮�鐩樺暒鏉垮潡娑ㄥ仠 = kpl_plate_limit_up_codes_info
+
+                            if kpl_head_plate_limit_up_codes_info is not None:
+                                stock_variables.寮�鐩樺暒棰嗘定鏉垮潡娑ㄥ仠 = kpl_head_plate_limit_up_codes_info
+
+                            if most_real_kpl_plate_limit_up_codes_info is not None:
+                                stock_variables.寮�鐩樺暒鏈�姝f澘鍧楁定鍋� = most_real_kpl_plate_limit_up_codes_info
+
+                            if block_in_datas:
+                                stock_variables.璧勯噾娴佸叆鏉垮潡 = block_in_datas
+
+                            stock_variables.鍙互涔扮殑鏉垮潡 = latest_can_buy_plates
+                            try:
+                                compute_result = self.__run_backtest(code, stock_variables)
+                                # print(compute_result)
+                                self.__process_test_result(code, stock_variables, next_trade_day, stock_variables.褰撳墠浠�,
+                                                           time_str, compute_result)
+                            except Exception as e:
+                                print(time_str)
+                                logging.exception(e)
+                        stock_variables.涓婁釜tick = tick
+
+            print("鍙拱棰樻潗锛�", all_new_plates)
+        finally:
+            self.finish = True
+
     def __process_test_result(self, code, stock_variables: StockVariables, next_trade_day, buy_price, time_str,
                               compute_result):
 
@@ -799,9 +1124,9 @@
     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-19",
             "2025-06-20", "2025-06-23", "2025-06-24", "2025-06-25", "2025-06-26", "2025-06-27", "2025-06-30",
-            "2025-07-01", "2025-07-02"]
+            "2025-07-01", "2025-07-02", "2025-07-03", "2025-07-04"]
 
-    # days = ["2025-05-23"]
+    days = ["2025-07-04"]
     #
     # 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"]
@@ -810,7 +1135,7 @@
     for day in days:
         if day not in back_test_dict:
             # back_test_dict[day] = BackTest(day, "浠婃棩閲忔槸鍚﹁冻澶�.py")
-            back_test_dict[day] = BackTest(day, "strategy_script_v6.py")
+            back_test_dict[day] = BackTest(day, "strategy_script_v7.py", target_codes={})
         print("=========================", day)
         # back_test_dict[day].run_volume()
-        back_test_dict[day].run()
+        back_test_dict[day].run_v2()

--
Gitblit v1.8.0