From 4ee8658d8936af0c13a39cdaa1a51d995d4a0974 Mon Sep 17 00:00:00 2001 From: blade <8019068@qq.com> Date: Thu, 31 Jul 2025 20:31:22 +0800 Subject: [PATCH] support statistics price-volume relationship --- .../__pycache__/huge_volume.cpython-312.pyc | Bin 0 -> 17275 bytes .../market_data_monitor.cpython-312.pyc | Bin 0 -> 9642 bytes .../__pycache__/trade_data.cpython-312.pyc | Bin 0 -> 5270 bytes core/{ => biz}/huge_volume.py | 212 ++++++++++++------ core/{ => biz}/market_data_monitor.py | 17 +- core/{ => biz}/quant_trader.py | 0 core/{ => biz}/strategy.py | 2 +- core/{ => biz}/trade_data.py | 4 +- .../db_huge_volume_data.cpython-312.pyc | Bin 0 -> 21027 bytes .../db/__pycache__/db_manager.cpython-312.pyc | Bin 0 -> 12164 bytes .../db_market_data.cpython-312.pyc | Bin 0 -> 6473 bytes .../__pycache__/db_trade_data.cpython-312.pyc | Bin 0 -> 17287 bytes core/{ => db}/db_huge_volume_data.py | 23 +- core/{ => db}/db_manager.py | 6 +- core/{ => db}/db_market_data.py | 33 +-- core/{ => db}/db_trade_data.py | 23 +- core/utils.py | 28 ++- .../DB_HUGE_VOLUME_UPDATE_SUMMARY.md | 0 .../DB_TRADE_DATA_SUMMARY.md | 0 .../HUGE_VOLUME_UPDATE_SUMMARY.md | 0 huge_volume_main.py | 148 +++++------- market_data_main.py | 73 +++--- sql/query/sql_playground.sql | 4 +- sql/table/crypto_huge_volume.sql | 10 +- test_db_huge_volume.py | 2 +- test_db_trade_data.py | 2 +- test_huge_volume.py | 2 +- trade_data_main.py | 29 +-- trade_main.py | 4 +- 29 files changed, 294 insertions(+), 328 deletions(-) create mode 100644 core/biz/__pycache__/huge_volume.cpython-312.pyc create mode 100644 core/biz/__pycache__/market_data_monitor.cpython-312.pyc create mode 100644 core/biz/__pycache__/trade_data.cpython-312.pyc rename core/{ => biz}/huge_volume.py (53%) rename core/{ => biz}/market_data_monitor.py (92%) rename core/{ => biz}/quant_trader.py (100%) rename core/{ => biz}/strategy.py (99%) rename core/{ => biz}/trade_data.py (97%) create mode 100644 core/db/__pycache__/db_huge_volume_data.cpython-312.pyc create mode 100644 core/db/__pycache__/db_manager.cpython-312.pyc create mode 100644 core/db/__pycache__/db_market_data.cpython-312.pyc create mode 100644 core/db/__pycache__/db_trade_data.cpython-312.pyc rename core/{ => db}/db_huge_volume_data.py (96%) rename core/{ => db}/db_manager.py (98%) rename core/{ => db}/db_market_data.py (81%) rename core/{ => db}/db_trade_data.py (95%) rename DB_HUGE_VOLUME_UPDATE_SUMMARY.md => doc/DB_HUGE_VOLUME_UPDATE_SUMMARY.md (100%) rename DB_TRADE_DATA_SUMMARY.md => doc/DB_TRADE_DATA_SUMMARY.md (100%) rename HUGE_VOLUME_UPDATE_SUMMARY.md => doc/HUGE_VOLUME_UPDATE_SUMMARY.md (100%) diff --git a/core/biz/__pycache__/huge_volume.cpython-312.pyc b/core/biz/__pycache__/huge_volume.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f72f6f78a12ce55168352f6029772e47555f3d4 GIT binary patch literal 17275 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%0DR8;>xx9c%N*kw@ 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 literal 0 HcmV?d00001 diff --git a/core/biz/__pycache__/market_data_monitor.cpython-312.pyc b/core/biz/__pycache__/market_data_monitor.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98427ee0e5943719f2c6170d1db46ab2ce428690 GIT binary patch literal 9642 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 literal 0 HcmV?d00001 diff --git a/core/biz/__pycache__/trade_data.cpython-312.pyc b/core/biz/__pycache__/trade_data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d0e21c0a74825a296deb6df05d7db5a4bf77156 GIT binary patch literal 5270 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 LW&H+`TAJa1c)FCD literal 0 HcmV?d00001 diff --git a/core/huge_volume.py b/core/biz/huge_volume.py similarity index 53% rename from core/huge_volume.py rename to core/biz/huge_volume.py index fd5f6a2..e7ee399 100644 --- a/core/huge_volume.py +++ b/core/biz/huge_volume.py @@ -4,6 +4,7 @@ import os import re import pandas as pd from datetime import datetime +from copy import deepcopy from typing import Optional, List, Dict, Any, Tuple logging.basicConfig( @@ -175,8 +176,7 @@ class HugeVolume: self, data: pd.DataFrame, window_size: int = 50, - periods: List[int] = [3, 5], - output_excel: bool = False + periods: List[int] = [1, 2, 3, 5, 10], ) -> Tuple[pd.DataFrame, pd.DataFrame]: """ 1. 根据period_count,计算每个timestamp的下一个periods的rise_or_fall @@ -195,9 +195,8 @@ class HugeVolume: 1000000000 100 1 103 rise 98 fall 因为之后第3个periods的close是103,所以next_3_result为rise 因为之后第5个periods的close是98,所以next_3_result为fall - - 2. 如果output_excel为True,则输出到excel - 3. 新建一个列表: result,计算huge_volume为1时,之后3或5个周期,close上涨或下跌的比例 + 2. 根据volume_ratio_percentile_10,将data分成10份,然后计算每一份的上涨与下跌次数,以及平均收益率 + 3. 新建一个列表: result,计算之后n个周期,close上涨或下跌的比例 a. 计算huge_volume为1时,且price_80_high为1时的数量,如100, 并且计算next_3_result为fall的次数,如50, 然后计算fall_ratio, 如50/100=0.5 b. 计算huge_volume为1时,且price_80_high为1时的数量,如100, 并且计算next_5_result为fall的次数,如30, 然后计算fall_ratio, 如30/100=0.3 c. 计算huge_volume为1时,且price_20_low为1时的数量,如100, 并且计算next_3_result为rise的次数,如50, 然后计算rise_ratio, 如50/100=0.5 @@ -206,21 +205,32 @@ class HugeVolume: Args: data: 包含巨量交易数据的DataFrame - periods: 计算周期列表,默认[3, 5] + periods: 计算周期列表,默认[1, 2, 3, 5, 10] output_excel: 是否输出到Excel文件,默认False Returns: Tuple[pd.DataFrame, pd.DataFrame]: (处理后的数据, 统计结果) """ + # 将huge_volume, volume_80_20_price_spike, volume_90_10_price_spike, + # price_80_high, price_20_low, price_90_high, price_10_low设置为整型 + data["huge_volume"] = data["huge_volume"].astype(int) + data["volume_80_20_price_spike"] = data["volume_80_20_price_spike"].astype(int) + data["volume_90_10_price_spike"] = data["volume_90_10_price_spike"].astype(int) + data["price_80_high"] = data["price_80_high"].astype(int) + data["price_20_low"] = data["price_20_low"].astype(int) + data["price_90_high"] = data["price_90_high"].astype(int) + data["price_10_low"] = data["price_10_low"].astype(int) + data = data.sort_values(by="timestamp", ascending=True) data = data.reset_index(drop=True) - + # 计算未来价格变化 for period in periods: data[f"next_{period}_close"] = data["close"].shift(-period) - data[f"next_{period}_result"] = ( - data[f"next_{period}_close"] / data["close"] - 1 + data[f"next_{period}_change"] = ( + (data[f"next_{period}_close"] / data["close"] - 1) * 100 ) - data[f"next_{period}_result"] = data[f"next_{period}_result"].apply( + # 添加一列next_{period}_result,如果next_{period}_change大于0,则值为rise,如果next_{period}_change小于0,则值为fall,如果next_{period}_change等于0,则值为draw + data[f"next_{period}_result"] = data[f"next_{period}_change"].apply( lambda x: ( "rise" if pd.notna(x) and x > 0 @@ -231,68 +241,126 @@ class HugeVolume: ) ) ) - - # 过滤data, 只获取huge_volume为1,且价格处于分位数位置的行 - price_conditions = [] - if "price_80_high" in data.columns: - price_conditions.append(data["price_80_high"] == 1) - if "price_20_low" in data.columns: - price_conditions.append(data["price_20_low"] == 1) - if "price_90_high" in data.columns: - price_conditions.append(data["price_90_high"] == 1) - if "price_10_low" in data.columns: - price_conditions.append(data["price_10_low"] == 1) - - if price_conditions: - combined_condition = data["huge_volume"] == 1 - for condition in price_conditions: - combined_condition = combined_condition | condition - data = data[combined_condition] - - data = data.reset_index(drop=True) - - # 统计各种分位数情况的数量 - price_stats = {} - for price_type in ["price_80_high", "price_20_low", "price_90_high", "price_10_low"]: - if price_type in data.columns: - price_stats[price_type] = len(data[(data["huge_volume"] == 1) & (data[price_type] == 1)]) - + + # 将volume_ratio按照百分位分成十份 + huge_volume_data = deepcopy(data[data["huge_volume"] == 1]) + huge_volume_data = huge_volume_data.sort_values(by="timestamp", ascending=True) + huge_volume_data = huge_volume_data.reset_index(drop=True) + huge_volume_data["volume_ratio_percentile"] = huge_volume_data["volume_ratio"].rank(pct=True) + huge_volume_data["volume_ratio_percentile_10"] = huge_volume_data["volume_ratio_percentile"].apply( + lambda x: 10 if x <= 0.1 else 20 + if x <= 0.2 else 30 if x <= 0.3 else 40 + if x <= 0.4 else 50 if x <= 0.5 else 60 + if x <= 0.6 else 70 if x <= 0.7 else 80 + if x <= 0.8 else 90 if x <= 0.9 else 100 + ) + huge_volume_ratio_percentile_10_mean = huge_volume_data.groupby("volume_ratio_percentile_10")["volume_ratio"].mean() + percentile_10_mean = round(float(huge_volume_data["volume_ratio"].mean()), 4) + # insert one row to huge_volume_ratio_percentile_10_mean, key is -1, value is percentile_10_mean + huge_volume_ratio_percentile_10_mean.loc["-1"] = percentile_10_mean + # transform huge_volume_ratio_percentile_10_mean index to int + huge_volume_ratio_percentile_10_mean.index = huge_volume_ratio_percentile_10_mean.index.astype(int) + # sort huge_volume_ratio_percentile_10_mean by index + huge_volume_ratio_percentile_10_mean = huge_volume_ratio_percentile_10_mean.sort_index() + results = [] - for period in periods: - for price_type, count in price_stats.items(): - if count > 0: - # 计算下跌次数 - fall_count = len( - data[ - (data["huge_volume"] == 1) & - (data[price_type] == 1) & - (data[f"next_{period}_result"] == "fall") - ] - ) - # 计算上涨次数 - rise_count = len( - data[ - (data["huge_volume"] == 1) & - (data[price_type] == 1) & - (data[f"next_{period}_result"] == "rise") - ] - ) - - results.append( - { - "symbol": data["symbol"].iloc[0] if len(data) > 0 else "", - "bar": data["bar"].iloc[0] if len(data) > 0 else "", - "window_size": window_size, - "huge_volume": 1, - "price_type": price_type, - "next_period": period, - "fall_count": fall_count, - "rise_count": rise_count, - "fall_ratio": fall_count / count, - "rise_ratio": rise_count / count, - "total_count": count, - } - ) + # iterate huge_volume_ratio_percentile_10_mean + for index, value in huge_volume_ratio_percentile_10_mean.items(): + if index == -1: + data_temp = deepcopy(huge_volume_data) + volume_ratio_percentile_10 = "all" + current_percentile_10_mean = percentile_10_mean + else: + data_temp = deepcopy(huge_volume_data[huge_volume_data["volume_ratio_percentile_10"] == index]) + volume_ratio_percentile_10 = str(index) + current_percentile_10_mean = round(value, 4) + + data_temp = data_temp.reset_index(drop=True) + data_temp = data_temp.sort_values(by="timestamp", ascending=True) + data_temp = data_temp.reset_index(drop=True) + + # 过滤data, 只获取huge_volume为1,且价格处于分位数位置的行 + price_conditions = [] + if "price_80_high" in data_temp.columns: + price_conditions.append(data_temp["price_80_high"] == 1) + if "price_20_low" in data_temp.columns: + price_conditions.append(data_temp["price_20_low"] == 1) + if "price_90_high" in data_temp.columns: + price_conditions.append(data_temp["price_90_high"] == 1) + if "price_10_low" in data_temp.columns: + price_conditions.append(data_temp["price_10_low"] == 1) + + if price_conditions: + combined_condition = data_temp["huge_volume"] == 1 + for condition in price_conditions: + combined_condition = combined_condition | condition + data_temp = data_temp[combined_condition] + + data_temp = data_temp.reset_index(drop=True) + + # 统计各种分位数情况的数量 + price_stats = {} + for price_type in ["price_80_high", "price_20_low", "price_90_high", "price_10_low"]: + if price_type in data.columns: + price_stats[price_type] = len(data_temp[(data_temp["huge_volume"] == 1) & (data_temp[price_type] == 1)]) + + for period in periods: + for price_type, count in price_stats.items(): + if count > 0: + # 计算下跌次数 + fall_count = len( + data_temp[ + (data_temp["huge_volume"] == 1) & + (data_temp[price_type] == 1) & + (data_temp[f"next_{period}_result"] == "fall") + ] + ) + # 计算上涨次数 + rise_count = len( + data_temp[ + (data_temp["huge_volume"] == 1) & + (data_temp[price_type] == 1) & + (data_temp[f"next_{period}_result"] == "rise") + ] + ) + + draw_count = len( + data_temp[ + (data_temp["huge_volume"] == 1) & + (data_temp[price_type] == 1) & + (data_temp[f"next_{period}_result"] == "draw") + ] + ) + # 根据data[f"next_{period}_result"]获得平均收益率 + average_return = float(data_temp[(data_temp["huge_volume"] == 1) & (data_temp[price_type] == 1)] + [f"next_{period}_change"].mean()) + max_return = float(data_temp[(data_temp["huge_volume"] == 1) & (data_temp[price_type] == 1)] + [f"next_{period}_change"].max()) + min_return = float(data_temp[(data_temp["huge_volume"] == 1) & (data_temp[price_type] == 1)] + [f"next_{period}_change"].min()) + + results.append( + { + "symbol": data_temp["symbol"].iloc[0] if len(data_temp) > 0 else "", + "bar": data_temp["bar"].iloc[0] if len(data_temp) > 0 else "", + "window_size": window_size, + "huge_volume": 1, + "volume_ratio_percentile_10": volume_ratio_percentile_10, + "volume_ratio_percentile_10_mean": current_percentile_10_mean, + "price_type": price_type, + "next_period": period, + "average_return": average_return, + "max_return": max_return, + "min_return": min_return, + "rise_count": rise_count, + "rise_ratio": round((rise_count / count) * 100, 4), + "fall_count": fall_count, + "fall_ratio": round((fall_count / count) * 100, 4), + "draw_count": draw_count, + "draw_ratio": round((draw_count / count) * 100, 4), + "total_count": count, + } + ) result_data = pd.DataFrame(results) - return data, result_data + return huge_volume_data, result_data diff --git a/core/market_data_monitor.py b/core/biz/market_data_monitor.py similarity index 92% rename from core/market_data_monitor.py rename to core/biz/market_data_monitor.py index 73c0773..069ef29 100644 --- a/core/market_data_monitor.py +++ b/core/biz/market_data_monitor.py @@ -5,7 +5,7 @@ from typing import Optional import pandas as pd import okx.MarketData as Market import okx.TradingData as TradingData -from core.utils import datetime_to_timestamp +from core.utils import transform_date_time_to_timestamp logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s') class MarketDataMonitor: @@ -46,18 +46,9 @@ class MarketDataMonitor: two_months_ago = datetime.now() - timedelta(days=60) start_time = int(two_months_ago.timestamp() * 1000) else: - try: - # 判断是否就是timestamp整型数据 - if isinstance(start, int): - start_time = start - # 判断是否为纯数字(UTC毫秒级timestamp) - elif start.isdigit(): - start_time = int(start) - else: - # 按北京时间字符串处理,转换为毫秒级timestamp - start_time = datetime_to_timestamp(start) - except Exception as e: - logging.error(f"start参数解析失败: {e}") + start_time = transform_date_time_to_timestamp(start) + if start_time is None: + logging.error(f"start参数解析失败: {start}") return None columns = ["timestamp", "open", "high", "low", "close", "volume", "volCcy", "volCCyQuote", "confirm"] all_data = [] diff --git a/core/quant_trader.py b/core/biz/quant_trader.py similarity index 100% rename from core/quant_trader.py rename to core/biz/quant_trader.py diff --git a/core/strategy.py b/core/biz/strategy.py similarity index 99% rename from core/strategy.py rename to core/biz/strategy.py index 547cd71..fc4305f 100644 --- a/core/strategy.py +++ b/core/biz/strategy.py @@ -3,7 +3,7 @@ from datetime import datetime import logging from typing import Optional import pandas as pd -from core.base import QuantTrader +from core.biz.quant_trader import QuantTrader logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s') diff --git a/core/trade_data.py b/core/biz/trade_data.py similarity index 97% rename from core/trade_data.py rename to core/biz/trade_data.py index 5d24a92..7a4fcad 100644 --- a/core/trade_data.py +++ b/core/biz/trade_data.py @@ -4,8 +4,8 @@ import logging from typing import Optional import pandas as pd import okx.MarketData as Market -from core.utils import datetime_to_timestamp, timestamp_to_datetime -from core.db_trade_data import DBTradeData +from core.utils import timestamp_to_datetime +from core.db.db_trade_data import DBTradeData logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s') diff --git a/core/db/__pycache__/db_huge_volume_data.cpython-312.pyc b/core/db/__pycache__/db_huge_volume_data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8669d470732c2d23d652af7eeb3e4301d48e0235 GIT binary patch literal 21027 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 C1PxUH literal 0 HcmV?d00001 diff --git a/core/db/__pycache__/db_market_data.cpython-312.pyc b/core/db/__pycache__/db_market_data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0fb8c836083a01b012d3f18df65048278960076d GIT binary patch literal 6473 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 int: """ @@ -31,7 +34,7 @@ def transform_data_type(data: dict): return data -def check_date_time_format(date_time: str) -> bool: +def check_date_time_format(date_time: str) -> str | None: """ 检查日期时间格式是否正确 """ @@ -42,4 +45,27 @@ def check_date_time_format(date_time: str) -> bool: elif re.match(r'^\d{4}\d{2}\d{2}$', date_time): return f"{date_time[0:4]}-{date_time[4:6]}-{date_time[6:8]} 00:00:00" else: + return None + +def transform_date_time_to_timestamp(date_time: int | str): + """ + 将日期时间转换为毫秒级timestamp + """ + try: + # 判断是否就是timestamp整型数据 + if isinstance(date_time, int): + date_time = date_time + # 判断是否为纯数字(UTC毫秒级timestamp) + elif date_time.isdigit(): + date_time = int(date_time) + else: + date_time = check_date_time_format(date_time) + if date_time is None: + logging.error(f"日期时间格式错误: {date_time}") + return None + # 按北京时间字符串处理,转换为毫秒级timestamp + date_time = datetime_to_timestamp(date_time) + return date_time + except Exception as e: + logging.error(f"start参数解析失败: {e}") return None \ No newline at end of file diff --git a/DB_HUGE_VOLUME_UPDATE_SUMMARY.md b/doc/DB_HUGE_VOLUME_UPDATE_SUMMARY.md similarity index 100% rename from DB_HUGE_VOLUME_UPDATE_SUMMARY.md rename to doc/DB_HUGE_VOLUME_UPDATE_SUMMARY.md diff --git a/DB_TRADE_DATA_SUMMARY.md b/doc/DB_TRADE_DATA_SUMMARY.md similarity index 100% rename from DB_TRADE_DATA_SUMMARY.md rename to doc/DB_TRADE_DATA_SUMMARY.md diff --git a/HUGE_VOLUME_UPDATE_SUMMARY.md b/doc/HUGE_VOLUME_UPDATE_SUMMARY.md similarity index 100% rename from HUGE_VOLUME_UPDATE_SUMMARY.md rename to doc/HUGE_VOLUME_UPDATE_SUMMARY.md diff --git a/huge_volume_main.py b/huge_volume_main.py index c744bf4..6479ee5 100644 --- a/huge_volume_main.py +++ b/huge_volume_main.py @@ -1,7 +1,7 @@ -from core.huge_volume import HugeVolume -from core.db_market_data import DBMarketData -from core.db_huge_volume_data import DBHugeVolumeData -from core.utils import timestamp_to_datetime +from core.biz.huge_volume import HugeVolume +from core.db.db_market_data import DBMarketData +from core.db.db_huge_volume_data import DBHugeVolumeData +from core.utils import timestamp_to_datetime, transform_date_time_to_timestamp from market_data_main import MarketDataMain import logging from config import MONITOR_CONFIG, MYSQL_CONFIG, WINDOW_SIZE @@ -43,7 +43,7 @@ class HugeVolumeMain: "initial_date", "2025-05-01 00:00:00" ) data = self.detect_volume_spike( - symbol, bar, window_size, start, only_output_huge_volume=True, is_update=False + symbol, bar, window_size, start, only_output_huge_volume=False, is_update=False ) if data is not None and len(data) > 0: logging.info(f"此次初始化巨量交易数据: {len(data)}条") @@ -88,7 +88,7 @@ class HugeVolumeMain: threshold=self.threshold, check_price=True, only_output_huge_volume=only_output_huge_volume, - output_excel=True, + output_excel=False, ) if data is not None: if is_update: @@ -125,7 +125,7 @@ class HugeVolumeMain: symbol, bar, window_size ) if latest_huge_volume_data is None or len(latest_huge_volume_data) == 0: - self.detect_volume_spike(symbol, bar, only_output_huge_volume=True) + self.detect_volume_spike(symbol, bar, only_output_huge_volume=False) return else: earliest_date_time = latest_huge_volume_data["date_time"] @@ -141,7 +141,7 @@ class HugeVolumeMain: bar=bar, window_size=window_size, start=earliest_date_time, - only_output_huge_volume=True, + only_output_huge_volume=False, is_update=True, ) logging.info( @@ -206,7 +206,6 @@ class HugeVolumeMain: start: str = None, end: str = None, periods: list = [3, 5], - output_excel: bool = False, ): if start is None: start = MONITOR_CONFIG.get("volume_monitor", {}).get( @@ -214,78 +213,29 @@ class HugeVolumeMain: ) if end is None: end = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - logging.info(f"开始计算巨量出现后,之后3或5个周期,上涨或下跌的比例: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") - huge_volume_data = ( + periods_text = ", ".join([str(period) for period in periods]) + logging.info(f"开始计算巨量出现后,之后{periods_text}个周期,上涨或下跌的比例: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") + volume_statistics_data = ( self.db_huge_volume_data.query_huge_volume_data_by_symbol_bar_window_size( symbol, bar, window_size, start, end ) ) - if huge_volume_data is None or len(huge_volume_data) == 0: + if volume_statistics_data is None or len(volume_statistics_data) == 0: logging.warning(f"获取巨量交易数据为空: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") return None else: - if isinstance(huge_volume_data, list): - huge_volume_data = pd.DataFrame(huge_volume_data) - elif isinstance(huge_volume_data, dict): - huge_volume_data = pd.DataFrame([huge_volume_data]) - market_data = self.db_market_data.query_market_data_by_symbol_bar( - symbol, bar, start, end - ) - if market_data is None or len(market_data) == 0: - logging.warning(f"获取行情数据为空: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") - return None - else: - if isinstance(market_data, list): - market_data = pd.DataFrame(market_data) - elif isinstance(market_data, dict): - market_data = pd.DataFrame([market_data]) + if isinstance(volume_statistics_data, list): + volume_statistics_data = pd.DataFrame(volume_statistics_data) + elif isinstance(volume_statistics_data, dict): + volume_statistics_data = pd.DataFrame([volume_statistics_data]) if ( - huge_volume_data is not None - and len(huge_volume_data) > 0 - and market_data is not None - and len(market_data) > 0 + volume_statistics_data is not None + and len(volume_statistics_data) > 0 ): - # 将huge_volume_data和market_data合并 - # market_data移除id列 - market_data = market_data.drop(columns=["id"]) - # huge_volume_data移除id列 - huge_volume_data = huge_volume_data.drop(columns=["id"]) - data = pd.merge(market_data, huge_volume_data, on="timestamp", how="left") - # 同名的列,只是后缀为_x和_y,需要合并 - data = data.rename( - columns={ - "symbol_x": "symbol", - "bar_x": "bar", - "date_time_x": "date_time", - "open_x": "open", - "high_x": "high", - "low_x": "low", - "close_x": "close", - "volume_x": "volume", - "volCcy_x": "volCcy", - "volCCyQuote_x": "volCCyQuote", - "create_time_x": "create_time", - } - ) - data = data.drop( - columns=[ - "symbol_y", - "bar_y", - "date_time_y", - "open_y", - "high_y", - "low_y", - "close_y", - "volume_y", - "volCcy_y", - "volCCyQuote_y", - "create_time_y", - ] - ) # 根据timestamp排序 - data = data.sort_values(by="timestamp", ascending=True) - data["window_size"] = window_size - data = data[ + volume_statistics_data = volume_statistics_data.sort_values(by="timestamp", ascending=True) + volume_statistics_data["window_size"] = window_size + volume_statistics_data = volume_statistics_data[ [ "symbol", "bar", @@ -299,24 +249,25 @@ class HugeVolumeMain: "volume", "huge_volume", "volume_ratio", - "volume_price_spike", - "price_high", - "price_low", + "volume_80_20_price_spike", + "price_80_high", + "price_20_low", + "volume_90_10_price_spike", + "price_90_high", + "price_10_low", ] ] - data = data.dropna() - data = data.reset_index(drop=True) - data, result_data = self.huge_volume.next_periods_rise_or_fall( - data=data, window_size=window_size, periods=periods, output_excel=output_excel + volume_statistics_data = volume_statistics_data.reset_index(drop=True) + huge_volume_data, result_data = self.huge_volume.next_periods_rise_or_fall( + data=volume_statistics_data, window_size=window_size, periods=periods ) - return data, result_data + return huge_volume_data, result_data def batch_next_periods_rise_or_fall( self, - window_size: int = 50, start: str = None, end: str = None, - periods: list = [3, 5], + next_periods: list = [1, 2, 3, 5, 10], output_excel: bool = False, ): if start is None: @@ -325,20 +276,25 @@ class HugeVolumeMain: ) if end is None: end = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - data_list = [] + huge_volume_data_list = [] result_data_list = [] + window_size_list = WINDOW_SIZE.get("window_sizes", None) + if window_size_list is None or not isinstance(window_size_list, list) or len(window_size_list) == 0: + window_size_list = [50, 80, 100, 120] + for symbol in self.market_data_main.symbols: for bar in self.market_data_main.bars: - data, result_data = self.next_periods_rise_or_fall( - symbol, bar, window_size, start, end, periods, output_excel - ) - data_list.append(data) - result_data_list.append(result_data) - data = pd.concat(data_list) - result_data = pd.concat(result_data_list) + for window_size in window_size_list: + huge_volume_data, result_data = self.next_periods_rise_or_fall( + symbol, bar, window_size, start, end, next_periods + ) + huge_volume_data_list.append(huge_volume_data) + result_data_list.append(result_data) + total_huge_volume_data = pd.concat(huge_volume_data_list) + total_result_data = pd.concat(result_data_list) if output_excel: - data = data.reset_index(drop=True) - result_data = result_data.reset_index(drop=True) + total_huge_volume_data = total_huge_volume_data.reset_index(drop=True) + total_result_data = total_result_data.reset_index(drop=True) current_date = datetime.now().strftime("%Y%m%d%H%M%S") file_name = ( f"next_periods_rise_or_fall_{current_date}.xlsx" @@ -347,13 +303,13 @@ class HugeVolumeMain: with pd.ExcelWriter( os.path.join(self.output_folder, file_name) ) as writer: - data.to_excel(writer, sheet_name="details", index=False) - result_data.to_excel( + total_huge_volume_data.to_excel(writer, sheet_name="details", index=False) + total_result_data.to_excel( writer, sheet_name="next_periods_statistics", index=False ) except Exception as e: logging.error(f"导出Excel文件失败: {e}") - return data, result_data + return total_huge_volume_data, total_result_data def batch_initial_detect_volume_spike(threshold: float = 2.0): @@ -379,4 +335,6 @@ def batch_update_volume_spike(threshold: float = 2.0): if __name__ == "__main__": # batch_initial_detect_volume_spike(threshold=2.0) - batch_update_volume_spike(threshold=2.0) + # batch_update_volume_spike(threshold=2.0) + huge_volume_main = HugeVolumeMain(threshold=2.0) + huge_volume_main.batch_next_periods_rise_or_fall(output_excel=True) diff --git a/market_data_main.py b/market_data_main.py index f63d93b..904acf2 100644 --- a/market_data_main.py +++ b/market_data_main.py @@ -1,9 +1,9 @@ import logging from datetime import datetime from time import sleep -from core.market_data_monitor import MarketDataMonitor -from core.db_market_data import DBMarketData -from core.utils import datetime_to_timestamp, timestamp_to_datetime +from core.biz.market_data_monitor import MarketDataMonitor +from core.db.db_market_data import DBMarketData +from core.utils import datetime_to_timestamp, timestamp_to_datetime, transform_date_time_to_timestamp from trade_data_main import TradeDataMain from config import ( API_KEY, @@ -72,16 +72,15 @@ class MarketDataMain: 获取保存数据 """ end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - end_time_ts = datetime_to_timestamp(end_time) - if isinstance(start, str): - if start.isdigit(): - start_time_ts = int(start) - else: - start_time_ts = datetime_to_timestamp(start) - elif isinstance(start, int): - start_time_ts = start - else: - raise ValueError(f"开始时间格式错误: {start}") + end_time_ts = transform_date_time_to_timestamp(end_time) + if end_time_ts is None: + logging.error(f"结束时间格式错误: {end_time}") + return None + + start_time_ts = transform_date_time_to_timestamp(start) + if start_time_ts is None: + logging.error(f"开始时间格式错误: {start}") + return None # 如果bar为5m, 15m: # end_time_ts与start_time_ts相差超过1天,则按照1天为单位 @@ -91,39 +90,26 @@ class MarketDataMain: # 获取数据,直到end_time_ts threshold = None if bar in ["5m", "15m"]: - threshold = 86400000 - 1 + threshold = 86400000 elif bar in ["1H", "4H"]: - threshold = 432000000 - 1 + threshold = 432000000 elif bar == "1D": - threshold = 864000000 - 1 + threshold = 864000000 while start_time_ts < end_time_ts: - current_end_time_ts = start_time_ts + threshold - if current_end_time_ts >= end_time_ts: - current_end_time_ts = end_time_ts - start_date_time = timestamp_to_datetime(start_time_ts) - end_date_time = timestamp_to_datetime(current_end_time_ts) + current_start_time_ts = end_time_ts - threshold + if current_start_time_ts < start_time_ts: + current_start_time_ts = start_time_ts + start_date_time = timestamp_to_datetime(current_start_time_ts) + end_date_time = timestamp_to_datetime(end_time_ts) logging.info( f"获取行情数据: {symbol} {bar} 从 {start_date_time} 到 {end_date_time}" ) - # 首先判断是否存在 > current_end_time_ts + 1 的数据 - # 如果存在,则跳过此次循环 - data = self.db_market_data.query_market_data_by_symbol_bar( - symbol=symbol, - bar=bar, - start=start_time_ts, - end=current_end_time_ts, - ) - if data is not None and len(data) > 0: - logging.info(f"已存在{symbol}, {bar} 从 {start_date_time} 到 {end_date_time} 的数据,跳过此次循环") - start_time_ts = current_end_time_ts - continue - # current_end_time_ts + 1, 目的是为了避免缺少最后一条数据 data = self.market_data_monitor.get_historical_kline_data( symbol=symbol, - start=start_time_ts, + start=current_start_time_ts, bar=bar, - end_time=current_end_time_ts + 1, + end_time=end_time_ts, ) if data is not None and len(data) > 0: data["buy_sz"] = -1 @@ -133,9 +119,8 @@ class MarketDataMain: # 比特币的数据获取过慢,暂时不获取交易数据 # if not symbol.endswith("-SWAP"): # # trade_data的end_time需要比market_data的end_time大一个周期 - # trade_end_time_ts = current_end_time_ts + BAR_THRESHOLD[bar] + 1 # trade_data = self.trade_data_main.get_trade_data( - # symbol=symbol, start_time=start_time_ts, end_time=trade_end_time_ts + # symbol=symbol, start_time=current_start_time_ts, end_time=end_time_ts # ) # for index, row in data.iterrows(): # try: @@ -180,8 +165,9 @@ class MarketDataMain: ] ] self.db_market_data.insert_data_to_mysql(data) - - start_time_ts = current_end_time_ts + if current_start_time_ts == start_time_ts: + break + end_time_ts = current_start_time_ts return data def batch_update_data(self): @@ -203,11 +189,14 @@ class MarketDataMain: logging.info(f"开始更新行情数据: {symbol} {bar}") latest_data = self.db_market_data.query_latest_data(symbol, bar) if not latest_data: + logging.info(f"{symbol}, {bar} 无数据,开始从{self.initial_date}初始化数据") data = self.fetch_save_data(symbol, bar, self.initial_date) else: latest_timestamp = latest_data.get("timestamp") if latest_timestamp: latest_timestamp = int(latest_timestamp) + latest_date_time = timestamp_to_datetime(latest_timestamp) + logging.info(f"{symbol}, {bar} 上次获取的最新数据时间: {latest_date_time}") else: logging.warning(f"获取{symbol}, {bar} 最新数据失败") return @@ -217,5 +206,5 @@ class MarketDataMain: if __name__ == "__main__": market_data_main = MarketDataMain() - # market_data_main.batch_update_data() - market_data_main.initial_data() + market_data_main.batch_update_data() + # market_data_main.initial_data() diff --git a/sql/query/sql_playground.sql b/sql/query/sql_playground.sql index 60c5878..558dfac 100644 --- a/sql/query/sql_playground.sql +++ b/sql/query/sql_playground.sql @@ -1,6 +1,6 @@ select * from crypto_market_data -WHERE symbol='DOGE-USDT-SWAP' and bar='1D' #and date_time > '2025-07-01' -order by timestamp; +WHERE symbol='XCH-USDT-SWAP' and bar='5m' #and date_time > '2025-07-01' +order by timestamp desc; delete FROM crypto_market_data where symbol != 'XCH-USDT'; diff --git a/sql/table/crypto_huge_volume.sql b/sql/table/crypto_huge_volume.sql index b3192ae..63db711 100644 --- a/sql/table/crypto_huge_volume.sql +++ b/sql/table/crypto_huge_volume.sql @@ -12,12 +12,12 @@ CREATE TABLE IF NOT EXISTS crypto_huge_volume ( volume DECIMAL(30,8) NOT NULL COMMENT '交易量', volCcy DECIMAL(30,8) NOT NULL COMMENT '交易量(基础货币)', volCCyQuote DECIMAL(30,8) NOT NULL COMMENT '交易量(计价货币)', - volume_ma DECIMAL(30,8) NOT NULL COMMENT '交易量移动平均', - volume_std DECIMAL(30,8) NOT NULL COMMENT '交易量标准差', - volume_threshold DECIMAL(30,8) NOT NULL COMMENT '交易量阈值', + volume_ma DECIMAL(30,8) NULL COMMENT '交易量移动平均', + volume_std DECIMAL(30,8) NULL COMMENT '交易量标准差', + volume_threshold DECIMAL(30,8) NULL COMMENT '交易量阈值', huge_volume TINYINT NOT NULL DEFAULT 0 COMMENT '是否为巨量(0:否,1:是)', - volume_ratio DECIMAL(20,8) NOT NULL COMMENT '交易量比率', - spike_intensity DECIMAL(20,8) NOT NULL COMMENT '尖峰强度', + volume_ratio DECIMAL(20,8) NULL COMMENT '交易量比率', + spike_intensity DECIMAL(20,8) NULL COMMENT '尖峰强度', close_80_percentile DECIMAL(20,5) NOT NULL COMMENT '收盘价80%分位数', close_20_percentile DECIMAL(20,5) NOT NULL COMMENT '收盘价20%分位数', price_80_high TINYINT NOT NULL DEFAULT 0 COMMENT '价格是否达到80%分位数高点(0:否,1:是)', diff --git a/test_db_huge_volume.py b/test_db_huge_volume.py index 24ed835..1c73555 100644 --- a/test_db_huge_volume.py +++ b/test_db_huge_volume.py @@ -9,7 +9,7 @@ import sys import os sys.path.append(os.path.dirname(os.path.abspath(__file__))) -from core.db_huge_volume_data import DBHugeVolumeData +from core.db.db_huge_volume_data import DBHugeVolumeData import logging # 配置日志 diff --git a/test_db_trade_data.py b/test_db_trade_data.py index 2f74a9b..14d0136 100644 --- a/test_db_trade_data.py +++ b/test_db_trade_data.py @@ -9,7 +9,7 @@ import sys import os sys.path.append(os.path.dirname(os.path.abspath(__file__))) -from core.db_trade_data import DBTradeData +from core.db.db_trade_data import DBTradeData import logging # 配置日志 diff --git a/test_huge_volume.py b/test_huge_volume.py index bbe7f77..e2d8179 100644 --- a/test_huge_volume.py +++ b/test_huge_volume.py @@ -12,7 +12,7 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__))) import pandas as pd import numpy as np from datetime import datetime, timedelta -from core.huge_volume import HugeVolume +from core.biz.huge_volume import HugeVolume import logging # 配置日志 diff --git a/trade_data_main.py b/trade_data_main.py index 06d6e81..c602570 100644 --- a/trade_data_main.py +++ b/trade_data_main.py @@ -2,8 +2,8 @@ import logging import time from datetime import datetime, timedelta import pandas as pd -from core.utils import datetime_to_timestamp, timestamp_to_datetime -from core.trade_data import TradeData +from core.utils import datetime_to_timestamp, timestamp_to_datetime, transform_date_time_to_timestamp +from core.biz.trade_data import TradeData from config import ( API_KEY, SECRET_KEY, @@ -44,14 +44,14 @@ class TradeDataMain: if end_time is None: end_time = int(time.time() * 1000) else: - end_time = self.transform_date_time(end_time) + end_time = transform_date_time_to_timestamp(end_time) # 处理start参数 if start_time is None: # 默认两个月前 start_time_str = MONITOR_CONFIG.get("volume_monitor", {}).get("initial_date", "2025-05-01 00:00:00") - start_time = self.transform_date_time(start_time_str) + start_time = transform_date_time_to_timestamp(start_time_str) else: - start_time = self.transform_date_time(start_time) + start_time = transform_date_time_to_timestamp(start_time) # 从数据库获取最早数据 earliest_data = self.trade_data.db_trade_data.query_earliest_data(symbol) db_earliest_time = None @@ -87,25 +87,6 @@ class TradeDataMain: logging.info(f"获取交易数据失败: {symbol}, {start_date_time}, {end_date_time}") return None - def transform_date_time(self, date_time: str): - """ - 将日期时间转换为毫秒级timestamp - """ - try: - # 判断是否就是timestamp整型数据 - if isinstance(date_time, int): - date_time = date_time - # 判断是否为纯数字(UTC毫秒级timestamp) - elif date_time.isdigit(): - date_time = int(date_time) - else: - # 按北京时间字符串处理,转换为毫秒级timestamp - date_time = datetime_to_timestamp(date_time) - return date_time - except Exception as e: - logging.error(f"start参数解析失败: {e}") - return None - if __name__ == "__main__": trade_data_main = TradeDataMain() diff --git a/trade_main.py b/trade_main.py index b1b654e..5fdcb81 100644 --- a/trade_main.py +++ b/trade_main.py @@ -1,7 +1,7 @@ import logging from time import sleep -from core.base import QuantTrader -from core.strategy import QuantStrategy +from core.biz.quant_trader import QuantTrader +from core.biz.strategy import QuantStrategy from config import API_KEY, SECRET_KEY, PASSPHRASE, SANDBOX, TRADING_CONFIG, TIME_CONFIG logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')