| | |
| | | @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): |
| | |
| | | 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个交易日,不包含当前交易日,时间倒序) |
| | |
| | | # 从最高价日期向前最多看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 |
| | |
| | | |
| | | 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: |
| | |
| | | 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'])) |
| | |
| | | import constant |
| | | from db.mysql_data_delegate import Mysqldb |
| | | from strategy import strategy_variable |
| | | from utils import huaxin_util, tool |
| | | |
| | | |
| | | class BackTest: |
| | |
| | | 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: {"代码":[(买单号, 量, 金额, 时间, 最终成交价)]} |
| | |
| | | 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]) |
| | |
| | | """ |
| | | 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) |
| | |
| | | } |
| | | 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 |
| | |
| | | self.buy_money = 2000 |
| | | # 最大买入票的数量 |
| | | self.max_buy_codes_count = 100 |
| | | # 每个板块最多买入的代码数量 |
| | | self.max_buy_codes_count_per_plate = 1 |
| | | |
| | | # 价格区间 |
| | | self.price_range = (3, 60) |
| | | # 老题材涨停数 |
| | |
| | | return f"{huaxin_timestamp[0:2]}:{huaxin_timestamp[2: 4]}:{huaxin_timestamp[4: 6]}" |
| | | |
| | | |
| | | def can_buy(): |
| | | def __can_buy(): |
| | | """ |
| | | @return: 是否可买, 不能买的原因/可买的板块, 是否量够 |
| | | """ |
| | |
| | | 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}" |
| | |
| | | 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%" |
| | |
| | | |
| | | # 目标票板块涨停个数>=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.连续老题材}" |
| | |
| | | |
| | | 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() |
| | |
| | | self.领涨板块信息 = set() |
| | | self.连续老题材 = set() |
| | | self.涨得高未放量 = False |
| | | self.涨停过未放量 = False |
| | | |
| | | def replace_variables(self, expression): |
| | | """ |
| | |
| | | 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: |
| | |
| | | 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 |
| | |
| | | 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 |
| | | |
| | |
| | | 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 |
| | |
| | | |
| | | # __load_target_codes_v1() |
| | | |
| | | __DataLoader = DataLoader("2025-06-04") |
| | | __DataLoader = DataLoader("2025-06-19") |
| | | # __test_jx_blocks(__DataLoader) |
| | | |
| | | # instance = StockVariables() |
| | |
| | | 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)) |
| | |
| | | # 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) |
| | |
| | | :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) |
| | | # 转换格式为:{时间: [("代码", (买单号, 量, 金额, 时间, 最终成交价))] |
| | |
| | | # 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) |
| | |
| | | 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: |
| | |
| | | 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) |
| | |
| | | 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 |
| | |
| | | |
| | | 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"] |
| | | |