support get huge volume by different window sizes

This commit is contained in:
blade 2025-07-30 17:28:01 +08:00
parent ca2efb002e
commit 5bc9b2b8c7
6 changed files with 129 additions and 72 deletions

View File

@ -70,6 +70,8 @@ MONITOR_CONFIG = {
} }
} }
WINDOW_SIZE = {"window_sizes":[50, 80, 100, 120]}
BAR_THRESHOLD = { BAR_THRESHOLD = {
"5m": 1000 * 60 * 5, "5m": 1000 * 60 * 5,
"15m": 1000 * 60 * 15, "15m": 1000 * 60 * 15,

View File

@ -17,6 +17,7 @@ class DBHugeVolumeData:
self.columns = [ self.columns = [
"symbol", "symbol",
"bar", "bar",
"window_size",
"timestamp", "timestamp",
"date_time", "date_time",
"open", "open",
@ -74,6 +75,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None, end: Optional[Union[str, int]] = None,
additional_conditions: Optional[List[str]] = None additional_conditions: Optional[List[str]] = None
@ -82,6 +84,7 @@ class DBHugeVolumeData:
构建查询条件 构建查询条件
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:param additional_conditions: 额外的查询条件 :param additional_conditions: 额外的查询条件
@ -96,7 +99,9 @@ class DBHugeVolumeData:
if bar: if bar:
conditions.append("bar = :bar") conditions.append("bar = :bar")
condition_dict["bar"] = bar condition_dict["bar"] = bar
if window_size:
conditions.append("window_size = :window_size")
condition_dict["window_size"] = window_size
# 处理时间参数 # 处理时间参数
start_timestamp = self._process_time_parameter(start) start_timestamp = self._process_time_parameter(start)
end_timestamp = self._process_time_parameter(end) end_timestamp = self._process_time_parameter(end)
@ -166,53 +171,57 @@ class DBHugeVolumeData:
self.db_manager.insert_data_to_mysql_simple(df) self.db_manager.insert_data_to_mysql_simple(df)
def query_latest_data(self, symbol: str, bar: str) -> Optional[Dict[str, Any]]: def query_latest_data(self, symbol: str, bar: str, window_size: int) -> Optional[Dict[str, Any]]:
""" """
查询最新巨量交易数据 查询最新巨量交易数据
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:return: 最新数据记录或None :return: 最新数据记录或None
""" """
sql = """ sql = """
SELECT * FROM crypto_huge_volume SELECT * FROM crypto_huge_volume
WHERE symbol = :symbol AND bar = :bar WHERE symbol = :symbol AND bar = :bar AND window_size = :window_size
ORDER BY timestamp DESC ORDER BY timestamp DESC
LIMIT 1 LIMIT 1
""" """
condition_dict = {"symbol": symbol, "bar": bar} condition_dict = {"symbol": symbol, "bar": bar, "window_size": window_size}
return self.db_manager.query_data(sql, condition_dict, return_multi=False) return self.db_manager.query_data(sql, condition_dict, return_multi=False)
def query_data_by_symbol_bar_timestamp(self, symbol: str, bar: str, timestamp: int) -> Optional[Dict[str, Any]]: def query_data_by_symbol_bar_window_size_timestamp(self, symbol: str, bar: str, window_size: int, timestamp: int) -> Optional[Dict[str, Any]]:
""" """
根据交易对K线周期和时间戳查询巨量交易数据 根据交易对K线周期, 窗口大小和时间戳查询巨量交易数据
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param timestamp: 时间戳 :param timestamp: 时间戳
:return: 数据记录或None :return: 数据记录或None
""" """
sql = """ sql = """
SELECT * FROM crypto_huge_volume SELECT * FROM crypto_huge_volume
WHERE symbol = :symbol AND bar = :bar AND timestamp = :timestamp WHERE symbol = :symbol AND bar = :bar AND window_size = :window_size AND timestamp = :timestamp
""" """
condition_dict = {"symbol": symbol, "bar": bar, "timestamp": timestamp} condition_dict = {"symbol": symbol, "bar": bar, "window_size": window_size, "timestamp": timestamp}
return self.db_manager.query_data(sql, condition_dict, return_multi=False) return self.db_manager.query_data(sql, condition_dict, return_multi=False)
def query_huge_volume_data_by_symbol_bar( def query_huge_volume_data_by_symbol_bar_window_size(
self, self,
symbol: str, symbol: str,
bar: str, bar: str,
window_size: int,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
""" """
根据交易对和K线周期查询巨量交易数据 根据交易对K线周期和窗口大小查询巨量交易数据
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:return: 数据记录列表或None :return: 数据记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions(symbol, bar, start, end) conditions, condition_dict = self._build_query_conditions(symbol, bar, window_size, start, end)
where_clause = " AND ".join(conditions) if conditions else "1=1" where_clause = " AND ".join(conditions) if conditions else "1=1"
sql = f""" sql = f"""
@ -227,6 +236,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
@ -234,12 +244,13 @@ class DBHugeVolumeData:
查询巨量交易记录只返回huge_volume=1的记录 查询巨量交易记录只返回huge_volume=1的记录
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:return: 巨量交易记录列表或None :return: 巨量交易记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, start, end, additional_conditions=["huge_volume = 1"] symbol, bar, window_size, start, end, additional_conditions=["huge_volume = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -255,6 +266,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
@ -262,12 +274,13 @@ class DBHugeVolumeData:
查询80/20量价尖峰记录只返回volume_80_20_price_spike=1的记录 查询80/20量价尖峰记录只返回volume_80_20_price_spike=1的记录
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:return: 80/20量价尖峰记录列表或None :return: 80/20量价尖峰记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, start, end, additional_conditions=["volume_80_20_price_spike = 1"] symbol, bar, window_size, start, end, additional_conditions=["volume_80_20_price_spike = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -283,6 +296,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
@ -290,12 +304,13 @@ class DBHugeVolumeData:
查询90/10量价尖峰记录只返回volume_90_10_price_spike=1的记录 查询90/10量价尖峰记录只返回volume_90_10_price_spike=1的记录
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:return: 90/10量价尖峰记录列表或None :return: 90/10量价尖峰记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, start, end, additional_conditions=["volume_90_10_price_spike = 1"] symbol, bar, window_size, start, end, additional_conditions=["volume_90_10_price_spike = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -311,6 +326,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
@ -318,12 +334,13 @@ class DBHugeVolumeData:
查询价格达到80%分位数高点的记录只返回price_80_high=1的记录 查询价格达到80%分位数高点的记录只返回price_80_high=1的记录
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:return: 价格80%分位数高点记录列表或None :return: 价格80%分位数高点记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, start, end, additional_conditions=["price_80_high = 1"] symbol, bar, window_size, start, end, additional_conditions=["price_80_high = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -339,6 +356,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
@ -351,7 +369,7 @@ class DBHugeVolumeData:
:return: 价格20%分位数低点记录列表或None :return: 价格20%分位数低点记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, start, end, additional_conditions=["price_20_low = 1"] symbol, bar, window_size, start, end, additional_conditions=["price_20_low = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -367,6 +385,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
@ -379,7 +398,7 @@ class DBHugeVolumeData:
:return: 价格90%分位数高点记录列表或None :return: 价格90%分位数高点记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, start, end, additional_conditions=["price_90_high = 1"] symbol, bar, window_size, start, end, additional_conditions=["price_90_high = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -395,6 +414,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
@ -407,7 +427,7 @@ class DBHugeVolumeData:
:return: 价格10%分位数低点记录列表或None :return: 价格10%分位数低点记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, start, end, additional_conditions=["price_10_low = 1"] symbol, bar, window_size, start, end, additional_conditions=["price_10_low = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -423,6 +443,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[Dict[str, Any]]: ) -> Optional[Dict[str, Any]]:
@ -430,11 +451,12 @@ class DBHugeVolumeData:
获取巨量交易统计摘要 获取巨量交易统计摘要
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:return: 统计摘要或None :return: 统计摘要或None
""" """
conditions, condition_dict = self._build_query_conditions(symbol, bar, start, end) conditions, condition_dict = self._build_query_conditions(symbol, bar, window_size, start, end)
where_clause = " AND ".join(conditions) if conditions else "1=1" where_clause = " AND ".join(conditions) if conditions else "1=1"
sql = f""" sql = f"""
@ -461,17 +483,19 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
limit: int = 10 limit: int = 10
) -> Optional[List[Dict[str, Any]]]: ) -> Optional[List[Dict[str, Any]]]:
""" """
获取成交量尖峰最高的记录 获取成交量尖峰最高的记录
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param limit: 返回记录数量 :param limit: 返回记录数量
:return: 成交量尖峰记录列表或None :return: 成交量尖峰记录列表或None
""" """
conditions, condition_dict = self._build_query_conditions( conditions, condition_dict = self._build_query_conditions(
symbol, bar, additional_conditions=["huge_volume = 1"] symbol, bar, window_size, additional_conditions=["huge_volume = 1"]
) )
where_clause = " AND ".join(conditions) where_clause = " AND ".join(conditions)
@ -489,6 +513,7 @@ class DBHugeVolumeData:
self, self,
symbol: Optional[str] = None, symbol: Optional[str] = None,
bar: Optional[str] = None, bar: Optional[str] = None,
window_size: Optional[int] = None,
start: Optional[Union[str, int]] = None, start: Optional[Union[str, int]] = None,
end: Optional[Union[str, int]] = None end: Optional[Union[str, int]] = None
) -> Optional[Dict[str, Any]]: ) -> Optional[Dict[str, Any]]:
@ -496,11 +521,12 @@ class DBHugeVolumeData:
获取分位数统计信息 获取分位数统计信息
:param symbol: 交易对 :param symbol: 交易对
:param bar: K线周期 :param bar: K线周期
:param window_size: 窗口大小
:param start: 开始时间 :param start: 开始时间
:param end: 结束时间 :param end: 结束时间
:return: 分位数统计信息或None :return: 分位数统计信息或None
""" """
conditions, condition_dict = self._build_query_conditions(symbol, bar, start, end) conditions, condition_dict = self._build_query_conditions(symbol, bar, window_size, start, end)
where_clause = " AND ".join(conditions) if conditions else "1=1" where_clause = " AND ".join(conditions) if conditions else "1=1"
sql = f""" sql = f"""

View File

@ -108,6 +108,7 @@ class HugeVolume:
# 按时间戳排序 # 按时间戳排序
data = data.sort_values(by="timestamp", ascending=True).copy() data = data.sort_values(by="timestamp", ascending=True).copy()
data["window_size"] = window_size
# 计算移动窗口的成交量均值和标准差 # 计算移动窗口的成交量均值和标准差
data["volume_ma"] = ( data["volume_ma"] = (
@ -159,7 +160,7 @@ class HugeVolume:
end_date = re.sub(r"[\:\-\s]", "", str(end_date)) end_date = re.sub(r"[\:\-\s]", "", str(end_date))
symbol = data["symbol"].iloc[0] symbol = data["symbol"].iloc[0]
bar = data["bar"].iloc[0] bar = data["bar"].iloc[0]
file_name = f"volume_spike_{symbol}_{bar}_{start_date}_{end_date}.xlsx" file_name = f"volume_spike_{symbol}_{bar}_{window_size}_{start_date}_{end_date}.xlsx"
try: try:
with pd.ExcelWriter( with pd.ExcelWriter(
os.path.join(self.output_folder, file_name) os.path.join(self.output_folder, file_name)
@ -173,6 +174,7 @@ class HugeVolume:
def next_periods_rise_or_fall( def next_periods_rise_or_fall(
self, self,
data: pd.DataFrame, data: pd.DataFrame,
window_size: int = 50,
periods: List[int] = [3, 5], periods: List[int] = [3, 5],
output_excel: bool = False output_excel: bool = False
) -> Tuple[pd.DataFrame, pd.DataFrame]: ) -> Tuple[pd.DataFrame, pd.DataFrame]:
@ -280,6 +282,7 @@ class HugeVolume:
{ {
"symbol": data["symbol"].iloc[0] if len(data) > 0 else "", "symbol": data["symbol"].iloc[0] if len(data) > 0 else "",
"bar": data["bar"].iloc[0] if len(data) > 0 else "", "bar": data["bar"].iloc[0] if len(data) > 0 else "",
"window_size": window_size,
"huge_volume": 1, "huge_volume": 1,
"price_type": price_type, "price_type": price_type,
"next_period": period, "next_period": period,

View File

@ -4,7 +4,7 @@ from core.db_huge_volume_data import DBHugeVolumeData
from core.utils import timestamp_to_datetime from core.utils import timestamp_to_datetime
from market_data_main import MarketDataMain from market_data_main import MarketDataMain
import logging import logging
from config import MONITOR_CONFIG, MYSQL_CONFIG from config import MONITOR_CONFIG, MYSQL_CONFIG, WINDOW_SIZE
from datetime import datetime from datetime import datetime
import pandas as pd import pandas as pd
import os import os
@ -16,7 +16,7 @@ logging.basicConfig(
class HugeVolumeMain: class HugeVolumeMain:
def __init__(self, window_size: int = 50, threshold: float = 2.0): def __init__(self, threshold: float = 2.0):
mysql_user = MYSQL_CONFIG.get("user", "xch") mysql_user = MYSQL_CONFIG.get("user", "xch")
mysql_password = MYSQL_CONFIG.get("password", "") mysql_password = MYSQL_CONFIG.get("password", "")
if not mysql_password: if not mysql_password:
@ -29,22 +29,21 @@ class HugeVolumeMain:
self.huge_volume = HugeVolume() self.huge_volume = HugeVolume()
self.db_market_data = DBMarketData(self.db_url) self.db_market_data = DBMarketData(self.db_url)
self.db_huge_volume_data = DBHugeVolumeData(self.db_url) self.db_huge_volume_data = DBHugeVolumeData(self.db_url)
self.monitor_main = MarketDataMain() self.market_data_main = MarketDataMain()
self.window_size = window_size
self.threshold = threshold self.threshold = threshold
self.output_folder = "./output/huge_volume_statistics/" self.output_folder = "./output/huge_volume_statistics/"
os.makedirs(self.output_folder, exist_ok=True) os.makedirs(self.output_folder, exist_ok=True)
def batch_initial_detect_volume_spike(self, start: str = None): def batch_initial_detect_volume_spike(self, window_size: int = 50, start: str = None):
for symbol in self.monitor_main.symbols: for symbol in self.market_data_main.symbols:
for bar in self.monitor_main.bars: for bar in self.market_data_main.bars:
if start is None: if start is None:
start = MONITOR_CONFIG.get("volume_monitor", {}).get( start = MONITOR_CONFIG.get("volume_monitor", {}).get(
"initial_date", "2025-05-01 00:00:00" "initial_date", "2025-05-01 00:00:00"
) )
data = self.detect_volume_spike( data = self.detect_volume_spike(
symbol, bar, start, only_output_huge_volume=True, is_update=False symbol, bar, window_size, start, only_output_huge_volume=True, is_update=False
) )
if data is not None and len(data) > 0: if data is not None and len(data) > 0:
logging.info(f"此次初始化巨量交易数据: {len(data)}") logging.info(f"此次初始化巨量交易数据: {len(data)}")
@ -55,6 +54,7 @@ class HugeVolumeMain:
self, self,
symbol: str = "XCH-USDT", symbol: str = "XCH-USDT",
bar: str = "5m", bar: str = "5m",
window_size: int = 50,
start: str = "2025-05-01 00:00:00", start: str = "2025-05-01 00:00:00",
end: str = None, end: str = None,
only_output_huge_volume: bool = False, only_output_huge_volume: bool = False,
@ -66,16 +66,16 @@ class HugeVolumeMain:
) )
if end is None: if end is None:
end = datetime.now().strftime("%Y-%m-%d %H:%M:%S") end = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
logging.info(f"开始处理巨量交易数据: {symbol} {bar} {start} {end}") logging.info(f"开始处理巨量交易数据: {symbol} {bar} 窗口大小: {window_size}{start} {end}")
data = self.db_market_data.query_market_data_by_symbol_bar( data = self.db_market_data.query_market_data_by_symbol_bar(
symbol, bar, start, end symbol, bar, start, end
) )
if data is None: if data is None:
logging.warning(f"获取行情数据失败: {symbol} {bar} {start} {end}") logging.warning(f"获取行情数据失败: {symbol} {bar} 窗口大小: {window_size}{start} {end}")
return None return None
else: else:
if len(data) == 0: if len(data) == 0:
logging.warning(f"获取行情数据为空: {symbol} {bar} {start} {end}") logging.warning(f"获取行情数据为空: {symbol} {bar} 窗口大小: {window_size}{start} {end}")
return None return None
else: else:
if isinstance(data, list): if isinstance(data, list):
@ -84,7 +84,7 @@ class HugeVolumeMain:
data = pd.DataFrame([data]) data = pd.DataFrame([data])
data = self.huge_volume.detect_huge_volume( data = self.huge_volume.detect_huge_volume(
data=data, data=data,
window_size=self.window_size, window_size=window_size,
threshold=self.threshold, threshold=self.threshold,
check_price=True, check_price=True,
only_output_huge_volume=only_output_huge_volume, only_output_huge_volume=only_output_huge_volume,
@ -94,8 +94,8 @@ class HugeVolumeMain:
if is_update: if is_update:
for index, row in data.iterrows(): for index, row in data.iterrows():
exist_huge_volume_data = ( exist_huge_volume_data = (
self.db_huge_volume_data.query_data_by_symbol_bar_timestamp( self.db_huge_volume_data.query_data_by_symbol_bar_window_size_timestamp(
symbol, bar, row["timestamp"] symbol, bar, window_size, row["timestamp"]
) )
) )
if exist_huge_volume_data is not None: if exist_huge_volume_data is not None:
@ -113,16 +113,16 @@ class HugeVolumeMain:
else: else:
return None return None
def batch_update_volume_spike(self): def batch_update_volume_spike(self, window_size: int = 50):
for symbol in self.monitor_main.symbols: for symbol in self.market_data_main.symbols:
for bar in self.monitor_main.bars: for bar in self.market_data_main.bars:
self.update_volume_spike(symbol, bar) self.update_volume_spike(symbol, bar, window_size)
def update_volume_spike(self, symbol: str, bar: str): def update_volume_spike(self, symbol: str, bar: str, window_size: int = 50):
try: try:
self.monitor_main.update_data(symbol, bar) self.market_data_main.update_data(symbol, bar)
latest_huge_volume_data = self.db_huge_volume_data.query_latest_data( latest_huge_volume_data = self.db_huge_volume_data.query_latest_data(
symbol, bar symbol, bar, window_size
) )
if latest_huge_volume_data is None or len(latest_huge_volume_data) == 0: 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=True)
@ -132,26 +132,27 @@ class HugeVolumeMain:
earliest_timestamp = latest_huge_volume_data["timestamp"] earliest_timestamp = latest_huge_volume_data["timestamp"]
seconds = self.get_seconds_by_bar(bar) seconds = self.get_seconds_by_bar(bar)
earliest_timestamp = earliest_timestamp - ( earliest_timestamp = earliest_timestamp - (
(self.window_size - 1) * seconds * 1000 (window_size - 1) * seconds * 1000
) )
earliest_date_time = timestamp_to_datetime(earliest_timestamp) earliest_date_time = timestamp_to_datetime(earliest_timestamp)
data = self.detect_volume_spike( data = self.detect_volume_spike(
symbol=symbol, symbol=symbol,
bar=bar, bar=bar,
window_size=window_size,
start=earliest_date_time, start=earliest_date_time,
only_output_huge_volume=True, only_output_huge_volume=True,
is_update=True, is_update=True,
) )
logging.info( logging.info(
f"更新巨量交易数据: {symbol} {bar} from {earliest_date_time}" f"更新巨量交易数据: {symbol} {bar} 窗口大小: {window_size}{earliest_date_time}{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
) )
if data is not None and len(data) > 0: if data is not None and len(data) > 0:
logging.info(f"此次更新巨量交易数据: {len(data)}") logging.info(f"此次更新巨量交易数据: {len(data)}")
else: else:
logging.info(f"此次更新巨量交易数据为空") logging.info(f"此次更新巨量交易数据为空")
except Exception as e: except Exception as e:
logging.error(f"更新巨量交易数据失败: {symbol} {bar} {e}") logging.error(f"更新巨量交易数据失败: {symbol} {bar} 窗口大小: {window_size}{earliest_date_time}{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: {e}")
def get_seconds_by_bar(self, bar: str): def get_seconds_by_bar(self, bar: str):
""" """
@ -201,6 +202,7 @@ class HugeVolumeMain:
self, self,
symbol: str, symbol: str,
bar: str, bar: str,
window_size: int = 50,
start: str = None, start: str = None,
end: str = None, end: str = None,
periods: list = [3, 5], periods: list = [3, 5],
@ -212,14 +214,14 @@ class HugeVolumeMain:
) )
if end is None: if end is None:
end = datetime.now().strftime("%Y-%m-%d %H:%M:%S") end = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
logging.info(f"开始计算巨量出现后之后3或5个周期上涨或下跌的比例: {symbol} {bar} {start} {end}") logging.info(f"开始计算巨量出现后之后3或5个周期上涨或下跌的比例: {symbol} {bar} 窗口大小: {window_size}{start} {end}")
huge_volume_data = ( huge_volume_data = (
self.db_huge_volume_data.query_huge_volume_data_by_symbol_bar( self.db_huge_volume_data.query_huge_volume_data_by_symbol_bar_window_size(
symbol, bar, start, end symbol, bar, window_size, start, end
) )
) )
if huge_volume_data is None or len(huge_volume_data) == 0: if huge_volume_data is None or len(huge_volume_data) == 0:
logging.warning(f"获取巨量交易数据为空: {symbol} {bar} {start} {end}") logging.warning(f"获取巨量交易数据为空: {symbol} {bar} 窗口大小: {window_size}{start} {end}")
return None return None
else: else:
if isinstance(huge_volume_data, list): if isinstance(huge_volume_data, list):
@ -230,7 +232,7 @@ class HugeVolumeMain:
symbol, bar, start, end symbol, bar, start, end
) )
if market_data is None or len(market_data) == 0: if market_data is None or len(market_data) == 0:
logging.warning(f"获取行情数据为空: {symbol} {bar} {start} {end}") logging.warning(f"获取行情数据为空: {symbol} {bar} 窗口大小: {window_size}{start} {end}")
return None return None
else: else:
if isinstance(market_data, list): if isinstance(market_data, list):
@ -282,10 +284,12 @@ class HugeVolumeMain:
) )
# 根据timestamp排序 # 根据timestamp排序
data = data.sort_values(by="timestamp", ascending=True) data = data.sort_values(by="timestamp", ascending=True)
data["window_size"] = window_size
data = data[ data = data[
[ [
"symbol", "symbol",
"bar", "bar",
"window_size",
"timestamp", "timestamp",
"date_time", "date_time",
"open", "open",
@ -303,12 +307,13 @@ class HugeVolumeMain:
data = data.dropna() data = data.dropna()
data = data.reset_index(drop=True) data = data.reset_index(drop=True)
data, result_data = self.huge_volume.next_periods_rise_or_fall( data, result_data = self.huge_volume.next_periods_rise_or_fall(
data=data, periods=periods, output_excel=output_excel data=data, window_size=window_size, periods=periods, output_excel=output_excel
) )
return data, result_data return data, result_data
def batch_next_periods_rise_or_fall( def batch_next_periods_rise_or_fall(
self, self,
window_size: int = 50,
start: str = None, start: str = None,
end: str = None, end: str = None,
periods: list = [3, 5], periods: list = [3, 5],
@ -322,10 +327,10 @@ class HugeVolumeMain:
end = datetime.now().strftime("%Y-%m-%d %H:%M:%S") end = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
data_list = [] data_list = []
result_data_list = [] result_data_list = []
for symbol in self.monitor_main.symbols: for symbol in self.market_data_main.symbols:
for bar in self.monitor_main.bars: for bar in self.market_data_main.bars:
data, result_data = self.next_periods_rise_or_fall( data, result_data = self.next_periods_rise_or_fall(
symbol, bar, start, end, periods, output_excel symbol, bar, window_size, start, end, periods, output_excel
) )
data_list.append(data) data_list.append(data)
result_data_list.append(result_data) result_data_list.append(result_data)
@ -351,13 +356,27 @@ class HugeVolumeMain:
return data, result_data return data, result_data
def batch_initial_detect_volume_spike(threshold: float = 2.0):
window_sizes = WINDOW_SIZE.get("window_sizes", None)
if window_sizes is None or not isinstance(window_sizes, list) or len(window_sizes) == 0:
window_sizes = [50, 80, 100, 120]
huge_volume_main = HugeVolumeMain(threshold)
for window_size in window_sizes:
huge_volume_main.batch_initial_detect_volume_spike(
window_size=window_size,
start="2025-05-01 00:00:00",
)
def batch_update_volume_spike(threshold: float = 2.0):
window_sizes = WINDOW_SIZE.get("window_sizes", None)
if window_sizes is None or not isinstance(window_sizes, list) or len(window_sizes) == 0:
window_sizes = [50, 80, 100, 120]
huge_volume_main = HugeVolumeMain(threshold)
for window_size in window_sizes:
huge_volume_main.batch_update_volume_spike(window_size=window_size)
if __name__ == "__main__": if __name__ == "__main__":
huge_volume_main = HugeVolumeMain() # batch_initial_detect_volume_spike(threshold=2.0)
# statistics_main.batch_initial_detect_volume_spike( batch_update_volume_spike(threshold=2.0)
# start="2025-05-01 00:00:00",
# )
huge_volume_main.batch_update_volume_spike()
# huge_volume_main.batch_next_periods_rise_or_fall(
# periods=[3, 5],
# output_excel=True,
# )

View File

@ -1,10 +1,14 @@
select * from crypto_market_data select * from crypto_market_data
WHERE symbol='XCH-USDT' and bar='5m' #and date_time > '2025-07-26' WHERE symbol='DOGE-USDT-SWAP' and bar='1D' #and date_time > '2025-07-01'
order by timestamp desc; order by timestamp;
delete FROM crypto_market_data where symbol != 'XCH-USDT';
select * from crypto_trade_data select * from crypto_trade_data
where date_time > '2025-05-03' where symbol='BTC-USDT'
order by ts, tradeId asc; order by ts desc;
select count(1) from crypto_trade_data;
select * from crypto_huge_volume select * from crypto_huge_volume
WHERE symbol='XCH-USDT' and bar='5m' and date_time > '2025-07-26' WHERE symbol='XCH-USDT' and bar='5m' and date_time > '2025-07-26'
@ -18,3 +22,5 @@ select * from crypto_huge_volume
WHERE symbol='BTC-USDT' and bar='5m' WHERE symbol='BTC-USDT' and bar='5m'
order by timestamp desc order by timestamp desc
limit 10; limit 10;
SHOW VARIABLES LIKE 'max_connections';

View File

@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS crypto_huge_volume (
id BIGINT AUTO_INCREMENT PRIMARY KEY, id BIGINT AUTO_INCREMENT PRIMARY KEY,
symbol VARCHAR(50) NOT NULL COMMENT '交易对', symbol VARCHAR(50) NOT NULL COMMENT '交易对',
bar VARCHAR(20) NOT NULL COMMENT 'K线周期', bar VARCHAR(20) NOT NULL COMMENT 'K线周期',
window_size INT NOT NULL COMMENT '窗口大小, 50, 80, 100, 120',
timestamp BIGINT NOT NULL COMMENT '时间戳', timestamp BIGINT NOT NULL COMMENT '时间戳',
date_time VARCHAR(50) NOT NULL COMMENT '日期时间', date_time VARCHAR(50) NOT NULL COMMENT '日期时间',
open DECIMAL(20,5) NOT NULL COMMENT '开盘价', open DECIMAL(20,5) NOT NULL COMMENT '开盘价',
@ -28,8 +29,8 @@ CREATE TABLE IF NOT EXISTS crypto_huge_volume (
price_10_low TINYINT NOT NULL DEFAULT 0 COMMENT '价格是否达到10%分位数低点(0:否,1:是)', price_10_low TINYINT NOT NULL DEFAULT 0 COMMENT '价格是否达到10%分位数低点(0:否,1:是)',
volume_90_10_price_spike TINYINT NOT NULL DEFAULT 0 COMMENT '是否出现90/10量价尖峰(0:否,1:是)', volume_90_10_price_spike TINYINT NOT NULL DEFAULT 0 COMMENT '是否出现90/10量价尖峰(0:否,1:是)',
create_time VARCHAR(50) NOT NULL COMMENT '创建时间', create_time VARCHAR(50) NOT NULL COMMENT '创建时间',
UNIQUE KEY uniq_symbol_bar_timestamp (symbol, bar, timestamp), UNIQUE KEY uniq_symbol_bar_window_size_timestamp (symbol, bar, window_size, timestamp),
INDEX idx_symbol_bar (symbol, bar), INDEX idx_symbol_bar_window_size (symbol, bar, window_size),
INDEX idx_timestamp (timestamp), INDEX idx_timestamp (timestamp),
INDEX idx_huge_volume (huge_volume), INDEX idx_huge_volume (huge_volume),
INDEX idx_volume_80_20_price_spike (volume_80_20_price_spike), INDEX idx_volume_80_20_price_spike (volume_80_20_price_spike),