From 5a45449b87a49e00bfe196c9f1052e10591e2da2 Mon Sep 17 00:00:00 2001 From: blade <8019068@qq.com> Date: Fri, 1 Aug 2025 16:18:36 +0800 Subject: [PATCH] support draw chart --- .../huge_volume_chart.cpython-312.pyc | Bin 0 -> 23518 bytes core/biz/huge_volume_chart.py | 459 ++++++++++++++++++ huge_volume_main.py | 102 +++- requirements.txt | 1 + sql/query/sql_playground.sql | 8 +- 5 files changed, 541 insertions(+), 29 deletions(-) create mode 100644 core/biz/__pycache__/huge_volume_chart.cpython-312.pyc create mode 100644 core/biz/huge_volume_chart.py diff --git a/core/biz/__pycache__/huge_volume_chart.cpython-312.pyc b/core/biz/__pycache__/huge_volume_chart.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..038ea1f9db9fa14cbdd34792366e33de3dfb7e6d GIT binary patch literal 23518 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 b!V#qlRf*Fwjwt`5Zb(PAkslE_!-D=F5 0: logging.info(f"此次初始化巨量交易数据: {len(data)}条") @@ -66,16 +74,22 @@ class HugeVolumeMain: ) if end is None: end = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - logging.info(f"开始处理巨量交易数据: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") + logging.info( + f"开始处理巨量交易数据: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}" + ) data = self.db_market_data.query_market_data_by_symbol_bar( symbol, bar, start, end ) if data is None: - logging.warning(f"获取行情数据失败: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") + logging.warning( + f"获取行情数据失败: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}" + ) return None else: if len(data) == 0: - logging.warning(f"获取行情数据为空: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") + logging.warning( + f"获取行情数据为空: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}" + ) return None else: if isinstance(data, list): @@ -93,10 +107,8 @@ class HugeVolumeMain: if data is not None: if is_update: for index, row in data.iterrows(): - exist_huge_volume_data = ( - self.db_huge_volume_data.query_data_by_symbol_bar_window_size_timestamp( - symbol, bar, window_size, row["timestamp"] - ) + exist_huge_volume_data = self.db_huge_volume_data.query_data_by_symbol_bar_window_size_timestamp( + symbol, bar, window_size, row["timestamp"] ) if exist_huge_volume_data is not None: # remove the exist_huge_volume_data from data @@ -152,7 +164,9 @@ class HugeVolumeMain: else: logging.info(f"此次更新巨量交易数据为空") except Exception as e: - logging.error(f"更新巨量交易数据失败: {symbol} {bar} 窗口大小: {window_size} 从 {earliest_date_time} 到 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: {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): """ @@ -214,26 +228,29 @@ class HugeVolumeMain: if end is None: end = datetime.now().strftime("%Y-%m-%d %H:%M:%S") periods_text = ", ".join([str(period) for period in periods]) - logging.info(f"开始计算巨量出现后,之后{periods_text}个周期,上涨或下跌的比例: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") + 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 volume_statistics_data is None or len(volume_statistics_data) == 0: - logging.warning(f"获取巨量交易数据为空: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}") + logging.warning( + f"获取巨量交易数据为空: {symbol} {bar} 窗口大小: {window_size} 从 {start} 到 {end}" + ) return None else: 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 ( - volume_statistics_data is not None - and len(volume_statistics_data) > 0 - ): + if volume_statistics_data is not None and len(volume_statistics_data) > 0: # 根据timestamp排序 - volume_statistics_data = volume_statistics_data.sort_values(by="timestamp", ascending=True) + 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[ [ @@ -279,7 +296,11 @@ class HugeVolumeMain: 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: + 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: @@ -296,14 +317,14 @@ class HugeVolumeMain: 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" - ) + file_name = f"next_periods_rise_or_fall_{current_date}.xlsx" try: with pd.ExcelWriter( os.path.join(self.output_folder, file_name) ) as writer: - total_huge_volume_data.to_excel(writer, sheet_name="details", index=False) + 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 ) @@ -311,10 +332,29 @@ class HugeVolumeMain: logging.error(f"导出Excel文件失败: {e}") return total_huge_volume_data, total_result_data + def plot_huge_volume_data( + self, + data_file_path: str, + sheet_name: str = "next_periods_statistics", + output_folder: str = "./output/huge_volume_statistics/", + ): + os.makedirs(output_folder, exist_ok=True) + huge_volume_data = pd.read_excel(data_file_path, sheet_name=sheet_name) + huge_volume_chart = HugeVolumeChart(huge_volume_data) + include_heatmap = True + include_line = False + huge_volume_chart.plot_entrance( + include_heatmap=include_heatmap, include_line=include_line + ) + 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: + 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: @@ -326,7 +366,11 @@ def batch_initial_detect_volume_spike(threshold: float = 2.0): 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: + 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: @@ -337,4 +381,12 @@ if __name__ == "__main__": # batch_initial_detect_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) + # huge_volume_main.batch_next_periods_rise_or_fall(output_excel=True) + data_file_path = "./output/huge_volume_statistics/next_periods_rise_or_fall_stat_20250731200304.xlsx" + sheet_name = "next_periods_statistics" + output_folder = "./output/huge_volume_statistics/" + huge_volume_main.plot_huge_volume_data( + data_file_path=data_file_path, + sheet_name=sheet_name, + output_folder=output_folder, + ) diff --git a/requirements.txt b/requirements.txt index 9b4c77c..4e58085 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ requests>=2.25.0 sqlalchemy >= 2.0.41 pymysql >= 1.1.1 wechatpy >= 1.8.18 +seaborn >= 0.13.2 \ No newline at end of file diff --git a/sql/query/sql_playground.sql b/sql/query/sql_playground.sql index 558dfac..f57bcc7 100644 --- a/sql/query/sql_playground.sql +++ b/sql/query/sql_playground.sql @@ -1,18 +1,18 @@ select * from crypto_market_data -WHERE symbol='XCH-USDT-SWAP' and bar='5m' #and date_time > '2025-07-01' +WHERE symbol='XCH-USDT' and bar='5m' #and date_time > '2025-07-01' order by timestamp desc; delete FROM crypto_market_data where symbol != 'XCH-USDT'; select * from crypto_trade_data -where symbol='BTC-USDT' +where symbol='XCH-USDT' order by ts desc; select count(1) from crypto_trade_data; select * from crypto_huge_volume -WHERE symbol='XCH-USDT' and bar='5m' and date_time > '2025-07-26' -order by timestamp desc; +WHERE symbol='XCH-USDT' and bar='5m' and window_size = 80 and date_time > '2025-07-26' +order by timestamp; select * from crypto_huge_volume WHERE symbol='XCH-USDT-SWAP' and bar='5m' and date_time > '2025-07-26'