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