From 892b50e242e3c59a738b92dfdfee1bf1ff8932f2 Mon Sep 17 00:00:00 2001 From: Administrator <admin@example.com> Date: 星期五, 21 十月 2022 16:59:58 +0800 Subject: [PATCH] 新策略修改 --- l2_data_manager.py | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 366 insertions(+), 29 deletions(-) diff --git a/l2_data_manager.py b/l2_data_manager.py index c19499f..a5eca4d 100644 --- a/l2_data_manager.py +++ b/l2_data_manager.py @@ -73,9 +73,9 @@ _key = "buy_compute_index_info-{}".format(code) _data_json = redis.get(_key) if _data_json is None: - return None, None, None, 0 + return None, None, None, 0, 0 _data = json.loads(_data_json) - return _data[0], _data[1], _data[2], _data[3] + return _data[0], _data[1], _data[2], _data[3], _data[4] # 璁剧疆涔板叆鐐圭殑鍊� # buy_single_index 涔板叆淇″彿浣� @@ -83,16 +83,16 @@ # compute_index 璁$畻浣嶇疆 # nums 绱绾拱棰� @staticmethod - def set_buy_compute_start_data(code, buy_single_index, buy_exec_index, compute_index, nums): + def set_buy_compute_start_data(code, buy_single_index, buy_exec_index, compute_index, nums, count): redis = TradePointManager.__get_redis() expire = tool.get_expire() _key = "buy_compute_index_info-{}".format(code) if buy_single_index is not None: - redis.setex(_key, expire, json.dumps((buy_single_index, buy_exec_index, compute_index, nums))) + redis.setex(_key, expire, json.dumps((buy_single_index, buy_exec_index, compute_index, nums, count))) else: - _buy_single_index, _buy_exec_index, _compute_index, _nums = TradePointManager.get_buy_compute_start_data( + _buy_single_index, _buy_exec_index, _compute_index, _nums, _count = TradePointManager.get_buy_compute_start_data( code) - redis.setex(_key, expire, json.dumps((_buy_single_index, buy_exec_index, compute_index, nums))) + redis.setex(_key, expire, json.dumps((_buy_single_index, buy_exec_index, compute_index, nums, count))) # 鑾峰彇鎾や拱鍏ュ紑濮嬭绠楃殑淇℃伅 # 杩斿洖鏁版嵁鐨勫唴瀹逛负锛氭挙閿�鐐圭储寮� 鎾や拱绾拱棰� 璁$畻鐨勬暟鎹储寮� @@ -265,6 +265,16 @@ saveL2Data(code, add_datas) +# 娓呴櫎l2鏁版嵁 +def clear_l2_data(code): + redis_l2 = redis_manager.RedisManager(1).getRedis() + keys = redis_l2.keys("l2-{}-*".format(code)) + for k in keys: + redis_l2.delete(k) + + redis_l2.delete("l2-data-latest-{}".format(code)) + + class L2DataUtil: @classmethod def is_same_time(cls, time1, time2): @@ -363,10 +373,11 @@ else: limitPrice = 0 item["limitPrice"] = "{}".format(limitPrice) - # 涓嶉渶瑕侀潪娑ㄥ仠鏁版嵁/闈炶穼鍋滄暟鎹� - if int(item["limitPrice"]) == 0: - continue operateType = item["operateType"] + # 涓嶉渶瑕侀潪娑ㄥ仠涔颁笌涔版挙 + if int(item["limitPrice"]) != 1 and (int(operateType) == 0 or int(operateType) == 1): + continue + cancelTime = item["cancelTime"] cancelTimeUnit = item["cancelTimeUnit"] key = "{}-{}-{}-{}-{}-{}-{}-{}".format(code, operateType, time, num, price, limitPrice, cancelTime, @@ -380,6 +391,8 @@ dataIndexs.setdefault(key, len(datas) - 1) l2_data_util.save_big_data(code, same_time_num, data) return datas + + @classmethod def get_time_as_second(cls, time_str): @@ -406,6 +419,20 @@ return False return True + @classmethod + def is_limit_up_price_sell(cls, val): + if int(val["limitPrice"]) != 1: + return False + + if int(val["operateType"]) != 2: + return False + + price = float(val["price"]) + num = int(val["num"]) + if price * num * 100 < 50 * 10000: + return False + return True + # 鏄惁娑ㄥ仠涔版挙 @classmethod def is_limit_up_price_buy_cancel(cls, val): @@ -420,6 +447,20 @@ if price * num * 100 < 50 * 10000: return False return True + + # 鏄惁鍗栨挙 + @classmethod + def is_sell_cancel(cls, val): + if int(val["operateType"]) == 3: + return True + return False + + # 鏄惁涓哄崠 + @classmethod + def is_sell(cls, val): + if int(val["operateType"]) == 2: + return True + return False # L2浜ゆ槗鏁版嵁澶勭悊鍣� @@ -484,17 +525,17 @@ latest_time = add_datas[len(add_datas) - 1]["val"]["time"] # 鏃堕棿宸笉鑳藉お澶ф墠鑳藉鐞� # TODO 鏆傛椂鍏抽棴澶勭悊 - if L2DataUtil.is_same_time(now_time_str, latest_time): - # 鍒ゆ柇鏄惁宸茬粡鎸傚崟 - state = trade_manager.get_trade_state(code) - start_index = len(total_datas) - len(add_datas) - end_index = len(total_datas) - 1 - if state == trade_manager.TRADE_STATE_BUY_DELEGATED or state == trade_manager.TRADE_STATE_BUY_PLACE_ORDER: - # 宸叉寕鍗� - cls.__process_order(code, start_index, end_index, capture_timestamp) - else: - # 鏈寕鍗� - cls.__process_not_order(code, start_index, end_index, capture_timestamp) + # if L2DataUtil.is_same_time(now_time_str, latest_time): + # # 鍒ゆ柇鏄惁宸茬粡鎸傚崟 + # state = trade_manager.get_trade_state(code) + # start_index = len(total_datas) - len(add_datas) + # end_index = len(total_datas) - 1 + # if state == trade_manager.TRADE_STATE_BUY_DELEGATED or state == trade_manager.TRADE_STATE_BUY_PLACE_ORDER: + # # 宸叉寕鍗� + # cls.__process_order(code, start_index, end_index, capture_timestamp) + # else: + # # 鏈寕鍗� + # cls.__process_not_order(code, start_index, end_index, capture_timestamp) logger_l2_process.info("code:{} 澶勭悊鏁版嵁鑼冨洿: {}-{} 澶勭悊鏃堕棿:{}", code, add_datas[0]["index"], add_datas[-1]["index"], round(t.time() * 1000) - __start_time) # 淇濆瓨鏁版嵁 @@ -722,6 +763,8 @@ except Exception as e: cls.debug(code, "鎵ц涔板叆寮傚父:{}", str(e)) pass + finally: + cls.debug(code, "m鍊煎奖鍝嶅洜瀛愶細", l2_trade_factor.L2TradeFactorUtil.factors_to_string(code)) # 鏄惁鍙互涔� @classmethod @@ -774,10 +817,19 @@ # 鍒犻櫎澶х兢鎾や簨浠剁殑澶у崟 L2BetchCancelBigNumProcessor.del_recod(code) L2ContinueLimitUpCountManager.del_data(code) + if code in cls.unreal_buy_dict: cls.unreal_buy_dict.pop(code) + # 鍙栨秷涔板叆鏍囪瘑 + TradePointManager.delete_buy_point(code) + TradePointManager.delete_buy_cancel_point(code) + TradePointManager.delete_compute_info_for_cancel_buy(code) + TradePointManager.delete_count_info_for_cancel_buy(code) + # 鍒犻櫎澶х兢鎾や簨浠剁殑澶у崟 + L2BetchCancelBigNumProcessor.del_recod(code) else: cls.__cancel_buy(code) + L2BigNumProcessor.del_big_num_pos(code) @classmethod @@ -905,7 +957,7 @@ count += datas[i]["re"] if count >= continue_count: return True, start - else: + elif not L2DataUtil.is_limit_up_price_sell(_val): last_index = None count = 0 start = None @@ -931,7 +983,7 @@ start = i start_time = L2DataUtil.get_time_as_second(_val["time"]) count += datas[i]["re"] - else: + elif not L2DataUtil.is_limit_up_price_sell(_val): if count >= continue_count: return start, i - 1 start = -1 @@ -967,7 +1019,7 @@ start = i start_time = L2DataUtil.get_time_as_second(_val["time"]) count += int(datas[i]["re"]) - else: + elif not L2DataUtil.is_limit_up_price_sell(_val): if count >= continue_count: return start, i - 1 start = -1 @@ -1323,7 +1375,7 @@ @classmethod def test_can_order(cls): - code = "002393" + code = "000948" global_util.load_industry() limit_up_time_manager.load_limit_up_time() @@ -1618,8 +1670,9 @@ if need_cancel: # 闇�瑕佹挙鍗� # 鎾ゅ崟 - cls.__cancel_buy(code, max_num_data["index"]) - L2TradeDataProcessor.cancel_debug(code, "璺熻釜鍒板ぇ鍗曟棤鎾や拱淇″彿-{}锛屾柊璺熻釜鐨勫ぇ鍗曢渶瑕佹挙涔�-{}", index, max_num_data["index"]) + cls.__cancel_buy(code, max_num_data["index"] if cancel_data is None else cancel_data) + L2TradeDataProcessor.cancel_debug(code, "鍘熸潵璺熻釜鍒板ぇ鍗曟棤鎾や拱淇″彿-{}锛屾柊璺熻釜鐨勫ぇ鍗曢渶瑕佹挙涔�-{}", index, + max_num_data["index"]) return True, cancel_data else: # 鏃犻渶鎾ゅ崟 @@ -1695,8 +1748,8 @@ if i <= latest_buy_index: total_count += total_datas[i]["re"] L2TradeDataProcessor.debug(code, "澶х兢鎾ゅぇ鍗曟暟閲忥細{}/{}", count, total_count) - # 澶у崟灏忎簬5绗旀棤鑴戞挙 - if total_count <= 5: + # 澶у崟灏忎簬5绗旀棤鑴戞挙锛屽悗淇敼涓烘棤澶у崟鏃犺剳鎾� + if total_count <= 0: return True # 澶у崟鎾ゅ崟绗旀暟澶т簬鎬诲ぇ鍗曠瑪鏁扮殑1/5灏辨挙鍗� @@ -1788,6 +1841,287 @@ index_set.add(d[1]) big_nums_info_new.append(d) cls.__save_recod(code, max_big_num_info, big_nums_info_new) + + +# 鍗栬窡韪� +class L2SellProcessor: + @classmethod + def __get_recod(cls, code): + redis = _redisManager.getRedis() + _val = redis.get("sell_num-{}".format(code)) + if _val is None: + return None, None + else: + datas = json.loads(_val) + return datas[0], datas[1] + + @classmethod + def del_recod(cls, code): + redis = _redisManager.getRedis() + key = "sell_num-{}".format(code) + redis.delete(key) + + @classmethod + def __save_recod(cls, code, process_index, count): + redis = _redisManager.getRedis() + key = "sell_num-{}".format(code) + redis.setex(key, tool.get_expire(), json.dumps((process_index, count))) + + # 鏆傛椂寮冪敤 + @classmethod + def need_cancel(cls, code, start_index, end_index): + # 鏄惁闇�瑕佹挙鍗� + process_index, count = cls.__get_recod(code) + if process_index is None: + # 鏃犲崠鐨勪俊鎭� + return False + if count is None: + count = 0 + limit_up_price = gpcode_manager.get_limit_up_price(code) + if limit_up_price is None: + return False + if float(limit_up_price) * count * 100 >= l2_trade_factor.L2TradeFactorUtil.get_base_safe_val( + global_util.zyltgb_map[code]): + return True + return False + + @classmethod + def process(cls, code, start_index, end_index): + # 澶勭悊澶у崟 + # 鑾峰彇澶у崟鍒楄〃,澶у崟鏍煎紡涓�:((num,index,re),[(num,index,re),(num,index,re)]) + total_datas = local_today_datas[code] + process_index, count = cls.__get_recod(code) + # 瀵绘壘鏈�澶у�� + for index in range(start_index, end_index + 1): + # 鍙鐞嗘定鍋滃崠 + if not L2DataUtil.is_limit_up_price_sell( + total_datas[index]["val"]): + continue + # 涓嶅鐞嗗巻鍙叉暟鎹� + if process_index is not None and process_index >= index: + continue + if count is None: + count = 0 + count += int(total_datas[index]["val"]["num"]) + if process_index is None: + process_index = end_index + cls.__save_recod(code, process_index, count) + + +# 娑ㄥ仠灏佸崟棰濈粺璁� +class L2LimitUpMoneyStatisticUtil: + _redisManager = redis_manager.RedisManager(1) + + @classmethod + def __get_redis(cls): + return cls._redisManager.getRedis() + + # 璁剧疆l2鐨勬瘡涓�绉掓定鍋滃皝鍗曢鏁版嵁 + @classmethod + def __set_l2_second_money_record(cls, code, time, num, from_index, to_index): + old_num, old_from, old_to = cls.__get_l2_second_money_record(code, time) + if old_num is None: + old_num = num + old_from = from_index + old_to = to_index + else: + old_num += num + old_to = to_index + + key = "l2_limit_up_second_money-{}-{}".format(code, time.replace(":", "")) + + cls.__get_redis().setex(key, tool.get_expire(), json.dumps((old_num, old_from, old_to))) + + @classmethod + def __get_l2_second_money_record(cls, code, time): + key = "l2_limit_up_second_money-{}-{}".format(code, time.replace(":", "")) + val = cls.__get_redis().get(key) + return cls.__format_second_money_record_val(val) + + @classmethod + def __format_second_money_record_val(cls, val): + if val is None: + return None, None, None + val = json.loads(val) + return val[0], val[1], val[2] + + @classmethod + def __get_l2_second_money_record_keys(cls, code, time_regex): + key = "l2_limit_up_second_money-{}-{}".format(code, time_regex) + keys = cls.__get_redis().keys(key) + return keys + + # 璁剧疆l2鏈�鏂扮殑灏佸崟棰濇暟鎹� + @classmethod + def __set_l2_latest_money_record(cls, code, index, num): + key = "l2_limit_up_money-{}".format(code) + cls.__get_redis().setex(key, tool.get_expire(), json.dumps((num, index))) + + # 杩斿洖鏁伴噺,绱㈠紩 + @classmethod + def __get_l2_latest_money_record(cls, code): + key = "l2_limit_up_money-{}".format(code) + result = cls.__get_redis().get(key) + if result: + result = json.loads(result) + return result[0], result[1] + else: + return 0, -1 + + # 鐭鏁版嵁 + # 鐭鏂规硶涓哄彇鐭鏃堕棿涓や晶鐨勭鍒嗗竷鏁版嵁锛岀敤浜庣‘瀹氳绠楃粨鏉熷潗鏍� + @classmethod + def verify_num(cls, code, num, time_str): + time_ = time_str.replace(":", "") + key = None + for i in range(4, -2, -2): + # 鑾峰彇鏈�(鍒嗛挓/灏忔椂/澶�)鍐呯鍒嗗竷鏁版嵁 + time_regex = "{}*".format(time_[:i]) + keys_ = cls.__get_l2_second_money_record_keys(code, time_regex) + if keys_ and len(keys_) > 1: + # 闇�瑕佹帓搴� + keys = [] + for k in keys_: + keys.append(k) + keys.sort(key=lambda tup: int(tup.split("-")[-1])) + # 鏈�2涓厓绱� + for index in range(0, len(keys) - 1): + time_1 = keys[index].split("-")[-1] + time_2 = keys[index + 1].split("-")[-1] + if int(time_1) <= int(time_) <= int(time_2): + # 鍦ㄦ鏃堕棿鑼冨洿鍐� + if time_ == time_2: + key = keys[index + 1] + else: + key = keys[index] + break + if key: + val = cls.__get_redis().get(key) + old_num, old_from, old_to = cls.__format_second_money_record_val(val) + end_index = old_to + # 淇濆瓨鏈�杩戠殑鏁版嵁 + cls.__set_l2_latest_money_record(code, end_index, num) + break + + # 璁$畻閲忥紝鐢ㄤ簬娑ㄥ仠灏佸崟閲忕殑璁$畻 + @classmethod + def __compute_num(cls, code, data, buy_single_data): + if L2DataUtil.is_limit_up_price_buy_cancel(data["val"]) or L2DataUtil.is_sell(data["val"]): + # 娑ㄥ仠涔版挙涓庡崠 + return 0 - int(data["val"]["num"]) * data["re"] + else: + # 鍗栨挙 + if L2DataUtil.is_sell_cancel(data["val"]): + # 鍗栨挙鐨勪拱鏁版嵁鏄惁鍦ㄤ拱鍏ヤ俊鍙蜂箣鍓嶏紝濡傛灉鍦ㄤ箣鍓嶅氨涓嶈绠楋紝涓嶅湪涔嬪墠灏辫绠� + if l2_data_util.is_sell_index_before_target(data, buy_single_data, + local_today_num_operate_map.get(code)): + return 0 + + return int(data["val"]["num"]) * data["re"] + + @classmethod + def clear(cls, code): + key = "l2_limit_up_money-{}".format(code) + cls.__get_redis().delete(key) + + # 杩斿洖鍙栨秷鐨勬爣蹇楁暟鎹� + # with_cancel 鏄惁闇�瑕佸垽鏂槸鍚︽挙閿� + @classmethod + def process_data(cls, code, start_index, end_index, buy_single_begin_index, with_cancel=True): + start_time = round(t.time() * 1000) + total_datas = local_today_datas[code] + time_dict_num = {} + # 璁板綍璁$畻鐨勫潗鏍� + time_dict_num_index = {} + num_dict = {} + # 缁熻鏃堕棿鍒嗗竷 + time_dict = {} + for i in range(start_index, end_index + 1): + data = total_datas[i] + val = data["val"] + time_ = val["time"] + if time_ not in time_dict: + time_dict[time_] = i + + for i in range(start_index, end_index + 1): + data = total_datas[i] + val = data["val"] + time_ = val["time"] + if time_ not in time_dict_num: + time_dict_num[time_] = 0 + time_dict_num_index[time_] = {"s": i, "e": i} + time_dict_num_index[time_]["e"] = i + num = cls.__compute_num(code, data, total_datas[buy_single_begin_index]) + num_dict[i] = num + time_dict_num[time_] = time_dict_num[time_] + num + for t_ in time_dict_num: + cls.__set_l2_second_money_record(code, t_, time_dict_num[t_], time_dict_num_index[t_]["s"], + time_dict_num_index[t_]["e"]) + + print("淇濆瓨娑ㄥ仠灏佸崟棰濇椂闂达細", round(t.time() * 1000) - start_time) + + # 绱鏈�鏂扮殑閲戦 + total_num, index = cls.__get_l2_latest_money_record(code) + if index == -1: + # 娌℃湁鑾峰彇鍒版渶鏂扮殑鐭灏佸崟棰濓紝闇�瑕佷粠涔板叆淇″彿寮�濮嬬偣璁$畻 + index = buy_single_begin_index - 1 + total_num = 0 + # TODO 寰呬紭鍖栬绠� + cancel_index = None + cancel_msg = None + # 寰呰绠楅噺 + limit_up_price = gpcode_manager.get_limit_up_price(code) + min_volumn = round(10000000 / (limit_up_price * 100)) + # 涓嶅悓鏃堕棿鐨勬暟鎹紑濮嬪潗鏍� + time_start_index_dict = {} + # 鏁版嵁鏃堕棿鍒嗗竷 + time_list = [] + # 鍒板綋鍓嶆椂闂寸疮绉殑涔�1閲� + time_total_num_dict = {} + for i in range(index + 1, end_index + 1): + data = total_datas[i] + time_ = data["val"]["time"] + if time_ not in time_start_index_dict: + # 璁板綍姣忎竴绉掔殑寮�濮嬩綅缃� + time_start_index_dict[time_] = i + # 璁板綍鏃堕棿鍒嗗竷 + time_list.append(time_) + # 涓婁竴娈垫椂闂寸殑鎬绘暟 + time_total_num_dict[time_] = total_num + + val = num_dict.get(i) + if val is None: + val = cls.__compute_num(code, data, total_datas[buy_single_begin_index]) + total_num += val + # 濡傛灉鏄噺灏忛」锛屼笖鍦ㄥ鐞嗘暟鎹殑鑼冨洿鍐咃紝灏遍渶瑕佸垽鏂槸鍚﹁鎾ゅ崟浜� + if val < 0 and start_index <= i <= end_index: + # 绱灏佸崟閲戦灏忎簬1000涓� + if total_num < min_volumn: + cancel_index = i + cancel_msg = "灏佸崟閲戦灏忎簬1000涓�" + break + # 鐩搁偦2s鍐呯殑鏁版嵁鍑忓皬50% + # 涓�1s鐨勬�绘暟 + last_second_total_volumn = time_total_num_dict.get(time_list[-1]) + if last_second_total_volumn > 0 and ( + last_second_total_volumn - total_num) / last_second_total_volumn >= 0.5: + # 鐩搁偦2s鍐呯殑鏁版嵁鍑忓皬50% + cancel_index = i + cancel_msg = "鐩搁偦2s({})鍐呯殑灏佸崟閲忓噺灏�50%({}->{})".format(time_, last_second_total_volumn, + total_num) + break + if not with_cancel: + cancel_index = None + + print("灏佸崟棰濊绠楁椂闂达細", round(t.time() * 1000) - start_time) + process_end_index = end_index + if cancel_index: + process_end_index = cancel_index + # 淇濆瓨鏈�鏂扮疮璁¢噾棰� + # cls.__set_l2_latest_money_record(code, process_end_index, total_num) + if cancel_index: + return total_datas[cancel_index], cancel_msg + return None, None def __get_time_second(time_str): @@ -2035,4 +2369,7 @@ if __name__ == "__main__": - L2TradeDataProcessor.test_can_order() + # 澶勭悊鏁版嵁 + code = "002898" + load_l2_data(code) + L2LimitUpMoneyStatisticUtil.verify_num(code, 70582, "09:42:00") -- Gitblit v1.8.0